Files
Siro/backend/tools/env_registry.php
T

143 lines
6.2 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
/**
* tools/env_registry.php — تصنيف مفاتيح البيئة وقراءة/كتابة .env
* ─────────────────────────────────────────────────────────────
* ‏مصدر واحد للحقيقة تستعمله الأداة السطرية (env_keys.php) واللوحة
* ‏(panel/index.php) معاً. نسختان من التصنيف كانتا ستتباعدان، وأخطر ما
* ‏فيهما التباعد هو قائمة «المجمّدة» — مفتاح يسقط منها في نسخة يعني
* ‏لوحةً تعرضه قابلاً للتعديل.
*/
// ═══════════════════════════════════════════════════════════════
// الأصناف
// ═══════════════════════════════════════════════════════════════
/** ‏أسرار داخلية: طرفاها كلاهما لنا. التوليد والتدوير آمنان. */
const ENV_GENERATED = [
'S2S_SHARED_KEY' => 32,
'WEBHOOK_SECRET_KEY' => 32,
'INTERNAL_SOCKET_KEY' => 32,
'BOT_SECRET_KEY' => 24,
'FCM_INTERNAL_API_KEY' => 32,
'MIGRATION_ADMIN_KEY' => 24,
'PAYMENT_KEY' => 32,
];
/** ‏تدويرها يبطل كل الجلسات القائمة — حدث تشغيلي واعٍ لا روتين. */
const ENV_SESSION_SCOPED = [
'JWT_SECRET_KEY' => 64,
'SECRET_KEY_HMAC' => 32,
];
/**
* ‏⛔ مجمّدة. بيانات مخزَّنة في القاعدة مشتقّة منها:
* ENC_KEY — أسماء الركّاب وهواتفهم ولوحات السيارات مشفّرة به
* BLIND_INDEX_PEPPER — أعمدة *_bidx محسوبة به ومخزَّنة
* TRACKING_SECRET_SALT / FP_PEPPER — قيم مشتقّة مخزَّنة
*
* ‏تغييرها لا يعيد تشفير شيء: يجعل الصفوف القديمة غير قابلة للقراءة
* ‏إلى الأبد، ولا نسخة احتياطية تنفع لأنها مشفّرة بالمفتاح القديم.
* ‏تدويرها ترحيلُ بيانات لا تغييرُ إعداد.
*
* ‏اللوحة تعرض حالتها فقط — لا قيمتها ولا حقل تعديل.
*/
const ENV_FROZEN = [
'ENC_KEY', 'ENCRYPTION_KEY_PATH', 'ENCRYPTION_MODE',
'BLIND_INDEX_PEPPER', 'TRACKING_SECRET_SALT', 'FP_PEPPER',
];
/** ‏لا تُولَّد — تُنسخ من لوحة المزوّد. قابلة للتحرير يدوياً. */
const ENV_EXTERNAL = [
'GEMINI_API_KEY', 'NABEH_API_KEY', 'NABEH_PASSWORD', 'NABEH_OTP_APP_KEY',
'ELEVENLABS_API_KEY', 'CREATOMATE_API_KEY', 'SOCIAL_BOT_TOKEN',
'MAIL_PASSWORD', 'SIRO_SMTP_PASSWORD', 'SMS_PASSWORD_EGYPT',
'VOICE_CALL_API_KEY', 'RASEEL_DRIVER_ACCESS_TOKEN',
];
/**
* ‏كلمات مرور القواعد ← اسم المستخدم ومتغيّر اسم القاعدة.
*
* ‏تغييرها في .env وحده لا يفعل شيئاً: متغيّرات صورة mysql تُقرأ مرة
* ‏واحدة عند تهيئة مجلّد بيانات فارغ، وبعدها كلمة المرور تعيش في
* ‏mysql.user. الترتيب الصحيح ALTER USER ثم .env — واللوحة تفعله
* ‏بهذا الترتيب وتتحقّق قبل أن تكتب.
*/
const ENV_DB_CREDENTIALS = [
'DB_PRIMARY_PASS_V2' => ['user' => 'DB_PRIMARY_USER_V2', 'db' => 'DB_PRIMARY_NAME_V2'],
'DB_PRIMARY_PASS' => ['user' => 'DB_PRIMARY_USER', 'db' => 'DB_PRIMARY_NAME'],
'DB_TRACKING_PASS' => ['user' => 'DB_TRACKING_USER', 'db' => 'DB_TRACKING_NAME'],
'DB_RIDE_PASS' => ['user' => 'DB_RIDE_USER', 'db' => 'DB_RIDE_NAME'],
'DB_TRANSIT_PASS' => ['user' => 'DB_TRANSIT_USER', 'db' => 'DB_TRANSIT_NAME'],
'DB_FOOD_PASS' => ['user' => 'DB_FOOD_USER', 'db' => 'DB_FOOD_NAME'],
];
/** ‏يقرأ .env إلى مصفوفة. */
function envRead(string $file): array
{
if (!is_readable($file)) return [];
$out = [];
foreach (file($file, FILE_IGNORE_NEW_LINES) as $line) {
if (preg_match('/^\s*([A-Z0-9_]+)\s*=\s*(.*)$/', $line, $m)) {
$out[$m[1]] = trim($m[2], "\"' ");
}
}
return $out;
}
/** ‏قيمة نائبة: موجودة شكلاً وغائبة فعلاً. */
function envIsPlaceholder(?string $v): bool
{
if ($v === null || $v === '') return true;
return (bool) preg_match('/^(<?CHANGE_ME|changeme|your[_-]|xxx+|TODO|REPLACE)/i', $v);
}
/**
* ‏يكتب مفاتيح في .env مع نسخة احتياطية.
*
* ‏الاستبدال في المكان يبقي التعليقات المجاورة مفيدة، والمفقود يُلحق
* ‏في النهاية.
*
* @return string|null ‏مسار النسخة الاحتياطية، أو null عند الفشل.
*/
function envWrite(string $file, array $changes): ?string
{
if (empty($changes)) return null;
// ‏.env التالف يوقف كل شيء، وهو خارج git بطبيعته فلا مصدر لاستعادته.
$backup = $file . '.bak.' . date('Ymd_His');
if (!copy($file, $backup)) return null;
@chmod($backup, 0600);
$out = rtrim((string) file_get_contents($file), "\n") . "\n";
$append = [];
foreach ($changes as $name => $value) {
$line = "$name=$value";
if (preg_match('/^\s*' . preg_quote($name, '/') . '\s*=/m', $out)) {
$out = preg_replace(
'/^\s*' . preg_quote($name, '/') . '\s*=.*$/m',
str_replace('$', '\\$', $line),
$out,
1
);
} else {
$append[] = $line;
}
}
if ($append) {
$out .= "\n# ── " . date('Y-m-d H:i') . " ──\n" . implode("\n", $append) . "\n";
}
if (file_put_contents($file, $out) === false) return null;
@chmod($file, 0600);
return $backup;
}
/** ‏سرّ عشوائي بصيغة hex. */
function envGenerate(int $bytes = 32): string
{
return bin2hex(random_bytes($bytes));
}