smtp config
This commit is contained in:
parent
fa60b1d6db
commit
9b31c32aba
@ -4,9 +4,11 @@ require_once __DIR__ . "/../db/config.php";
|
|||||||
require_permission("settings_view");
|
require_permission("settings_view");
|
||||||
require_once __DIR__ . '/../db/config.php';
|
require_once __DIR__ . '/../db/config.php';
|
||||||
require_once __DIR__ . '/../includes/WablasService.php';
|
require_once __DIR__ . '/../includes/WablasService.php';
|
||||||
|
require_once __DIR__ . '/../mail/MailService.php';
|
||||||
|
|
||||||
$pdo = db();
|
$pdo = db();
|
||||||
$wablasTestResult = null;
|
$wablasTestResult = null;
|
||||||
|
$smtpTestResult = null;
|
||||||
$message = '';
|
$message = '';
|
||||||
|
|
||||||
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
@ -61,6 +63,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SMTP
|
||||||
|
if ($provider === 'smtp') {
|
||||||
|
$keys = ['host', 'port', 'secure', 'username', 'password', 'from_email', 'from_name'];
|
||||||
|
foreach ($keys as $k) {
|
||||||
|
$val = $_POST[$k] ?? '';
|
||||||
|
$stmt = $pdo->prepare("INSERT INTO integration_settings (provider, setting_key, setting_value) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE setting_value = VALUES(setting_value)");
|
||||||
|
$stmt->execute(['smtp', $k, $val]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($action === 'save') {
|
||||||
|
header("Location: integrations.php?msg=saved");
|
||||||
|
exit;
|
||||||
|
} elseif ($action === 'test') {
|
||||||
|
$testEmail = $_POST['test_email'] ?? '';
|
||||||
|
if (!empty($testEmail)) {
|
||||||
|
// We need to use the new values immediately for testing
|
||||||
|
// MailService usually loads from config, which we will update next
|
||||||
|
// For now, let's just use the provided values if we can or wait until config is updated.
|
||||||
|
// Actually, let's update config first then test.
|
||||||
|
$smtpTestResult = MailService::sendMail($testEmail, "SMTP Test Message", "Your SMTP configuration is working correctly!", "Your SMTP configuration is working correctly!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch current settings
|
// Fetch current settings
|
||||||
@ -86,6 +112,15 @@ $wablasSecKey = getSetting($allSettings, 'wablas', 'secret_key');
|
|||||||
$wablasTemplate = getSetting($allSettings, 'wablas', 'order_template');
|
$wablasTemplate = getSetting($allSettings, 'wablas', 'order_template');
|
||||||
$wablasEnabled = getSetting($allSettings, 'wablas', 'is_enabled');
|
$wablasEnabled = getSetting($allSettings, 'wablas', 'is_enabled');
|
||||||
|
|
||||||
|
// SMTP Settings
|
||||||
|
$smtpHost = getSetting($allSettings, 'smtp', 'host');
|
||||||
|
$smtpPort = getSetting($allSettings, 'smtp', 'port') ?: '587';
|
||||||
|
$smtpSecure = getSetting($allSettings, 'smtp', 'secure') ?: 'tls';
|
||||||
|
$smtpUser = getSetting($allSettings, 'smtp', 'username');
|
||||||
|
$smtpPass = getSetting($allSettings, 'smtp', 'password');
|
||||||
|
$smtpFromEmail = getSetting($allSettings, 'smtp', 'from_email');
|
||||||
|
$smtpFromName = getSetting($allSettings, 'smtp', 'from_name');
|
||||||
|
|
||||||
// Default template if empty
|
// Default template if empty
|
||||||
if (empty($wablasTemplate)) {
|
if (empty($wablasTemplate)) {
|
||||||
$wablasTemplate = "Dear *{customer_name}*,
|
$wablasTemplate = "Dear *{customer_name}*,
|
||||||
@ -127,7 +162,76 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($smtpTestResult): ?>
|
||||||
|
<div class="alert alert-<?= $smtpTestResult['success'] ? 'success' : 'danger' ?> alert-dismissible fade show" role="alert">
|
||||||
|
<strong>SMTP Test Result:</strong> <?= $smtpTestResult['success'] ? 'Success! Test email sent.' : 'Error: ' . htmlspecialchars($smtpTestResult['error']) ?>
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Close"></button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
<!-- SMTP Settings -->
|
||||||
|
<div class="col-md-12 mb-4">
|
||||||
|
<div class="card shadow">
|
||||||
|
<div class="card-header py-3 d-flex flex-row align-items-center justify-content-between">
|
||||||
|
<h6 class="m-0 fw-bold text-primary">SMTP Configuration</h6>
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<form method="POST">
|
||||||
|
<input type="hidden" name="provider" value="smtp">
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">SMTP Host</label>
|
||||||
|
<input type="text" class="form-control" name="host" placeholder="smtp.gmail.com" value="<?= htmlspecialchars($smtpHost) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<label class="form-label">SMTP Port</label>
|
||||||
|
<input type="number" class="form-control" name="port" value="<?= htmlspecialchars($smtpPort) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-3 mb-3">
|
||||||
|
<label class="form-label">Encryption</label>
|
||||||
|
<select class="form-select" name="secure" <?= !has_permission('settings_add') ? 'disabled' : '' ?>>
|
||||||
|
<option value="tls" <?= $smtpSecure == 'tls' ? 'selected' : '' ?>>TLS</option>
|
||||||
|
<option value="ssl" <?= $smtpSecure == 'ssl' ? 'selected' : '' ?>>SSL</option>
|
||||||
|
<option value="none" <?= $smtpSecure == 'none' ? 'selected' : '' ?>>None</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">SMTP Username</label>
|
||||||
|
<input type="text" class="form-control" name="username" value="<?= htmlspecialchars($smtpUser) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">SMTP Password</label>
|
||||||
|
<input type="password" class="form-control" name="password" value="<?= htmlspecialchars($smtpPass) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">From Email</label>
|
||||||
|
<input type="email" class="form-control" name="from_email" placeholder="noreply@yourdomain.com" value="<?= htmlspecialchars($smtpFromEmail) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||||
|
</div>
|
||||||
|
<div class="col-md-6 mb-3">
|
||||||
|
<label class="form-label">From Name</label>
|
||||||
|
<input type="text" class="form-control" name="from_name" placeholder="Business Name" value="<?= htmlspecialchars($smtpFromName) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if (has_permission('settings_add')): ?>
|
||||||
|
<div class="mb-3 border-top pt-3">
|
||||||
|
<label class="form-label text-muted small">Test SMTP Connection</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<input type="email" class="form-control" name="test_email" placeholder="receiver@example.com" value="<?= htmlspecialchars($_POST['test_email'] ?? '') ?>">
|
||||||
|
<button type="submit" name="action" value="test" class="btn btn-info text-white">Save & Send Test Email</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-flex justify-content-end">
|
||||||
|
<button type="submit" name="action" value="save" class="btn btn-primary">Save SMTP Settings</button>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Thawani -->
|
<!-- Thawani -->
|
||||||
<div class="col-md-6 mb-4">
|
<div class="col-md-6 mb-4">
|
||||||
<div class="card shadow h-100">
|
<div class="card shadow h-100">
|
||||||
@ -174,7 +278,6 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
<form method="POST" id="wablas_form">
|
<form method="POST" id="wablas_form">
|
||||||
<input type="hidden" name="provider" value="wablas">
|
<input type="hidden" name="provider" value="wablas">
|
||||||
|
|
||||||
<!-- Also keep a hidden input to send '0' if checkbox is unchecked (handled in PHP POST block too) -->
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Domain</label>
|
<label class="form-label">Domain</label>
|
||||||
<input type="text" class="form-control" name="domain" placeholder="https://..." value="<?= htmlspecialchars($wablasDom) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
<input type="text" class="form-control" name="domain" placeholder="https://..." value="<?= htmlspecialchars($wablasDom) ?>" <?= !has_permission('settings_add') ? 'readonly' : '' ?>>
|
||||||
@ -206,7 +309,6 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
<input type="text" class="form-control" name="test_phone" placeholder="e.g. 62812345678" value="<?= htmlspecialchars($_POST['test_phone'] ?? '') ?>">
|
<input type="text" class="form-control" name="test_phone" placeholder="e.g. 62812345678" value="<?= htmlspecialchars($_POST['test_phone'] ?? '') ?>">
|
||||||
<button type="submit" name="action" value="test" class="btn btn-info text-white">Test & Send Message</button>
|
<button type="submit" name="action" value="test" class="btn btn-info text-white">Test & Send Message</button>
|
||||||
</div>
|
</div>
|
||||||
<small class="text-muted">Enter a phone number to send a real test message.</small>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="d-flex justify-content-end">
|
<div class="d-flex justify-content-end">
|
||||||
@ -220,4 +322,4 @@ require_once __DIR__ . '/includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php require_once __DIR__ . '/includes/header.php'; ?>
|
<?php require_once __DIR__ . '/includes/footer.php'; ?>
|
||||||
|
|||||||
@ -56,7 +56,7 @@ if (isset($_GET['delete'])) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fetch Outlets for Filter
|
// Fetch Outlets for Filter
|
||||||
$outlets = $pdo->query("SELECT id, name FROM outlets ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
|
$outlets = $pdo->query("SELECT id, name FROM outlets WHERE is_deleted = 0 ORDER BY name")->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
// Build Query with Filters
|
// Build Query with Filters
|
||||||
$params = [];
|
$params = [];
|
||||||
@ -78,11 +78,16 @@ if (!empty($_GET['end_date'])) {
|
|||||||
$params[':end_date'] = $_GET['end_date'];
|
$params[':end_date'] = $_GET['end_date'];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter: Search (Order No)
|
// Filter: Search (Order No / Customer Name)
|
||||||
if (!empty($_GET['search'])) {
|
if (!empty($_GET['search'])) {
|
||||||
if (is_numeric($_GET['search'])) {
|
$searchTerm = $_GET['search'];
|
||||||
$where[] = "o.id = :search";
|
if (is_numeric($searchTerm)) {
|
||||||
$params[':search'] = $_GET['search'];
|
$where[] = "(o.id = :search_exact OR o.customer_name LIKE :search_like)";
|
||||||
|
$params[':search_exact'] = $searchTerm;
|
||||||
|
$params[':search_like'] = "%$searchTerm%";
|
||||||
|
} else {
|
||||||
|
$where[] = "o.customer_name LIKE :search";
|
||||||
|
$params[':search'] = "%$searchTerm%";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,7 +251,7 @@ include 'includes/header.php';
|
|||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<label class="form-label small fw-bold text-muted">Search</label>
|
<label class="form-label small fw-bold text-muted">Search</label>
|
||||||
<input type="text" name="search" class="form-control" placeholder="Order No (ID)" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
|
<input type="text" name="search" class="form-control" placeholder="Order No / Customer" value="<?= htmlspecialchars($_GET['search'] ?? '') ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="col-md-3">
|
<div class="col-md-3">
|
||||||
<div class="d-flex gap-2">
|
<div class="d-flex gap-2">
|
||||||
|
|||||||
@ -16,7 +16,7 @@ ALTER TABLE `tables` ADD COLUMN IF NOT EXISTS `is_deleted` TINYINT(1) DEFAULT 0;
|
|||||||
-- But usually, if they have 'name', they need to move it to 'table_number'.
|
-- But usually, if they have 'name', they need to move it to 'table_number'.
|
||||||
|
|
||||||
-- If name exists, this will work. If not, it will fail, which is okay for this specific fix.
|
-- If name exists, this will work. If not, it will fail, which is okay for this specific fix.
|
||||||
UPDATE `tables` SET `table_number` = `name` WHERE `table_number` IS NULL OR `table_number` = '';
|
-- UPDATE `tables` SET `table_number` = `name` WHERE `table_number` IS NULL OR `table_number` = '';
|
||||||
|
|
||||||
-- Fix areas foreign key if it was wrong in their initial setup
|
-- Fix areas foreign key if it was wrong in their initial setup
|
||||||
ALTER TABLE `areas` DROP FOREIGN KEY IF EXISTS `areas_ibfk_1`;
|
ALTER TABLE `areas` DROP FOREIGN KEY IF EXISTS `areas_ibfk_1`;
|
||||||
|
|||||||
2
db/migrations/042_add_reset_token_to_users.sql
Normal file
2
db/migrations/042_add_reset_token_to_users.sql
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ALTER TABLE users ADD COLUMN reset_token VARCHAR(255) DEFAULT NULL;
|
||||||
|
ALTER TABLE users ADD COLUMN reset_token_expiry DATETIME DEFAULT NULL;
|
||||||
122
forgot_password.php
Normal file
122
forgot_password.php
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
require_once __DIR__ . '/includes/functions.php';
|
||||||
|
require_once __DIR__ . '/mail/MailService.php';
|
||||||
|
|
||||||
|
init_session();
|
||||||
|
|
||||||
|
$baseUrl = get_base_url();
|
||||||
|
$settings = get_company_settings();
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$success = '';
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
|
$email = $_POST['email'] ?? '';
|
||||||
|
|
||||||
|
if (empty($email)) {
|
||||||
|
$error = 'Please enter your email address.';
|
||||||
|
} else {
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT id, username, full_name FROM users WHERE email = ? AND is_deleted = 0 LIMIT 1");
|
||||||
|
$stmt->execute([$email]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if ($user) {
|
||||||
|
$token = bin2hex(random_bytes(32));
|
||||||
|
$expiry = date('Y-m-d H:i:s', strtotime('+1 hour'));
|
||||||
|
|
||||||
|
$stmt = $pdo->prepare("UPDATE users SET reset_token = ?, reset_token_expiry = ? WHERE id = ?");
|
||||||
|
$stmt->execute([$token, $expiry, $user['id']]);
|
||||||
|
|
||||||
|
$resetLink = $baseUrl . "reset_password.php?token=" . $token;
|
||||||
|
|
||||||
|
$subject = "Password Reset Request - " . $settings['company_name'];
|
||||||
|
$messageHtml = "
|
||||||
|
<h2>Password Reset Request</h2>
|
||||||
|
<p>Hello " . htmlspecialchars($user['full_name'] ?: $user['username']) . ",</p>
|
||||||
|
<p>We received a request to reset your password. Click the button below to set a new password:</p>
|
||||||
|
<p><a href='$resetLink' style='background: #0d6efd; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px; display: inline-block;'>Reset Password</a></p>
|
||||||
|
<p>If you did not request this, please ignore this email.</p>
|
||||||
|
<p>This link will expire in 1 hour.</p>
|
||||||
|
";
|
||||||
|
$messageTxt = "Hello, click here to reset your password: $resetLink. This link will expire in 1 hour.";
|
||||||
|
|
||||||
|
$res = MailService::sendMail($email, $subject, $messageHtml, $messageTxt);
|
||||||
|
|
||||||
|
if (!empty($res['success'])) {
|
||||||
|
$success = 'Password reset instructions have been sent to your email.';
|
||||||
|
} else {
|
||||||
|
$error = 'Failed to send reset email. Please contact administrator.';
|
||||||
|
// error_log($res['error']);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// We show success anyway for security reasons to prevent email enumeration
|
||||||
|
$success = 'If that email exists in our system, you will receive reset instructions shortly.';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Forgot Password - <?= htmlspecialchars($settings['company_name']) ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||||
|
<link rel="stylesheet" href="<?= $baseUrl ?>assets/css/custom.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.login-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
box-shadow: 0 1rem 3rem rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="card login-card border-0">
|
||||||
|
<div class="card-body p-5">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h3 class="fw-bold">Forgot Password</h3>
|
||||||
|
<p class="text-muted small">Enter your email address to receive a reset link</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger small py-2"><?= $error ?></div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if ($success): ?>
|
||||||
|
<div class="alert alert-success small py-2"><?= $success ?></div>
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<a href="login.php" class="small text-decoration-none"><i class="bi bi-arrow-left"></i> Back to Login</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="POST">
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label small fw-medium">Email Address</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text bg-light border-0"><i class="bi bi-envelope"></i></span>
|
||||||
|
<input type="email" name="email" class="form-control bg-light border-0" required autofocus>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold mb-3">Send Reset Link</button>
|
||||||
|
<div class="text-center">
|
||||||
|
<a href="login.php" class="small text-decoration-none">Back to Login</a>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@ -82,6 +82,10 @@ $settings = get_company_settings();
|
|||||||
<div class="alert alert-danger small py-2"><?= $error ?></div>
|
<div class="alert alert-danger small py-2"><?= $error ?></div>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<?php if (isset($_GET['reset']) && $_GET['reset'] == 'success'): ?>
|
||||||
|
<div class="alert alert-success small py-2">Password reset successfully. You can now login.</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
<form method="POST">
|
<form method="POST">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label small fw-medium">Username</label>
|
<label class="form-label small fw-medium">Username</label>
|
||||||
@ -90,13 +94,16 @@ $settings = get_company_settings();
|
|||||||
<input type="text" name="username" class="form-control bg-light border-0" required autofocus>
|
<input type="text" name="username" class="form-control bg-light border-0" required autofocus>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-4">
|
<div class="mb-3">
|
||||||
<label class="form-label small fw-medium">Password</label>
|
<label class="form-label small fw-medium">Password</label>
|
||||||
<div class="input-group">
|
<div class="input-group">
|
||||||
<span class="input-group-text bg-light border-0"><i class="bi bi-lock"></i></span>
|
<span class="input-group-text bg-light border-0"><i class="bi bi-lock"></i></span>
|
||||||
<input type="password" name="password" class="form-control bg-light border-0" required>
|
<input type="password" name="password" class="form-control bg-light border-0" required>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="mb-4 d-flex justify-content-end">
|
||||||
|
<a href="forgot_password.php" class="small text-decoration-none">Forgot Password?</a>
|
||||||
|
</div>
|
||||||
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold">Login</button>
|
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold">Login</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
<?php
|
<?php
|
||||||
// Mail configuration sourced from environment variables.
|
// Mail configuration sourced from environment variables or Database.
|
||||||
// No secrets are stored here; the file just maps env -> config array for MailService.
|
|
||||||
|
|
||||||
function env_val(string $key, $default = null) {
|
function env_val(string $key, $default = null) {
|
||||||
$v = getenv($key);
|
$v = getenv($key);
|
||||||
@ -8,8 +7,6 @@ function env_val(string $key, $default = null) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback: if critical vars are missing from process env, try to parse executor/.env
|
// Fallback: if critical vars are missing from process env, try to parse executor/.env
|
||||||
// This helps in web/Apache contexts where .env is not exported.
|
|
||||||
// Supports simple KEY=VALUE lines; ignores quotes and comments.
|
|
||||||
function load_dotenv_if_needed(array $keys): void {
|
function load_dotenv_if_needed(array $keys): void {
|
||||||
$missing = array_filter($keys, fn($k) => getenv($k) === false || getenv($k) === '');
|
$missing = array_filter($keys, fn($k) => getenv($k) === false || getenv($k) === '');
|
||||||
if (empty($missing)) return;
|
if (empty($missing)) return;
|
||||||
@ -22,9 +19,7 @@ function load_dotenv_if_needed(array $keys): void {
|
|||||||
if ($line[0] === '#' || trim($line) === '') continue;
|
if ($line[0] === '#' || trim($line) === '') continue;
|
||||||
if (!str_contains($line, '=')) continue;
|
if (!str_contains($line, '=')) continue;
|
||||||
[$k, $v] = array_map('trim', explode('=', $line, 2));
|
[$k, $v] = array_map('trim', explode('=', $line, 2));
|
||||||
// Strip potential surrounding quotes
|
$v = trim($v, "'\" ");
|
||||||
$v = trim($v, "\"' ");
|
|
||||||
// Do not override existing env
|
|
||||||
if ($k !== '' && (getenv($k) === false || getenv($k) === '')) {
|
if ($k !== '' && (getenv($k) === false || getenv($k) === '')) {
|
||||||
putenv("{$k}={$v}");
|
putenv("{$k}={$v}");
|
||||||
}
|
}
|
||||||
@ -35,42 +30,43 @@ 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','MAIL_TO',
|
'MAIL_FROM','MAIL_FROM_NAME','MAIL_REPLY_TO','MAIL_TO'
|
||||||
'DKIM_DOMAIN','DKIM_SELECTOR','DKIM_PRIVATE_KEY_PATH'
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$transport = env_val('MAIL_TRANSPORT', 'smtp');
|
// Try to load from Database
|
||||||
$smtp_host = env_val('SMTP_HOST');
|
$dbSettings = [];
|
||||||
$smtp_port = (int) env_val('SMTP_PORT', 587);
|
try {
|
||||||
$smtp_secure = env_val('SMTP_SECURE', 'tls'); // tls | ssl | null
|
require_once __DIR__ . '/../db/config.php';
|
||||||
$smtp_user = env_val('SMTP_USER');
|
$pdo = db();
|
||||||
$smtp_pass = env_val('SMTP_PASS');
|
$stmt = $pdo->prepare("SELECT setting_key, setting_value FROM integration_settings WHERE provider = 'smtp'");
|
||||||
|
$stmt->execute();
|
||||||
|
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$dbSettings[$row['setting_key']] = $row['setting_value'];
|
||||||
|
}
|
||||||
|
} catch (Exception $e) {
|
||||||
|
// Database might not be ready or table might not exist yet
|
||||||
|
}
|
||||||
|
|
||||||
$from_email = env_val('MAIL_FROM', 'no-reply@localhost');
|
$transport = $dbSettings['transport'] ?? env_val('MAIL_TRANSPORT', 'smtp');
|
||||||
$from_name = env_val('MAIL_FROM_NAME', 'App');
|
$smtp_host = $dbSettings['host'] ?? env_val('SMTP_HOST');
|
||||||
|
$smtp_port = (int) ($dbSettings['port'] ?? env_val('SMTP_PORT', 587));
|
||||||
|
$smtp_secure = $dbSettings['secure'] ?? env_val('SMTP_SECURE', 'tls');
|
||||||
|
$smtp_user = $dbSettings['username'] ?? env_val('SMTP_USER');
|
||||||
|
$smtp_pass = $dbSettings['password'] ?? env_val('SMTP_PASS');
|
||||||
|
|
||||||
|
$from_email = $dbSettings['from_email'] ?? env_val('MAIL_FROM', 'no-reply@localhost');
|
||||||
|
$from_name = $dbSettings['from_name'] ?? env_val('MAIL_FROM_NAME', 'App');
|
||||||
$reply_to = env_val('MAIL_REPLY_TO');
|
$reply_to = env_val('MAIL_REPLY_TO');
|
||||||
|
|
||||||
$dkim_domain = env_val('DKIM_DOMAIN');
|
|
||||||
$dkim_selector = env_val('DKIM_SELECTOR');
|
|
||||||
$dkim_private_key_path = env_val('DKIM_PRIVATE_KEY_PATH');
|
|
||||||
|
|
||||||
return [
|
return [
|
||||||
'transport' => $transport,
|
'transport' => $transport,
|
||||||
|
|
||||||
// SMTP
|
|
||||||
'smtp_host' => $smtp_host,
|
'smtp_host' => $smtp_host,
|
||||||
'smtp_port' => $smtp_port,
|
'smtp_port' => $smtp_port,
|
||||||
'smtp_secure' => $smtp_secure,
|
'smtp_secure' => $smtp_secure,
|
||||||
'smtp_user' => $smtp_user,
|
'smtp_user' => $smtp_user,
|
||||||
'smtp_pass' => $smtp_pass,
|
'smtp_pass' => $smtp_pass,
|
||||||
|
|
||||||
// From / Reply-To
|
|
||||||
'from_email' => $from_email,
|
'from_email' => $from_email,
|
||||||
'from_name' => $from_name,
|
'from_name' => $from_name,
|
||||||
'reply_to' => $reply_to,
|
'reply_to' => $reply_to,
|
||||||
|
|
||||||
// DKIM (optional)
|
|
||||||
'dkim_domain' => $dkim_domain,
|
|
||||||
'dkim_selector' => $dkim_selector,
|
|
||||||
'dkim_private_key_path' => $dkim_private_key_path,
|
|
||||||
];
|
];
|
||||||
1058
qorder.php
1058
qorder.php
File diff suppressed because it is too large
Load Diff
110
reset_password.php
Normal file
110
reset_password.php
Normal file
@ -0,0 +1,110 @@
|
|||||||
|
<?php
|
||||||
|
require_once __DIR__ . '/db/config.php';
|
||||||
|
require_once __DIR__ . '/includes/functions.php';
|
||||||
|
|
||||||
|
init_session();
|
||||||
|
|
||||||
|
$baseUrl = get_base_url();
|
||||||
|
$settings = get_company_settings();
|
||||||
|
|
||||||
|
$error = '';
|
||||||
|
$token = $_GET['token'] ?? '';
|
||||||
|
$user = null;
|
||||||
|
|
||||||
|
if (empty($token)) {
|
||||||
|
header("Location: login.php");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = db();
|
||||||
|
$stmt = $pdo->prepare("SELECT id, username, reset_token_expiry FROM users WHERE reset_token = ? AND is_deleted = 0 LIMIT 1");
|
||||||
|
$stmt->execute([$token]);
|
||||||
|
$user = $stmt->fetch(PDO::FETCH_ASSOC);
|
||||||
|
|
||||||
|
if (!$user || strtotime($user['reset_token_expiry']) < time()) {
|
||||||
|
$error = "This password reset link is invalid or has expired.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($_SERVER['REQUEST_METHOD'] === 'POST' && !$error) {
|
||||||
|
$password = $_POST['password'] ?? '';
|
||||||
|
$confirmPassword = $_POST['confirm_password'] ?? '';
|
||||||
|
|
||||||
|
if (empty($password)) {
|
||||||
|
$error = "Please enter a new password.";
|
||||||
|
} elseif ($password !== $confirmPassword) {
|
||||||
|
$error = "Passwords do not match.";
|
||||||
|
} elseif (strlen($password) < 6) {
|
||||||
|
$error = "Password must be at least 6 characters long.";
|
||||||
|
} else {
|
||||||
|
$hashedPassword = password_hash($password, PASSWORD_DEFAULT);
|
||||||
|
$stmt = $pdo->prepare("UPDATE users SET password = ?, reset_token = NULL, reset_token_expiry = NULL WHERE id = ?");
|
||||||
|
$stmt->execute([$hashedPassword, $user['id']]);
|
||||||
|
|
||||||
|
header("Location: login.php?reset=success");
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
?>
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Reset Password - <?= htmlspecialchars($settings['company_name']) ?></title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||||
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css">
|
||||||
|
<link rel="stylesheet" href="<?= $baseUrl ?>assets/css/custom.css">
|
||||||
|
<style>
|
||||||
|
body {
|
||||||
|
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.login-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 400px;
|
||||||
|
border-radius: 1.5rem;
|
||||||
|
box-shadow: 0 1rem 3rem rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
|
||||||
|
<div class="card login-card border-0">
|
||||||
|
<div class="card-body p-5">
|
||||||
|
<div class="text-center mb-4">
|
||||||
|
<h3 class="fw-bold">Set New Password</h3>
|
||||||
|
<p class="text-muted small">Choose a secure password for your account</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<?php if ($error): ?>
|
||||||
|
<div class="alert alert-danger small py-2"><?= $error ?></div>
|
||||||
|
<div class="text-center mt-3">
|
||||||
|
<a href="forgot_password.php" class="small text-decoration-none">Request a new reset link</a>
|
||||||
|
</div>
|
||||||
|
<?php else: ?>
|
||||||
|
<form method="POST">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label class="form-label small fw-medium">New Password</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text bg-light border-0"><i class="bi bi-lock"></i></span>
|
||||||
|
<input type="password" name="password" class="form-control bg-light border-0" required autofocus>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="mb-4">
|
||||||
|
<label class="form-label small fw-medium">Confirm New Password</label>
|
||||||
|
<div class="input-group">
|
||||||
|
<span class="input-group-text bg-light border-0"><i class="bi bi-lock-fill"></i></span>
|
||||||
|
<input type="password" name="confirm_password" class="form-control bg-light border-0" required>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button type="submit" class="btn btn-primary w-100 py-2 fw-bold">Reset Password</button>
|
||||||
|
</form>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
x
Reference in New Issue
Block a user