chore(*.php): use pint for php formatting

This commit is contained in:
Cory Dransfeldt 2025-06-14 16:55:03 -07:00
parent bd1855a65e
commit 753f3433ce
No known key found for this signature in database
40 changed files with 2261 additions and 1900 deletions

View file

@ -1,214 +1,241 @@
<?php
require_once __DIR__ . '/../bootstrap.php';
require_once __DIR__.'/../bootstrap.php';
use App\Classes\BaseHandler;
use GuzzleHttp\Client;
class ContactHandler extends BaseHandler
{
protected string $postgrestUrl;
protected string $postgrestApiKey;
private string $forwardEmailApiKey;
private Client $httpClient;
protected string $postgrestUrl;
public function __construct(?Client $httpClient = null)
{
parent::__construct();
protected string $postgrestApiKey;
$this->httpClient = $httpClient ?? new Client();
$this->forwardEmailApiKey = $_ENV["FORWARDEMAIL_API_KEY"] ?? getenv("FORWARDEMAIL_API_KEY");
}
private string $forwardEmailApiKey;
public function handleRequest(): void
{
try {
$this->validateReferer();
$this->checkRateLimit();
$this->enforceHttps();
private Client $httpClient;
$contentType = $_SERVER["CONTENT_TYPE"] ?? "";
$formData = null;
public function __construct(?Client $httpClient = null)
{
parent::__construct();
if (strpos($contentType, "application/json") !== false) {
$rawBody = file_get_contents("php://input");
$formData = json_decode($rawBody, true);
if (!$formData || !isset($formData["data"])) throw new \Exception("Invalid JSON payload.");
$formData = $formData["data"];
} elseif (
strpos($contentType, "application/x-www-form-urlencoded") !== false
) {
$formData = $_POST;
} else {
$this->sendErrorResponse("Unsupported Content-Type. Use application/json or application/x-www-form-urlencoded.", 400);
}
if (!empty($formData["hp_name"])) $this->sendErrorResponse("Invalid submission.", 400);
$name = htmlspecialchars(
trim($formData["name"] ?? ""),
ENT_QUOTES,
"UTF-8"
);
$email = filter_var($formData["email"] ?? "", FILTER_VALIDATE_EMAIL);
$message = htmlspecialchars(
trim($formData["message"] ?? ""),
ENT_QUOTES,
"UTF-8"
);
if (empty($name)) $this->sendErrorResponse("Name is required.", 400);
if (!$email) $this->sendErrorResponse("Valid email is required.", 400);
if (empty($message)) $this->sendErrorResponse("Message is required.", 400);
if (strlen($name) > 100) $this->sendErrorResponse("Name is too long. Max 100 characters allowed.", 400);
if (strlen($message) > 1000) $this->sendErrorResponse("Message is too long. Max 1000 characters allowed.", 400);
if ($this->isBlockedDomain($email)) $this->sendErrorResponse("Submission from blocked domain.", 400);
$contactData = [
"name" => $name,
"email" => $email,
"message" => $message,
"replied" => false,
];
$this->saveToDatabase($contactData);
$this->sendNotificationEmail($contactData);
$this->sendRedirect("/contact/success");
} catch (\Exception $e) {
error_log("Error handling contact form submission: " . $e->getMessage());
$this->sendErrorResponse($e->getMessage(), 400);
$this->httpClient = $httpClient ?? new Client();
$this->forwardEmailApiKey = $_ENV['FORWARDEMAIL_API_KEY'] ?? getenv('FORWARDEMAIL_API_KEY');
}
}
private function validateReferer(): void
{
$referer = $_SERVER["HTTP_REFERER"] ?? "";
$allowedDomain = "coryd.dev";
public function handleRequest(): void
{
try {
$this->validateReferer();
$this->checkRateLimit();
$this->enforceHttps();
if (!str_contains($referer, $allowedDomain)) throw new \Exception("Invalid submission origin.");
}
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
$formData = null;
private function checkRateLimit(): void
{
$ipAddress = $_SERVER["REMOTE_ADDR"] ?? "unknown";
$cacheFile = sys_get_temp_dir() . "/rate_limit_" . md5($ipAddress);
$rateLimitDuration = 60;
$maxRequests = 5;
if (strpos($contentType, 'application/json') !== false) {
$rawBody = file_get_contents('php://input');
$formData = json_decode($rawBody, true);
if (file_exists($cacheFile)) {
$data = json_decode(file_get_contents($cacheFile), true);
if (! $formData || ! isset($formData['data'])) {
throw new \Exception('Invalid JSON payload.');
}
$formData = $formData['data'];
} elseif (
strpos($contentType, 'application/x-www-form-urlencoded') !== false
) {
$formData = $_POST;
} else {
$this->sendErrorResponse('Unsupported Content-Type. Use application/json or application/x-www-form-urlencoded.', 400);
}
if (! empty($formData['hp_name'])) {
$this->sendErrorResponse('Invalid submission.', 400);
}
$name = htmlspecialchars(
trim($formData['name'] ?? ''),
ENT_QUOTES,
'UTF-8'
);
$email = filter_var($formData['email'] ?? '', FILTER_VALIDATE_EMAIL);
$message = htmlspecialchars(
trim($formData['message'] ?? ''),
ENT_QUOTES,
'UTF-8'
);
if (empty($name)) {
$this->sendErrorResponse('Name is required.', 400);
}
if (! $email) {
$this->sendErrorResponse('Valid email is required.', 400);
}
if (empty($message)) {
$this->sendErrorResponse('Message is required.', 400);
}
if (strlen($name) > 100) {
$this->sendErrorResponse('Name is too long. Max 100 characters allowed.', 400);
}
if (strlen($message) > 1000) {
$this->sendErrorResponse('Message is too long. Max 1000 characters allowed.', 400);
}
if ($this->isBlockedDomain($email)) {
$this->sendErrorResponse('Submission from blocked domain.', 400);
}
$contactData = [
'name' => $name,
'email' => $email,
'message' => $message,
'replied' => false,
];
$this->saveToDatabase($contactData);
$this->sendNotificationEmail($contactData);
$this->sendRedirect('/contact/success');
} catch (\Exception $e) {
error_log('Error handling contact form submission: '.$e->getMessage());
$this->sendErrorResponse($e->getMessage(), 400);
}
}
private function validateReferer(): void
{
$referer = $_SERVER['HTTP_REFERER'] ?? '';
$allowedDomain = 'coryd.dev';
if (! str_contains($referer, $allowedDomain)) {
throw new \Exception('Invalid submission origin.');
}
}
private function checkRateLimit(): void
{
$ipAddress = $_SERVER['REMOTE_ADDR'] ?? 'unknown';
$cacheFile = sys_get_temp_dir().'/rate_limit_'.md5($ipAddress);
$rateLimitDuration = 60;
$maxRequests = 5;
if (file_exists($cacheFile)) {
$data = json_decode(file_get_contents($cacheFile), true);
if (time() < $data['timestamp'] + $rateLimitDuration && $data['count'] >= $maxRequests) {
header('Location: /429', true, 302);
exit();
}
$data['count']++;
} else {
$data = ['count' => 1, 'timestamp' => time()];
}
file_put_contents($cacheFile, json_encode($data));
}
private function enforceHttps(): void
{
if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] !== 'on') {
throw new \Exception('Secure connection required. Use HTTPS.');
}
}
private function isBlockedDomain(string $email): bool
{
$domain = substr(strrchr($email, '@'), 1);
if (! $domain) {
return false;
}
$response = $this->httpClient->get(
"{$this->postgrestUrl}/blocked_domains",
[
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => "Bearer {$this->postgrestApiKey}",
],
'query' => [
'domain_name' => "eq.{$domain}",
'limit' => 1,
],
]
);
$blockedDomains = json_decode($response->getBody(), true);
return ! empty($blockedDomains);
}
private function saveToDatabase(array $contactData): void
{
$response = $this->httpClient->post("{$this->postgrestUrl}/contacts", [
'headers' => [
'Content-Type' => 'application/json',
'Authorization' => "Bearer {$this->postgrestApiKey}",
],
'json' => $contactData,
]);
if ($response->getStatusCode() >= 400) {
$errorResponse = json_decode($response->getBody(), true);
throw new \Exception('PostgREST error: '.($errorResponse['message'] ?? 'Unknown error'));
}
}
private function sendNotificationEmail(array $contactData): void
{
$authHeader = 'Basic '.base64_encode("{$this->forwardEmailApiKey}:");
$emailSubject = 'Contact form submission';
$emailText = sprintf(
"Name: %s\nEmail: %s\nMessage: %s\n",
$contactData['name'],
$contactData['email'],
$contactData['message']
);
$response = $this->httpClient->post(
'https://api.forwardemail.net/v1/emails',
[
'headers' => [
'Content-Type' => 'application/x-www-form-urlencoded',
'Authorization' => $authHeader,
],
'form_params' => [
'from' => 'coryd.dev <hi@admin.coryd.dev>',
'to' => 'hi@coryd.dev',
'subject' => $emailSubject,
'text' => $emailText,
'replyTo' => $contactData['email'],
],
]
);
if ($response->getStatusCode() >= 400) {
throw new \Exception('Failed to send email notification.');
}
}
private function sendRedirect(string $path): void
{
$protocol = (! empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? 'https' : 'http';
$host = $_SERVER['HTTP_HOST'];
$redirectUrl = "{$protocol}://{$host}{$path}";
header("Location: $redirectUrl", true, 302);
if ($data["timestamp"] + $rateLimitDuration > time() && $data["count"] >= $maxRequests) {
header("Location: /429", true, 302);
exit();
}
$data["count"]++;
} else {
$data = ["count" => 1, "timestamp" => time()];
}
file_put_contents($cacheFile, json_encode($data));
}
private function enforceHttps(): void
{
if (empty($_SERVER["HTTPS"]) || $_SERVER["HTTPS"] !== "on") throw new \Exception("Secure connection required. Use HTTPS.");
}
private function isBlockedDomain(string $email): bool
{
$domain = substr(strrchr($email, "@"), 1);
if (!$domain) return false;
$response = $this->httpClient->get(
"{$this->postgrestUrl}/blocked_domains",
[
"headers" => [
"Content-Type" => "application/json",
"Authorization" => "Bearer {$this->postgrestApiKey}",
],
"query" => [
"domain_name" => "eq.{$domain}",
"limit" => 1,
],
]
);
$blockedDomains = json_decode($response->getBody(), true);
return !empty($blockedDomains);
}
private function saveToDatabase(array $contactData): void
{
$response = $this->httpClient->post("{$this->postgrestUrl}/contacts", [
"headers" => [
"Content-Type" => "application/json",
"Authorization" => "Bearer {$this->postgrestApiKey}",
],
"json" => $contactData,
]);
if ($response->getStatusCode() >= 400) {
$errorResponse = json_decode($response->getBody(), true);
throw new \Exception("PostgREST error: " . ($errorResponse["message"] ?? "Unknown error"));
}
}
private function sendNotificationEmail(array $contactData): void
{
$authHeader = "Basic " . base64_encode("{$this->forwardEmailApiKey}:");
$emailSubject = "Contact form submission";
$emailText = sprintf(
"Name: %s\nEmail: %s\nMessage: %s\n",
$contactData["name"],
$contactData["email"],
$contactData["message"]
);
$response = $this->httpClient->post(
"https://api.forwardemail.net/v1/emails",
[
"headers" => [
"Content-Type" => "application/x-www-form-urlencoded",
"Authorization" => $authHeader,
],
"form_params" => [
"from" => "coryd.dev <hi@admin.coryd.dev>",
"to" => "hi@coryd.dev",
"subject" => $emailSubject,
"text" => $emailText,
"replyTo" => $contactData["email"],
],
]
);
if ($response->getStatusCode() >= 400) throw new \Exception("Failed to send email notification.");
}
private function sendRedirect(string $path): void
{
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https" : "http";
$host = $_SERVER['HTTP_HOST'];
$redirectUrl = "{$protocol}://{$host}{$path}";
header("Location: $redirectUrl", true, 302);
exit();
}
}
try {
$handler = new ContactHandler();
$handler->handleRequest();
$handler = new ContactHandler();
$handler->handleRequest();
} catch (\Exception $e) {
error_log("Contact form error: " . $e->getMessage());
error_log('Contact form error: '.$e->getMessage());
echo json_encode(["error" => $e->getMessage()]);
echo json_encode(['error' => $e->getMessage()]);
http_response_code(500);
http_response_code(500);
}