Compare commits

..

9 Commits

Author SHA1 Message Date
Flatlogic Bot
b3465c50eb Auto commit: 2025-11-20T13:22:26.468Z 2025-11-20 13:22:26 +00:00
Flatlogic Bot
5c76810a10 preview is working 2025-11-20 12:54:27 +00:00
Flatlogic Bot
4480b6fac8 Auto commit: 2025-11-20T12:39:07.935Z 2025-11-20 12:39:07 +00:00
Flatlogic Bot
a04ddd5be8 Auto commit: 2025-11-20T12:34:31.391Z 2025-11-20 12:34:31 +00:00
Flatlogic Bot
7ceedc82a3 Auto commit: 2025-11-20T12:28:51.343Z 2025-11-20 12:28:51 +00:00
Flatlogic Bot
d78986f2c1 Auto commit: 2025-11-20T12:25:12.227Z 2025-11-20 12:25:12 +00:00
Flatlogic Bot
bb853fb0c8 Auto commit: 2025-11-20T12:20:29.851Z 2025-11-20 12:20:29 +00:00
Flatlogic Bot
28bed0ead6 Auto commit: 2025-11-20T12:17:08.078Z 2025-11-20 12:17:08 +00:00
Flatlogic Bot
2c882d18a4 Auto commit: 2025-11-20T12:08:26.355Z 2025-11-20 12:08:26 +00:00
37 changed files with 1092 additions and 145 deletions

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

@ -0,0 +1,68 @@
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
background-color: #F8F9FA;
}
.navbar-brand {
font-weight: 600;
}
.hero {
background: linear-gradient(135deg, #0D6EFD, #4D8BF2);
color: white;
padding: 4rem 0;
text-align: center;
}
.hero h1 {
font-weight: 700;
font-size: 3rem;
}
.translation-box {
background-color: #FFFFFF;
border-radius: 0.5rem;
padding: 2rem;
box-shadow: 0 4px 12px rgba(0,0,0,0.05);
}
.form-label {
font-weight: 500;
}
.btn-primary {
background-color: #0D6EFD;
border-color: #0D6EFD;
padding: 0.75rem 1.5rem;
font-weight: 500;
}
.btn-primary:hover {
background-color: #0b5ed7;
border-color: #0a58ca;
}
.custom-file-upload {
border: 2px dashed #0D6EFD;
border-radius: 0.375rem;
padding: 2rem;
text-align: center;
cursor: pointer;
background-color: #f8f9fa;
transition: background-color 0.2s;
}
.custom-file-upload:hover {
background-color: #e9ecef;
}
#file-upload-filename {
margin-top: 1rem;
font-style: italic;
color: #6c757d;
}
#processing-message {
display: none;
margin-top: 1.5rem;
}

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

@ -0,0 +1,221 @@
document.addEventListener('DOMContentLoaded', function () {
const fileUploadInput = document.getElementById('document-upload');
const fileUploadLabel = document.querySelector('.custom-file-upload');
const fileNameDisplay = document.getElementById('file-upload-filename');
const translateBtn = document.getElementById('translate-btn');
const processingMessage = document.getElementById('processing-message');
const previewContainer = document.getElementById('preview-container');
const imagePreview = document.getElementById('image-preview');
const startTranslationBtn = document.getElementById('start-translation-btn');
let uploadedFilePath = '';
if(fileUploadLabel) {
fileUploadLabel.addEventListener('click', () => {
if(fileUploadInput) {
fileUploadInput.click();
}
});
}
const debugPanel = document.getElementById('debug-panel');
const debugLog = document.getElementById('debug-log');
function logToDebug(message) {
if(debugPanel && debugLog) {
debugPanel.style.display = 'block';
debugLog.textContent += message + '\n';
}
console.log(message);
}
if(fileUploadInput) {
fileUploadInput.addEventListener('change', () => {
if (fileUploadInput.files.length > 0) {
const file = fileUploadInput.files[0];
logToDebug('File selected: ' + file.name);
const formData = new FormData();
formData.append('document', file);
processingMessage.style.display = 'block';
fileUploadLabel.style.display = 'none';
fetch('upload.php', {
method: 'POST',
body: formData
})
.then(response => {
logToDebug('Received response from server. Status: ' + response.status);
return response.json();
})
.then(data => {
processingMessage.style.display = 'none';
if (data.success && data.path) {
uploadedFilePath = data.path;
const file = fileUploadInput.files[0];
const fileExtension = uploadedFilePath.split('.').pop().toLowerCase();
const imageExtensions = ['jpg', 'jpeg', 'png', 'gif', 'tiff', 'bmp', 'webp'];
fileUploadLabel.style.display = 'none';
fileNameDisplay.textContent = file.name;
if (imageExtensions.includes(fileExtension)) {
imagePreview.onload = function() {
previewContainer.style.display = 'block';
imagePreview.style.display = 'block';
startTranslationBtn.style.display = 'block';
};
imagePreview.onerror = function() {
alert('Error loading image preview.');
fileUploadLabel.style.display = 'block';
};
imagePreview.src = uploadedFilePath + '?t=' + new Date().getTime();
} else if (fileExtension === 'pdf') {
const pdfFilename = document.getElementById('pdf-filename');
pdfFilename.textContent = file.name;
previewContainer.style.display = 'block';
document.getElementById('pdf-preview').style.display = 'block';
startTranslationBtn.style.display = 'block';
} else {
alert("Unsupported file type for preview.");
fileUploadLabel.style.display = 'block';
}
} else {
throw new Error(data.message || 'File upload failed.');
}
})
.catch(error => {
logToDebug('An error occurred in the upload process: ' + error.message);
processingMessage.style.display = 'none';
const responseMessageDiv = document.getElementById('response-message');
responseMessageDiv.innerHTML = `<div class="alert alert-danger" role="alert">An unexpected error occurred: ${error.message}</div>`;
responseMessageDiv.style.display = 'block';
});
}
});
}
if(startTranslationBtn) {
startTranslationBtn.addEventListener('click', function(e) {
e.preventDefault();
const formData = new FormData();
formData.append('file_path', uploadedFilePath);
formData.append('source-lang', document.getElementById('source-lang').value);
formData.append('target-lang', document.getElementById('target-lang').value);
const responseMessageDiv = document.getElementById('response-message');
if(processingMessage) {
processingMessage.style.display = 'block';
}
if(responseMessageDiv){
responseMessageDiv.innerHTML = '';
responseMessageDiv.style.display = 'none';
}
startTranslationBtn.disabled = true;
startTranslationBtn.innerHTML = '<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span> Translating...';
fetch('translate.php', {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
return response.json().then(err => {
throw new Error(err.message || response.statusText);
}).catch(() => {
throw new Error(response.statusText);
});
}
return response.json();
})
.then(data => {
processingMessage.style.display = 'none';
if (data.status === 'success') {
const translatedText = data.data.translatedText;
const outputContainer = document.getElementById('translation-output-container');
const outputElement = document.getElementById('translation-output');
if (outputElement && outputContainer) {
outputElement.textContent = translatedText;
outputContainer.style.display = 'block';
}
responseMessageDiv.innerHTML = `<div class="alert alert-success" role="alert">${data.message}</div>`;
responseMessageDiv.style.display = 'block';
} else {
responseMessageDiv.innerHTML = `<div class="alert alert-danger" role="alert"><strong>Error:</strong> ${data.message}</div>`;
responseMessageDiv.style.display = 'block';
}
})
.catch(error => {
processingMessage.style.display = 'none';
responseMessageDiv.innerHTML = `<div class="alert alert-danger" role="alert">An unexpected error occurred: ${error.message}</div>`;
responseMessageDiv.style.display = 'block';
console.error('Fetch Error:', error);
})
.finally(() => {
startTranslationBtn.disabled = false;
startTranslationBtn.innerHTML = '<i class="bi bi-stars"></i> Translate';
});
});
}
const copyBtn = document.getElementById('copy-btn');
const downloadBtn = document.getElementById('download-btn');
const translationOutput = document.getElementById('translation-output');
if (copyBtn) {
copyBtn.addEventListener('click', () => {
if (translationOutput && navigator.clipboard) {
navigator.clipboard.writeText(translationOutput.textContent).then(() => {
const originalText = copyBtn.innerHTML;
copyBtn.innerHTML = '<i class="bi bi-check-lg"></i> Copied!';
copyBtn.classList.add('btn-success');
copyBtn.classList.remove('btn-secondary');
setTimeout(() => {
copyBtn.innerHTML = originalText;
copyBtn.classList.remove('btn-success');
copyBtn.classList.add('btn-secondary');
}, 2000);
}).catch(err => {
console.error('Failed to copy text: ', err);
alert('Failed to copy text. Please try again.');
});
}
});
}
if (downloadBtn) {
downloadBtn.addEventListener('click', () => {
if (translationOutput && window.jspdf) {
try {
const { jsPDF } = window.jspdf;
const doc = new jsPDF();
doc.setFont('Helvetica');
doc.setFontSize(12);
const text = translationOutput.textContent;
const lines = doc.splitTextToSize(text, 180);
doc.text(lines, 15, 20);
doc.save('translation.pdf');
} catch (error) {
console.error('Failed to generate PDF:', error);
alert('Failed to generate PDF. An error occurred.');
}
} else if (!window.jspdf) {
alert('The PDF generation library is not loaded.');
}
});
}
});

Binary file not shown.

After

Width:  |  Height:  |  Size: 890 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 320 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 357 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 59 KiB

294
index.php
View File

@ -1,150 +1,154 @@
<?php <!DOCTYPE html>
declare(strict_types=1);
@ini_set('display_errors', '1');
@error_reporting(E_ALL);
@date_default_timezone_set('UTC');
$phpVersion = PHP_VERSION;
$now = date('Y-m-d H:i:s');
?>
<!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>New Style</title> <title>easyenglishtranslator</title>
<?php <meta name="description" content="Translate text on images and PDF scans easily. Upload your document, choose languages, and let AI provide you with an accurate translation.">
// Read project preview data from environment <meta name="keywords" content="document translator, pdf translator, image translator, ocr translation, ai translation, language tool, easy english translator, Built with Flatlogic Generator">
$projectDescription = $_SERVER['PROJECT_DESCRIPTION'] ?? ''; <meta property="og:title" content="easyenglishtranslator">
$projectImageUrl = $_SERVER['PROJECT_IMAGE_URL'] ?? ''; <meta property="og:description" content="Translate text on images and PDF scans easily.">
?> <meta property="og:image" content="">
<?php if ($projectDescription): ?> <meta name="twitter:card" content="summary_large_image">
<!-- Meta description --> <meta name="twitter:image" content="">
<meta name="description" content='<?= htmlspecialchars($projectDescription) ?>' />
<!-- Open Graph meta tags --> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
<meta property="og:description" content="<?= htmlspecialchars($projectDescription) ?>" /> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css">
<!-- Twitter meta tags --> <link rel="preconnect" href="https://fonts.googleapis.com">
<meta property="twitter:description" content="<?= htmlspecialchars($projectDescription) ?>" /> <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<?php endif; ?> <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<?php if ($projectImageUrl): ?> <link rel="stylesheet" href="assets/css/custom.css?v=<?php echo time(); ?>">
<!-- Open Graph image -->
<meta property="og:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<!-- Twitter image -->
<meta property="twitter:image" content="<?= htmlspecialchars($projectImageUrl) ?>" />
<?php endif; ?>
<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=Inter:wght@400;700&display=swap" rel="stylesheet">
<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> </head>
<body> <body>
<main>
<div class="card"> <nav class="navbar navbar-expand-lg navbar-light bg-white shadow-sm">
<h1>Analyzing your requirements and generating your website…</h1> <div class="container">
<div class="loader" role="status" aria-live="polite" aria-label="Applying initial changes"> <a class="navbar-brand" href="#">
<span class="sr-only">Loading…</span> <i class="bi bi-translate text-primary"></i>
</div> easyenglishtranslator
<p class="hint"><?= ($_SERVER['HTTP_HOST'] ?? '') === 'appwizzy.com' ? 'AppWizzy' : 'Flatlogic' ?> AI is collecting your requirements and applying the first changes.</p> </a>
<p class="hint">This page will update automatically as the plan is implemented.</p> </div>
<p>Runtime: PHP <code><?= htmlspecialchars($phpVersion) ?></code> — UTC <code><?= htmlspecialchars($now) ?></code></p> </nav>
</div>
</main> <header class="hero">
<footer> <div class="container">
Page updated: <?= htmlspecialchars($now) ?> (UTC) <h1>Document Translation Made Simple</h1>
</footer> <p class="lead">Upload a PDF or image, select your languages, and let our AI do the rest.</p>
</div>
</header>
<main class="container my-5">
<div class="row justify-content-center">
<div class="col-lg-8">
<div class="translation-box">
<form id="translation-form">
<div class="mb-4">
<label for="document-upload" class="form-label">1. Upload Your Document</label>
<div class="custom-file-upload">
<i class="bi bi-cloud-arrow-up-fill fs-1 text-primary"></i>
<p class="mt-2 mb-0">Click to browse or drag & drop your file here</p>
<p class="text-muted small">(PDF, JPG, PNG, TIFF)</p>
</div>
<input type="file" id="document-upload" class="d-none" accept=".pdf,.jpg,.jpeg,.png,.tiff">
<div id="file-upload-filename" class="text-center"></div>
</div>
<div class="row mb-4">
<div class="col-md-6">
<label for="source-lang" class="form-label">2. Source Language</label>
<select class="form-select" id="source-lang" required>
<option selected disabled value="">Choose...</option>
<option value="auto">Auto-detect</option>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="de">German</option>
<option value="it">Italian</option>
<option value="pt">Portuguese</option>
<option value="nl">Dutch</option>
<option value="ru">Russian</option>
<option value="ja">Japanese</option>
<option value="ko">Korean</option>
<option value="zh">Chinese (Simplified)</option>
<option value="ar">Arabic</option>
<option value="hi">Hindi</option>
</select>
</div>
<div class="col-md-6">
<label for="target-lang" class="form-label">3. Target Language</label>
<select class="form-select" id="target-lang" required>
<option selected disabled value="">Choose...</option>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
<option value="de">German</option>
<option value="it">Italian</option>
<option value="pt">Portuguese</option>
<option value="nl">Dutch</option>
<option value="ru">Russian</option>
<option value="ja">Japanese</option>
<option value="ko">Korean</option>
<option value="zh">Chinese (Simplified)</option>
<option value="ar">Arabic</option>
<option value="hi">Hindi</option>
</select>
</div>
</div>
<div id="preview-container" class="text-center" style="display: none;">
<h5 class="mt-4">Document Preview</h5>
<img id="image-preview" src="#" alt="Image Preview" class="img-fluid rounded border" style="max-height: 400px; display: none;"/>
<div id="pdf-preview" class="py-5" style="display: none;">
<i class="bi bi-file-earmark-pdf-fill text-danger" style="font-size: 4rem;"></i>
<p id="pdf-filename" class="mt-2 mb-0"></p>
</div>
<div class="d-grid mt-3">
<button type="button" id="start-translation-btn" class="btn btn-primary btn-lg" style="display: none;">
<i class="bi bi-stars"></i> Translate
</button>
</div>
</div>
</form>
<div id="response-message" class="mt-4"></div>
<div id="processing-message" class="alert alert-info mt-4" role="alert" style="display: none;">
<div class="d-flex align-items-center">
<div class="spinner-border text-primary me-3" role="status">
<span class="visually-hidden">Loading...</span>
</div>
<span>Processing your document... This may take a moment.</span>
</div>
</div>
<div id="translation-output-container" class="card mt-4" style="display: none;">
<div class="card-header fw-bold">
<i class="bi bi-check-circle-fill text-success me-2"></i>Translation Result
</div>
<div class="card-body">
<pre id="translation-output" class="mb-0" style="white-space: pre-wrap; font-family: inherit; font-size: 1rem;"></pre>
</div>
<div class="card-footer bg-light text-end">
<button id="copy-btn" class="btn btn-secondary btn-sm"><i class="bi bi-clipboard me-1"></i> Copy Text</button>
<button id="download-btn" class="btn btn-primary btn-sm"><i class="bi bi-download me-1"></i> Download PDF</button>
</div>
</div>
</div>
</div>
</div>
</main>
<footer class="text-center py-4 text-muted">
<div class="container">
<p>&copy; <?php echo date("Y"); ?> easyenglishtranslator. All Rights Reserved. Built with Flatlogic.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
<script src="assets/js/main.js?v=<?php echo time(); ?>"></script>
</body> </body>
</html> </html>

109
translate.php Normal file
View File

@ -0,0 +1,109 @@
<?php
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/error_log.txt');
// --- Debug Logging ---
$log_file = __DIR__ . '/translate_log.txt';
file_put_contents($log_file, "--- New Translation Request ---
", FILE_APPEND);
function write_log($message) {
global $log_file;
$timestamp = date('Y-m-d H:i:s');
file_put_contents($log_file, "[{$timestamp}] " . print_r($message, true) . "\n", FILE_APPEND);
}
// --- End Debug Logging ---
require_once __DIR__ . '/ai/LocalAIApi.php';
function json_response($status, $message, $data = null) {
write_log("Sending JSON response: Status: {$status}, Message: {$message}");
header('Content-Type: application/json');
echo json_encode([
'status' => $status,
'message' => $message,
'data' => $data
]);
exit;
}
write_log("Request method: " . $_SERVER['REQUEST_METHOD']);
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
json_response('error', 'Invalid request method.');
}
write_log("POST data: " . json_encode($_POST));
if (!isset($_POST['file_path']) || !isset($_POST['source-lang']) || !isset($_POST['target-lang'])) {
json_response('error', 'Missing required parameters.');
}
$file_path = $_POST['file_path'];
$source_lang = $_POST['source-lang'];
$target_lang = $_POST['target-lang'];
$url_path = parse_url($file_path, PHP_URL_PATH);
$full_path = __DIR__ . $url_path;
write_log("Full file path: {$full_path}");
if (!file_exists($full_path)) {
json_response('error', 'File not found.');
}
try {
// Step 1: Perform OCR on the image
write_log("Performing OCR on file: {$full_path}");
// Use escapeshellarg to prevent command injection vulnerabilities
$escaped_path = escapeshellarg($full_path);
$ocr_command = "tesseract {$escaped_path} stdout";
write_log("Executing OCR command: {$ocr_command}");
// Execute the command and capture the output
$extracted_text = shell_exec($ocr_command);
write_log("OCR raw output: " . $extracted_text);
if ($extracted_text === null || trim($extracted_text) === '') {
json_response('error', 'OCR failed or no text was found in the image.');
}
$extracted_text = trim($extracted_text);
write_log("Extracted text (trimmed): " . $extracted_text);
// Step 2: Translate the extracted text
write_log("Sending extracted text to AI for translation...");
$prompt = "Translate the following text from {$source_lang} to {$target_lang}:\n\n{$extracted_text}";
$payload = [
'input' => [
[
'role' => 'user',
'content' => $prompt
]
]
];
write_log("AI request payload: " . json_encode($payload, JSON_PRETTY_PRINT));
$resp = LocalAIApi::createResponse($payload);
write_log("Received response from AI API: " . json_encode($resp, JSON_PRETTY_PRINT));
if (!empty($resp['success'])) {
$translated_text = LocalAIApi::extractText($resp);
if ($translated_text === '') {
$decoded = LocalAIApi::decodeJsonFromResponse($resp);
$error_details = $decoded ? json_encode($decoded, JSON_PRETTY_PRINT) : 'AI returned an empty or invalid response.';
json_response('error', 'AI translation failed. Details: ' . $error_details);
} else {
json_response('success', 'Translation successful!', ['translatedText' => $translated_text, 'extractedText' => $extracted_text]);
}
} else {
$error_message = $resp['error'] ?? 'Unknown AI error.';
json_response('error', 'AI translation failed: ' . $error_message);
}
} catch (Exception $e) {
write_log("An exception occurred: " . $e->getMessage());
write_log("Stack trace: " . $e->getTraceAsString());
json_response('error', 'An unexpected error occurred: ' . $e->getMessage());
}
?>

482
translate_log.txt Normal file

File diff suppressed because one or more lines are too long

63
upload.php Normal file
View File

@ -0,0 +1,63 @@
<?php
ini_set('display_errors', 0);
ini_set('log_errors', 1);
ini_set('error_log', __DIR__ . '/error_log.txt');
header('Content-Type: application/json');
function json_response($success, $data) {
$response = ['success' => $success];
if (is_string($data)) {
$response['message'] = $data;
} elseif (is_array($data)) {
$response = array_merge($response, $data);
}
echo json_encode($response);
exit;
}
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
json_response(false, 'Invalid request method.');
}
if (!isset($_FILES['document']) || $_FILES['document']['error'] == UPLOAD_ERR_NO_FILE) {
json_response(false, 'No file was uploaded.');
}
$file = $_FILES['document'];
if ($file['error'] !== UPLOAD_ERR_OK) {
$upload_errors = [
UPLOAD_ERR_INI_SIZE => 'The uploaded file exceeds the upload_max_filesize directive in php.ini.',
UPLOAD_ERR_FORM_SIZE => 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form.',
UPLOAD_ERR_PARTIAL => 'The uploaded file was only partially uploaded.',
UPLOAD_ERR_NO_FILE => 'No file was uploaded.',
UPLOAD_ERR_NO_TMP_DIR => 'Missing a temporary folder.',
UPLOAD_ERR_CANT_WRITE => 'Failed to write file to disk.',
UPLOAD_ERR_EXTENSION => 'A PHP extension stopped the file upload.',
];
$error_message = $upload_errors[$file['error']] ?? 'Unknown upload error.';
json_response(false, $error_message);
}
$upload_dir = __DIR__ . '/uploads/';
if (!is_dir($upload_dir)) {
if (!mkdir($upload_dir, 0775, true)) {
json_response(false, 'Failed to create upload directory.');
}
}
$file_extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$file_name = uniqid('doc_') . '.' . $file_extension;
$target_path = $upload_dir . $file_name;
if (move_uploaded_file($file['tmp_name'], $target_path)) {
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || $_SERVER['SERVER_PORT'] == 443 ? "https://" : "http://";
$domain_name = $_SERVER['HTTP_HOST'];
$web_path = $protocol . $domain_name . '/uploads/' . $file_name;
json_response(true, ['path' => $web_path, 'message' => 'File uploaded successfully.']);
} else {
json_response(false, 'Failed to move uploaded file.');
}
?>

BIN
uploads/20251120_150540.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 KiB