Update: 2026-07-30 02:27:45

This commit is contained in:
Hamza-Ayed
2026-07-30 02:27:45 +03:00
parent 5f62455113
commit ca4a7c2e70
56 changed files with 3391 additions and 709 deletions
@@ -0,0 +1,85 @@
<?php
/**
* CLI-ONLY migration script.
* Moved out of the public webroot on 2026-07-30 - it used to be reachable
* unauthenticated over HTTP. Run with: php scripts/legacy_migrations/<file>
*/
if (PHP_SAPI !== 'cli') {
http_response_code(404);
exit('Not Found');
}
require_once __DIR__ . '/../../app/bootstrap/init.php';
use App\Core\Database;
use App\Core\Encryption;
try {
$db = Database::getInstance();
$db->beginTransaction();
// 1. Generate UUIDs
$tenantId = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
$userId = sprintf('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff),
mt_rand(0, 0x0fff) | 0x4000, mt_rand(0, 0x3fff) | 0x8000,
mt_rand(0, 0xffff), mt_rand(0, 0xffff), mt_rand(0, 0xffff)
);
// 2. Test Account Data
$tenantName = "مكتب المراجعة التجريبي";
$tenantEmail = "reviewer@musadaq.jo";
$userName = "App Reviewer";
$userEmail = "reviewer@musadaq.jo";
// Password must be supplied on the command line - never hardcoded.
// php scripts/legacy_migrations/create_test_account.php '<password>'
$userPassword = $argv[1] ?? '';
if (strlen($userPassword) < 12) {
exit("Usage: php create_test_account.php '<password>' (min 12 chars)\n");
}
// 3. Encrypt data
$encryptedTenantName = Encryption::encrypt($tenantName);
$encryptedTenantEmail = Encryption::encrypt($tenantEmail);
$encryptedUserName = Encryption::encrypt($userName);
$encryptedUserEmail = Encryption::encrypt($userEmail);
$emailHash = hash('sha256', strtolower($userEmail));
$passwordHash = password_hash($userPassword, PASSWORD_DEFAULT);
// 4. Delete existing if any (prevent duplicates on re-run)
$stmt = $db->prepare("DELETE FROM users WHERE email_hash = ?");
$stmt->execute([$emailHash]);
// 5. Insert Tenant
$stmt = $db->prepare("INSERT INTO tenants (id, name, email, status, created_at) VALUES (?, ?, ?, 'active', NOW())");
$stmt->execute([
$tenantId,
$encryptedTenantName,
$encryptedTenantEmail
]);
// 6. Insert User (Manager)
$stmtUser = $db->prepare("INSERT INTO users (id, tenant_id, name, email, email_hash, password_hash, role, created_at) VALUES (?, ?, ?, ?, ?, ?, 'admin', NOW())");
$stmtUser->execute([
$userId,
$tenantId,
$encryptedUserName,
$encryptedUserEmail,
$emailHash,
$passwordHash
]);
$db->commit();
echo "<h3 style='color:green'>✅ تم إنشاء الحساب التجريبي بنجاح!</h3>";
echo "<p><b>البريد الإلكتروني:</b> $userEmail</p>";
echo "<p><b>كلمة المرور:</b> (the one you passed on the command line)</p>";
} catch (\Exception $e) {
$db->rollBack();
echo "<h3 style='color:red'>❌ Error: " . htmlspecialchars($e->getMessage()) . "</h3>";
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
* CLI-ONLY migration script.
* Moved out of the public webroot on 2026-07-30 - it used to be reachable
* unauthenticated over HTTP. Run with: php scripts/legacy_migrations/<file>
*/
if (PHP_SAPI !== 'cli') {
http_response_code(404);
exit('Not Found');
}
require_once __DIR__ . '/../../app/bootstrap/init.php';
use App\Core\Database;
try {
$db = Database::getInstance();
$sql = file_get_contents(__DIR__ . '/../../database/migrations/008_invoice_lines_enhance.sql');
// Split by semicolon and execute
$queries = array_filter(array_map('trim', explode(';', $sql)));
echo "<h1>Running Migration 008...</h1>";
echo "<ul>";
foreach ($queries as $query) {
if (empty($query)) continue;
try {
$db->exec($query);
echo "<li>✅ Executed: <pre>" . htmlspecialchars(substr($query, 0, 70)) . "...</pre></li>";
} catch (\Exception $innerE) {
if (str_contains($innerE->getMessage(), 'Duplicate column name')) {
echo "<li>ℹ️ تخطي (العمود موجود مسبقاً): <pre>" . htmlspecialchars(substr($query, 0, 70)) . "...</pre></li>";
} else {
throw $innerE;
}
}
}
echo "</ul>";
echo "<h2>Migration completed successfully!</h2>";
echo "<p>Please delete this file (public/migrate_008.php) for security.</p>";
} catch (\Exception $e) {
echo "<h2 style='color:red;'>Migration failed:</h2>";
echo "<pre>" . htmlspecialchars($e->getMessage()) . "</pre>";
}
+43
View File
@@ -0,0 +1,43 @@
<?php
/**
* CLI-ONLY migration script.
* Moved out of the public webroot on 2026-07-30 - it used to be reachable
* unauthenticated over HTTP. Run with: php scripts/legacy_migrations/<file>
*/
if (PHP_SAPI !== 'cli') {
http_response_code(404);
exit('Not Found');
}
require_once __DIR__ . '/../../app/bootstrap/init.php';
use App\Core\Database;
try {
$db = Database::getInstance();
echo "Checking columns in 'users' table...\n";
$stmt = $db->query("DESCRIBE users");
$columns = $stmt->fetchAll(PDO::FETCH_COLUMN);
if (!in_array('phone_hash', $columns)) {
echo "Adding 'phone_hash' column...\n";
$db->exec("ALTER TABLE users ADD COLUMN phone_hash VARCHAR(64) NULL AFTER phone");
$db->exec("CREATE INDEX idx_phone_hash ON users(phone_hash)");
echo "Column 'phone_hash' added successfully.\n";
} else {
echo "Column 'phone_hash' already exists.\n";
}
if (!in_array('email_hash', $columns)) {
echo "Adding 'email_hash' column...\n";
$db->exec("ALTER TABLE users ADD COLUMN email_hash VARCHAR(64) NULL AFTER email");
$db->exec("CREATE INDEX idx_email_hash ON users(email_hash)");
echo "Column 'email_hash' added successfully.\n";
} else {
echo "Column 'email_hash' already exists.\n";
}
echo "Migration completed.\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}
@@ -0,0 +1,93 @@
<?php
/**
* CLI-ONLY migration script.
* Moved out of the public webroot on 2026-07-30 - it used to be reachable
* unauthenticated over HTTP. Run with: php scripts/legacy_migrations/<file>
*/
if (PHP_SAPI !== 'cli') {
http_response_code(404);
exit('Not Found');
}
require_once __DIR__ . '/../../app/bootstrap/init.php';
use App\Core\Database;
try {
$db = Database::getInstance();
echo "Running Phase 2 Migrations...\n";
// 1. user_devices
$db->exec("
CREATE TABLE IF NOT EXISTS user_devices (
id CHAR(36) PRIMARY KEY,
user_id CHAR(36) NOT NULL,
device_fingerprint VARCHAR(64) NOT NULL,
device_name VARCHAR(100),
platform ENUM('android','ios') NOT NULL,
app_version VARCHAR(20),
push_token TEXT NULL,
device_secret VARCHAR(255) NULL,
is_trusted BOOLEAN DEFAULT FALSE,
last_seen_at DATETIME,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
UNIQUE KEY uq_user_device (user_id, device_fingerprint)
)
");
echo "Created user_devices table.\n";
// 2. invoice_batches
$db->exec("
CREATE TABLE IF NOT EXISTS invoice_batches (
id CHAR(36) PRIMARY KEY,
tenant_id CHAR(36) NOT NULL,
company_id CHAR(36) NOT NULL,
uploaded_by CHAR(36) NOT NULL,
total_images INT NOT NULL DEFAULT 0,
processed_images INT NOT NULL DEFAULT 0,
status ENUM('uploading','processing','done','partial_fail') DEFAULT 'uploading',
source ENUM('mobile_scan','web_upload','whatsapp') DEFAULT 'mobile_scan',
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
completed_at DATETIME NULL,
FOREIGN KEY (tenant_id) REFERENCES tenants(id) ON DELETE CASCADE,
FOREIGN KEY (company_id) REFERENCES companies(id) ON DELETE CASCADE,
FOREIGN KEY (uploaded_by) REFERENCES users(id) ON DELETE SET NULL
)
");
echo "Created invoice_batches table.\n";
// 3. invoice_processing_queue
$db->exec("
CREATE TABLE IF NOT EXISTS invoice_processing_queue (
id INT AUTO_INCREMENT PRIMARY KEY,
batch_id CHAR(36) NOT NULL,
invoice_id CHAR(36) NULL,
tenant_id CHAR(36) NOT NULL,
image_path VARCHAR(500) NOT NULL,
status ENUM('pending','processing','done','failed') DEFAULT 'pending',
attempts INT DEFAULT 0,
error_message TEXT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
processed_at DATETIME NULL,
FOREIGN KEY (batch_id) REFERENCES invoice_batches(id) ON DELETE CASCADE
)
");
// MySQL has no "CREATE INDEX IF NOT EXISTS" - check information_schema instead.
$idxStmt = $db->prepare("
SELECT COUNT(*) FROM information_schema.statistics
WHERE table_schema = DATABASE()
AND table_name = 'invoice_processing_queue'
AND index_name = 'idx_status_tenant'
");
$idxStmt->execute();
if ((int)$idxStmt->fetchColumn() === 0) {
$db->exec("CREATE INDEX idx_status_tenant ON invoice_processing_queue (status, tenant_id)");
}
echo "Created invoice_processing_queue table.\n";
echo "Phase 2 Migrations completed successfully.\n";
} catch (Exception $e) {
echo "Error: " . $e->getMessage() . "\n";
}