This commit is contained in:
Flatlogic Bot 2025-09-10 17:18:29 +00:00
parent a54bec77fa
commit e62cdf478f
14 changed files with 953 additions and 135 deletions

27
admin.php Normal file
View File

@ -0,0 +1,27 @@
<?php
require_once 'templates/header.php';
// Protect this page
if (!isset($_SESSION['user_id'])) {
header("Location: login.php");
exit();
}
?>
<main class="container my-5">
<section class="py-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="feature-card p-4">
<h2 class="text-center mb-4">Admin Panel</h2>
<p class="text-center">Welcome, <?php echo htmlspecialchars($_SESSION['username']); ?>!</p>
<p class="text-center">This is your admin dashboard. You can add content and manage your site from here.</p>
</div>
</div>
</div>
</div>
</section>
</main>
<?php require_once 'templates/footer.php'; ?>

88
assets/css/custom.css Normal file
View File

@ -0,0 +1,88 @@
:root {
--primary-color: #0D6EFD;
--secondary-color: #6C757D;
--background-color: #F8F9FA;
--surface-color: #FFFFFF;
--primary-font: 'Poppins', sans-serif;
}
body {
font-family: var(--primary-font);
background: linear-gradient(270deg, #a1c4fd, #c2e9fb);
background-size: 400% 400%;
animation: moveGradient 15s ease infinite;
}
@keyframes moveGradient {
0%{background-position:0% 50%}
50%{background-position:100% 50%}
100%{background-position:0% 50%}
}
.hero-section {
background: linear-gradient(rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 0.5)), url('https://picsum.photos/seed/nimbus-hero/1600/900');
background-size: cover;
background-position: center;
color: white;
padding: 10rem 0;
}
.navbar.scrolled {
background-color: rgba(255, 255, 255, 0.9);
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
.form-control:focus {
box-shadow: 0 0 0 0.25rem rgba(13, 110, 253, 0.25);
border-color: #86b7fe;
}
.btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
}
.section-icon {
font-size: 2.5rem;
color: var(--primary-color);
}
.feature-card {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px); /* For Safari */
border-radius: 15px;
border: 1px solid rgba(255, 255, 255, 0.2);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.feature-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 12px rgba(0, 0, 0, 0.15);
}
.hero-section .btn-primary {
background-color: var(--primary-color);
border-color: var(--primary-color);
padding: 12px 30px;
font-weight: 600;
transition: background-color 0.3s ease;
}
.hero-section .btn-primary:hover {
background-color: #0b5ed7;
}
.hero-section .btn-outline-secondary {
color: white;
border-color: white;
padding: 12px 30px;
font-weight: 600;
transition: background-color 0.3s ease, color 0.3s ease;
}
.hero-section .btn-outline-secondary:hover {
background-color: white;
color: black;
}

39
assets/js/main.js Normal file
View File

@ -0,0 +1,39 @@
document.addEventListener('DOMContentLoaded', function () {
// Navbar shrink on scroll
const navbar = document.querySelector('.navbar');
if (navbar) {
window.addEventListener('scroll', () => {
if (window.scrollY > 50) {
navbar.classList.add('scrolled');
} else {
navbar.classList.remove('scrolled');
}
});
}
// Smooth scroll for anchor links
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
const target = document.querySelector(this.getAttribute('href'));
if (target) {
target.scrollIntoView({
behavior: 'smooth'
});
}
});
});
// Pricing toggle
const priceToggle = document.getElementById('priceToggle');
if (priceToggle) {
priceToggle.addEventListener('change', function() {
const isYearly = this.checked;
document.querySelectorAll('[data-monthly]').forEach(priceEl => {
const monthly = priceEl.getAttribute('data-monthly');
const yearly = priceEl.getAttribute('data-yearly');
priceEl.textContent = isYearly ? yearly : monthly;
});
});
}
});

29
db/migrate.php Normal file
View File

@ -0,0 +1,29 @@
<?php
// Simple migration runner
require_once __DIR__ . '/config.php';
try {
$pdo = db();
$migrationsDir = __DIR__ . '/migrations';
$files = glob($migrationsDir . '/*.sql');
if (empty($files)) {
echo "No migration files found.\n";
exit;
}
sort($files);
foreach ($files as $file) {
echo "Running migration: " . basename($file) . "...\n";
$sql = file_get_contents($file);
$pdo->exec($sql);
echo "Success.\n";
}
echo "\nAll migrations completed successfully.\n";
} catch (PDOException $e) {
die("Database error: " . $e->getMessage() . "\n");
}

View File

@ -0,0 +1,7 @@
CREATE TABLE IF NOT EXISTS `users` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`username` VARCHAR(50) NOT NULL UNIQUE,
`email` VARCHAR(100) NOT NULL UNIQUE,
`password_hash` VARCHAR(255) NOT NULL,
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

409
index.php
View File

@ -1,131 +1,288 @@
<?php <?php
declare(strict_types=1); session_start();
@ini_set('display_errors', '1'); $message_sent = null;
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION; if ($_SERVER["REQUEST_METHOD"] == "POST" && isset($_POST['form_submitted'])) {
$now = date('Y-m-d H:i:s'); require_once __DIR__ . '/mail/MailService.php';
$name = filter_var(trim($_POST['name']), FILTER_SANITIZE_STRING);
$email = filter_var(trim($_POST['email']), FILTER_SANITIZE_EMAIL);
$message_text = filter_var(trim($_POST['message']), FILTER_SANITIZE_STRING);
if (filter_var($email, FILTER_VALIDATE_EMAIL) && !empty($name) && !empty($message_text)) {
$to = getenv('MAIL_TO') ?: 'support@yourdomain.com'; // Fallback recipient
$subject = 'New Lead from Nimbus Landing Page';
$res = MailService::sendContactMessage($name, $email, $message_text, $to, $subject);
if (!empty($res['success'])) {
$_SESSION['message_sent'] = 'success';
} else {
$_SESSION['message_sent'] = 'error';
// Log the detailed error
$log_message = date('[Y-m-d H:i:s]') . ' Mailer Error: ' . ($res['error'] ?? 'Unknown error') . PHP_EOL;
file_put_contents('mail_log.txt', $log_message, FILE_APPEND);
}
} else {
$_SESSION['message_sent'] = 'validation_error';
}
// Redirect to avoid form resubmission
header("Location: " . $_SERVER['PHP_SELF'] . "#contact");
exit();
}
if (isset($_SESSION['message_sent'])) {
$message_sent = $_SESSION['message_sent'];
unset($_SESSION['message_sent']);
}
?> ?>
<!doctype html> <?php require_once 'templates/header.php'; ?>
<html lang="en">
<head> <!-- Hero -->
<meta charset="utf-8" /> <section class="hero-section text-center py-5">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <div class="container py-5">
<title>New Style</title> <h1 class="display-3 fw-bold">Your Cloud, Simplified</h1>
<link rel="preconnect" href="https://fonts.googleapis.com"> <p class="lead mb-5">Nimbus is the single pane of glass for your entire cloud infrastructure. Monitor, optimize, and automate with ease.</p>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin> <a href="#contact" class="btn btn-primary btn-lg">Start Your Free Trial</a>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;700&display=swap" rel="stylesheet"> <a href="#features" class="btn btn-outline-secondary btn-lg">Explore Features</a>
<style>
:root {
--bg-color-start: #6a11cb;
--bg-color-end: #2575fc;
--text-color: #ffffff;
--card-bg-color: rgba(255, 255, 255, 0.01);
--card-border-color: rgba(255, 255, 255, 0.1);
}
body {
margin: 0;
font-family: 'Inter', sans-serif;
background: linear-gradient(45deg, var(--bg-color-start), var(--bg-color-end));
color: var(--text-color);
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
overflow: hidden;
position: relative;
}
body::before {
content: '';
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><path d="M-10 10L110 10M10 -10L10 110" stroke-width="1" stroke="rgba(255,255,255,0.05)"/></svg>');
animation: bg-pan 20s linear infinite;
z-index: -1;
}
@keyframes bg-pan {
0% { background-position: 0% 0%; }
100% { background-position: 100% 100%; }
}
main {
padding: 2rem;
}
.card {
background: var(--card-bg-color);
border: 1px solid var(--card-border-color);
border-radius: 16px;
padding: 2rem;
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.1);
}
.loader {
margin: 1.25rem auto 1.25rem;
width: 48px;
height: 48px;
border: 3px solid rgba(255, 255, 255, 0.25);
border-top-color: #fff;
border-radius: 50%;
animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
.hint {
opacity: 0.9;
}
.sr-only {
position: absolute;
width: 1px; height: 1px;
padding: 0; margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap; border: 0;
}
h1 {
font-size: 3rem;
font-weight: 700;
margin: 0 0 1rem;
letter-spacing: -1px;
}
p {
margin: 0.5rem 0;
font-size: 1.1rem;
}
code {
background: rgba(0,0,0,0.2);
padding: 2px 6px;
border-radius: 4px;
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}
footer {
position: absolute;
bottom: 1rem;
font-size: 0.8rem;
opacity: 0.7;
}
</style>
</head>
<body>
<main>
<div class="card">
<h1>Analyzing your requirements and generating your website…</h1>
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes">
<span class="sr-only">Loading…</span>
</div> </div>
<p class="hint">Flatlogic AI is collecting your requirements and applying the first changes.</p> </section>
<p class="hint">This page will update automatically as the plan is implemented.</p>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p> <!-- Social Proof -->
<section class="py-5">
<div class="container text-center">
<h6 class="text-muted mb-4">TRUSTED BY LEADING COMPANIES WORLDWIDE</h6>
<div class="d-flex flex-wrap justify-content-center align-items-center">
<i class="bi bi-cloud-haze2 fs-1 text-muted mx-4"></i>
<i class="bi bi-bar-chart-line fs-1 text-muted mx-4"></i>
<i class="bi bi-shield-lock fs-1 text-muted mx-4"></i>
<i class="bi bi-hdd-stack fs-1 text-muted mx-4"></i>
<i class="bi bi-globe2 fs-1 text-muted mx-4"></i>
</div> </div>
</main> </div>
<footer> </section>
Page updated: <?= htmlspecialchars($now) ?> (UTC)
</footer> <!-- Features -->
</body> <section id="features" class="py-5">
</html> <div class="container">
<div class="text-center mb-5">
<h2>Core Features</h2>
<p class="lead">Everything you need in one platform.</p>
</div>
<div class="row">
<div class="col-md-4 mb-4">
<div class="feature-card text-center p-4 h-100">
<i class="bi bi-graph-up-arrow section-icon mb-3"></i>
<h4>Unified Monitoring</h4>
<p>Get a bird's-eye view of your entire infrastructure in one place.</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card text-center p-4 h-100">
<i class="bi bi-piggy-bank section-icon mb-3"></i>
<h4>Cost Optimization</h4>
<p>Identify and eliminate wasted cloud spend with smart recommendations.</p>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card text-center p-4 h-100">
<i class="bi bi-shield-check section-icon mb-3"></i>
<h4>Automated Security</h4>
<p>Proactively secure your cloud environment against threats.</p>
</div>
</div>
</div>
</div>
</section>
<!-- Product Screenshot -->
<section class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2>See Nimbus in Action</h2>
</div>
<img src="https://picsum.photos/seed/nimbus-dashboard/1200/800" class="img-fluid rounded shadow" alt="A mock screenshot of the Nimbus dashboard showing key performance metrics.">
</div>
</section>
<!-- Testimonials -->
<section id="testimonials" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2>What Our Customers Say</h2>
</div>
<div class="row">
<div class="col-md-4 mb-4">
<div class="feature-card p-4 h-100">
<p class="fst-italic">"Nimbus has been a game-changer for us. We've saved over 30% on our cloud bills."</p>
<div class="d-flex align-items-center mt-4">
<img src="https://picsum.photos/seed/avatar1/96/96" class="rounded-circle me-3" alt="Avatar of a satisfied Nimbus customer.">
<div>
<h6 class="m-0">Jane Doe</h6>
<small class="text-muted">CTO, TechCorp</small>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card p-4 h-100">
<p class="fst-italic">"The unified dashboard is incredibly intuitive. Our team was up and running in minutes."</p>
<div class="d-flex align-items-center mt-4">
<img src="https://picsum.photos/seed/avatar2/96/96" class="rounded-circle me-3" alt="Avatar of a satisfied Nimbus customer.">
<div>
<h6 class="m-0">John Smith</h6>
<small class="text-muted">CEO, Innovate LLC</small>
</div>
</div>
</div>
</div>
<div class="col-md-4 mb-4">
<div class="feature-card p-4 h-100">
<p class="fst-italic">"I can't imagine managing our infrastructure without Nimbus. It's essential."</p>
<div class="d-flex align-items-center mt-4">
<img src="https://picsum.photos/seed/avatar3/96/96" class="rounded-circle me-3" alt="Avatar of a satisfied Nimbus customer.">
<div>
<h6 class="m-0">Sam Wilson</h6>
<small class="text-muted">DevOps Lead, Future Solutions</small>
</div>
</div>
</div>
</div>
</div>
</div>
</section>
<!-- Pricing -->
<section id="pricing" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2>Simple, Transparent Pricing</h2>
<div class="form-check form-switch d-inline-block">
<input class="form-check-input" type="checkbox" id="priceToggle">
<label class="form-check-label" for="priceToggle">Yearly (Save 20%)</label>
</div>
</div>
<div class="row">
<!-- Pricing Tier 1 -->
<div class="col-lg-4 mb-4">
<div class="feature-card text-center p-4 h-100">
<h4>Starter</h4>
<h5 class="card-title my-4">
<span data-monthly="$49" data-yearly="$39">$49</span>/mo
</h5>
<ul class="list-unstyled my-4">
<li>10 Projects</li>
<li>Basic Analytics</li>
<li>Email Support</li>
</ul>
<button class="btn btn-outline-light">Choose Plan</button>
</div>
</div>
<!-- Pricing Tier 2 -->
<div class="col-lg-4 mb-4">
<div class="feature-card text-center p-4 h-100">
<h4>Pro</h4>
<h5 class="card-title my-4">
<span data-monthly="$99" data-yearly="$79">$99</span>/mo
</h5>
<ul class="list-unstyled my-4">
<li>Unlimited Projects</li>
<li>Advanced Analytics</li>
<li>Priority Support</li>
</ul>
<button class="btn btn-light">Choose Plan</button>
</div>
</div>
<!-- Pricing Tier 3 -->
<div class="col-lg-4 mb-4">
<div class="feature-card text-center p-4 h-100">
<h4>Enterprise</h4>
<h5 class="card-title my-4">Contact Us</h5>
<ul class="list-unstyled my-4">
<li>Dedicated Infrastructure</li>
<li>Custom Integrations</li>
<li>24/7 Support</li>
</ul>
<button class="btn btn-outline-light">Contact Sales</button>
</div>
</div>
</div>
</div>
</section>
<!-- FAQ -->
<section id="faq" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2>Frequently Asked Questions</h2>
</div>
<div class="accordion" id="faqAccordion">
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#faq1">What is Nimbus?</button></h2>
<div id="faq1" class="accordion-collapse collapse show" data-bs-parent="#faqAccordion">
<div class="accordion-body">Nimbus is a B2B SaaS platform that helps businesses manage their cloud infrastructure.</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq2">What cloud providers do you support?</button></h2>
<div id="faq2" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body">We support AWS, Google Cloud, and Azure.</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header"><button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#faq3">Can I cancel my subscription?</button></h2>
<div id="faq3" class="accordion-collapse collapse" data-bs-parent="#faqAccordion">
<div class="accordion-body">Yes, you can cancel your subscription at any time.</div>
</div>
</div>
</div>
</div>
</section>
<!-- Contact Form -->
<section id="contact" class="py-5">
<div class="container">
<div class="text-center mb-5">
<h2>Get in Touch</h2>
<p class="lead">We'd love to hear from you.</p>
</div>
<div class="row justify-content-center">
<div class="col-lg-6">
<?php if ($message_sent === 'success'): ?>
<div class="alert alert-success" role="alert">
<strong>Thank you!</strong> Your message has been sent successfully.
</div>
<?php elseif ($message_sent === 'error'): ?>
<div class="alert alert-danger" role="alert">
<strong>Error!</strong> There was a problem sending your message. Please try again later.
</div>
<?php elseif ($message_sent === 'validation_error'): ?>
<div class="alert alert-warning" role="alert">
<strong>Oops!</strong> Please fill out all fields correctly.
</div>
<?php endif; ?>
<form id="leadForm" method="POST" action="#contact" novalidate>
<input type="hidden" name="form_submitted" value="1">
<div class="mb-3">
<label for="name" class="form-label">Name</label>
<input type="text" class="form-control" id="name" name="name" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" required>
<div class="invalid-feedback">Please provide a valid email.</div>
</div>
<div class="mb-3">
<label for="message" class="form-label">Message</label>
<textarea class="form-control" id="message" name="message" rows="4" required></textarea>
</div>
<button type="submit" class="btn btn-primary">Send Message</button>
</form>
</div>
</div>
</div>
</section>
<?php require_once 'templates/footer.php'; ?>

86
login.php Normal file
View File

@ -0,0 +1,86 @@
<?php
require_once 'db/config.php';
require_once 'templates/header.php';
$errors = [];
$email = '';
if (isset($_SESSION['user_id'])) {
header("Location: admin.php");
exit();
}
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$email = trim($_POST['email']);
$password = $_POST['password'];
if (empty($email)) {
$errors[] = 'Email is required';
}
if (empty($password)) {
$errors[] = 'Password is required';
}
if (empty($errors)) {
$stmt = db()->prepare("SELECT id, username, password_hash FROM users WHERE email = ?");
$stmt->execute([$email]);
$user = $stmt->fetch();
if ($user && password_verify($password, $user['password_hash'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
header("Location: admin.php");
exit();
} else {
$errors[] = 'Invalid email or password';
}
}
}
?>
<main class="container my-5">
<section class="py-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-6">
<div class="feature-card p-4">
<h2 class="text-center mb-4">Login</h2>
<?php if (isset($_GET['registration']) && $_GET['registration'] === 'success'): ?>
<div class="alert alert-success">
Registration successful! You can now log in.
</div>
<?php endif; ?>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p class="m-0"><?php echo $error; ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form method="POST" action="login.php">
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" value="<?php echo htmlspecialchars($email); ?>" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Login</button>
</div>
</form>
<p class="text-center mt-3">
Don't have an account? <a href="register.php">Register here</a>.
</p>
</div>
</div>
</div>
</div>
</section>
</main>
<?php require_once 'templates/footer.php'; ?>

7
logout.php Normal file
View File

@ -0,0 +1,7 @@
<?php
session_start();
session_unset();
session_destroy();
header("Location: login.php");
exit();
?>

View File

@ -75,7 +75,9 @@ class MailService
foreach ($toList as $addr) { foreach ($toList as $addr) {
if (filter_var($addr, FILTER_VALIDATE_EMAIL)) { $mail->addAddress($addr); $added++; } if (filter_var($addr, FILTER_VALIDATE_EMAIL)) { $mail->addAddress($addr); $added++; }
} }
if ($added === 0) { $mail->addAddress($fromEmail, $fromName); } if ($added === 0) {
return [ 'success' => false, 'error' => 'No recipients defined (set MAIL_TO or pass $to)' ];
}
foreach ((array)($opts['cc'] ?? []) as $cc) { if (filter_var($cc, FILTER_VALIDATE_EMAIL)) $mail->addCC($cc); } foreach ((array)($opts['cc'] ?? []) as $cc) { if (filter_var($cc, FILTER_VALIDATE_EMAIL)) $mail->addCC($cc); }
foreach ((array)($opts['bcc'] ?? []) as $bcc){ if (filter_var($bcc, FILTER_VALIDATE_EMAIL)) $mail->addBCC($bcc); } foreach ((array)($opts['bcc'] ?? []) as $bcc){ if (filter_var($bcc, FILTER_VALIDATE_EMAIL)) $mail->addBCC($bcc); }
@ -156,8 +158,6 @@ class MailService
{ {
$mail = new PHPMailer\PHPMailer\PHPMailer(true); $mail = new PHPMailer\PHPMailer\PHPMailer(true);
try { try {
// Enable verbose debug output
$mail->SMTPDebug = 0;
$mail->isSMTP(); $mail->isSMTP();
$mail->Host = $cfg['smtp_host'] ?? ''; $mail->Host = $cfg['smtp_host'] ?? '';
$mail->Port = (int)($cfg['smtp_port'] ?? 587); $mail->Port = (int)($cfg['smtp_port'] ?? 587);
@ -181,7 +181,7 @@ class MailService
$mail->addReplyTo($cfg['reply_to']); $mail->addReplyTo($cfg['reply_to']);
} }
// Destination: prefer dynamic recipients ($to), fallback to MAIL_TO, then MAIL_FROM // Destination: prefer dynamic recipients ($to), fallback to MAIL_TO; no silent FROM fallback
$toList = []; $toList = [];
if ($to) { if ($to) {
if (is_string($to)) { if (is_string($to)) {
@ -201,8 +201,7 @@ class MailService
} }
} }
if ($added === 0) { if ($added === 0) {
// Final fallback: send to FROM address return [ 'success' => false, 'error' => 'No recipients defined (set MAIL_TO or pass $to)' ];
$mail->addAddress($fromEmail, $fromName);
} }
// DKIM (optional) // DKIM (optional)

View File

@ -35,7 +35,8 @@ function load_dotenv_if_needed(array $keys): void {
load_dotenv_if_needed([ load_dotenv_if_needed([
'MAIL_TRANSPORT','SMTP_HOST','SMTP_PORT','SMTP_SECURE','SMTP_USER','SMTP_PASS', 'MAIL_TRANSPORT','SMTP_HOST','SMTP_PORT','SMTP_SECURE','SMTP_USER','SMTP_PASS',
'MAIL_FROM','MAIL_FROM_NAME','MAIL_REPLY_TO','DKIM_DOMAIN','DKIM_SELECTOR','DKIM_PRIVATE_KEY_PATH' 'MAIL_FROM','MAIL_FROM_NAME','MAIL_REPLY_TO','MAIL_TO',
'DKIM_DOMAIN','DKIM_SELECTOR','DKIM_PRIVATE_KEY_PATH'
]); ]);
$transport = env_val('MAIL_TRANSPORT', 'smtp'); $transport = env_val('MAIL_TRANSPORT', 'smtp');

224
mail_log.txt Normal file
View File

@ -0,0 +1,224 @@
SERVER -> CLIENT: 220 email-smtp.amazonaws.com ESMTP SimpleEmailService-d-UJAP95IYE gQZPHeokgr2tbwK3Dh8W
CLIENT -> SERVER: EHLO vm-30855.dev.flatlogic.app
SERVER -> CLIENT: 250-email-smtp.amazonaws.com
250-8BITMIME
250-STARTTLS
250-AUTH PLAIN LOGIN
250 Ok
CLIENT -> SERVER: STARTTLS
SERVER -> CLIENT: 220 Ready to start TLS
CLIENT -> SERVER: EHLO vm-30855.dev.flatlogic.app
SERVER -> CLIENT: 250-email-smtp.amazonaws.com
250-8BITMIME
250-STARTTLS
250-AUTH PLAIN LOGIN
250 Ok
CLIENT -> SERVER: AUTH LOGIN
SERVER -> CLIENT: 334 VXNlcm5hbWU6
CLIENT -> SERVER: [credentials hidden]
SERVER -> CLIENT: 334 UGFzc3dvcmQ6
CLIENT -> SERVER: [credentials hidden]
SERVER -> CLIENT: 235 Authentication successful.
CLIENT -> SERVER: MAIL FROM:<app@flatlogic.app>
SERVER -> CLIENT: 250 Ok
CLIENT -> SERVER: RCPT TO:<support@yourdomain.com>
SERVER -> CLIENT: 250 Ok
CLIENT -> SERVER: DATA
SERVER -> CLIENT: 354 End data with <CR><LF>.<CR><LF>
CLIENT -> SERVER: Date: Wed, 10 Sep 2025 13:14:43 +0000
CLIENT -> SERVER: To: support@yourdomain.com
CLIENT -> SERVER: From: 10 <app@flatlogic.app>
CLIENT -> SERVER: Reply-To: Administrator <Blarior@gmail.com>, app@flatlogic.app
CLIENT -> SERVER: Subject: New Lead from Nimbus Landing Page
CLIENT -> SERVER: Message-ID: <RGGz281O0f5ZTbjFzdr3xDUogbl4E1VbKQwy0wGzlc@vm-30855.dev.flatlogic.app>
CLIENT -> SERVER: X-Mailer: PHPMailer 6.6.3 (https://github.com/PHPMailer/PHPMailer)
CLIENT -> SERVER: MIME-Version: 1.0
CLIENT -> SERVER: Content-Type: multipart/alternative;
CLIENT -> SERVER: boundary="b1_RGGz281O0f5ZTbjFzdr3xDUogbl4E1VbKQwy0wGzlc"
CLIENT -> SERVER: Content-Transfer-Encoding: 8bit
CLIENT -> SERVER:
CLIENT -> SERVER: This is a multi-part message in MIME format.
CLIENT -> SERVER:
CLIENT -> SERVER: --b1_RGGz281O0f5ZTbjFzdr3xDUogbl4E1VbKQwy0wGzlc
CLIENT -> SERVER: Content-Type: text/plain; charset=us-ascii
CLIENT -> SERVER:
CLIENT -> SERVER: Name: Administrator
CLIENT -> SERVER: Email: Blarior@gmail.com
CLIENT -> SERVER:
CLIENT -> SERVER: 123123
CLIENT -> SERVER:
CLIENT -> SERVER: --b1_RGGz281O0f5ZTbjFzdr3xDUogbl4E1VbKQwy0wGzlc
CLIENT -> SERVER: Content-Type: text/html; charset=us-ascii
CLIENT -> SERVER:
CLIENT -> SERVER: <p><strong>Name:</strong> Administrator</p><p><strong>Email:</strong> Blarior@gmail.com</p><hr>123123
CLIENT -> SERVER:
CLIENT -> SERVER:
CLIENT -> SERVER: --b1_RGGz281O0f5ZTbjFzdr3xDUogbl4E1VbKQwy0wGzlc--
CLIENT -> SERVER:
CLIENT -> SERVER: .
SERVER -> CLIENT: 250 Ok 0100019933c3a725-dee3f92c-89a8-43b8-b180-c9a6a6fc68db-000000
CLIENT -> SERVER: QUIT
SERVER -> CLIENT: 221 Bye
SERVER -> CLIENT: 220 email-smtp.amazonaws.com ESMTP SimpleEmailService-d-UJAP95IYE 6w6jayOyd9KI2UxK7oOd
CLIENT -> SERVER: EHLO vm-30855.dev.flatlogic.app
SERVER -> CLIENT: 250-email-smtp.amazonaws.com
250-8BITMIME
250-STARTTLS
250-AUTH PLAIN LOGIN
250 Ok
CLIENT -> SERVER: STARTTLS
SERVER -> CLIENT: 220 Ready to start TLS
CLIENT -> SERVER: EHLO vm-30855.dev.flatlogic.app
SERVER -> CLIENT: 250-email-smtp.amazonaws.com
250-8BITMIME
250-STARTTLS
250-AUTH PLAIN LOGIN
250 Ok
CLIENT -> SERVER: AUTH LOGIN
SERVER -> CLIENT: 334 VXNlcm5hbWU6
CLIENT -> SERVER: [credentials hidden]
SERVER -> CLIENT: 334 UGFzc3dvcmQ6
CLIENT -> SERVER: [credentials hidden]
SERVER -> CLIENT: 235 Authentication successful.
CLIENT -> SERVER: MAIL FROM:<app@flatlogic.app>
SERVER -> CLIENT: 250 Ok
CLIENT -> SERVER: RCPT TO:<support@yourdomain.com>
SERVER -> CLIENT: 250 Ok
CLIENT -> SERVER: DATA
SERVER -> CLIENT: 354 End data with <CR><LF>.<CR><LF>
CLIENT -> SERVER: Date: Wed, 10 Sep 2025 13:14:44 +0000
CLIENT -> SERVER: To: support@yourdomain.com
CLIENT -> SERVER: From: 10 <app@flatlogic.app>
CLIENT -> SERVER: Reply-To: Administrator <Blarior@gmail.com>, app@flatlogic.app
CLIENT -> SERVER: Subject: New Lead from Nimbus Landing Page
CLIENT -> SERVER: Message-ID: <cFHV6zdk7XiFa9a6KOQI4Pr2H941RS0y1Dmc5CeJU@vm-30855.dev.flatlogic.app>
CLIENT -> SERVER: X-Mailer: PHPMailer 6.6.3 (https://github.com/PHPMailer/PHPMailer)
CLIENT -> SERVER: MIME-Version: 1.0
CLIENT -> SERVER: Content-Type: multipart/alternative;
CLIENT -> SERVER: boundary="b1_cFHV6zdk7XiFa9a6KOQI4Pr2H941RS0y1Dmc5CeJU"
CLIENT -> SERVER: Content-Transfer-Encoding: 8bit
CLIENT -> SERVER:
CLIENT -> SERVER: This is a multi-part message in MIME format.
CLIENT -> SERVER:
CLIENT -> SERVER: --b1_cFHV6zdk7XiFa9a6KOQI4Pr2H941RS0y1Dmc5CeJU
CLIENT -> SERVER: Content-Type: text/plain; charset=us-ascii
CLIENT -> SERVER:
CLIENT -> SERVER: Name: Administrator
CLIENT -> SERVER: Email: Blarior@gmail.com
CLIENT -> SERVER:
CLIENT -> SERVER: 123123
CLIENT -> SERVER:
CLIENT -> SERVER: --b1_cFHV6zdk7XiFa9a6KOQI4Pr2H941RS0y1Dmc5CeJU
CLIENT -> SERVER: Content-Type: text/html; charset=us-ascii
CLIENT -> SERVER:
CLIENT -> SERVER: <p><strong>Name:</strong> Administrator</p><p><strong>Email:</strong> Blarior@gmail.com</p><hr>123123
CLIENT -> SERVER:
CLIENT -> SERVER:
CLIENT -> SERVER: --b1_cFHV6zdk7XiFa9a6KOQI4Pr2H941RS0y1Dmc5CeJU--
CLIENT -> SERVER:
CLIENT -> SERVER: .
SERVER -> CLIENT: 250 Ok 0100019933c3aa2d-e6f1f09d-c150-43d9-9c5a-d8f926c41ff2-000000
CLIENT -> SERVER: QUIT
SERVER -> CLIENT: 221 Bye

102
register.php Normal file
View File

@ -0,0 +1,102 @@
<?php
require_once 'db/config.php';
require_once 'templates/header.php';
$errors = [];
$username = '';
$email = '';
if ($_SERVER["REQUEST_METHOD"] == "POST") {
$username = trim($_POST['username']);
$email = trim($_POST['email']);
$password = $_POST['password'];
$password_confirm = $_POST['password_confirm'];
// Validation
if (empty($username)) {
$errors[] = 'Username is required';
}
if (empty($email)) {
$errors[] = 'Email is required';
} elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
$errors[] = 'Invalid email format';
}
if (empty($password)) {
$errors[] = 'Password is required';
}
if ($password !== $password_confirm) {
$errors[] = 'Passwords do not match';
}
// Check if user already exists
if (empty($errors)) {
$stmt = db()->prepare("SELECT id FROM users WHERE username = ? OR email = ?");
$stmt->execute([$username, $email]);
if ($stmt->fetch()) {
$errors[] = 'Username or email already taken';
}
}
// Insert user if no errors
if (empty($errors)) {
$password_hash = password_hash($password, PASSWORD_DEFAULT);
$stmt = db()->prepare("INSERT INTO users (username, email, password_hash) VALUES (?, ?, ?)");
if ($stmt->execute([$username, $email, $password_hash])) {
// Redirect to login page
header("Location: login.php?registration=success");
exit();
} else {
$errors[] = 'Something went wrong. Please try again.';
}
}
}
?>
<main class="container my-5">
<section class="py-5">
<div class="container">
<div class="row justify-content-center">
<div class="col-lg-6">
<div class="feature-card p-4">
<h2 class="text-center mb-4">Create an Account</h2>
<?php if (!empty($errors)): ?>
<div class="alert alert-danger">
<?php foreach ($errors as $error): ?>
<p class="m-0"><?php echo $error; ?></p>
<?php endforeach; ?>
</div>
<?php endif; ?>
<form method="POST" action="register.php">
<div class="mb-3">
<label for="username" class="form-label">Username</label>
<input type="text" class="form-control" id="username" name="username" value="<?php echo htmlspecialchars($username); ?>" required>
</div>
<div class="mb-3">
<label for="email" class="form-label">Email</label>
<input type="email" class="form-control" id="email" name="email" value="<?php echo htmlspecialchars($email); ?>" required>
</div>
<div class="mb-3">
<label for="password" class="form-label">Password</label>
<input type="password" class="form-control" id="password" name="password" required>
</div>
<div class="mb-3">
<label for="password_confirm" class="form-label">Confirm Password</label>
<input type="password" class="form-control" id="password_confirm" name="password_confirm" required>
</div>
<div class="d-grid">
<button type="submit" class="btn btn-primary">Register</button>
</div>
</form>
<p class="text-center mt-3">
Already have an account? <a href="login.php">Login here</a>.
</p>
</div>
</div>
</div>
</div>
</section>
</main>
<?php require_once 'templates/footer.php'; ?>

12
templates/footer.php Normal file
View File

@ -0,0 +1,12 @@
<!-- Footer -->
<footer class="py-4 bg-dark text-white text-center">
<div class="container">
<p class="m-0">&copy; <?php echo date("Y"); ?> Nimbus. All Rights Reserved.</p>
<p class="m-0"><a href="#" class="text-white">Privacy Policy</a> &middot; <a href="#" class="text-white">Terms of Service</a></p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body>
</html>

40
templates/header.php Normal file
View File

@ -0,0 +1,40 @@
<?php if (session_status() === PHP_SESSION_NONE) { session_start(); } ?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nimbus - Effortless Cloud Management</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
</head>
<body>
<!-- Navbar -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark sticky-top">
<div class="container">
<a class="navbar-brand fw-bold" href="index.php">Nimbus</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav ms-auto">
<li class="nav-item"><a class="nav-link" href="index.php#features">Features</a></li>
<li class="nav-item"><a class="nav-link" href="index.php#testimonials">Testimonials</a></li>
<li class="nav-item"><a class="nav-link" href="index.php#pricing">Pricing</a></li>
<li class="nav-item"><a class="nav-link" href="index.php#faq">FAQ</a></li>
<?php if (isset($_SESSION['user_id'])): ?>
<li class="nav-item"><a class="nav-link" href="admin.php">Admin</a></li>
<li class="nav-item"><a class="btn btn-primary ms-lg-2" href="logout.php">Logout</a></li>
<?php else: ?>
<li class="nav-item"><a class="nav-link" href="login.php">Login</a></li>
<li class="nav-item"><a class="btn btn-primary ms-lg-2" href="register.php">Register</a></li>
<?php endif; ?>
</ul>
</div>
</div>
</nav>