loreal-global-kickoff/submit.php
DJP 64b99c7a58 Add comprehensive application logging and activity tracking system
ApplicationLogger Class:
- Structured JSON logging to logs/application.log
- Tracks all actions: master_asset_submission, global_to_local_transform, box_upload, omg_api_lookup
- Captures: timestamp, user email/name, status, detailed data, IP address, user agent
- Methods: getRecentLogs(), getLogsByAction(), getLogsByUser(), getStatistics()

Logging Integration:
- submit.php: Logs all asset submissions (success/failure)
- process-csv.php: Logs all CSV transformations
- upload-to-box.php: Logs all Box uploads
- Tracks campaign numbers, business units, file counts, dates

Logs Viewer (logs-viewer.php):
- New tab "Activity Logs" in navigation
- Statistics dashboard (total actions, success rate, errors, unique users)
- Filterable table (by action type, user, date range)
- View detailed data for each log entry (expandable JSON)
- Export to CSV functionality
- Shows last 100 entries by default (configurable)

Email Service Enhancement:
- Added SMTP support (in addition to Mailgun API)
- Configured for smtp.mailgun.org with provided credentials
- Sends notifications to logged-in user email
- Proper SMTP protocol implementation with AUTH LOGIN

OMG API Configuration:
- Added 'enabled' flag (currently false due to 403 error)
- Added 'fallback_business_unit' for when OMG disabled
- Uses X-API-Key header format
- Comprehensive error logging
- When API permissions are fixed, set enabled=true

Security:
- logs/ directory excluded from git
- .gitkeep file to preserve directory structure
- Protected by .htaccess (log files already blocked)

Usage:
- All activity automatically logged
- View reports at /logs-viewer.php
- Export logs as CSV for analysis
- Filter by action type, user, or time period
- Monitor system health and usage patterns

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-18 09:39:21 -05:00

172 lines
5.4 KiB
PHP

<?php
/**
* Form Submission Handler
* Processes the form and sends data to webhook
*/
// Enable error reporting for debugging
error_reporting(E_ALL);
ini_set('display_errors', 1);
ini_set('log_errors', 1);
// Set JSON header
header('Content-Type: application/json');
try {
// Load dependencies
require_once __DIR__ . '/vendor/autoload.php';
require_once __DIR__ . '/AuthMiddleware.php';
require_once __DIR__ . '/BoxService.php';
require_once __DIR__ . '/ApplicationLogger.php';
// Check authentication
$auth = new AuthMiddleware();
$user = $auth->requireAuth();
// Initialize logger
$logger = new ApplicationLogger();
// Check if this is a POST request
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
http_response_code(405);
echo json_encode([
'success' => false,
'error' => 'Method not allowed'
]);
exit;
}
// Get request body
$input = json_decode(file_get_contents('php://input'), true);
// Validate required fields
$requiredFields = ['boxId', 'supplyDate', 'liveDate', 'endDate', 'boxData'];
foreach ($requiredFields as $field) {
if (!isset($input[$field]) || empty($input[$field])) {
http_response_code(400);
echo json_encode([
'success' => false,
'error' => "Missing required field: $field"
]);
exit;
}
}
// Load configuration
$config = require __DIR__ . '/config.php';
$webhookConfig = $config['webhook'];
// Prepare webhook payload
$payload = [
'userEmail' => $user['email'],
'userName' => $user['name'],
'boxId' => $input['boxId'],
'masterCampaignNumber' => $input['boxData']['masterCampaignNumber'] ?? 'N/A',
'masterCampaignId' => $input['boxData']['masterCampaignId'] ?? null,
'supplyDate' => $input['supplyDate'],
'liveDate' => $input['liveDate'],
'endDate' => $input['endDate'],
'boxContents' => [
'folderName' => $input['boxData']['folderName'] ?? '',
'totalItems' => $input['boxData']['contents']['total'] ?? 0,
'folders' => $input['boxData']['contents']['folders'] ?? [],
'files' => $input['boxData']['contents']['files'] ?? []
],
'submittedAt' => date('Y-m-d H:i:s')
];
// Send to webhook
// Make.com webhooks typically don't need API key headers
// The URL itself is the authentication
$ch = curl_init($webhookConfig['url']);
$jsonPayload = json_encode($payload);
error_log('Sending to webhook: ' . $webhookConfig['url']);
error_log('Payload: ' . $jsonPayload);
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_POSTFIELDS => $jsonPayload,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => $webhookConfig['timeout'],
CURLOPT_HTTPHEADER => [
'Content-Type: application/json',
'x-make-apikey: ' . $webhookConfig['api_key']
]
]);
$webhookResponse = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
$curlError = curl_error($ch);
curl_close($ch);
if ($curlError) {
throw new Exception('Webhook request failed: ' . $curlError);
}
// Parse webhook response
$webhookData = json_decode($webhookResponse, true);
// Check if webhook returned success
if ($httpCode >= 200 && $httpCode < 300) {
// Log successful submission
$logger->logAssetSubmission(
$user,
$input['boxId'],
$input['boxData']['masterCampaignNumber'] ?? 'N/A',
[
'supply' => $input['supplyDate'],
'live' => $input['liveDate'],
'end' => $input['endDate']
],
'success'
);
// Webhook accepted the data
echo json_encode([
'success' => true,
'message' => 'Submission successful',
'webhookResponse' => $webhookData,
'webhookStatus' => $httpCode
]);
} else {
// Log failed submission
$logger->logAssetSubmission(
$user,
$input['boxId'],
$input['boxData']['masterCampaignNumber'] ?? 'N/A',
[
'supply' => $input['supplyDate'],
'live' => $input['liveDate'],
'end' => $input['endDate']
],
'error',
'Webhook returned status ' . $httpCode
);
// Webhook rejected or failed - return 200 to client but indicate webhook failure
error_log('Webhook failed with status: ' . $httpCode);
error_log('Webhook response: ' . $webhookResponse);
echo json_encode([
'success' => false,
'error' => 'Webhook processing failed',
'webhookResponse' => $webhookData ?? $webhookResponse,
'webhookStatus' => $httpCode,
'message' => 'The webhook returned status ' . $httpCode . '. Please check the webhook configuration.'
]);
}
} catch (Exception $e) {
// Log error
error_log('Submission error: ' . $e->getMessage());
error_log('Stack trace: ' . $e->getTraceAsString());
http_response_code(500);
echo json_encode([
'success' => false,
'error' => 'Server error occurred during submission',
'debug' => $e->getMessage(),
'file' => $e->getFile(),
'line' => $e->getLine()
]);
}