Compare commits
89
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
58310330b0 | ||
|
|
9aa45ad605 | ||
|
|
5028d20881 | ||
|
|
cb91e5f0de | ||
|
|
043cd052b6 | ||
|
|
8dbb336e0a | ||
|
|
dc9a736e26 | ||
|
|
cff5a8e1ce | ||
|
|
e8f5de585d | ||
|
|
ce31d79ba6 | ||
|
|
82b1045f9c | ||
|
|
e819186201 | ||
|
|
126c94df63 | ||
|
|
335aa299da | ||
|
|
0bf13824c9 | ||
|
|
91e9e71ce4 | ||
|
|
a8470eb76c | ||
|
|
279c9fa510 | ||
|
|
ce896df30a | ||
|
|
55830beee8 | ||
|
|
43ad2e0ad4 | ||
|
|
aa2b9f131f | ||
|
|
17e8f8eeac | ||
|
|
5e3be672ee | ||
|
|
c10fd2396c | ||
|
|
4a7859f4c2 | ||
|
|
7216d86740 | ||
|
|
96b22d1fc0 | ||
|
|
8a0cf35116 | ||
|
|
ad4b5d991c | ||
|
|
ff97821fd0 | ||
|
|
398daceffb | ||
|
|
0849b7b53c | ||
|
|
f14d40dac7 | ||
|
|
7572d6b4c5 | ||
|
|
e1e634fcb3 | ||
|
|
429496dd3c | ||
|
|
d7119a53eb | ||
|
|
50ecf9bfbf | ||
|
|
9e65afae59 | ||
|
|
81d88ee770 | ||
|
|
431d1f95ff | ||
|
|
4f6595b1eb | ||
|
|
52a912380f | ||
|
|
06895d6720 | ||
|
|
313c2f641f | ||
|
|
956dae8e75 | ||
|
|
b98ce56622 | ||
|
|
3d13d31101 | ||
|
|
470d6d959d | ||
|
|
aa2967964f | ||
|
|
ad231c6f20 | ||
|
|
9088af0534 | ||
|
|
fc4be2b8fd | ||
|
|
3109d782d3 | ||
|
|
ac4a74d0e0 | ||
|
|
c28d7f236e | ||
|
|
0d21c1bfbf | ||
|
|
bab24892c7 | ||
|
|
cb384765fb | ||
|
|
8375773898 | ||
|
|
5d813f3a3f | ||
|
|
f1e31cb721 | ||
|
|
13ff137359 | ||
|
|
c9b3249aa5 | ||
|
|
ba4ae15eab | ||
|
|
0a854c4302 | ||
|
|
57e3da7ea4 | ||
|
|
06543ee8e0 | ||
|
|
ba2f211438 | ||
|
|
c35f6e61f2 | ||
|
|
b039281972 | ||
|
|
b1ee482830 | ||
|
|
6bd0bc0ce5 | ||
|
|
91c36d99c9 | ||
|
|
23dde3c8e3 | ||
|
|
97b8b27ddf | ||
|
|
2691eb68eb | ||
|
|
9247499f50 | ||
|
|
941420f5c0 | ||
|
|
bd287d155b | ||
|
|
a0a5a09135 | ||
|
|
03cdc6fe9c | ||
|
|
b3ffa7c71d | ||
|
|
844774bd1b | ||
|
|
126761004c | ||
|
|
8061f3e7a3 | ||
|
|
3136a2b7aa | ||
|
|
32865e78df |
@@ -1,9 +0,0 @@
|
||||
Downloading Syria map data...
|
||||
% Total % Received % Xferd Average Speed Time Time Time Current
|
||||
Dload Upload Total Spent Left Speed
|
||||
␍ 0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0␍100 239 100 239 0 0 829 0 --:--:-- --:--:-- --:--:-- 829
|
||||
Warning: Failed to open the file
|
||||
Warning: /home/hamzadoctor/app/infrastructure/osm-data/syria-latest.osm.pbf:
|
||||
Warning: No such file or directory
|
||||
␍ 0 73.8M 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
|
||||
curl: (23) Failure writing output to destination, passed 4096 returned 4294967295
|
||||
@@ -44,3 +44,7 @@ PAYMOB_IFRAME_ID=837992
|
||||
|
||||
BINANCE_PAY_API_KEY="المفتاح_الخاص_بك_هنا"
|
||||
BINANCE_PAY_SECRET_KEY="المفتاح_السري_الخاص_بك_هنا"
|
||||
|
||||
# Google Gemini AI API Key (For Tactical AI Strategic Advisor & Fuel Pricing Intelligence)
|
||||
GEMINI_API_KEY=
|
||||
|
||||
|
||||
+64
-12
@@ -1,28 +1,46 @@
|
||||
# Database Dumps
|
||||
# Database Dumps & Archives
|
||||
*.sql
|
||||
*.tar
|
||||
*.tar.gz
|
||||
*.zip
|
||||
*.mbtiles
|
||||
*.db
|
||||
*.db-*
|
||||
*.db.gz
|
||||
|
||||
# Node modules
|
||||
node_modules/
|
||||
.npm/
|
||||
# Node modules and Web build output
|
||||
**/node_modules/
|
||||
**/dist/
|
||||
**/.npm/
|
||||
**/.npm-cache/
|
||||
*.tsbuildinfo
|
||||
|
||||
# Environment Variables
|
||||
# Environment Variables & Secrets
|
||||
.env
|
||||
.env.local
|
||||
.env.*
|
||||
*.env
|
||||
*.env.*
|
||||
**/.env
|
||||
**/.env.*
|
||||
*.key
|
||||
*.pem
|
||||
|
||||
# Virtual Environments
|
||||
# Python Virtual Environments
|
||||
venv/
|
||||
venv_overture/
|
||||
.venv/
|
||||
.venv*/
|
||||
env/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Map Data
|
||||
# Map Data & Intermediate Geodata
|
||||
*.osm.pbf
|
||||
*.pbf
|
||||
infrastructure/osm-data/default-gh/
|
||||
apps/.npm-cache/
|
||||
*.json.bak
|
||||
apps/api/src/telemetry/*.json
|
||||
**/dem_tiles/
|
||||
|
||||
# OS generated files
|
||||
.DS_Store
|
||||
@@ -32,6 +50,40 @@ apps/api/src/telemetry/*.json
|
||||
.Trashes
|
||||
ehthumbs.db
|
||||
Thumbs.db
|
||||
.venv-osm/
|
||||
__pycache__/
|
||||
infrastructure/osm-data/*.orig
|
||||
|
||||
# IDE & Editor files
|
||||
.idea/
|
||||
.vscode/
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
|
||||
# Flutter / Dart Mobile SDKs (all packages)
|
||||
**/.dart_tool/
|
||||
**/.flutter-plugins
|
||||
**/.flutter-plugins-dependencies
|
||||
**/.packages
|
||||
**/.pub-cache/
|
||||
**/.pub/
|
||||
**/build/
|
||||
**/coverage/
|
||||
|
||||
# Android build artifacts
|
||||
**/.gradle/
|
||||
**/android/.gradle/
|
||||
**/android/app/build/
|
||||
**/android/app/debug/
|
||||
**/android/app/release/
|
||||
**/android/app/profile/
|
||||
*.apk
|
||||
*.aab
|
||||
|
||||
# iOS build artifacts
|
||||
**/ios/Pods/
|
||||
**/ios/.symlinks/
|
||||
**/ios/Flutter/Flutter.framework
|
||||
**/ios/Flutter/Flutter.podspec
|
||||
**/ios/Flutter/.last_build_id
|
||||
**/ios/DerivedData/
|
||||
*.ipa
|
||||
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
.git/
|
||||
.github/
|
||||
.idea/
|
||||
.vscode/
|
||||
.DS_Store
|
||||
node_modules/
|
||||
build/
|
||||
.dart_tool/
|
||||
.gradle/
|
||||
Pods/
|
||||
.symlinks/
|
||||
*.apk
|
||||
*.aab
|
||||
*.ipa
|
||||
dem_tiles/
|
||||
infrastructure/osm-data/*.osm.pbf
|
||||
infrastructure/osm-data/dem_tiles/
|
||||
infrastructure/osm-data/valhalla-work/
|
||||
venv/
|
||||
.venv*/
|
||||
apps/api/dist/
|
||||
apps/web/dist/
|
||||
packages/*/dist/
|
||||
.npm/
|
||||
.npm-cache/
|
||||
@@ -45,9 +45,21 @@ A complete self-hosted mapping platform prototype for Jordan, built with OpenStr
|
||||
- **GraphHopper Latest**
|
||||
- **MapLibre GL JS**
|
||||
|
||||
## 🎖️ البوابات السيادية والعسكرية / Sovereign & Defense Portals
|
||||
|
||||
- **بوابة المنظومة العسكرية والتوجيه التكتيكي الميداني (المعتمد للجهات العسكرية)**:
|
||||
`https://map-saas.intaleqapp.com/sovereign-tactical-landing.html`
|
||||
- **مركز القيادة التفاعلي (رَصْد)**:
|
||||
`https://map-saas.intaleqapp.com/sovereign-command.html`
|
||||
- **المنظومة التكتيكية الميدانية الحية**:
|
||||
`https://map-saas.intaleqapp.com/#tactical`
|
||||
- **وثيقة الإيجاز الأمني والمعمارية التقنية (المركز الجغرافي الملكي)**:
|
||||
`https://map-saas.intaleqapp.com/security_brief_rjgc.html`
|
||||
|
||||
## 📝 Arabic Comments / ملاحظات باللغة العربية
|
||||
تم تصميم هذا النظام ليكون نواة لمنصة خرائط تجارية تدعم تطبيقات التوصيل والنقل الذكي في الأردن، مع التركيز على استيعاب بيانات الموقع اللحظية للسائقين كل 3 ثواني ومعالجتها مكانياً.
|
||||
تم تصميم هذا النظام ليكون نواة لمنصة خرائط تجارية وسيادية تدعم تطبيقات التوصيل والنقل الذكي والأنظمة التكتيكية في الأردن، مع التركيز على استيعاب بيانات الموقع اللحظية للسائقين كل 3 ثواني ومعالجتها مكانياً واستقلالية العمل 100% بدون إنترنت.
|
||||
|
||||
## ⚠️ Limitations / القيود الحالية
|
||||
- Does not include full Map Matching for traffic light delay logic (Phase 2).
|
||||
- Requires manual download of MBTiles for high-performance tile serving in production.
|
||||
|
||||
|
||||
@@ -0,0 +1,402 @@
|
||||
# دليل الانتقال السيادي الشامل وخطة استعادة المنصة والنسخ الاحتياطي
|
||||
## Sovereign Map Platform: Server Migration, Disaster Recovery & Offline Hosting Runbook
|
||||
|
||||
**المهندس المعماري والاستراتيجي للمنظومة:** حمزة عايد (Hamza Ayed)
|
||||
*Founding Tech Architect & Sovereign Mobility Strategist*
|
||||
**المنصة:** سيرو للخرائط السيادية والذكاء المكاني والتكتيكي (Siro Sovereign Map Engine / Map-SaaS)
|
||||
**تاريخ التوثيق:** أيلول (سبتمبر) 2026
|
||||
**الإصدار:** v2.4 Enterprise Sovereign Edition
|
||||
|
||||
---
|
||||
|
||||
## 1. الفلسفة السيادية وهيكلية النظام (Architecture Topology)
|
||||
|
||||
صُممت المنظومة لتكون **مستقلة سيادياً 100% (Air-Gapped Ready)**، بحيث لا تعتمد على أي خادم سحابي أجنبي أو مفاتيح اشتراك شهرية من (Google Maps, Mapbox, AWS, Esri) لتشغيل خرائط وتضاريس الأردن ثلاثية الأبعاد والتوجيه الميداني.
|
||||
|
||||
```
|
||||
[ Internet / VPN / Tactical Mesh ]
|
||||
│
|
||||
[ CloudPanel / Nginx ]
|
||||
(SSL: map-saas.intaleqapp.com, tiles, api)
|
||||
│
|
||||
┌──────────────┬──────────────┬────────┴──────┬──────────────┬──────────────┐
|
||||
│ :3201 │ :3204 │ :3200 │ :3202 │ :8989 │
|
||||
┌────┴─────┐ ┌──────┴──────┐ ┌─────┴──────┐ ┌──────┴──────┐ ┌─────┴──────┐
|
||||
│ map-web │ │map-dashboard│ │ map-api │ │ map-martin │ │map-routing │
|
||||
│ (React + │ │(Admin/Usage │ │ (NestJS + │ │(Rust Vector │ │(GraphHopper│
|
||||
│ MapLibre)│ │ Developer) │ │ Spatial) │ │ + MBTiles) │ │ Engine) │
|
||||
└────┬─────┘ └─────────────┘ └─────┬──────┘ └──────┬──────┘ └─────┬──────┘
|
||||
│ │ │ │
|
||||
│ ▼ ▼ │
|
||||
│ ┌──────────────┬──────────────┐ │
|
||||
│ │ map-db │ map-redis │ │
|
||||
│ │(PostGIS 15 + │ (Cache & Bus │ │
|
||||
│ │ Topology) │ Port 6381) │ │
|
||||
│ └──────────────┴──────────────┘ │
|
||||
│ │
|
||||
└──────────────────────────[ Volumes ]───────────────────────┘
|
||||
• postgres_data (جداول وخطوط الكنتور والطرق)
|
||||
• data/mbtiles (تضاريس 3D DEM + أقمار صناعية)
|
||||
• infrastructure/osm-data (شبكة الطرق الخام)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. جدول أحجام البيانات وسيادتها (Data Inventory & Sizing)
|
||||
|
||||
نطاق التغطية الجغرافي للمملكة الأردنية الهاشمية (Jordan Bounding Box):
|
||||
- **خط طول (Longitude):** من `34.8° E` إلى `39.3° E`
|
||||
- **دائرة عرض (Latitude):** من `29.1° N` إلى `33.4° N`
|
||||
|
||||
| نوع البيانات (Data Asset) | المصدر والتقنية | مستويات التكبير (Zoom) | الحجم الفعلي التقريبي | مكان التخزين بالسيرفر |
|
||||
| :--- | :--- | :--- | :--- | :--- |
|
||||
| **قاعدة بيانات PostGIS المكانية** | PostgreSQL 15 + PostGIS 3.3 | N/A (Geometries) | ~1.5 - 2.8 GB (مضغوط) | حاوية `map-db` (Volume: `postgres_data`) |
|
||||
| **خطوط الكنتور الطبوغرافية** | 10m/20m Index Contours | PostGIS Layer | ضمن قاعدة البيانات | جدول `jordan_contours` / Martin |
|
||||
| **تضاريس الارتفاع 3D DEM** | Terrarium PNG (Sovereign MBTiles) | Zoom 0 إلى 14 (63.6k بلاطة) | ~930 MB | `data/mbtiles/jordan-dem.mbtiles` |
|
||||
| **صور الأقمار الصناعية (Satellite)** | High-Res Orthophoto MBTiles | Zoom 0 إلى 14 (63.6k بلاطة) | ~1.5 GB (~6 GB لـ z15) | `data/mbtiles/jordan-satellite.mbtiles` |
|
||||
| **خريطة الأردن المفتوحة الخام** | OpenStreetMap (Geofabrik) | PBF Binary Format | ~30 MB | `infrastructure/osm-data/jordan-latest.osm.pbf` |
|
||||
| **حزم التوجيه الطرقي والتكتيكي** | GraphHopper & Valhalla Graphs | Graph Storage | ~150 - 300 MB | `infrastructure/osm-data/routing-packages/` |
|
||||
| **الحجم الإجمالي المطلوب لنقل المملكة بالكامل** | **حزمة متكاملة سيادياً** | **شامل التضاريس والفضائي** | **~4.5 إلى 9 GB فقط!** | يمكن نقله في دقائق عبر SSH |
|
||||
|
||||
---
|
||||
|
||||
## 3. المرحلة الأولى: أخذ النسخة الاحتياطية من السيرفر الحالي (Source Server Backup)
|
||||
|
||||
### الخيار (أ): التشغيل التلقائي عبر السكربت الذكي (Automated Script)
|
||||
قمنا ببرمجة سكربت أوتوماتيكي يقوم بتفريغ الداتابيز، وفحص الـ Redis، ونسخ الإعدادات والخرائط وتوليد بصمات `SHA256`:
|
||||
|
||||
```bash
|
||||
# الانتقال لمسار المشروع
|
||||
cd /home/hamzadoctor/app
|
||||
|
||||
# تشغيل سكربت النسخ الاحتياطي الشامل
|
||||
./infrastructure/scripts/backup_sovereign_map.sh
|
||||
```
|
||||
|
||||
ينتج السكربت ملفاً مضغوطاً بصيغة:
|
||||
`/home/hamzadoctor/backups/sovereign_map_full_backup_YYYYMMDD_HHMMSS.tar.gz`
|
||||
|
||||
---
|
||||
|
||||
### الخيار (ب): الإجراء اليدوي خطوة بخطوة (Manual Fallback)
|
||||
|
||||
إذا أردت تنفيذ النسخ يدوياً بأوامر مباشرة:
|
||||
|
||||
#### 1. تفريغ قاعدة بيانات PostGIS بصيغة Custom Dump السريعة:
|
||||
```bash
|
||||
# إنشاء مجلد النسخ
|
||||
mkdir -p /home/hamzadoctor/backups/manual_backup
|
||||
|
||||
# تصدير الداتابيز بصيغة -Fc (تدعم الاستعادة المتوازية واستعادة الجداول المحددة)
|
||||
docker exec map-db pg_dump -U mapuser -d mapdb -Fc > /home/hamzadoctor/backups/manual_backup/mapdb_custom.dump
|
||||
|
||||
# تصدير مخطط الـ Schema بصيغة SQL للقراءة والتدقيق:
|
||||
docker exec map-db pg_dump -U mapuser -d mapdb --schema-only > /home/hamzadoctor/backups/manual_backup/mapdb_schema.sql
|
||||
```
|
||||
|
||||
#### 2. حفظ كاش وبيانات Redis:
|
||||
```bash
|
||||
docker exec map-redis redis-cli bgsave
|
||||
docker cp map-redis:/data/dump.rdb /home/hamzadoctor/backups/manual_backup/redis_dump.rdb
|
||||
```
|
||||
|
||||
#### 3. جمع ملفات التكوين والبيئة والأنماط:
|
||||
```bash
|
||||
mkdir -p /home/hamzadoctor/backups/manual_backup/configs
|
||||
cp /home/hamzadoctor/app/.env /home/hamzadoctor/backups/manual_backup/configs/.env.backup
|
||||
cp /home/hamzadoctor/app/docker-compose*.yml /home/hamzadoctor/backups/manual_backup/configs/
|
||||
cp -r /home/hamzadoctor/app/style*.json /home/hamzadoctor/backups/manual_backup/configs/
|
||||
```
|
||||
|
||||
#### 4. ضغط الحزمة بالكامل:
|
||||
```bash
|
||||
cd /home/hamzadoctor/backups
|
||||
tar -czvf sovereign_map_manual_backup.tar.gz manual_backup/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. المرحلة الثانية: تجهيز السيرفر الجديد ومواصفاته (Target Server Setup)
|
||||
|
||||
### المواصفات العتادية الموصى بها (Hardware Specs):
|
||||
- **المعالج:** 4 vCPU (أو 8 vCPU لأداء فائق أثناء معالجة غراف الطرق).
|
||||
- **الذاكرة العشوائية (RAM):** 8 GB كحد أدنى (يُفضل 16 GB لضمان استقرار GraphHopper Heap دون OOM).
|
||||
- **التخزين:** 80 GB NVMe SSD (لقاعدة البيانات والتضاريس وسجلات النظام).
|
||||
- **نظام التشغيل:** Ubuntu 22.04 LTS أو Ubuntu 24.04 LTS x86_64.
|
||||
|
||||
### إعداد السيرفر الجديد برمجياً:
|
||||
|
||||
#### 1. تحديث النظام وتثبيت حزم الأدوات الأساسية:
|
||||
```bash
|
||||
sudo apt update && sudo apt upgrade -y
|
||||
sudo apt install -y curl wget git rsync htop ufw unzip jq sqlite3
|
||||
```
|
||||
|
||||
#### 2. ضبط ملف المبادلة (Swap File - حاسم جداً لمنع OOM):
|
||||
بسبب استهلاك محرك GraphHopper للذاكرة أثناء بناء شبكة التوجيه، يجب إنشاء 4GB Swap:
|
||||
```bash
|
||||
sudo fallocate -l 4G /swapfile
|
||||
sudo chmod 600 /swapfile
|
||||
sudo mkswap /swapfile
|
||||
sudo swapon /swapfile
|
||||
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
|
||||
```
|
||||
|
||||
#### 3. تثبيت Docker Engine و Docker Compose V2 الرسمي:
|
||||
```bash
|
||||
curl -fsSL https://get.docker.com -o get-docker.sh
|
||||
sudo sh get-docker.sh
|
||||
sudo usermod -aG docker $USER
|
||||
```
|
||||
|
||||
#### 4. ضبط الجدار الناري (UFW Firewall):
|
||||
```bash
|
||||
sudo ufw default deny incoming
|
||||
sudo ufw default allow outgoing
|
||||
sudo ufw allow 22/tcp # SSH
|
||||
sudo ufw allow 80/tcp # HTTP (Reverse Proxy)
|
||||
sudo ufw allow 443/tcp # HTTPS (SSL)
|
||||
sudo ufw enable
|
||||
```
|
||||
*(ملاحظة أمنية: لا تفتح المنافذ الداخلية 5432، 6381، 3200، 3201، 3202 للعامة، بل اتركها خلف الـ Nginx Reverse Proxy الداخلي).*
|
||||
|
||||
---
|
||||
|
||||
## 5. المرحلة الثالثة: نقل البيانات بين السيرفرين (Direct Server-to-Server Sync)
|
||||
|
||||
### الطريقة الموصى بها: النقل المباشر فائق السرعة عبر `rsync`:
|
||||
|
||||
نفّذ الأمر التالي من السيرفر المصدر (أو من جهاز التحكم الخاص بك):
|
||||
|
||||
```bash
|
||||
# نقل ملف النسخة الاحتياطية الشاملة إلى السيرفر الجديد
|
||||
rsync -avzP -e "ssh -i ~/.ssh/your-key" \
|
||||
/home/hamzadoctor/backups/sovereign_map_full_backup_*.tar.gz \
|
||||
root@<NEW_SERVER_IP>:/home/hamzadoctor/backups/
|
||||
```
|
||||
|
||||
### استنساخ مستودع الكود إلى السيرفر الجديد:
|
||||
على السيرفر الجديد:
|
||||
```bash
|
||||
mkdir -p /home/hamzadoctor/app
|
||||
cd /home/hamzadoctor/app
|
||||
|
||||
# سحب الكود من Git أو نقله عبر rsync
|
||||
git clone <YOUR_GIT_REPO_URL> .
|
||||
# أو نسخ ملفات المشروع عبر rsync من السيرفر القديم:
|
||||
# rsync -avzP --exclude 'node_modules' --exclude '.git' root@<OLD_SERVER_IP>:/home/hamzadoctor/app/ /home/hamzadoctor/app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. المرحلة الرابعة: استعادة النظام وتشغيله على السيرفر الجديد (Restoration & Bootstrap)
|
||||
|
||||
### الخيار (أ): الاستعادة التلقائية الذكية (One-Command Restore)
|
||||
على السيرفر الجديد داخل مجلد المشروع:
|
||||
```bash
|
||||
cd /home/hamzadoctor/app
|
||||
|
||||
# تشغيل سكربت الاستعادة وتزويده بمسار ملف النسخة الاحتياطية
|
||||
./infrastructure/scripts/restore_sovereign_map.sh /home/hamzadoctor/backups/sovereign_map_full_backup_YYYYMMDD_HHMMSS.tar.gz
|
||||
```
|
||||
يقوم هذا السكربت بـ:
|
||||
1. استخراج الإعدادات والتأكد من ملف `.env`.
|
||||
2. تشغيل حاوية `map-db` والانتظار حتى تصبح جاهزة للحقن.
|
||||
3. تفعيل إضافات PostGIS واستعادة كافة الجداول والفهارس المكانية بدقة.
|
||||
4. تنفيذ `VACUUM ANALYZE` لضمان أقصى سرعة لاستعلامات الخرائط.
|
||||
5. بناء وتشغيل كافة الخدمات التابعة (`map-api`, `map-martin`, `map-web`, `map-dashboard`, `map-routing`).
|
||||
6. فحص صحة المنافذ والتأكد من استجابتها.
|
||||
|
||||
---
|
||||
|
||||
### الخيار (ب): الاستعادة اليدوية خطوة بخطوة
|
||||
|
||||
```bash
|
||||
cd /home/hamzadoctor/app
|
||||
|
||||
# 1. تجهيز ملف البيئة
|
||||
cp .env.example .env
|
||||
# قم بمراجعة وتعديل كلمات المرور في .env إذا أردت
|
||||
|
||||
# 2. تشغيل قاعدة البيانات والكاش
|
||||
docker compose up -d db redis
|
||||
|
||||
# 3. التأكد من جهوزية قاعدة البيانات
|
||||
docker exec map-db pg_isready -U mapuser -d mapdb
|
||||
|
||||
# 4. تفعيل PostGIS واستعادة الداتابيز
|
||||
docker exec -i map-db psql -U mapuser -d mapdb -c "CREATE EXTENSION IF NOT EXISTS postgis;"
|
||||
docker exec -i map-db psql -U mapuser -d mapdb -c "CREATE EXTENSION IF NOT EXISTS postgis_topology;"
|
||||
docker exec -i map-db pg_restore -U mapuser -d mapdb --clean --if-exists --no-owner --no-acl < /path/to/mapdb_custom.dump
|
||||
docker exec -i map-db psql -U mapuser -d mapdb -c "VACUUM ANALYZE;"
|
||||
|
||||
# 5. تشغيل وبناء بقية الخدمات
|
||||
docker compose up -d --build api martin web dashboard routing
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. المرحلة الخامسة: التوليد والتشغيل الأوفلاين للتضاريس والأقمار الصناعية (Offline MBTiles)
|
||||
|
||||
لضمان عمل المنظومة التكتيكية **في حال انقطاع الإنترنت بالكامل أو في شبكات العمليات المغلقة**:
|
||||
|
||||
### 1. تشغيل مولدات التخزين السيادية (Pre-seeding Jordan Tiles):
|
||||
قمنا بتجهيز سكربتات بايثون متعددة المسارات (Multi-threaded) لتحميل وتخزين مربعات الأردن كاملة في ملفات SQLite MBTiles قياسية:
|
||||
|
||||
```bash
|
||||
cd /home/hamzadoctor/app
|
||||
|
||||
# إنشاء مجلد ملفات MBTiles
|
||||
mkdir -p data/mbtiles
|
||||
|
||||
# تحميل وتخزين تضاريس الأردن ثلاثية الأبعاد (DEM z0 إلى z14 ~ 930MB)
|
||||
python3 infrastructure/scripts/seed_jordan_dem.py --format mbtiles --threads 16 --output data/mbtiles/jordan-dem.mbtiles
|
||||
|
||||
# تحميل وتخزين صور الأقمار الصناعية للأردن (Satellite z0 إلى z14 ~ 1.5GB)
|
||||
python3 infrastructure/scripts/seed_jordan_satellite.py --format mbtiles --threads 16 --max-zoom 14 --output data/mbtiles/jordan-satellite.mbtiles
|
||||
```
|
||||
|
||||
### 2. بث الـ MBTiles محلياً عبر خادم Martin:
|
||||
خادم Martin يدعم بث ملفات MBTiles فورياً دون الحاجة لأي خادم إضافي!
|
||||
في ملف `docker-compose.yml`، نقوم بربط مجلد `data/mbtiles`:
|
||||
```yaml
|
||||
martin:
|
||||
image: maplibre/martin:latest
|
||||
container_name: map-martin
|
||||
ports:
|
||||
- "3202:3000"
|
||||
environment:
|
||||
- WATCH_DB=true
|
||||
volumes:
|
||||
- ./data/mbtiles:/mbtiles:ro
|
||||
command: ["/mbtiles", "postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@db:5432/${POSTGRES_DB}"]
|
||||
```
|
||||
بذلك يوفر خادم Martin الروابط المحلية التالية بسرعة استجابة فائقة (Sub-millisecond):
|
||||
- **تضاريس DEM 3D:** `https://tiles.intaleqapp.com/jordan-dem/{z}/{x}/{y}`
|
||||
- **صور الأقمار:** `https://tiles.intaleqapp.com/jordan-satellite/{z}/{x}/{y}`
|
||||
- **الطبقات المتجهة والكنتور:** `https://tiles.intaleqapp.com/jordan_contours/{z}/{x}/{y}`
|
||||
|
||||
---
|
||||
|
||||
## 8. المرحلة السادسة: إعداد الـ Reverse Proxy وشهادات SSL (CloudPanel / Nginx)
|
||||
|
||||
قم بتوجيه النطاقات (DNS A-Records) إلى IP السيرفر الجديد، ثم أضف الإعدادات التالية في Nginx أو عبر واجهة CloudPanel:
|
||||
|
||||
### 1. بوابة الويب والمناورة التكتيكية (`map-saas.intaleqapp.com`):
|
||||
```nginx
|
||||
server {
|
||||
listen 80;
|
||||
server_name map-saas.intaleqapp.com;
|
||||
return 301 https://$host$request_uri;
|
||||
}
|
||||
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name map-saas.intaleqapp.com;
|
||||
|
||||
# شهادات Let's Encrypt
|
||||
ssl_certificate /etc/letsencrypt/live/map-saas.intaleqapp.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/map-saas.intaleqapp.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3201;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection 'upgrade';
|
||||
proxy_set_header Host $host;
|
||||
proxy_cache_bypass $http_upgrade;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto $scheme;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 2. خادم مربعات الخرائط والتضاريس السيادي (`tiles.intaleqapp.com`):
|
||||
```nginx
|
||||
server {
|
||||
listen 443 ssl http2;
|
||||
server_name tiles.intaleqapp.com;
|
||||
|
||||
ssl_certificate /etc/letsencrypt/live/tiles.intaleqapp.com/fullchain.pem;
|
||||
ssl_certificate_key /etc/letsencrypt/live/tiles.intaleqapp.com/privkey.pem;
|
||||
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3202;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
|
||||
# تفعيل كاش المتصفح للبلاطات
|
||||
expires 7d;
|
||||
add_header Cache-Control "public, no-transform";
|
||||
add_header Access-Control-Allow-Origin *;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. الواجهة الخلفية والذكاء المكاني (`api.map.intaleqapp.com`):
|
||||
```nginx
|
||||
location / {
|
||||
proxy_pass http://127.0.0.1:3200;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. الفحوصات والتدقيق التشغيلي بعد الانتقال (Verification Checklist)
|
||||
|
||||
نفّذ هذه الأوامر على السيرفر الجديد للتأكد من سلامة كافة الأجزاء بنسبة 100%:
|
||||
|
||||
```bash
|
||||
# 1. فحص حاويات Docker
|
||||
docker ps --format "table {{.Names}}\t{{.Status}}\t{{.Ports}}"
|
||||
|
||||
# 2. فحص استجابة الـ API
|
||||
curl -I http://localhost:3200/api/health
|
||||
|
||||
# 3. فحص خادم البلاطات Martin
|
||||
curl -s http://localhost:3202/catalog | jq .
|
||||
|
||||
# 4. فحص اتصال قاعدة البيانات PostGIS والتأكد من جداول خطوط الكنتور
|
||||
docker exec -it map-db psql -U mapuser -d mapdb -c "
|
||||
SELECT table_name, (xpath('/row/cnt/text()', xml_count))[1]::text::int as row_count
|
||||
FROM (
|
||||
SELECT table_name, query_to_xml(format('select count(*) as cnt from %I', table_name), false, true, '') as xml_count
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public' AND table_name IN ('jordan_contours', 'planet_osm_roads', 'planet_osm_line')
|
||||
) t;
|
||||
"
|
||||
|
||||
# 5. فحص محرك التوجيه GraphHopper
|
||||
curl -I http://localhost:8989/health
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 10. جدول الصيانة والنسخ الاحتياطي التلقائي اليومي (Daily Automated Cron)
|
||||
|
||||
لضمان عدم فقدان أي بيانات مستقبلية، قم بإضافة مهمة مجدولة (Cron Job) لتوليد نسخة احتياطية مشفرة كل ليلة في الساعة 3:00 فجراً والاحتفاظ بآخر 7 أيام تلقائياً:
|
||||
|
||||
```bash
|
||||
# فتح جدول المهام
|
||||
crontab -e
|
||||
```
|
||||
أضف السطر التالي:
|
||||
```cron
|
||||
0 3 * * * /home/hamzadoctor/app/infrastructure/scripts/backup_sovereign_map.sh /home/hamzadoctor/backups >> /home/hamzadoctor/backups/backup.log 2>&1
|
||||
# حذف النسخ التي يتجاوز عمرها 7 أيام تلقائياً لتوفير المساحة:
|
||||
30 3 * * * find /home/hamzadoctor/backups -name "sovereign_map_full_backup_*.tar.gz" -mtime +7 -delete
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 11. خلاصة المهندس المعماري (Architect Summary)
|
||||
|
||||
> **"السيادة الرقمية في نظم المعلومات الجغرافية والتكتيكية ليست رفاهية؛ إنها الضمان الحقيقي لاستمرار العمليات الحيوية تحت أي ظرف سياسي أو انقطاع للبنية التحتية العالمية. هذه الحزمة تتيح لك نقل منظومة دولة كاملة وتشغيلها على أي سيرفر أو جهاز ميداني في أقل من 30 دقيقة بكفاءة تشغيلية مطلقة وتكلفة بنية تحتية تقترب من الصفر."**
|
||||
> — *حمزة عايد (Hamza Ayed)*
|
||||
Vendored
-6
@@ -1,6 +0,0 @@
|
||||
import { AppService } from './app.service';
|
||||
export declare class AppController {
|
||||
private readonly appService;
|
||||
constructor(appService: AppService);
|
||||
getHello(): string;
|
||||
}
|
||||
Vendored
-35
@@ -1,35 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AppController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const app_service_1 = require("./app.service");
|
||||
let AppController = class AppController {
|
||||
appService;
|
||||
constructor(appService) {
|
||||
this.appService = appService;
|
||||
}
|
||||
getHello() {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
};
|
||||
exports.AppController = AppController;
|
||||
__decorate([
|
||||
(0, common_1.Get)(),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", String)
|
||||
], AppController.prototype, "getHello", null);
|
||||
exports.AppController = AppController = __decorate([
|
||||
(0, common_1.Controller)(),
|
||||
__metadata("design:paramtypes", [app_service_1.AppService])
|
||||
], AppController);
|
||||
//# sourceMappingURL=app.controller.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,+CAA2C;AAGpC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACK;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAGvD,QAAQ;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CACF,CAAA;AAPY,sCAAa;AAIxB;IADC,IAAA,YAAG,GAAE;;;;6CAGL;wBANU,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAE8B,wBAAU;GADxC,aAAa,CAOzB"}
|
||||
Vendored
-2
@@ -1,2 +0,0 @@
|
||||
export declare class AppModule {
|
||||
}
|
||||
Vendored
-65
@@ -1,65 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AppModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const core_1 = require("@nestjs/core");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const schedule_1 = require("@nestjs/schedule");
|
||||
const telemetry_module_1 = require("./telemetry/telemetry.module");
|
||||
const maps_module_1 = require("./maps/maps.module");
|
||||
const geocoding_module_1 = require("./geocoding/geocoding.module");
|
||||
const auth_module_1 = require("./auth/auth.module");
|
||||
const throttler_1 = require("@nestjs/throttler");
|
||||
const usage_module_1 = require("./usage/usage.module");
|
||||
const billing_module_1 = require("./billing/billing.module");
|
||||
const mail_module_1 = require("./common/mail.module");
|
||||
const usage_interceptor_1 = require("./usage/usage.interceptor");
|
||||
let AppModule = class AppModule {
|
||||
};
|
||||
exports.AppModule = AppModule;
|
||||
exports.AppModule = AppModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [
|
||||
config_1.ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
schedule_1.ScheduleModule.forRoot(),
|
||||
typeorm_1.TypeOrmModule.forRootAsync({
|
||||
imports: [config_1.ConfigModule],
|
||||
inject: [config_1.ConfigService],
|
||||
useFactory: (config) => ({
|
||||
type: 'postgres',
|
||||
url: config.get('DATABASE_URL'),
|
||||
autoLoadEntities: true,
|
||||
synchronize: false,
|
||||
}),
|
||||
}),
|
||||
throttler_1.ThrottlerModule.forRoot([{
|
||||
ttl: 60000,
|
||||
limit: 10,
|
||||
}]),
|
||||
auth_module_1.AuthModule,
|
||||
telemetry_module_1.TelemetryModule,
|
||||
maps_module_1.MapsModule,
|
||||
geocoding_module_1.GeocodingModule,
|
||||
usage_module_1.UsageModule,
|
||||
billing_module_1.BillingModule,
|
||||
mail_module_1.MailModule,
|
||||
],
|
||||
controllers: [],
|
||||
providers: [
|
||||
{
|
||||
provide: core_1.APP_INTERCEPTOR,
|
||||
useClass: usage_interceptor_1.UsageInterceptor,
|
||||
},
|
||||
],
|
||||
})
|
||||
], AppModule);
|
||||
//# sourceMappingURL=app.module.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,uCAA0D;AAC1D,2CAA6D;AAC7D,6CAAgD;AAChD,+CAAkD;AAClD,mEAA+D;AAC/D,oDAAgD;AAChD,mEAA+D;AAC/D,oDAAgD;AAChD,iDAAoD;AACpD,uDAAmD;AACnD,6DAAyD;AACzD,sDAAkD;AAClD,iEAA6D;AAsCtD,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IApCrB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,qBAAY,CAAC,OAAO,CAAC;gBACnB,QAAQ,EAAE,IAAI;aACf,CAAC;YACF,yBAAc,CAAC,OAAO,EAAE;YACxB,uBAAa,CAAC,YAAY,CAAC;gBACzB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,MAAM,EAAE,CAAC,sBAAa,CAAC;gBACvB,UAAU,EAAE,CAAC,MAAqB,EAAE,EAAE,CAAC,CAAC;oBACtC,IAAI,EAAE,UAAU;oBAChB,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC,cAAc,CAAC;oBAC/B,gBAAgB,EAAE,IAAI;oBACtB,WAAW,EAAE,KAAK;iBACnB,CAAC;aACH,CAAC;YACF,2BAAe,CAAC,OAAO,CAAC,CAAC;oBACvB,GAAG,EAAE,KAAK;oBACV,KAAK,EAAE,EAAE;iBACV,CAAC,CAAC;YACH,wBAAU;YACV,kCAAe;YACf,wBAAU;YACV,kCAAe;YACf,0BAAW;YACX,8BAAa;YACb,wBAAU;SACX;QACD,WAAW,EAAE,EAAE;QACf,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,sBAAe;gBACxB,QAAQ,EAAE,oCAAgB;aAC3B;SACF;KACF,CAAC;GACW,SAAS,CAAG"}
|
||||
Vendored
-3
@@ -1,3 +0,0 @@
|
||||
export declare class AppService {
|
||||
getHello(): string;
|
||||
}
|
||||
Vendored
-20
@@ -1,20 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AppService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
let AppService = class AppService {
|
||||
getHello() {
|
||||
return 'Hello World!';
|
||||
}
|
||||
};
|
||||
exports.AppService = AppService;
|
||||
exports.AppService = AppService = __decorate([
|
||||
(0, common_1.Injectable)()
|
||||
], AppService);
|
||||
//# sourceMappingURL=app.service.js.map
|
||||
Vendored
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"app.service.js","sourceRoot":"","sources":["../src/app.service.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAA4C;AAGrC,IAAM,UAAU,GAAhB,MAAM,UAAU;IACrB,QAAQ;QACN,OAAO,cAAc,CAAC;IACxB,CAAC;CACF,CAAA;AAJY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;GACA,UAAU,CAItB"}
|
||||
Vendored
-9
@@ -1,9 +0,0 @@
|
||||
import { OnModuleInit } from '@nestjs/common';
|
||||
import { AuthService } from './auth.service';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class AuthModule implements OnModuleInit {
|
||||
private readonly authService;
|
||||
private readonly configService;
|
||||
constructor(authService: AuthService, configService: ConfigService);
|
||||
onModuleInit(): Promise<void>;
|
||||
}
|
||||
Vendored
-53
@@ -1,53 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AuthModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const auth_service_1 = require("./auth.service");
|
||||
const tenant_entity_1 = require("./entities/tenant.entity");
|
||||
const api_key_entity_1 = require("./entities/api-key.entity");
|
||||
const redis_module_1 = require("../common/redis.module");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const tenant_controller_1 = require("./tenant.controller");
|
||||
const firebase_admin_service_1 = require("./firebase-admin.service");
|
||||
const firebase_auth_guard_1 = require("./guards/firebase-auth.guard");
|
||||
let AuthModule = class AuthModule {
|
||||
authService;
|
||||
configService;
|
||||
constructor(authService, configService) {
|
||||
this.authService = authService;
|
||||
this.configService = configService;
|
||||
}
|
||||
async onModuleInit() {
|
||||
const defaultKey = this.configService.get('MAP_API_KEY');
|
||||
if (defaultKey) {
|
||||
await this.authService.seedDefaultKey('Default System', 'admin@intaleq.xyz', defaultKey);
|
||||
console.log('✅ Default System API Key seeded successfully');
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.AuthModule = AuthModule;
|
||||
exports.AuthModule = AuthModule = __decorate([
|
||||
(0, common_1.Global)(),
|
||||
(0, common_1.Module)({
|
||||
imports: [
|
||||
typeorm_1.TypeOrmModule.forFeature([tenant_entity_1.Tenant, api_key_entity_1.ApiKey]),
|
||||
redis_module_1.RedisModule,
|
||||
],
|
||||
controllers: [tenant_controller_1.TenantController],
|
||||
providers: [auth_service_1.AuthService, firebase_admin_service_1.FirebaseAdminService, firebase_auth_guard_1.FirebaseAuthGuard],
|
||||
exports: [auth_service_1.AuthService, firebase_admin_service_1.FirebaseAdminService, firebase_auth_guard_1.FirebaseAuthGuard],
|
||||
}),
|
||||
__metadata("design:paramtypes", [auth_service_1.AuthService, typeof (_a = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _a : Object])
|
||||
], AuthModule);
|
||||
//# sourceMappingURL=auth.module.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"auth.module.js","sourceRoot":"","sources":["../../src/auth/auth.module.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAA8D;AAC9D,6CAAgD;AAChD,iDAA6C;AAC7C,4DAAkD;AAClD,8DAAmD;AACnD,yDAAqD;AACrD,2CAA+C;AAC/C,2DAAuD;AACvD,qEAAgE;AAChE,sEAAiE;AAY1D,IAAM,UAAU,GAAhB,MAAM,UAAU;IAEF;IACA;IAFnB,YACmB,WAAwB,EACxB,aAA4B;QAD5B,gBAAW,GAAX,WAAW,CAAa;QACxB,kBAAa,GAAb,aAAa,CAAe;IAC5C,CAAC;IAMJ,KAAK,CAAC,YAAY;QAChB,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,aAAa,CAAC,CAAC;QACjE,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC,gBAAgB,EAAE,mBAAmB,EAAE,UAAU,CAAC,CAAC;YACzF,OAAO,CAAC,GAAG,CAAC,8CAA8C,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;CACF,CAAA;AAjBY,gCAAU;qBAAV,UAAU;IAVtB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,sBAAM,EAAE,uBAAM,CAAC,CAAC;YAC1C,0BAAW;SACZ;QACD,WAAW,EAAE,CAAC,oCAAgB,CAAC;QAC/B,SAAS,EAAE,CAAC,0BAAW,EAAE,6CAAoB,EAAE,uCAAiB,CAAC;QACjE,OAAO,EAAE,CAAC,0BAAW,EAAE,6CAAoB,EAAE,uCAAiB,CAAC;KAChE,CAAC;qCAGgC,0BAAW,sBACT,sBAAa,oBAAb,sBAAa;GAHpC,UAAU,CAiBtB"}
|
||||
Vendored
-24
@@ -1,24 +0,0 @@
|
||||
import { Repository } from 'typeorm';
|
||||
import { ApiKey } from './entities/api-key.entity';
|
||||
import { Tenant } from './entities/tenant.entity';
|
||||
import { RedisService } from '../common/redis.service';
|
||||
export declare class AuthService {
|
||||
private readonly apiKeyRepository;
|
||||
private readonly tenantRepository;
|
||||
private readonly redisService;
|
||||
private readonly logger;
|
||||
constructor(apiKeyRepository: Repository<ApiKey>, tenantRepository: Repository<Tenant>, redisService: RedisService);
|
||||
validateApiKey(key: string, origin?: string, referer?: string): Promise<{
|
||||
tenant: Tenant;
|
||||
apiKey: ApiKey;
|
||||
rateLimit: number;
|
||||
}>;
|
||||
private validateRestrictions;
|
||||
private isDomainAllowed;
|
||||
hashSecret(secret: string): string;
|
||||
seedDefaultKey(name: string, email: string, keyString: string): Promise<void>;
|
||||
getApiKeys(tenantId: string): Promise<ApiKey[]>;
|
||||
createApiKey(tenantId: string, name: string, rateLimit?: number, allowedOrigins?: string[]): Promise<ApiKey>;
|
||||
getDefaultTenant(): Promise<Tenant>;
|
||||
findOrCreateByFirebaseUid(uid: string, email: string, name: string, photoUrl?: string): Promise<Tenant>;
|
||||
}
|
||||
Vendored
-180
@@ -1,180 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var AuthService_1;
|
||||
var _a, _b;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AuthService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const crypto_1 = require("crypto");
|
||||
const api_key_entity_1 = require("./entities/api-key.entity");
|
||||
const tenant_entity_1 = require("./entities/tenant.entity");
|
||||
const redis_service_1 = require("../common/redis.service");
|
||||
let AuthService = AuthService_1 = class AuthService {
|
||||
apiKeyRepository;
|
||||
tenantRepository;
|
||||
redisService;
|
||||
logger = new common_1.Logger(AuthService_1.name);
|
||||
constructor(apiKeyRepository, tenantRepository, redisService) {
|
||||
this.apiKeyRepository = apiKeyRepository;
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.redisService = redisService;
|
||||
}
|
||||
async validateApiKey(key, origin, referer) {
|
||||
const cacheKey = `auth:apikey:${key}`;
|
||||
const cachedData = await this.redisService.get(cacheKey);
|
||||
if (cachedData) {
|
||||
this.validateRestrictions(cachedData.apiKey, origin, referer);
|
||||
return cachedData;
|
||||
}
|
||||
const apiKey = await this.apiKeyRepository.findOne({
|
||||
where: { key, isActive: true },
|
||||
relations: ['tenant'],
|
||||
});
|
||||
if (!apiKey || !apiKey.tenant || !apiKey.tenant.isActive) {
|
||||
throw new common_1.UnauthorizedException('Invalid or inactive API Key');
|
||||
}
|
||||
this.validateRestrictions(apiKey, origin, referer);
|
||||
const result = {
|
||||
tenant: apiKey.tenant,
|
||||
apiKey: apiKey,
|
||||
rateLimit: apiKey.rateLimit || tenant_entity_1.RATE_LIMITS[apiKey.tenant.plan] || 100,
|
||||
};
|
||||
await this.redisService.set(cacheKey, result, 3600);
|
||||
this.apiKeyRepository.update(apiKey.id, { lastUsedAt: new Date() }).catch(err => this.logger.error(`Failed to update lastUsedAt for API key ${apiKey.id}: ${err.message}`));
|
||||
return result;
|
||||
}
|
||||
validateRestrictions(apiKey, origin, referer) {
|
||||
if (apiKey.allowedOrigins && apiKey.allowedOrigins.length > 0) {
|
||||
if (!origin || !this.isDomainAllowed(origin, apiKey.allowedOrigins)) {
|
||||
this.logger.warn(`Origin mismatch for API key ${apiKey.id}. Received: ${origin}`);
|
||||
throw new common_1.UnauthorizedException('Request Origin not allowed for this API Key');
|
||||
}
|
||||
}
|
||||
if (apiKey.allowedReferrers && apiKey.allowedReferrers.length > 0) {
|
||||
if (!referer || !this.isDomainAllowed(referer, apiKey.allowedReferrers)) {
|
||||
this.logger.warn(`Referer mismatch for API key ${apiKey.id}. Received: ${referer}`);
|
||||
throw new common_1.UnauthorizedException('Request Referer not allowed for this API Key');
|
||||
}
|
||||
}
|
||||
}
|
||||
isDomainAllowed(domain, allowedList) {
|
||||
const cleanDomain = domain.replace(/^https?:\/\//, '').split('/')[0];
|
||||
return allowedList.some(allowed => {
|
||||
if (allowed === '*')
|
||||
return true;
|
||||
const regex = new RegExp('^' + allowed.replace(/\*/g, '.*') + '$');
|
||||
return regex.test(cleanDomain);
|
||||
});
|
||||
}
|
||||
hashSecret(secret) {
|
||||
return (0, crypto_1.createHash)('sha256').update(secret).digest('hex');
|
||||
}
|
||||
async seedDefaultKey(name, email, keyString) {
|
||||
let tenant = await this.tenantRepository.findOne({ where: { email } });
|
||||
if (!tenant) {
|
||||
tenant = await this.tenantRepository.save({
|
||||
name,
|
||||
email,
|
||||
isActive: true,
|
||||
plan: tenant_entity_1.TenantPlan.ENTERPRISE,
|
||||
});
|
||||
}
|
||||
const existingKey = await this.apiKeyRepository.findOne({ where: { key: keyString } });
|
||||
if (!existingKey) {
|
||||
await this.apiKeyRepository.save({
|
||||
key: keyString,
|
||||
secretHash: this.hashSecret(keyString),
|
||||
name: 'Default Production Key',
|
||||
isActive: true,
|
||||
tenantId: tenant.id,
|
||||
rateLimit: 1000
|
||||
});
|
||||
}
|
||||
}
|
||||
async getApiKeys(tenantId) {
|
||||
return this.apiKeyRepository.find({
|
||||
where: { tenantId },
|
||||
order: { createdAt: 'DESC' }
|
||||
});
|
||||
}
|
||||
async createApiKey(tenantId, name, rateLimit, allowedOrigins) {
|
||||
const existingKeysCount = await this.apiKeyRepository.count({ where: { tenantId } });
|
||||
if (existingKeysCount >= 1) {
|
||||
throw new common_1.ConflictException('Limit reached: Only 1 API key allowed per developer currently.');
|
||||
}
|
||||
const key = `in_${(0, crypto_1.createHash)('md5').update(Math.random().toString()).digest('hex').substring(0, 24)}`;
|
||||
const apiKey = this.apiKeyRepository.create({
|
||||
key,
|
||||
secretHash: this.hashSecret(key),
|
||||
name,
|
||||
tenantId,
|
||||
rateLimit: rateLimit || 100,
|
||||
allowedOrigins: allowedOrigins || [],
|
||||
isActive: true
|
||||
});
|
||||
return this.apiKeyRepository.save(apiKey);
|
||||
}
|
||||
async getDefaultTenant() {
|
||||
const tenant = await this.tenantRepository.findOne({ where: {} });
|
||||
if (!tenant)
|
||||
throw new common_1.NotFoundException('No tenants found in system');
|
||||
return tenant;
|
||||
}
|
||||
async findOrCreateByFirebaseUid(uid, email, name, photoUrl) {
|
||||
let tenant = await this.tenantRepository.findOne({ where: { firebaseUid: uid } });
|
||||
if (!tenant) {
|
||||
tenant = await this.tenantRepository.findOne({ where: { email } });
|
||||
if (tenant) {
|
||||
this.logger.log(`Linking existing tenant ${email} to Firebase UID: ${uid}`);
|
||||
tenant.firebaseUid = uid;
|
||||
if (photoUrl)
|
||||
tenant.photoUrl = photoUrl;
|
||||
tenant = await this.tenantRepository.save(tenant);
|
||||
}
|
||||
else {
|
||||
this.logger.log(`Creating new tenant for Firebase user: ${email} (${uid})`);
|
||||
const isAdmin = email === 'hamzaaleghwairyeen@gmail.com' || email === 'hamzadoctor@gmail.com' || email === 'hamzaayedpython@gmail.com';
|
||||
tenant = await this.tenantRepository.save({
|
||||
firebaseUid: uid,
|
||||
email,
|
||||
name,
|
||||
photoUrl,
|
||||
plan: isAdmin ? tenant_entity_1.TenantPlan.ENTERPRISE : tenant_entity_1.TenantPlan.FREE,
|
||||
isActive: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
const isAdmin = email === 'hamzaaleghwairyeen@gmail.com' || email === 'hamzadoctor@gmail.com' || email === 'hamzaayedpython@gmail.com';
|
||||
if (isAdmin && tenant.plan !== tenant_entity_1.TenantPlan.ENTERPRISE) {
|
||||
tenant.plan = tenant_entity_1.TenantPlan.ENTERPRISE;
|
||||
await this.tenantRepository.save(tenant);
|
||||
}
|
||||
if (tenant.photoUrl !== photoUrl || tenant.name !== name) {
|
||||
await this.tenantRepository.update(tenant.id, { photoUrl, name });
|
||||
}
|
||||
}
|
||||
return tenant;
|
||||
}
|
||||
};
|
||||
exports.AuthService = AuthService;
|
||||
exports.AuthService = AuthService = AuthService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(0, (0, typeorm_1.InjectRepository)(api_key_entity_1.ApiKey)),
|
||||
__param(1, (0, typeorm_1.InjectRepository)(tenant_entity_1.Tenant)),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _a : Object, typeof (_b = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _b : Object, redis_service_1.RedisService])
|
||||
], AuthService);
|
||||
//# sourceMappingURL=auth.service.js.map
|
||||
-1
File diff suppressed because one or more lines are too long
@@ -1,5 +0,0 @@
|
||||
export declare class CreateKeyDto {
|
||||
name: string;
|
||||
rateLimit?: number;
|
||||
allowedOrigins?: string[];
|
||||
}
|
||||
@@ -1,35 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CreateKeyDto = void 0;
|
||||
const class_validator_1 = require("class-validator");
|
||||
class CreateKeyDto {
|
||||
name;
|
||||
rateLimit;
|
||||
allowedOrigins;
|
||||
}
|
||||
exports.CreateKeyDto = CreateKeyDto;
|
||||
__decorate([
|
||||
(0, class_validator_1.IsString)(),
|
||||
__metadata("design:type", String)
|
||||
], CreateKeyDto.prototype, "name", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsOptional)(),
|
||||
(0, class_validator_1.IsNumber)(),
|
||||
__metadata("design:type", Number)
|
||||
], CreateKeyDto.prototype, "rateLimit", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsOptional)(),
|
||||
(0, class_validator_1.IsArray)(),
|
||||
(0, class_validator_1.IsString)({ each: true }),
|
||||
__metadata("design:type", Array)
|
||||
], CreateKeyDto.prototype, "allowedOrigins", void 0);
|
||||
//# sourceMappingURL=create-key.dto.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"create-key.dto.js","sourceRoot":"","sources":["../../../../src/auth/dto/management/create-key.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAA0E;AAE1E,MAAa,YAAY;IAEvB,IAAI,CAAS;IAIb,SAAS,CAAU;IAKnB,cAAc,CAAY;CAC3B;AAZD,oCAYC;AAVC;IADC,IAAA,0BAAQ,GAAE;;0CACE;AAIb;IAFC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;;+CACQ;AAKnB;IAHC,IAAA,4BAAU,GAAE;IACZ,IAAA,yBAAO,GAAE;IACT,IAAA,0BAAQ,EAAC,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;;oDACC"}
|
||||
-16
@@ -1,16 +0,0 @@
|
||||
import { Tenant } from './tenant.entity';
|
||||
export declare class ApiKey {
|
||||
id: string;
|
||||
key: string;
|
||||
secretHash: string;
|
||||
name: string;
|
||||
isActive: boolean;
|
||||
rateLimit: number;
|
||||
allowedOrigins: string[];
|
||||
allowedReferrers: string[];
|
||||
tenant: Tenant;
|
||||
tenantId: string;
|
||||
lastUsedAt: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
-87
@@ -1,87 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiKey = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
const tenant_entity_1 = require("./tenant.entity");
|
||||
let ApiKey = class ApiKey {
|
||||
id;
|
||||
key;
|
||||
secretHash;
|
||||
name;
|
||||
isActive;
|
||||
rateLimit;
|
||||
allowedOrigins;
|
||||
allowedReferrers;
|
||||
tenant;
|
||||
tenantId;
|
||||
lastUsedAt;
|
||||
createdAt;
|
||||
updatedAt;
|
||||
};
|
||||
exports.ApiKey = ApiKey;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
||||
__metadata("design:type", String)
|
||||
], ApiKey.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ unique: true }),
|
||||
(0, typeorm_1.Index)(),
|
||||
__metadata("design:type", String)
|
||||
], ApiKey.prototype, "key", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)(),
|
||||
__metadata("design:type", String)
|
||||
], ApiKey.prototype, "secretHash", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)(),
|
||||
__metadata("design:type", String)
|
||||
], ApiKey.prototype, "name", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ default: true }),
|
||||
__metadata("design:type", Boolean)
|
||||
], ApiKey.prototype, "isActive", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], ApiKey.prototype, "rateLimit", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('simple-array', { nullable: true }),
|
||||
__metadata("design:type", Array)
|
||||
], ApiKey.prototype, "allowedOrigins", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('simple-array', { nullable: true }),
|
||||
__metadata("design:type", Array)
|
||||
], ApiKey.prototype, "allowedReferrers", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.ManyToOne)(() => tenant_entity_1.Tenant, (tenant) => tenant.apiKeys),
|
||||
__metadata("design:type", tenant_entity_1.Tenant)
|
||||
], ApiKey.prototype, "tenant", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)(),
|
||||
__metadata("design:type", String)
|
||||
], ApiKey.prototype, "tenantId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", Date)
|
||||
], ApiKey.prototype, "lastUsedAt", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], ApiKey.prototype, "createdAt", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.UpdateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], ApiKey.prototype, "updatedAt", void 0);
|
||||
exports.ApiKey = ApiKey = __decorate([
|
||||
(0, typeorm_1.Entity)('api_keys')
|
||||
], ApiKey);
|
||||
//# sourceMappingURL=api-key.entity.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"api-key.entity.js","sourceRoot":"","sources":["../../../src/auth/entities/api-key.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAuH;AACvH,mDAAyC;AAGlC,IAAM,MAAM,GAAZ,MAAM,MAAM;IAEjB,EAAE,CAAS;IAIX,GAAG,CAAS;IAGZ,UAAU,CAAS;IAGnB,IAAI,CAAS;IAGb,QAAQ,CAAU;IAGlB,SAAS,CAAS;IAGlB,cAAc,CAAW;IAGzB,gBAAgB,CAAW;IAG3B,MAAM,CAAS;IAGf,QAAQ,CAAS;IAGjB,UAAU,CAAO;IAGjB,SAAS,CAAO;IAGhB,SAAS,CAAO;CACjB,CAAA;AAxCY,wBAAM;AAEjB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;kCACpB;AAIX;IAFC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;IACxB,IAAA,eAAK,GAAE;;mCACI;AAGZ;IADC,IAAA,gBAAM,GAAE;;0CACU;AAGnB;IADC,IAAA,gBAAM,GAAE;;oCACI;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;wCACR;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACT;AAGlB;IADC,IAAA,gBAAM,EAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CAClB;AAGzB;IADC,IAAA,gBAAM,EAAC,cAAc,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;gDAChB;AAG3B;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,sBAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,OAAO,CAAC;8BAC5C,sBAAM;sCAAC;AAGf;IADC,IAAA,gBAAM,GAAE;;wCACQ;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BACf,IAAI;0CAAC;AAGjB;IADC,IAAA,0BAAgB,GAAE;8BACR,IAAI;yCAAC;AAGhB;IADC,IAAA,0BAAgB,GAAE;8BACR,IAAI;yCAAC;iBAvCL,MAAM;IADlB,IAAA,gBAAM,EAAC,UAAU,CAAC;GACN,MAAM,CAwClB"}
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
import { ApiKey } from './api-key.entity';
|
||||
export declare enum TenantPlan {
|
||||
FREE = "FREE",
|
||||
STARTER = "STARTER",
|
||||
PRO = "PRO",
|
||||
ENTERPRISE = "ENTERPRISE"
|
||||
}
|
||||
export declare enum TenantRole {
|
||||
USER = "USER",
|
||||
ADMIN = "ADMIN"
|
||||
}
|
||||
export declare const QUOTA_LIMITS: Record<TenantPlan, number>;
|
||||
export declare const RATE_LIMITS: Record<TenantPlan, number>;
|
||||
export declare class Tenant {
|
||||
id: string;
|
||||
name: string;
|
||||
email: string;
|
||||
plan: TenantPlan;
|
||||
firebaseUid: string;
|
||||
role: TenantRole;
|
||||
photoUrl: string;
|
||||
isActive: boolean;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
apiKeys: ApiKey[];
|
||||
}
|
||||
-108
@@ -1,108 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Tenant = exports.RATE_LIMITS = exports.QUOTA_LIMITS = exports.TenantRole = exports.TenantPlan = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
const api_key_entity_1 = require("./api-key.entity");
|
||||
var TenantPlan;
|
||||
(function (TenantPlan) {
|
||||
TenantPlan["FREE"] = "FREE";
|
||||
TenantPlan["STARTER"] = "STARTER";
|
||||
TenantPlan["PRO"] = "PRO";
|
||||
TenantPlan["ENTERPRISE"] = "ENTERPRISE";
|
||||
})(TenantPlan || (exports.TenantPlan = TenantPlan = {}));
|
||||
var TenantRole;
|
||||
(function (TenantRole) {
|
||||
TenantRole["USER"] = "USER";
|
||||
TenantRole["ADMIN"] = "ADMIN";
|
||||
})(TenantRole || (exports.TenantRole = TenantRole = {}));
|
||||
exports.QUOTA_LIMITS = {
|
||||
[TenantPlan.FREE]: 8000,
|
||||
[TenantPlan.STARTER]: 25000,
|
||||
[TenantPlan.PRO]: 100000,
|
||||
[TenantPlan.ENTERPRISE]: 500000,
|
||||
};
|
||||
exports.RATE_LIMITS = {
|
||||
[TenantPlan.FREE]: 5,
|
||||
[TenantPlan.STARTER]: 100,
|
||||
[TenantPlan.PRO]: 500,
|
||||
[TenantPlan.ENTERPRISE]: 50000,
|
||||
};
|
||||
let Tenant = class Tenant {
|
||||
id;
|
||||
name;
|
||||
email;
|
||||
plan;
|
||||
firebaseUid;
|
||||
role;
|
||||
photoUrl;
|
||||
isActive;
|
||||
createdAt;
|
||||
updatedAt;
|
||||
apiKeys;
|
||||
};
|
||||
exports.Tenant = Tenant;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
||||
__metadata("design:type", String)
|
||||
], Tenant.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)(),
|
||||
__metadata("design:type", String)
|
||||
], Tenant.prototype, "name", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ unique: true }),
|
||||
__metadata("design:type", String)
|
||||
], Tenant.prototype, "email", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'enum',
|
||||
enum: TenantPlan,
|
||||
default: TenantPlan.FREE,
|
||||
}),
|
||||
__metadata("design:type", String)
|
||||
], Tenant.prototype, "plan", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true, unique: true }),
|
||||
__metadata("design:type", String)
|
||||
], Tenant.prototype, "firebaseUid", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'enum',
|
||||
enum: TenantRole,
|
||||
default: TenantRole.USER,
|
||||
}),
|
||||
__metadata("design:type", String)
|
||||
], Tenant.prototype, "role", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], Tenant.prototype, "photoUrl", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ default: true }),
|
||||
__metadata("design:type", Boolean)
|
||||
], Tenant.prototype, "isActive", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], Tenant.prototype, "createdAt", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.UpdateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], Tenant.prototype, "updatedAt", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.OneToMany)(() => api_key_entity_1.ApiKey, (apiKey) => apiKey.tenant),
|
||||
__metadata("design:type", Array)
|
||||
], Tenant.prototype, "apiKeys", void 0);
|
||||
exports.Tenant = Tenant = __decorate([
|
||||
(0, typeorm_1.Entity)('tenants')
|
||||
], Tenant);
|
||||
//# sourceMappingURL=tenant.entity.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"tenant.entity.js","sourceRoot":"","sources":["../../../src/auth/entities/tenant.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAgH;AAChH,qDAA0C;AAE1C,IAAY,UAKX;AALD,WAAY,UAAU;IACpB,2BAAa,CAAA;IACb,iCAAmB,CAAA;IACnB,yBAAW,CAAA;IACX,uCAAyB,CAAA;AAC3B,CAAC,EALW,UAAU,0BAAV,UAAU,QAKrB;AAED,IAAY,UAGX;AAHD,WAAY,UAAU;IACpB,2BAAa,CAAA;IACb,6BAAe,CAAA;AACjB,CAAC,EAHW,UAAU,0BAAV,UAAU,QAGrB;AAEY,QAAA,YAAY,GAA+B;IACtD,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,IAAI;IACvB,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,KAAK;IAC3B,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,MAAM;IACxB,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM;CAChC,CAAC;AAEW,QAAA,WAAW,GAA+B;IACrD,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;IACpB,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,GAAG;IACzB,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,GAAG;IACrB,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,KAAK;CAC/B,CAAC;AAGK,IAAM,MAAM,GAAZ,MAAM,MAAM;IAEjB,EAAE,CAAS;IAGX,IAAI,CAAS;IAGb,KAAK,CAAS;IAOd,IAAI,CAAa;IAGjB,WAAW,CAAS;IAOpB,IAAI,CAAa;IAGjB,QAAQ,CAAS;IAGjB,QAAQ,CAAU;IAGlB,SAAS,CAAO;IAGhB,SAAS,CAAO;IAGhB,OAAO,CAAW;CACnB,CAAA;AAzCY,wBAAM;AAEjB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;kCACpB;AAGX;IADC,IAAA,gBAAM,GAAE;;oCACI;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;;qCACX;AAOd;IALC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,UAAU,CAAC,IAAI;KACzB,CAAC;;oCACe;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;;2CACrB;AAOpB;IALC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,UAAU;QAChB,OAAO,EAAE,UAAU,CAAC,IAAI;KACzB,CAAC;;oCACe;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wCACV;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;wCACR;AAGlB;IADC,IAAA,0BAAgB,GAAE;8BACR,IAAI;yCAAC;AAGhB;IADC,IAAA,0BAAgB,GAAE;8BACR,IAAI;yCAAC;AAGhB;IADC,IAAA,mBAAS,EAAC,GAAG,EAAE,CAAC,uBAAM,EAAE,CAAC,MAAM,EAAE,EAAE,CAAC,MAAM,CAAC,MAAM,CAAC;;uCACjC;iBAxCP,MAAM;IADlB,IAAA,gBAAM,EAAC,SAAS,CAAC;GACL,MAAM,CAyClB"}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
import { OnModuleInit } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import * as admin from 'firebase-admin';
|
||||
export declare class FirebaseAdminService implements OnModuleInit {
|
||||
private configService;
|
||||
private readonly logger;
|
||||
private firebaseApp;
|
||||
constructor(configService: ConfigService);
|
||||
onModuleInit(): void;
|
||||
verifyIdToken(token: string): Promise<admin.auth.DecodedIdToken>;
|
||||
}
|
||||
-99
@@ -1,99 +0,0 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var FirebaseAdminService_1;
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.FirebaseAdminService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const admin = __importStar(require("firebase-admin"));
|
||||
const fs = __importStar(require("fs"));
|
||||
const path = __importStar(require("path"));
|
||||
let FirebaseAdminService = FirebaseAdminService_1 = class FirebaseAdminService {
|
||||
configService;
|
||||
logger = new common_1.Logger(FirebaseAdminService_1.name);
|
||||
firebaseApp;
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
}
|
||||
onModuleInit() {
|
||||
const keyPath = this.configService.get('FIREBASE_SERVICE_ACCOUNT_PATH');
|
||||
if (!keyPath) {
|
||||
this.logger.error('FIREBASE_SERVICE_ACCOUNT_PATH is not defined in environment variables');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
const absolutePath = path.isAbsolute(keyPath)
|
||||
? keyPath
|
||||
: path.resolve(process.cwd(), keyPath);
|
||||
if (!fs.existsSync(absolutePath)) {
|
||||
this.logger.error(`Firebase service account key not found at: ${absolutePath}`);
|
||||
return;
|
||||
}
|
||||
const serviceAccount = JSON.parse(fs.readFileSync(absolutePath, 'utf8'));
|
||||
this.firebaseApp = admin.initializeApp({
|
||||
credential: admin.credential.cert(serviceAccount),
|
||||
});
|
||||
this.logger.log('✅ Firebase Admin SDK initialized successfully');
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Failed to initialize Firebase Admin SDK: ${error.message}`);
|
||||
}
|
||||
}
|
||||
async verifyIdToken(token) {
|
||||
try {
|
||||
return await admin.auth().verifyIdToken(token);
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Token verification failed: ${error.message}`);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.FirebaseAdminService = FirebaseAdminService;
|
||||
exports.FirebaseAdminService = FirebaseAdminService = FirebaseAdminService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _a : Object])
|
||||
], FirebaseAdminService);
|
||||
//# sourceMappingURL=firebase-admin.service.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"firebase-admin.service.js","sourceRoot":"","sources":["../../src/auth/firebase-admin.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAkE;AAClE,2CAA+C;AAC/C,sDAAwC;AACxC,uCAAyB;AACzB,2CAA6B;AAGtB,IAAM,oBAAoB,4BAA1B,MAAM,oBAAoB;IAIX;IAHH,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IACxD,WAAW,CAAgB;IAEnC,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAEpD,YAAY;QACV,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,+BAA+B,CAAC,CAAC;QAEhF,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uEAAuE,CAAC,CAAC;YAC3F,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YAEH,MAAM,YAAY,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;gBAC3C,CAAC,CAAC,OAAO;gBACT,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,OAAO,CAAC,CAAC;YAEzC,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;gBACjC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,8CAA8C,YAAY,EAAE,CAAC,CAAC;gBAChF,OAAO;YACT,CAAC;YAED,MAAM,cAAc,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;YAEzE,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,aAAa,CAAC;gBACrC,UAAU,EAAE,KAAK,CAAC,UAAU,CAAC,IAAI,CAAC,cAAc,CAAC;aAClD,CAAC,CAAC;YAEH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;QACnE,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,4CAA4C,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACjF,CAAC;IACH,CAAC;IAED,KAAK,CAAC,aAAa,CAAC,KAAa;QAC/B,IAAI,CAAC;YACH,OAAO,MAAM,KAAK,CAAC,IAAI,EAAE,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;QACjD,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,8BAA8B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACjE,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;CACF,CAAA;AA7CY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;yDAKwB,sBAAa,oBAAb,sBAAa;GAJrC,oBAAoB,CA6ChC"}
|
||||
@@ -1,10 +0,0 @@
|
||||
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { FirebaseAdminService } from '../firebase-admin.service';
|
||||
import { AuthService } from '../auth.service';
|
||||
export declare class FirebaseAuthGuard implements CanActivate {
|
||||
private firebaseAdminService;
|
||||
private authService;
|
||||
private readonly logger;
|
||||
constructor(firebaseAdminService: FirebaseAdminService, authService: AuthService);
|
||||
canActivate(context: ExecutionContext): Promise<boolean>;
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var FirebaseAuthGuard_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.FirebaseAuthGuard = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const firebase_admin_service_1 = require("../firebase-admin.service");
|
||||
const auth_service_1 = require("../auth.service");
|
||||
let FirebaseAuthGuard = FirebaseAuthGuard_1 = class FirebaseAuthGuard {
|
||||
firebaseAdminService;
|
||||
authService;
|
||||
logger = new common_1.Logger(FirebaseAuthGuard_1.name);
|
||||
constructor(firebaseAdminService, authService) {
|
||||
this.firebaseAdminService = firebaseAdminService;
|
||||
this.authService = authService;
|
||||
}
|
||||
async canActivate(context) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const authHeader = request.headers.authorization;
|
||||
if (!authHeader || !authHeader.startsWith('Bearer ')) {
|
||||
throw new common_1.UnauthorizedException('Missing or invalid Authorization header');
|
||||
}
|
||||
const token = authHeader.split('Bearer ')[1];
|
||||
try {
|
||||
const decodedToken = await this.firebaseAdminService.verifyIdToken(token);
|
||||
const tenant = await this.authService.findOrCreateByFirebaseUid(decodedToken.uid, decodedToken.email || '', decodedToken.name || decodedToken.email?.split('@')[0] || 'Unknown User', decodedToken.picture);
|
||||
if (!tenant.isActive) {
|
||||
throw new common_1.UnauthorizedException('Tenant account is disabled');
|
||||
}
|
||||
request['tenant'] = tenant;
|
||||
request['user'] = decodedToken;
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Firebase Auth failed: ${error.message}`);
|
||||
throw new common_1.UnauthorizedException(error.message || 'Authentication failed');
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.FirebaseAuthGuard = FirebaseAuthGuard;
|
||||
exports.FirebaseAuthGuard = FirebaseAuthGuard = FirebaseAuthGuard_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [firebase_admin_service_1.FirebaseAdminService,
|
||||
auth_service_1.AuthService])
|
||||
], FirebaseAuthGuard);
|
||||
//# sourceMappingURL=firebase-auth.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"firebase-auth.guard.js","sourceRoot":"","sources":["../../../src/auth/guards/firebase-auth.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAMwB;AACxB,sEAAiE;AACjE,kDAA8C;AAGvC,IAAM,iBAAiB,yBAAvB,MAAM,iBAAiB;IAIlB;IACA;IAJO,MAAM,GAAG,IAAI,eAAM,CAAC,mBAAiB,CAAC,IAAI,CAAC,CAAC;IAE7D,YACU,oBAA0C,EAC1C,WAAwB;QADxB,yBAAoB,GAApB,oBAAoB,CAAsB;QAC1C,gBAAW,GAAX,WAAW,CAAa;IAC/B,CAAC;IAEJ,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,UAAU,GAAG,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC;QAEjD,IAAI,CAAC,UAAU,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;YACrD,MAAM,IAAI,8BAAqB,CAAC,yCAAyC,CAAC,CAAC;QAC7E,CAAC;QAED,MAAM,KAAK,GAAG,UAAU,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;QAE7C,IAAI,CAAC;YAEH,MAAM,YAAY,GAAG,MAAM,IAAI,CAAC,oBAAoB,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAG1E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,yBAAyB,CAC7D,YAAY,CAAC,GAAG,EAChB,YAAY,CAAC,KAAK,IAAI,EAAE,EACxB,YAAY,CAAC,IAAI,IAAI,YAAY,CAAC,KAAK,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,cAAc,EACxE,YAAY,CAAC,OAAO,CACrB,CAAC;YAEF,IAAI,CAAC,MAAM,CAAC,QAAQ,EAAE,CAAC;gBACrB,MAAM,IAAI,8BAAqB,CAAC,4BAA4B,CAAC,CAAC;YAChE,CAAC;YAGD,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC;YAC3B,OAAO,CAAC,MAAM,CAAC,GAAG,YAAY,CAAC;YAE/B,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC5D,MAAM,IAAI,8BAAqB,CAAC,KAAK,CAAC,OAAO,IAAI,uBAAuB,CAAC,CAAC;QAC5E,CAAC;IACH,CAAC;CACF,CAAA;AA5CY,8CAAiB;4BAAjB,iBAAiB;IAD7B,IAAA,mBAAU,GAAE;qCAKqB,6CAAoB;QAC7B,0BAAW;GALvB,iBAAiB,CA4C7B"}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { AuthService } from './auth.service';
|
||||
import { CreateKeyDto } from './dto/management/create-key.dto';
|
||||
export declare class TenantController {
|
||||
private readonly authService;
|
||||
constructor(authService: AuthService);
|
||||
createKey(req: any, dto: CreateKeyDto): Promise<import("./entities/api-key.entity").ApiKey>;
|
||||
getKeys(req: any): Promise<import("./entities/api-key.entity").ApiKey[]>;
|
||||
getMe(req: any): Promise<any>;
|
||||
}
|
||||
-74
@@ -1,74 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TenantController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const auth_service_1 = require("./auth.service");
|
||||
const create_key_dto_1 = require("./dto/management/create-key.dto");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
const firebase_auth_guard_1 = require("./guards/firebase-auth.guard");
|
||||
let TenantController = class TenantController {
|
||||
authService;
|
||||
constructor(authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
async createKey(req, dto) {
|
||||
if (req.user && req.user.email_verified === false) {
|
||||
throw new common_1.ForbiddenException('You must verify your email address before creating an API key.');
|
||||
}
|
||||
const tenantId = req.tenant.id;
|
||||
return this.authService.createApiKey(tenantId, dto.name, dto.rateLimit, dto.allowedOrigins);
|
||||
}
|
||||
async getKeys(req) {
|
||||
const tenantId = req.tenant.id;
|
||||
return this.authService.getApiKeys(tenantId);
|
||||
}
|
||||
async getMe(req) {
|
||||
return req.tenant;
|
||||
}
|
||||
};
|
||||
exports.TenantController = TenantController;
|
||||
__decorate([
|
||||
(0, common_1.Post)('keys'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Create a new API key' }),
|
||||
__param(0, (0, common_1.Req)()),
|
||||
__param(1, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, create_key_dto_1.CreateKeyDto]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TenantController.prototype, "createKey", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('keys'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get all API keys for the authenticated tenant' }),
|
||||
__param(0, (0, common_1.Req)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TenantController.prototype, "getKeys", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('me'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get current authenticated tenant info' }),
|
||||
__param(0, (0, common_1.Req)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TenantController.prototype, "getMe", null);
|
||||
exports.TenantController = TenantController = __decorate([
|
||||
(0, swagger_1.ApiTags)('auth'),
|
||||
(0, swagger_1.ApiBearerAuth)(),
|
||||
(0, common_1.UseGuards)(firebase_auth_guard_1.FirebaseAuthGuard),
|
||||
(0, common_1.Controller)('auth/management'),
|
||||
__metadata("design:paramtypes", [auth_service_1.AuthService])
|
||||
], TenantController);
|
||||
//# sourceMappingURL=tenant.controller.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"tenant.controller.js","sourceRoot":"","sources":["../../src/auth/tenant.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAwG;AACxG,iDAA6C;AAC7C,oEAA+D;AAC/D,6CAAuE;AACvE,sEAAiE;AAM1D,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IACE;IAA7B,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAInD,AAAN,KAAK,CAAC,SAAS,CACN,GAAQ,EACP,GAAiB;QAEzB,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,CAAC,cAAc,KAAK,KAAK,EAAE,CAAC;YAClD,MAAM,IAAI,2BAAkB,CAAC,gEAAgE,CAAC,CAAC;QACjG,CAAC;QAED,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,YAAY,CAClC,QAAQ,EACR,GAAG,CAAC,IAAI,EACR,GAAG,CAAC,SAAS,EACb,GAAG,CAAC,cAAc,CACnB,CAAC;IACJ,CAAC;IAIK,AAAN,KAAK,CAAC,OAAO,CAAQ,GAAQ;QAC3B,MAAM,QAAQ,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;QAC/B,OAAO,IAAI,CAAC,WAAW,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC/C,CAAC;IAIK,AAAN,KAAK,CAAC,KAAK,CAAQ,GAAQ;QACzB,OAAO,GAAG,CAAC,MAAM,CAAC;IACpB,CAAC;CACF,CAAA;AAlCY,4CAAgB;AAKrB;IAFL,IAAA,aAAI,EAAC,MAAM,CAAC;IACZ,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,sBAAsB,EAAE,CAAC;IAE/C,WAAA,IAAA,YAAG,GAAE,CAAA;IACL,WAAA,IAAA,aAAI,GAAE,CAAA;;6CAAM,6BAAY;;iDAa1B;AAIK;IAFL,IAAA,YAAG,EAAC,MAAM,CAAC;IACX,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,+CAA+C,EAAE,CAAC;IAC5D,WAAA,IAAA,YAAG,GAAE,CAAA;;;;+CAGnB;AAIK;IAFL,IAAA,YAAG,EAAC,IAAI,CAAC;IACT,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,uCAAuC,EAAE,CAAC;IACtD,WAAA,IAAA,YAAG,GAAE,CAAA;;;;6CAEjB;2BAjCU,gBAAgB;IAJ5B,IAAA,iBAAO,EAAC,MAAM,CAAC;IACf,IAAA,uBAAa,GAAE;IACf,IAAA,kBAAS,EAAC,uCAAiB,CAAC;IAC5B,IAAA,mBAAU,EAAC,iBAAiB,CAAC;qCAEc,0BAAW;GAD1C,gBAAgB,CAkC5B"}
|
||||
-36
@@ -1,36 +0,0 @@
|
||||
import { BillingService } from './billing.service';
|
||||
import { PayMobProvider } from './providers/paymob.provider';
|
||||
import { BinanceProvider } from './providers/binance.provider';
|
||||
import { PaymentProvider } from './entities/transaction.entity';
|
||||
export declare class BillingController {
|
||||
private billingService;
|
||||
private paymobProvider;
|
||||
private binanceProvider;
|
||||
private readonly logger;
|
||||
constructor(billingService: BillingService, paymobProvider: PayMobProvider, binanceProvider: BinanceProvider);
|
||||
getSubscription(req: any): Promise<import("./entities/subscription.entity").Subscription>;
|
||||
checkout(req: any, body: {
|
||||
plan: string;
|
||||
provider: PaymentProvider;
|
||||
}): Promise<{
|
||||
checkoutUrl: any;
|
||||
orderId: string;
|
||||
prepayId: any;
|
||||
} | {
|
||||
checkoutUrl: string;
|
||||
orderId: string;
|
||||
}>;
|
||||
handlePaymobCallback(query: any, req: any): Promise<string>;
|
||||
handlePaymobWebhook(body: any, hmac: string): Promise<{
|
||||
status: string;
|
||||
message: string;
|
||||
} | {
|
||||
status: string;
|
||||
message?: undefined;
|
||||
}>;
|
||||
handleBinanceWebhook(body: any, req: any): Promise<{
|
||||
returnCode: string;
|
||||
returnMessage: any;
|
||||
}>;
|
||||
getInvoices(req: any): Promise<import("./entities/transaction.entity").Transaction[]>;
|
||||
}
|
||||
-259
@@ -1,259 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var BillingController_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BillingController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const billing_service_1 = require("./billing.service");
|
||||
const paymob_provider_1 = require("./providers/paymob.provider");
|
||||
const binance_provider_1 = require("./providers/binance.provider");
|
||||
const firebase_auth_guard_1 = require("../auth/guards/firebase-auth.guard");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
const transaction_entity_1 = require("./entities/transaction.entity");
|
||||
let BillingController = BillingController_1 = class BillingController {
|
||||
billingService;
|
||||
paymobProvider;
|
||||
binanceProvider;
|
||||
logger = new common_1.Logger(BillingController_1.name);
|
||||
constructor(billingService, paymobProvider, binanceProvider) {
|
||||
this.billingService = billingService;
|
||||
this.paymobProvider = paymobProvider;
|
||||
this.binanceProvider = binanceProvider;
|
||||
}
|
||||
async getSubscription(req) {
|
||||
return this.billingService.getSubscription(req.tenant.id);
|
||||
}
|
||||
async checkout(req, body) {
|
||||
const tenantId = req.tenant.id;
|
||||
const plan = body.plan;
|
||||
const pricing = {
|
||||
'STARTER': 29,
|
||||
'PRO': 89,
|
||||
};
|
||||
const amount = pricing[plan] || 0;
|
||||
if (amount === 0 && plan !== 'FREE') {
|
||||
throw new common_1.BadRequestException('Invalid plan or price not configured');
|
||||
}
|
||||
if (body.provider === transaction_entity_1.PaymentProvider.PAYMOB) {
|
||||
const { paymentKey, orderId } = await this.paymobProvider.createPaymentKey(tenantId, amount, plan);
|
||||
const iframeId = this.billingService.getIframeId();
|
||||
const checkoutUrl = `https://accept.paymob.com/api/acceptance/iframes/${iframeId}?payment_token=${paymentKey}`;
|
||||
return { checkoutUrl, orderId };
|
||||
}
|
||||
if (body.provider === transaction_entity_1.PaymentProvider.BINANCE) {
|
||||
return this.binanceProvider.createOrder(tenantId, amount, plan);
|
||||
}
|
||||
throw new common_1.BadRequestException('Invalid payment provider');
|
||||
}
|
||||
async handlePaymobCallback(query, req) {
|
||||
this.logger.log(`🔄 PayMob Redirect Callback: ID ${query.id}, Success: ${query.success}`);
|
||||
if (query.success === 'true' && query.id) {
|
||||
try {
|
||||
const txDetails = await this.paymobProvider.getTransactionDetails(query.id);
|
||||
if (txDetails && txDetails.success === true) {
|
||||
const extraDesc = txDetails.order?.shipping_data?.extra_description || "";
|
||||
const [tenantId, plan] = extraDesc.split('|');
|
||||
if (tenantId) {
|
||||
await this.billingService.processSuccessfulPayment(query.id.toString(), transaction_entity_1.PaymentProvider.PAYMOB, txDetails.amount_cents / 100, { tenantId, plan: plan || 'PRO' });
|
||||
this.logger.log(`🚀 Immediate Activation triggered via Callback for Tenant ${tenantId}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
this.logger.error(`Failed during immediate activation fallback: ${e.message}`);
|
||||
}
|
||||
}
|
||||
const targetUrl = `https://map-dashboard.intaleqapp.com/dashboard.html#billing?payment_status=${query.success === 'true' ? 'success' : 'failed'}&id=${query.id}`;
|
||||
return `
|
||||
<!DOCTYPE html>
|
||||
<html lang="ar" dir="rtl">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Intaleq Maps | تمت العملية بنجاح</title>
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Cairo:wght@400;700;900&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Cairo', sans-serif; background: #050505; color: white; overflow: hidden; }
|
||||
.pulse { animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite; }
|
||||
@keyframes pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: .7; transform: scale(0.95); } }
|
||||
</style>
|
||||
</head>
|
||||
<body class="flex items-center justify-center h-screen">
|
||||
<div class="text-center space-y-8 max-w-md p-10 bg-white shadow-2xl rounded-[3rem] border border-white/10 relative overflow-hidden">
|
||||
<!-- Animated Background Glow -->
|
||||
<div class="absolute -top-20 -left-20 w-40 h-40 bg-blue-600/20 blur-[80px] rounded-full"></div>
|
||||
<div class="absolute -bottom-20 -right-20 w-40 h-40 bg-emerald-600/20 blur-[80px] rounded-full"></div>
|
||||
|
||||
<div class="relative z-10">
|
||||
<div class="w-24 h-24 bg-emerald-500 rounded-full mx-auto flex items-center justify-center shadow-[0_0_50px_rgba(16,185,129,0.4)] pulse mb-8">
|
||||
<svg class="w-12 h-12 text-white" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7"></path>
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<h1 class="text-3xl font-black text-slate-900 mb-4">شكراً لك، حمزة!</h1>
|
||||
<p class="text-slate-500 font-bold mb-8">تم تفعيل خطة <span class="text-blue-600">PRO</span> بنجاح. يتم الآن توجيهك إلى لوحة التحكم...</p>
|
||||
|
||||
<div class="flex items-center justify-center gap-2">
|
||||
<div class="w-2 h-2 bg-blue-600 rounded-full animate-bounce"></div>
|
||||
<div class="w-2 h-2 bg-blue-600 rounded-full animate-bounce [animation-delay:-0.15s]"></div>
|
||||
<div class="w-2 h-2 bg-blue-600 rounded-full animate-bounce [animation-delay:-0.3s]"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
setTimeout(() => {
|
||||
window.location.href = "${targetUrl}";
|
||||
}, 2500);
|
||||
</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
}
|
||||
async handlePaymobWebhook(body, hmac) {
|
||||
this.logger.log(`📥 PayMob Webhook Received: Transaction ID ${body.obj?.id}`);
|
||||
if (!this.paymobProvider.verifyHmac(body.obj, hmac)) {
|
||||
this.logger.error('❌ PayMob HMAC Verification Failed');
|
||||
throw new common_1.BadRequestException('Invalid signature');
|
||||
}
|
||||
if (body.obj.success === true) {
|
||||
const extraDesc = body.obj.order?.shipping_data?.extra_description || "";
|
||||
const [tenantId, plan] = extraDesc.split('|');
|
||||
if (!tenantId) {
|
||||
this.logger.error(`❌ PayMob Webhook failed: No tenantId in metadata. Raw: ${extraDesc}`);
|
||||
return { status: 'error', message: 'No tenantId found' };
|
||||
}
|
||||
await this.billingService.processSuccessfulPayment(body.obj.id.toString(), transaction_entity_1.PaymentProvider.PAYMOB, body.obj.amount_cents / 100, {
|
||||
tenantId,
|
||||
plan: plan || 'PRO'
|
||||
});
|
||||
}
|
||||
return { status: 'success' };
|
||||
}
|
||||
async handleBinanceWebhook(body, req) {
|
||||
const timestamp = req.headers['binancepay-timestamp'];
|
||||
const nonce = req.headers['binancepay-nonce'];
|
||||
const signature = req.headers['binancepay-signature'];
|
||||
this.logger.log(`📥 Binance Webhook Received: Transaction ID ${body.bizId || 'UNKNOWN'}`);
|
||||
if (!timestamp || !nonce || !signature) {
|
||||
this.logger.error('❌ Binance Webhook: Missing required headers');
|
||||
throw new common_1.BadRequestException('Missing headers');
|
||||
}
|
||||
if (!this.binanceProvider.verifySignature(timestamp, nonce, signature, body)) {
|
||||
this.logger.error('❌ Binance Signature Verification Failed');
|
||||
throw new common_1.BadRequestException('Invalid signature');
|
||||
}
|
||||
if (body.bizStatus === 'PAY_SUCCESS') {
|
||||
try {
|
||||
let dataObj = {};
|
||||
if (body.data) {
|
||||
dataObj = JSON.parse(body.data);
|
||||
}
|
||||
let tenantId = '';
|
||||
let plan = 'PRO';
|
||||
if (dataObj.passThroughInfo) {
|
||||
const passThrough = JSON.parse(dataObj.passThroughInfo);
|
||||
tenantId = passThrough.tenantId;
|
||||
plan = passThrough.plan;
|
||||
}
|
||||
if (!tenantId) {
|
||||
this.logger.error(`❌ Binance Webhook failed: No tenantId found. Body: ${JSON.stringify(body)}`);
|
||||
return { returnCode: 'FAIL', returnMessage: 'No tenantId found' };
|
||||
}
|
||||
const externalTxId = dataObj.merchantTradeNo || body.bizId?.toString();
|
||||
await this.billingService.processSuccessfulPayment(externalTxId, transaction_entity_1.PaymentProvider.BINANCE, 0, { tenantId, plan });
|
||||
this.logger.log(`✅ Binance Payment Processed Successfully for Tenant ${tenantId}`);
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error(`❌ Failed to process Binance payment: ${err.message}`);
|
||||
return { returnCode: 'FAIL', returnMessage: err.message };
|
||||
}
|
||||
}
|
||||
return {
|
||||
returnCode: 'SUCCESS',
|
||||
returnMessage: null,
|
||||
};
|
||||
}
|
||||
async getInvoices(req) {
|
||||
return this.billingService.getTransactionHistory(req.tenant.id);
|
||||
}
|
||||
};
|
||||
exports.BillingController = BillingController;
|
||||
__decorate([
|
||||
(0, swagger_1.ApiBearerAuth)(),
|
||||
(0, common_1.UseGuards)(firebase_auth_guard_1.FirebaseAuthGuard),
|
||||
(0, common_1.Get)('subscription'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get current subscription details' }),
|
||||
__param(0, (0, common_1.Req)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], BillingController.prototype, "getSubscription", null);
|
||||
__decorate([
|
||||
(0, swagger_1.ApiBearerAuth)(),
|
||||
(0, common_1.UseGuards)(firebase_auth_guard_1.FirebaseAuthGuard),
|
||||
(0, common_1.Post)('checkout'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Initialize a checkout session' }),
|
||||
__param(0, (0, common_1.Req)()),
|
||||
__param(1, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], BillingController.prototype, "checkout", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('callback/paymob'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'PayMob Redirection Handler' }),
|
||||
__param(0, (0, common_1.Query)()),
|
||||
__param(1, (0, common_1.Req)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], BillingController.prototype, "handlePaymobCallback", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('webhooks/paymob'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'PayMob Payment Webhook' }),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__param(1, (0, common_1.Query)('hmac')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, String]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], BillingController.prototype, "handlePaymobWebhook", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('webhooks/binance'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Binance Payment Webhook' }),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__param(1, (0, common_1.Req)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object, Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], BillingController.prototype, "handleBinanceWebhook", null);
|
||||
__decorate([
|
||||
(0, swagger_1.ApiBearerAuth)(),
|
||||
(0, common_1.UseGuards)(firebase_auth_guard_1.FirebaseAuthGuard),
|
||||
(0, common_1.Get)('invoices'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get payment history' }),
|
||||
__param(0, (0, common_1.Req)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], BillingController.prototype, "getInvoices", null);
|
||||
exports.BillingController = BillingController = BillingController_1 = __decorate([
|
||||
(0, swagger_1.ApiTags)('billing'),
|
||||
(0, common_1.Controller)('billing'),
|
||||
__metadata("design:paramtypes", [billing_service_1.BillingService,
|
||||
paymob_provider_1.PayMobProvider,
|
||||
binance_provider_1.BinanceProvider])
|
||||
], BillingController);
|
||||
//# sourceMappingURL=billing.controller.js.map
|
||||
File diff suppressed because one or more lines are too long
-2
@@ -1,2 +0,0 @@
|
||||
export declare class BillingModule {
|
||||
}
|
||||
-32
@@ -1,32 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BillingModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const subscription_entity_1 = require("./entities/subscription.entity");
|
||||
const transaction_entity_1 = require("./entities/transaction.entity");
|
||||
const billing_service_1 = require("./billing.service");
|
||||
const billing_controller_1 = require("./billing.controller");
|
||||
const paymob_provider_1 = require("./providers/paymob.provider");
|
||||
const binance_provider_1 = require("./providers/binance.provider");
|
||||
const tenant_entity_1 = require("../auth/entities/tenant.entity");
|
||||
let BillingModule = class BillingModule {
|
||||
};
|
||||
exports.BillingModule = BillingModule;
|
||||
exports.BillingModule = BillingModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [
|
||||
typeorm_1.TypeOrmModule.forFeature([subscription_entity_1.Subscription, transaction_entity_1.Transaction, tenant_entity_1.Tenant]),
|
||||
],
|
||||
providers: [billing_service_1.BillingService, paymob_provider_1.PayMobProvider, binance_provider_1.BinanceProvider],
|
||||
controllers: [billing_controller_1.BillingController],
|
||||
exports: [billing_service_1.BillingService],
|
||||
})
|
||||
], BillingModule);
|
||||
//# sourceMappingURL=billing.module.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"billing.module.js","sourceRoot":"","sources":["../../src/billing/billing.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,wEAA8D;AAC9D,sEAA4D;AAC5D,uDAAmD;AACnD,6DAAyD;AACzD,iEAA6D;AAC7D,mEAA+D;AAC/D,kEAAwD;AAUjD,IAAM,aAAa,GAAnB,MAAM,aAAa;CAAG,CAAA;AAAhB,sCAAa;wBAAb,aAAa;IARzB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,kCAAY,EAAE,gCAAW,EAAE,sBAAM,CAAC,CAAC;SAC9D;QACD,SAAS,EAAE,CAAC,gCAAc,EAAE,gCAAc,EAAE,kCAAe,CAAC;QAC5D,WAAW,EAAE,CAAC,sCAAiB,CAAC;QAChC,OAAO,EAAE,CAAC,gCAAc,CAAC;KAC1B,CAAC;GACW,aAAa,CAAG"}
|
||||
-20
@@ -1,20 +0,0 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Repository } from 'typeorm';
|
||||
import { Subscription } from './entities/subscription.entity';
|
||||
import { Transaction, PaymentProvider } from './entities/transaction.entity';
|
||||
import { Tenant } from '../auth/entities/tenant.entity';
|
||||
import { MailService } from '../common/mail.service';
|
||||
export declare class BillingService {
|
||||
private subscriptionRepository;
|
||||
private transactionRepository;
|
||||
private tenantRepository;
|
||||
private configService;
|
||||
private mailService;
|
||||
private readonly logger;
|
||||
constructor(subscriptionRepository: Repository<Subscription>, transactionRepository: Repository<Transaction>, tenantRepository: Repository<Tenant>, configService: ConfigService, mailService: MailService);
|
||||
getIframeId(): string;
|
||||
getSubscription(tenantId: string): Promise<Subscription>;
|
||||
processSuccessfulPayment(externalTxId: string, provider: PaymentProvider, amount: number, metadata: any): Promise<void>;
|
||||
private upgradeTenantPlan;
|
||||
getTransactionHistory(tenantId: string): Promise<Transaction[]>;
|
||||
}
|
||||
-131
@@ -1,131 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var BillingService_1;
|
||||
var _a, _b, _c, _d;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BillingService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const subscription_entity_1 = require("./entities/subscription.entity");
|
||||
const transaction_entity_1 = require("./entities/transaction.entity");
|
||||
const tenant_entity_1 = require("../auth/entities/tenant.entity");
|
||||
const mail_service_1 = require("../common/mail.service");
|
||||
let BillingService = BillingService_1 = class BillingService {
|
||||
subscriptionRepository;
|
||||
transactionRepository;
|
||||
tenantRepository;
|
||||
configService;
|
||||
mailService;
|
||||
logger = new common_1.Logger(BillingService_1.name);
|
||||
constructor(subscriptionRepository, transactionRepository, tenantRepository, configService, mailService) {
|
||||
this.subscriptionRepository = subscriptionRepository;
|
||||
this.transactionRepository = transactionRepository;
|
||||
this.tenantRepository = tenantRepository;
|
||||
this.configService = configService;
|
||||
this.mailService = mailService;
|
||||
}
|
||||
getIframeId() {
|
||||
return this.configService.get('PAYMOB_IFRAME_ID', '837992');
|
||||
}
|
||||
async getSubscription(tenantId) {
|
||||
let sub = await this.subscriptionRepository.findOne({ where: { tenantId } });
|
||||
if (!sub) {
|
||||
sub = await this.subscriptionRepository.save({
|
||||
tenantId,
|
||||
plan: 'FREE',
|
||||
monthlyRequestLimit: 5000,
|
||||
status: subscription_entity_1.SubscriptionStatus.ACTIVE,
|
||||
});
|
||||
}
|
||||
return sub;
|
||||
}
|
||||
async processSuccessfulPayment(externalTxId, provider, amount, metadata) {
|
||||
let txn = await this.transactionRepository.findOne({ where: { externalTransactionId: externalTxId } });
|
||||
if (txn && txn.status === transaction_entity_1.PaymentStatus.SUCCESS) {
|
||||
this.logger.warn(`Transaction ${externalTxId} already processed.`);
|
||||
return;
|
||||
}
|
||||
if (!txn) {
|
||||
const tenantId = metadata.tenantId;
|
||||
if (!tenantId)
|
||||
throw new common_1.BadRequestException('No tenantId found in payment metadata');
|
||||
txn = await this.transactionRepository.save({
|
||||
tenantId,
|
||||
externalTransactionId: externalTxId,
|
||||
amount,
|
||||
provider,
|
||||
status: transaction_entity_1.PaymentStatus.SUCCESS,
|
||||
metadata,
|
||||
});
|
||||
}
|
||||
else {
|
||||
txn.status = transaction_entity_1.PaymentStatus.SUCCESS;
|
||||
txn.metadata = { ...txn.metadata, ...metadata };
|
||||
await this.transactionRepository.save(txn);
|
||||
}
|
||||
const tenantId = txn.tenantId;
|
||||
const plan = metadata.plan || 'PRO';
|
||||
await this.upgradeTenantPlan(tenantId, plan);
|
||||
try {
|
||||
const tenant = await this.tenantRepository.findOne({ where: { id: tenantId } });
|
||||
if (tenant && tenant.email) {
|
||||
await this.mailService.sendInvoiceEmail(tenant.email, {
|
||||
tenantName: tenant.name,
|
||||
plan: plan,
|
||||
amount: `${amount} EGP`,
|
||||
transactionId: externalTxId,
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error(`Failed to send invoice email for tx ${externalTxId}: ${err.message}`);
|
||||
}
|
||||
this.logger.log(`✅ Tenant ${tenantId} upgraded to ${plan} after payment ${externalTxId}`);
|
||||
}
|
||||
async upgradeTenantPlan(tenantId, plan) {
|
||||
await this.tenantRepository.update(tenantId, { plan });
|
||||
const limits = {
|
||||
[tenant_entity_1.TenantPlan.FREE]: 5000,
|
||||
[tenant_entity_1.TenantPlan.STARTER]: 25000,
|
||||
[tenant_entity_1.TenantPlan.PRO]: 100000,
|
||||
[tenant_entity_1.TenantPlan.ENTERPRISE]: 500000,
|
||||
};
|
||||
const sub = await this.getSubscription(tenantId);
|
||||
sub.plan = plan;
|
||||
sub.monthlyRequestLimit = limits[plan] || 8000;
|
||||
sub.status = subscription_entity_1.SubscriptionStatus.ACTIVE;
|
||||
sub.currentPeriodStart = new Date();
|
||||
const nextMonth = new Date();
|
||||
nextMonth.setMonth(nextMonth.getMonth() + 1);
|
||||
sub.currentPeriodEnd = nextMonth;
|
||||
await this.subscriptionRepository.save(sub);
|
||||
}
|
||||
async getTransactionHistory(tenantId) {
|
||||
return this.transactionRepository.find({
|
||||
where: { tenantId },
|
||||
order: { createdAt: 'DESC' },
|
||||
});
|
||||
}
|
||||
};
|
||||
exports.BillingService = BillingService;
|
||||
exports.BillingService = BillingService = BillingService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(0, (0, typeorm_1.InjectRepository)(subscription_entity_1.Subscription)),
|
||||
__param(1, (0, typeorm_1.InjectRepository)(transaction_entity_1.Transaction)),
|
||||
__param(2, (0, typeorm_1.InjectRepository)(tenant_entity_1.Tenant)),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _a : Object, typeof (_b = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _b : Object, typeof (_c = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _c : Object, typeof (_d = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _d : Object, mail_service_1.MailService])
|
||||
], BillingService);
|
||||
//# sourceMappingURL=billing.service.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"billing.service.js","sourceRoot":"","sources":["../../src/billing/billing.service.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,18 +0,0 @@
|
||||
export declare enum SubscriptionStatus {
|
||||
ACTIVE = "ACTIVE",
|
||||
PAST_DUE = "PAST_DUE",
|
||||
CANCELED = "CANCELED",
|
||||
TRIALING = "TRIALING"
|
||||
}
|
||||
export declare class Subscription {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
plan: string;
|
||||
status: SubscriptionStatus;
|
||||
monthlyRequestLimit: number;
|
||||
currentPeriodStart: Date;
|
||||
currentPeriodEnd: Date;
|
||||
canceledAt: Date;
|
||||
createdAt: Date;
|
||||
updatedAt: Date;
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Subscription = exports.SubscriptionStatus = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
var SubscriptionStatus;
|
||||
(function (SubscriptionStatus) {
|
||||
SubscriptionStatus["ACTIVE"] = "ACTIVE";
|
||||
SubscriptionStatus["PAST_DUE"] = "PAST_DUE";
|
||||
SubscriptionStatus["CANCELED"] = "CANCELED";
|
||||
SubscriptionStatus["TRIALING"] = "TRIALING";
|
||||
})(SubscriptionStatus || (exports.SubscriptionStatus = SubscriptionStatus = {}));
|
||||
let Subscription = class Subscription {
|
||||
id;
|
||||
tenantId;
|
||||
plan;
|
||||
status;
|
||||
monthlyRequestLimit;
|
||||
currentPeriodStart;
|
||||
currentPeriodEnd;
|
||||
canceledAt;
|
||||
createdAt;
|
||||
updatedAt;
|
||||
};
|
||||
exports.Subscription = Subscription;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
||||
__metadata("design:type", String)
|
||||
], Subscription.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Index)(),
|
||||
(0, typeorm_1.Column)(),
|
||||
__metadata("design:type", String)
|
||||
], Subscription.prototype, "tenantId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ default: 'FREE' }),
|
||||
__metadata("design:type", String)
|
||||
], Subscription.prototype, "plan", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'enum',
|
||||
enum: SubscriptionStatus,
|
||||
default: SubscriptionStatus.ACTIVE,
|
||||
}),
|
||||
__metadata("design:type", String)
|
||||
], Subscription.prototype, "status", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'int', default: 8000 }),
|
||||
__metadata("design:type", Number)
|
||||
], Subscription.prototype, "monthlyRequestLimit", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'timestamp', nullable: true }),
|
||||
__metadata("design:type", Date)
|
||||
], Subscription.prototype, "currentPeriodStart", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'timestamp', nullable: true }),
|
||||
__metadata("design:type", Date)
|
||||
], Subscription.prototype, "currentPeriodEnd", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'timestamp', nullable: true }),
|
||||
__metadata("design:type", Date)
|
||||
], Subscription.prototype, "canceledAt", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], Subscription.prototype, "createdAt", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.UpdateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], Subscription.prototype, "updatedAt", void 0);
|
||||
exports.Subscription = Subscription = __decorate([
|
||||
(0, typeorm_1.Entity)('subscriptions')
|
||||
], Subscription);
|
||||
//# sourceMappingURL=subscription.entity.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"subscription.entity.js","sourceRoot":"","sources":["../../../src/billing/entities/subscription.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAQiB;AAGjB,IAAY,kBAKX;AALD,WAAY,kBAAkB;IAC5B,uCAAiB,CAAA;IACjB,2CAAqB,CAAA;IACrB,2CAAqB,CAAA;IACrB,2CAAqB,CAAA;AACvB,CAAC,EALW,kBAAkB,kCAAlB,kBAAkB,QAK7B;AAGM,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEvB,EAAE,CAAS;IAIX,QAAQ,CAAS;IAGjB,IAAI,CAAS;IAOb,MAAM,CAAqB;IAG3B,mBAAmB,CAAS;IAG5B,kBAAkB,CAAO;IAGzB,gBAAgB,CAAO;IAGvB,UAAU,CAAO;IAGjB,SAAS,CAAO;IAGhB,SAAS,CAAO;CACjB,CAAA;AAnCY,oCAAY;AAEvB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;wCACpB;AAIX;IAFC,IAAA,eAAK,GAAE;IACP,IAAA,gBAAM,GAAE;;8CACQ;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;;0CACf;AAOb;IALC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,kBAAkB;QACxB,OAAO,EAAE,kBAAkB,CAAC,MAAM;KACnC,CAAC;;4CACyB;AAG3B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;yDACX;AAG5B;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BAC1B,IAAI;wDAAC;AAGzB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BAC5B,IAAI;sDAAC;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BAClC,IAAI;gDAAC;AAGjB;IADC,IAAA,0BAAgB,GAAE;8BACR,IAAI;+CAAC;AAGhB;IADC,IAAA,0BAAgB,GAAE;8BACR,IAAI;+CAAC;uBAlCL,YAAY;IADxB,IAAA,gBAAM,EAAC,eAAe,CAAC;GACX,YAAY,CAmCxB"}
|
||||
@@ -1,23 +0,0 @@
|
||||
export declare enum PaymentStatus {
|
||||
PENDING = "PENDING",
|
||||
SUCCESS = "SUCCESS",
|
||||
FAILED = "FAILED",
|
||||
REFUNDED = "REFUNDED"
|
||||
}
|
||||
export declare enum PaymentProvider {
|
||||
PAYMOB = "PAYMOB",
|
||||
BINANCE = "BINANCE",
|
||||
MANUAL = "MANUAL"
|
||||
}
|
||||
export declare class Transaction {
|
||||
id: string;
|
||||
tenantId: string;
|
||||
subscriptionId: string;
|
||||
amount: number;
|
||||
currency: string;
|
||||
provider: PaymentProvider;
|
||||
externalTransactionId: string;
|
||||
status: PaymentStatus;
|
||||
metadata: Record<string, any>;
|
||||
createdAt: Date;
|
||||
}
|
||||
@@ -1,92 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.Transaction = exports.PaymentProvider = exports.PaymentStatus = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
var PaymentStatus;
|
||||
(function (PaymentStatus) {
|
||||
PaymentStatus["PENDING"] = "PENDING";
|
||||
PaymentStatus["SUCCESS"] = "SUCCESS";
|
||||
PaymentStatus["FAILED"] = "FAILED";
|
||||
PaymentStatus["REFUNDED"] = "REFUNDED";
|
||||
})(PaymentStatus || (exports.PaymentStatus = PaymentStatus = {}));
|
||||
var PaymentProvider;
|
||||
(function (PaymentProvider) {
|
||||
PaymentProvider["PAYMOB"] = "PAYMOB";
|
||||
PaymentProvider["BINANCE"] = "BINANCE";
|
||||
PaymentProvider["MANUAL"] = "MANUAL";
|
||||
})(PaymentProvider || (exports.PaymentProvider = PaymentProvider = {}));
|
||||
let Transaction = class Transaction {
|
||||
id;
|
||||
tenantId;
|
||||
subscriptionId;
|
||||
amount;
|
||||
currency;
|
||||
provider;
|
||||
externalTransactionId;
|
||||
status;
|
||||
metadata;
|
||||
createdAt;
|
||||
};
|
||||
exports.Transaction = Transaction;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
||||
__metadata("design:type", String)
|
||||
], Transaction.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Index)(),
|
||||
(0, typeorm_1.Column)(),
|
||||
__metadata("design:type", String)
|
||||
], Transaction.prototype, "tenantId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], Transaction.prototype, "subscriptionId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 2 }),
|
||||
__metadata("design:type", Number)
|
||||
], Transaction.prototype, "amount", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ default: 'USD' }),
|
||||
__metadata("design:type", String)
|
||||
], Transaction.prototype, "currency", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'enum',
|
||||
enum: PaymentProvider,
|
||||
}),
|
||||
__metadata("design:type", String)
|
||||
], Transaction.prototype, "provider", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Index)(),
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], Transaction.prototype, "externalTransactionId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'enum',
|
||||
enum: PaymentStatus,
|
||||
default: PaymentStatus.PENDING,
|
||||
}),
|
||||
__metadata("design:type", String)
|
||||
], Transaction.prototype, "status", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'jsonb', nullable: true }),
|
||||
__metadata("design:type", Object)
|
||||
], Transaction.prototype, "metadata", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], Transaction.prototype, "createdAt", void 0);
|
||||
exports.Transaction = Transaction = __decorate([
|
||||
(0, typeorm_1.Entity)('transactions')
|
||||
], Transaction);
|
||||
//# sourceMappingURL=transaction.entity.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"transaction.entity.js","sourceRoot":"","sources":["../../../src/billing/entities/transaction.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAMiB;AAEjB,IAAY,aAKX;AALD,WAAY,aAAa;IACvB,oCAAmB,CAAA;IACnB,oCAAmB,CAAA;IACnB,kCAAiB,CAAA;IACjB,sCAAqB,CAAA;AACvB,CAAC,EALW,aAAa,6BAAb,aAAa,QAKxB;AAED,IAAY,eAIX;AAJD,WAAY,eAAe;IACzB,oCAAiB,CAAA;IACjB,sCAAmB,CAAA;IACnB,oCAAiB,CAAA;AACnB,CAAC,EAJW,eAAe,+BAAf,eAAe,QAI1B;AAGM,IAAM,WAAW,GAAjB,MAAM,WAAW;IAEtB,EAAE,CAAS;IAIX,QAAQ,CAAS;IAGjB,cAAc,CAAS;IAGvB,MAAM,CAAS;IAGf,QAAQ,CAAS;IAMjB,QAAQ,CAAkB;IAI1B,qBAAqB,CAAS;IAO9B,MAAM,CAAgB;IAGtB,QAAQ,CAAsB;IAG9B,SAAS,CAAO;CACjB,CAAA;AAvCY,kCAAW;AAEtB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;uCACpB;AAIX;IAFC,IAAA,eAAK,GAAE;IACP,IAAA,gBAAM,GAAE;;6CACQ;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACJ;AAGvB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;;2CACtC;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;;6CACV;AAMjB;IAJC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,eAAe;KACtB,CAAC;;6CACwB;AAI1B;IAFC,IAAA,eAAK,GAAE;IACP,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0DACG;AAO9B;IALC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,MAAM;QACZ,IAAI,EAAE,aAAa;QACnB,OAAO,EAAE,aAAa,CAAC,OAAO;KAC/B,CAAC;;2CACoB;AAGtB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CACZ;AAG9B;IADC,IAAA,0BAAgB,GAAE;8BACR,IAAI;8CAAC;sBAtCL,WAAW;IADvB,IAAA,gBAAM,EAAC,cAAc,CAAC;GACV,WAAW,CAuCvB"}
|
||||
@@ -1,16 +0,0 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class BinanceProvider {
|
||||
private configService;
|
||||
private readonly logger;
|
||||
private readonly apiUrl;
|
||||
constructor(configService: ConfigService);
|
||||
private get apiKey();
|
||||
private get secretKey();
|
||||
private generateSignature;
|
||||
createOrder(tenantId: string, amount: number, plan: string): Promise<{
|
||||
checkoutUrl: any;
|
||||
orderId: string;
|
||||
prepayId: any;
|
||||
}>;
|
||||
verifySignature(timestamp: string, nonce: string, signature: string, payloadBody: any): boolean;
|
||||
}
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var BinanceProvider_1;
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.BinanceProvider = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
const crypto = __importStar(require("crypto"));
|
||||
let BinanceProvider = BinanceProvider_1 = class BinanceProvider {
|
||||
configService;
|
||||
logger = new common_1.Logger(BinanceProvider_1.name);
|
||||
apiUrl = 'https://bpay.binanceapi.com/binancepay/openapi/v2/order';
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
}
|
||||
get apiKey() {
|
||||
return this.configService.get('BINANCE_PAY_API_KEY') || '';
|
||||
}
|
||||
get secretKey() {
|
||||
return this.configService.get('BINANCE_PAY_SECRET_KEY') || '';
|
||||
}
|
||||
generateSignature(timestamp, nonce, body) {
|
||||
const payload = timestamp + '\n' + nonce + '\n' + JSON.stringify(body) + '\n';
|
||||
return crypto
|
||||
.createHmac('sha512', this.secretKey)
|
||||
.update(payload)
|
||||
.digest('hex')
|
||||
.toUpperCase();
|
||||
}
|
||||
async createOrder(tenantId, amount, plan) {
|
||||
this.logger.log(`Creating Binance Pay order for ${tenantId} - ${plan}`);
|
||||
if (!this.apiKey || !this.secretKey) {
|
||||
this.logger.error('Binance Pay API keys are not configured');
|
||||
throw new common_1.InternalServerErrorException('Payment provider is not configured properly');
|
||||
}
|
||||
const nonce = crypto.randomBytes(16).toString('hex');
|
||||
const timestamp = Date.now().toString();
|
||||
const merchantTradeNo = `txn_${Date.now()}_${Math.floor(Math.random() * 10000)}`;
|
||||
const body = {
|
||||
env: {
|
||||
terminalType: 'WEB',
|
||||
},
|
||||
merchantTradeNo: merchantTradeNo,
|
||||
orderAmount: amount,
|
||||
currency: 'USDT',
|
||||
goods: {
|
||||
goodsType: '01',
|
||||
goodsCategory: 'Z000',
|
||||
referenceGoodsId: plan,
|
||||
goodsName: `${plan} Plan Subscription`,
|
||||
goodsDetail: `Subscription to ${plan} plan for Maps SaaS`,
|
||||
},
|
||||
passThroughInfo: JSON.stringify({ tenantId, plan }),
|
||||
returnUrl: `https://map-dashboard.intaleqapp.com/dashboard.html#billing`,
|
||||
};
|
||||
const signature = this.generateSignature(timestamp, nonce, body);
|
||||
try {
|
||||
const response = await axios_1.default.post(this.apiUrl, body, {
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
'BinancePay-Timestamp': timestamp,
|
||||
'BinancePay-Nonce': nonce,
|
||||
'BinancePay-Certificate-SN': this.apiKey,
|
||||
'BinancePay-Signature': signature,
|
||||
},
|
||||
});
|
||||
if (response.data && response.data.status === 'SUCCESS') {
|
||||
return {
|
||||
checkoutUrl: response.data.data.checkoutUrl,
|
||||
orderId: merchantTradeNo,
|
||||
prepayId: response.data.data.prepayId,
|
||||
};
|
||||
}
|
||||
else {
|
||||
this.logger.error(`Binance API Error: ${JSON.stringify(response.data)}`);
|
||||
throw new common_1.InternalServerErrorException('Failed to create Binance order');
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Error communicating with Binance API: ${error.message}`);
|
||||
throw new common_1.InternalServerErrorException('Failed to communicate with payment provider');
|
||||
}
|
||||
}
|
||||
verifySignature(timestamp, nonce, signature, payloadBody) {
|
||||
const payload = timestamp + '\n' + nonce + '\n' + JSON.stringify(payloadBody) + '\n';
|
||||
const expectedSignature = crypto
|
||||
.createHmac('sha512', this.secretKey)
|
||||
.update(payload)
|
||||
.digest('hex')
|
||||
.toUpperCase();
|
||||
try {
|
||||
return crypto.timingSafeEqual(Buffer.from(signature || ''), Buffer.from(expectedSignature));
|
||||
}
|
||||
catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.BinanceProvider = BinanceProvider;
|
||||
exports.BinanceProvider = BinanceProvider = BinanceProvider_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _a : Object])
|
||||
], BinanceProvider);
|
||||
//# sourceMappingURL=binance.provider.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"binance.provider.js","sourceRoot":"","sources":["../../../src/billing/providers/binance.provider.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,14 +0,0 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class PayMobProvider {
|
||||
private configService;
|
||||
private readonly logger;
|
||||
private readonly baseUrl;
|
||||
constructor(configService: ConfigService);
|
||||
createPaymentKey(tenantId: string, amount: number, plan: string): Promise<{
|
||||
paymentKey: string;
|
||||
orderId: string;
|
||||
}>;
|
||||
getTransactionDetails(transactionId: string): Promise<any>;
|
||||
private getAuthToken;
|
||||
verifyHmac(payload: any, hmac: string): boolean;
|
||||
}
|
||||
-179
@@ -1,179 +0,0 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var PayMobProvider_1;
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.PayMobProvider = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
const crypto = __importStar(require("crypto"));
|
||||
let PayMobProvider = PayMobProvider_1 = class PayMobProvider {
|
||||
configService;
|
||||
logger = new common_1.Logger(PayMobProvider_1.name);
|
||||
baseUrl = 'https://accept.paymob.com/api';
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
}
|
||||
async createPaymentKey(tenantId, amount, plan) {
|
||||
try {
|
||||
const authToken = await this.getAuthToken();
|
||||
const orderRes = await axios_1.default.post(`${this.baseUrl}/ecommerce/orders`, {
|
||||
auth_token: authToken,
|
||||
delivery_needed: "false",
|
||||
amount_cents: amount * 50 * 100,
|
||||
currency: "EGP",
|
||||
items: [{
|
||||
name: `${plan} Subscription`,
|
||||
amount_cents: amount * 50 * 100,
|
||||
description: `Intaleq Maps ${plan} Plan`
|
||||
}],
|
||||
shipping_data: {
|
||||
first_name: "Intaleq",
|
||||
last_name: "User",
|
||||
email: "user@intaleq.com",
|
||||
phone_number: "+201000000000",
|
||||
extra_description: `${tenantId}|${plan}`
|
||||
}
|
||||
});
|
||||
const orderId = orderRes.data.id;
|
||||
const keyRes = await axios_1.default.post(`${this.baseUrl}/acceptance/payment_keys`, {
|
||||
auth_token: authToken,
|
||||
amount_cents: amount * 50 * 100,
|
||||
expiration: 3600,
|
||||
order_id: orderId,
|
||||
billing_data: {
|
||||
apartment: "NA",
|
||||
email: "user@intaleq.com",
|
||||
floor: "NA",
|
||||
first_name: "Intaleq",
|
||||
street: "NA",
|
||||
building: "NA",
|
||||
phone_number: "+201000000000",
|
||||
shipping_method: "NA",
|
||||
postal_code: "NA",
|
||||
city: "NA",
|
||||
country: "NA",
|
||||
last_name: "User",
|
||||
state: "NA"
|
||||
},
|
||||
currency: "EGP",
|
||||
integration_id: this.configService.get('PAYMOB_INTEGRATION_ID') || 4556055,
|
||||
lock_order_when_paid: "false"
|
||||
});
|
||||
return {
|
||||
paymentKey: keyRes.data.token,
|
||||
orderId: orderId.toString()
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`PayMob Payment Key failed: ${error.response?.data ? JSON.stringify(error.response.data) : error.message}`);
|
||||
throw new common_1.BadRequestException('Failed to initialize PayMob payment');
|
||||
}
|
||||
}
|
||||
async getTransactionDetails(transactionId) {
|
||||
try {
|
||||
const authToken = await this.getAuthToken();
|
||||
const res = await axios_1.default.get(`${this.baseUrl}/acceptance/transactions/${transactionId}`, {
|
||||
headers: { Authorization: `Bearer ${authToken}` }
|
||||
});
|
||||
return res.data;
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Failed to fetch PayMob transaction ${transactionId}: ${error.message}`);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
async getAuthToken() {
|
||||
const authRes = await axios_1.default.post(`${this.baseUrl}/auth/tokens`, {
|
||||
api_key: this.configService.get('PAYMOB_API_KEY'),
|
||||
});
|
||||
return authRes.data.token;
|
||||
}
|
||||
verifyHmac(payload, hmac) {
|
||||
const secret = this.configService.get('PAYMOB_HMAC_SECRET');
|
||||
if (!secret)
|
||||
return false;
|
||||
const { amount_cents, created_at, currency, error_occured, has_parent_transaction, id, integration_id, is_3d_secure, is_auth, is_capture, is_refunded, is_standalone_payment, is_voided, order, owner, pending, success } = payload;
|
||||
const source_pan = payload.source_data?.pan || "";
|
||||
const source_sub_type = payload.source_data?.sub_type || "";
|
||||
const source_type = payload.source_data?.type || "";
|
||||
const data = [
|
||||
amount_cents,
|
||||
created_at,
|
||||
currency,
|
||||
error_occured,
|
||||
has_parent_transaction,
|
||||
id,
|
||||
integration_id,
|
||||
is_3d_secure,
|
||||
is_auth,
|
||||
is_capture,
|
||||
is_refunded,
|
||||
is_standalone_payment,
|
||||
is_voided,
|
||||
order.id || order,
|
||||
owner,
|
||||
pending,
|
||||
source_pan,
|
||||
source_sub_type,
|
||||
source_type,
|
||||
success
|
||||
].join("");
|
||||
const hash = crypto
|
||||
.createHmac('sha512', secret)
|
||||
.update(data)
|
||||
.digest('hex');
|
||||
return hash === hmac;
|
||||
}
|
||||
};
|
||||
exports.PayMobProvider = PayMobProvider;
|
||||
exports.PayMobProvider = PayMobProvider = PayMobProvider_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _a : Object])
|
||||
], PayMobProvider);
|
||||
//# sourceMappingURL=paymob.provider.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"paymob.provider.js","sourceRoot":"","sources":["../../../src/billing/providers/paymob.provider.ts"],"names":[],"mappings":"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"}
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class CacheService {
|
||||
private configService;
|
||||
private client;
|
||||
constructor(configService: ConfigService);
|
||||
set(key: string, value: any, ttlSeconds?: number): Promise<void>;
|
||||
get(key: string): Promise<any>;
|
||||
del(key: string): Promise<void>;
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.CacheService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const redis_1 = require("redis");
|
||||
const config_1 = require("@nestjs/config");
|
||||
let CacheService = class CacheService {
|
||||
configService;
|
||||
client;
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
const redisUrl = this.configService.get('REDIS_URL', 'redis://redis:6379');
|
||||
this.client = (0, redis_1.createClient)({ url: redisUrl });
|
||||
this.client.connect().catch(console.error);
|
||||
}
|
||||
async set(key, value, ttlSeconds = 3600) {
|
||||
await this.client.set(key, JSON.stringify(value), {
|
||||
EX: ttlSeconds,
|
||||
});
|
||||
}
|
||||
async get(key) {
|
||||
const data = await this.client.get(key);
|
||||
return data ? JSON.parse(data) : null;
|
||||
}
|
||||
async del(key) {
|
||||
await this.client.del(key);
|
||||
}
|
||||
};
|
||||
exports.CacheService = CacheService;
|
||||
exports.CacheService = CacheService = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _a : Object])
|
||||
], CacheService);
|
||||
//# sourceMappingURL=cache.service.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"cache.service.js","sourceRoot":"","sources":["../../src/common/cache.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;AAAA,2CAAoD;AACpD,iCAAqC;AACrC,2CAA+C;AAGxC,IAAM,YAAY,GAAlB,MAAM,YAAY;IAGH;IAFZ,MAAM,CAAC;IAEf,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;QAC9C,MAAM,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,EAAE,oBAAoB,CAAC,CAAC;QAC3E,IAAI,CAAC,MAAM,GAAG,IAAA,oBAAY,EAAC,EAAE,GAAG,EAAE,QAAQ,EAAE,CAAC,CAAC;QAC9C,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAC7C,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAU,EAAE,aAAqB,IAAI;QAG1D,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE;YAChD,EAAE,EAAE,UAAU;SACf,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QAGnB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACxC,OAAO,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;IACxC,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC7B,CAAC;CACF,CAAA;AA3BY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;yDAIwB,sBAAa,oBAAb,sBAAa;GAHrC,YAAY,CA2BxB"}
|
||||
Vendored
-8
@@ -1,8 +0,0 @@
|
||||
export declare const JORDAN_BOUNDS: {
|
||||
minLat: number;
|
||||
maxLat: number;
|
||||
minLng: number;
|
||||
maxLng: number;
|
||||
};
|
||||
export declare function isWithinJordan(lat: number, lng: number): boolean;
|
||||
export declare function mockMapMatch(lat: number, lng: number): string;
|
||||
Vendored
-21
@@ -1,21 +0,0 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.JORDAN_BOUNDS = void 0;
|
||||
exports.isWithinJordan = isWithinJordan;
|
||||
exports.mockMapMatch = mockMapMatch;
|
||||
exports.JORDAN_BOUNDS = {
|
||||
minLat: 29.1,
|
||||
maxLat: 33.4,
|
||||
minLng: 34.9,
|
||||
maxLng: 39.3,
|
||||
};
|
||||
function isWithinJordan(lat, lng) {
|
||||
return (lat >= exports.JORDAN_BOUNDS.minLat &&
|
||||
lat <= exports.JORDAN_BOUNDS.maxLat &&
|
||||
lng >= exports.JORDAN_BOUNDS.minLng &&
|
||||
lng <= exports.JORDAN_BOUNDS.maxLng);
|
||||
}
|
||||
function mockMapMatch(lat, lng) {
|
||||
return `osm_way_${Math.floor(Math.random() * 1000000)}`;
|
||||
}
|
||||
//# sourceMappingURL=gis.utils.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"gis.utils.js","sourceRoot":"","sources":["../../src/common/gis.utils.ts"],"names":[],"mappings":";;;AAYA,wCAOC;AAMD,oCAGC;AAvBY,QAAA,aAAa,GAAG;IAC3B,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,IAAI;CACb,CAAC;AAEF,SAAgB,cAAc,CAAC,GAAW,EAAE,GAAW;IACrD,OAAO,CACL,GAAG,IAAI,qBAAa,CAAC,MAAM;QAC3B,GAAG,IAAI,qBAAa,CAAC,MAAM;QAC3B,GAAG,IAAI,qBAAa,CAAC,MAAM;QAC3B,GAAG,IAAI,qBAAa,CAAC,MAAM,CAC5B,CAAC;AACJ,CAAC;AAMD,SAAgB,YAAY,CAAC,GAAW,EAAE,GAAW;IAEnD,OAAO,WAAW,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,OAAO,CAAC,EAAE,CAAC;AAC1D,CAAC"}
|
||||
@@ -1,4 +0,0 @@
|
||||
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
export declare class AdminGuard implements CanActivate {
|
||||
canActivate(context: ExecutionContext): boolean;
|
||||
}
|
||||
-29
@@ -1,29 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AdminGuard = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const tenant_entity_1 = require("../../auth/entities/tenant.entity");
|
||||
let AdminGuard = class AdminGuard {
|
||||
canActivate(context) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const tenant = request['tenant'];
|
||||
if (!tenant) {
|
||||
throw new common_1.UnauthorizedException('Tenant not found in request');
|
||||
}
|
||||
if (tenant.plan !== tenant_entity_1.TenantPlan.ENTERPRISE) {
|
||||
throw new common_1.ForbiddenException('Admin access required. Your tenant plan must be ENTERPRISE.');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
exports.AdminGuard = AdminGuard;
|
||||
exports.AdminGuard = AdminGuard = __decorate([
|
||||
(0, common_1.Injectable)()
|
||||
], AdminGuard);
|
||||
//# sourceMappingURL=admin.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"admin.guard.js","sourceRoot":"","sources":["../../../src/common/guards/admin.guard.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAsH;AACtH,qEAA+D;AAGxD,IAAM,UAAU,GAAhB,MAAM,UAAU;IACrB,WAAW,CAAC,OAAyB;QACnC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;QAEjC,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,MAAM,IAAI,8BAAqB,CAAC,6BAA6B,CAAC,CAAC;QACjE,CAAC;QAED,IAAI,MAAM,CAAC,IAAI,KAAK,0BAAU,CAAC,UAAU,EAAE,CAAC;YAC1C,MAAM,IAAI,2BAAkB,CAAC,6DAA6D,CAAC,CAAC;QAC9F,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAfY,gCAAU;qBAAV,UAAU;IADtB,IAAA,mBAAU,GAAE;GACA,UAAU,CAetB"}
|
||||
@@ -1,7 +0,0 @@
|
||||
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { AuthService } from '../../auth/auth.service';
|
||||
export declare class ApiKeyGuard implements CanActivate {
|
||||
private authService;
|
||||
constructor(authService: AuthService);
|
||||
canActivate(context: ExecutionContext): Promise<boolean>;
|
||||
}
|
||||
-45
@@ -1,45 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ApiKeyGuard = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const auth_service_1 = require("../../auth/auth.service");
|
||||
let ApiKeyGuard = class ApiKeyGuard {
|
||||
authService;
|
||||
constructor(authService) {
|
||||
this.authService = authService;
|
||||
}
|
||||
async canActivate(context) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const apiKeyHeader = request.headers['x-api-key'] || request.query['api_key'];
|
||||
if (!apiKeyHeader) {
|
||||
throw new common_1.UnauthorizedException('API Key (x-api-key) is missing');
|
||||
}
|
||||
const origin = request.headers['origin'];
|
||||
const referer = request.headers['referer'];
|
||||
try {
|
||||
const { tenant, apiKey, rateLimit } = await this.authService.validateApiKey(apiKeyHeader, origin, referer);
|
||||
request['tenant'] = tenant;
|
||||
request['apiKey'] = apiKey;
|
||||
request['rateLimit'] = rateLimit;
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
throw new common_1.UnauthorizedException(error.message || 'Invalid API Key');
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.ApiKeyGuard = ApiKeyGuard;
|
||||
exports.ApiKeyGuard = ApiKeyGuard = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [auth_service_1.AuthService])
|
||||
], ApiKeyGuard);
|
||||
//# sourceMappingURL=api-key.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"api-key.guard.js","sourceRoot":"","sources":["../../../src/common/guards/api-key.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAKwB;AACxB,0DAAsD;AAG/C,IAAM,WAAW,GAAjB,MAAM,WAAW;IACF;IAApB,YAAoB,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAEhD,KAAK,CAAC,WAAW,CAAC,OAAyB;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAGpD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC;QAE9E,IAAI,CAAC,YAAY,EAAE,CAAC;YAClB,MAAM,IAAI,8BAAqB,CAAC,gCAAgC,CAAC,CAAC;QACpE,CAAC;QAGD,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;QACzC,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;QAE3C,IAAI,CAAC;YAEH,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,cAAc,CACzE,YAAY,EACZ,MAAM,EACN,OAAO,CACR,CAAC;YAGF,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC;YAC3B,OAAO,CAAC,QAAQ,CAAC,GAAG,MAAM,CAAC;YAC3B,OAAO,CAAC,WAAW,CAAC,GAAG,SAAS,CAAC;YAEjC,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,8BAAqB,CAAC,KAAK,CAAC,OAAO,IAAI,iBAAiB,CAAC,CAAC;QACtE,CAAC;IACH,CAAC;CACF,CAAA;AAnCY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAEsB,0BAAW;GADjC,WAAW,CAmCvB"}
|
||||
@@ -1,6 +0,0 @@
|
||||
import { ThrottlerGuard, ThrottlerRequest } from '@nestjs/throttler';
|
||||
export declare class TenantThrottlerGuard extends ThrottlerGuard {
|
||||
private readonly logger;
|
||||
protected getTracker(req: Record<string, any>): Promise<string>;
|
||||
protected handleRequest(requestProps: ThrottlerRequest): Promise<boolean>;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var TenantThrottlerGuard_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TenantThrottlerGuard = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const throttler_1 = require("@nestjs/throttler");
|
||||
let TenantThrottlerGuard = TenantThrottlerGuard_1 = class TenantThrottlerGuard extends throttler_1.ThrottlerGuard {
|
||||
logger = new common_1.Logger(TenantThrottlerGuard_1.name);
|
||||
async getTracker(req) {
|
||||
const apiKeyId = req['apiKey']?.id || req.ip;
|
||||
return `throttler:key:${apiKeyId}`;
|
||||
}
|
||||
async handleRequest(requestProps) {
|
||||
const { context, limit, ttl } = requestProps;
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const dynamicLimit = request['rateLimit'];
|
||||
if (dynamicLimit) {
|
||||
this.logger.debug(`Applying dynamic rate limit: ${dynamicLimit} req/min for key ${request['apiKey']?.id}`);
|
||||
requestProps.limit = dynamicLimit;
|
||||
}
|
||||
else {
|
||||
this.logger.debug(`Applying default rate limit: ${limit} req/min`);
|
||||
}
|
||||
return super.handleRequest(requestProps);
|
||||
}
|
||||
};
|
||||
exports.TenantThrottlerGuard = TenantThrottlerGuard;
|
||||
exports.TenantThrottlerGuard = TenantThrottlerGuard = TenantThrottlerGuard_1 = __decorate([
|
||||
(0, common_1.Injectable)()
|
||||
], TenantThrottlerGuard);
|
||||
//# sourceMappingURL=rate-limiter.guard.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"rate-limiter.guard.js","sourceRoot":"","sources":["../../../src/common/guards/rate-limiter.guard.ts"],"names":[],"mappings":";;;;;;;;;;AAAA,2CAAsE;AACtE,iDAAqE;AAG9D,IAAM,oBAAoB,4BAA1B,MAAM,oBAAqB,SAAQ,0BAAc;IACrC,MAAM,GAAG,IAAI,eAAM,CAAC,sBAAoB,CAAC,IAAI,CAAC,CAAC;IAEtD,KAAK,CAAC,UAAU,CAAC,GAAwB;QACjD,MAAM,QAAQ,GAAG,GAAG,CAAC,QAAQ,CAAC,EAAE,EAAE,IAAI,GAAG,CAAC,EAAE,CAAC;QAC7C,OAAO,iBAAiB,QAAQ,EAAE,CAAC;IACrC,CAAC;IAES,KAAK,CAAC,aAAa,CAC3B,YAA8B;QAE9B,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,GAAG,YAAY,CAAC;QAC7C,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QAGpD,MAAM,YAAY,GAAG,OAAO,CAAC,WAAW,CAAC,CAAC;QAE1C,IAAI,YAAY,EAAE,CAAC;YACjB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,YAAY,oBAAoB,OAAO,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YAC3G,YAAY,CAAC,KAAK,GAAG,YAAY,CAAC;QACpC,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,gCAAgC,KAAK,UAAU,CAAC,CAAC;QACrE,CAAC;QAED,OAAO,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;IAC3C,CAAC;CACF,CAAA;AA1BY,oDAAoB;+BAApB,oBAAoB;IADhC,IAAA,mBAAU,GAAE;GACA,oBAAoB,CA0BhC"}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
export declare class MailModule {
|
||||
}
|
||||
Vendored
-22
@@ -1,22 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MailModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const mail_service_1 = require("./mail.service");
|
||||
let MailModule = class MailModule {
|
||||
};
|
||||
exports.MailModule = MailModule;
|
||||
exports.MailModule = MailModule = __decorate([
|
||||
(0, common_1.Global)(),
|
||||
(0, common_1.Module)({
|
||||
providers: [mail_service_1.MailService],
|
||||
exports: [mail_service_1.MailService],
|
||||
})
|
||||
], MailModule);
|
||||
//# sourceMappingURL=mail.module.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"mail.module.js","sourceRoot":"","sources":["../../src/common/mail.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,iDAA6C;AAOtC,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IALtB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE,CAAC,0BAAW,CAAC;QACxB,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,UAAU,CAAG"}
|
||||
-13
@@ -1,13 +0,0 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class MailService {
|
||||
private configService;
|
||||
private readonly logger;
|
||||
private transporter;
|
||||
constructor(configService: ConfigService);
|
||||
sendInvoiceEmail(to: string, data: {
|
||||
tenantName: string;
|
||||
plan: string;
|
||||
amount: string;
|
||||
transactionId: string;
|
||||
}): Promise<boolean>;
|
||||
}
|
||||
Vendored
-122
@@ -1,122 +0,0 @@
|
||||
"use strict";
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var MailService_1;
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MailService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const nodemailer = __importStar(require("nodemailer"));
|
||||
let MailService = MailService_1 = class MailService {
|
||||
configService;
|
||||
logger = new common_1.Logger(MailService_1.name);
|
||||
transporter;
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
const host = this.configService.get('SMTP_HOST');
|
||||
const port = this.configService.get('SMTP_PORT') || 587;
|
||||
const user = this.configService.get('SMTP_USER');
|
||||
const pass = this.configService.get('SMTP_PASS');
|
||||
if (host && user && pass) {
|
||||
this.transporter = nodemailer.createTransport({
|
||||
host,
|
||||
port,
|
||||
secure: port === 465,
|
||||
auth: {
|
||||
user,
|
||||
pass,
|
||||
},
|
||||
});
|
||||
this.logger.log(`📧 MailService initialized with host: ${host}`);
|
||||
}
|
||||
else {
|
||||
this.logger.warn('⚠️ SMTP settings not fully configured. MailService will run in mock mode.');
|
||||
}
|
||||
}
|
||||
async sendInvoiceEmail(to, data) {
|
||||
const from = this.configService.get('SMTP_FROM') || 'support@intaleqapp.com';
|
||||
const htmlContent = `
|
||||
<div style="font-family: sans-serif; max-width: 600px; margin: auto; border: 1px solid #eee; padding: 20px; border-radius: 10px;">
|
||||
<h2 style="color: #2563eb;">Intaleq Maps - Payment Successful</h2>
|
||||
<p>Hello <b>${data.tenantName}</b>,</p>
|
||||
<p>Thank you for subscribing to our <b>${data.plan}</b> plan. Your payment has been processed successfully.</p>
|
||||
|
||||
<div style="background: #f8fafc; padding: 15px; border-radius: 8px; margin: 20px 0;">
|
||||
<p style="margin: 5px 0;"><b>Plan:</b> ${data.plan}</p>
|
||||
<p style="margin: 5px 0;"><b>Amount:</b> ${data.amount}</p>
|
||||
<p style="margin: 5px 0;"><b>Transaction ID:</b> ${data.transactionId}</p>
|
||||
<p style="margin: 5px 0;"><b>Date:</b> ${new Date().toLocaleDateString()}</p>
|
||||
</div>
|
||||
|
||||
<p>You can now access all your premium features from your dashboard.</p>
|
||||
<hr style="border: none; border-top: 1px solid #eee; margin: 20px 0;">
|
||||
<p style="font-size: 12px; color: #64748b;">If you have any questions, contact us at ${from}</p>
|
||||
</div>
|
||||
`;
|
||||
if (!this.transporter) {
|
||||
this.logger.warn(`[Mock Email] To: ${to}, Plan: ${data.plan}, Amount: ${data.amount}`);
|
||||
return true;
|
||||
}
|
||||
try {
|
||||
await this.transporter.sendMail({
|
||||
from: `"Intaleq Support" <${from}>`,
|
||||
to,
|
||||
subject: `Invoice: ${data.plan} Subscription - Intaleq Maps`,
|
||||
html: htmlContent,
|
||||
});
|
||||
this.logger.log(`✅ Invoice email sent to ${to}`);
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`❌ Failed to send email: ${error.message}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.MailService = MailService;
|
||||
exports.MailService = MailService = MailService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _a : Object])
|
||||
], MailService);
|
||||
//# sourceMappingURL=mail.service.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"mail.service.js","sourceRoot":"","sources":["../../src/common/mail.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2CAA+C;AAC/C,uDAAyC;AAGlC,IAAM,WAAW,mBAAjB,MAAM,WAAW;IAIF;IAHH,MAAM,GAAG,IAAI,eAAM,CAAC,aAAW,CAAC,IAAI,CAAC,CAAC;IAC/C,WAAW,CAAyB;IAE5C,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;QAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,WAAW,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,WAAW,CAAC,IAAI,GAAG,CAAC;QAChE,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,WAAW,CAAC,CAAC;QACzD,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,WAAW,CAAC,CAAC;QAEzD,IAAI,IAAI,IAAI,IAAI,IAAI,IAAI,EAAE,CAAC;YACzB,IAAI,CAAC,WAAW,GAAG,UAAU,CAAC,eAAe,CAAC;gBAC5C,IAAI;gBACJ,IAAI;gBACJ,MAAM,EAAE,IAAI,KAAK,GAAG;gBACpB,IAAI,EAAE;oBACJ,IAAI;oBACJ,IAAI;iBACL;aACF,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,yCAAyC,IAAI,EAAE,CAAC,CAAC;QACnE,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,2EAA2E,CAAC,CAAC;QAChG,CAAC;IACH,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,EAAU,EAAE,IAKlC;QACC,MAAM,IAAI,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,WAAW,CAAC,IAAI,wBAAwB,CAAC;QAErF,MAAM,WAAW,GAAG;;;sBAGF,IAAI,CAAC,UAAU;iDACY,IAAI,CAAC,IAAI;;;mDAGP,IAAI,CAAC,IAAI;qDACP,IAAI,CAAC,MAAM;6DACH,IAAI,CAAC,aAAa;mDAC5B,IAAI,IAAI,EAAE,CAAC,kBAAkB,EAAE;;;;;+FAKa,IAAI;;KAE9F,CAAC;QAEF,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC;YACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,oBAAoB,EAAE,WAAW,IAAI,CAAC,IAAI,aAAa,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACvF,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC;gBAC9B,IAAI,EAAE,sBAAsB,IAAI,GAAG;gBACnC,EAAE;gBACF,OAAO,EAAE,YAAY,IAAI,CAAC,IAAI,8BAA8B;gBAC5D,IAAI,EAAE,WAAW;aAClB,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,2BAA2B,EAAE,EAAE,CAAC,CAAC;YACjD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,2BAA2B,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YAC9D,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;CACF,CAAA;AAxEY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;yDAKwB,sBAAa,oBAAb,sBAAa;GAJrC,WAAW,CAwEvB"}
|
||||
-2
@@ -1,2 +0,0 @@
|
||||
export declare class RedisModule {
|
||||
}
|
||||
Vendored
-36
@@ -1,36 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RedisModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const redis_1 = require("redis");
|
||||
const redis_service_1 = require("./redis.service");
|
||||
let RedisModule = class RedisModule {
|
||||
};
|
||||
exports.RedisModule = RedisModule;
|
||||
exports.RedisModule = RedisModule = __decorate([
|
||||
(0, common_1.Global)(),
|
||||
(0, common_1.Module)({
|
||||
providers: [
|
||||
{
|
||||
provide: 'REDIS_CLIENT',
|
||||
useFactory: async (configService) => {
|
||||
const url = configService.get('REDIS_URL', 'redis://redis:6379');
|
||||
const client = (0, redis_1.createClient)({ url });
|
||||
await client.connect();
|
||||
return client;
|
||||
},
|
||||
inject: [config_1.ConfigService],
|
||||
},
|
||||
redis_service_1.RedisService,
|
||||
],
|
||||
exports: ['REDIS_CLIENT', redis_service_1.RedisService],
|
||||
})
|
||||
], RedisModule);
|
||||
//# sourceMappingURL=redis.module.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"redis.module.js","sourceRoot":"","sources":["../../src/common/redis.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAgD;AAChD,2CAA+C;AAC/C,iCAAqC;AACrC,mDAA+C;AAwBxC,IAAM,WAAW,GAAjB,MAAM,WAAW;CAAG,CAAA;AAAd,kCAAW;sBAAX,WAAW;IAjBvB,IAAA,eAAM,GAAE;IACR,IAAA,eAAM,EAAC;QACN,SAAS,EAAE;YACT;gBACE,OAAO,EAAE,cAAc;gBACvB,UAAU,EAAE,KAAK,EAAE,aAA4B,EAAE,EAAE;oBACjD,MAAM,GAAG,GAAG,aAAa,CAAC,GAAG,CAAS,WAAW,EAAE,oBAAoB,CAAC,CAAC;oBACzE,MAAM,MAAM,GAAG,IAAA,oBAAY,EAAC,EAAE,GAAG,EAAE,CAAC,CAAC;oBACrC,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC;oBACvB,OAAO,MAAM,CAAC;gBAChB,CAAC;gBACD,MAAM,EAAE,CAAC,sBAAa,CAAC;aACxB;YACD,4BAAY;SACb;QACD,OAAO,EAAE,CAAC,cAAc,EAAE,4BAAY,CAAC;KACxC,CAAC;GACW,WAAW,CAAG"}
|
||||
-11
@@ -1,11 +0,0 @@
|
||||
import { OnModuleDestroy } from '@nestjs/common';
|
||||
import type { RedisClientType } from 'redis';
|
||||
export declare class RedisService implements OnModuleDestroy {
|
||||
private readonly redisClient;
|
||||
constructor(redisClient: RedisClientType);
|
||||
getClient(): RedisClientType;
|
||||
set(key: string, value: any, ttlSeconds?: number): Promise<void>;
|
||||
get<T>(key: string): Promise<T | null>;
|
||||
del(key: string): Promise<void>;
|
||||
onModuleDestroy(): Promise<void>;
|
||||
}
|
||||
-58
@@ -1,58 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.RedisService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
let RedisService = class RedisService {
|
||||
redisClient;
|
||||
constructor(redisClient) {
|
||||
this.redisClient = redisClient;
|
||||
}
|
||||
getClient() {
|
||||
return this.redisClient;
|
||||
}
|
||||
async set(key, value, ttlSeconds) {
|
||||
const stringValue = typeof value === 'string' ? value : JSON.stringify(value);
|
||||
if (ttlSeconds) {
|
||||
await this.redisClient.set(key, stringValue, { EX: ttlSeconds });
|
||||
}
|
||||
else {
|
||||
await this.redisClient.set(key, stringValue);
|
||||
}
|
||||
}
|
||||
async get(key) {
|
||||
const value = await this.redisClient.get(key);
|
||||
if (!value)
|
||||
return null;
|
||||
try {
|
||||
return JSON.parse(value);
|
||||
}
|
||||
catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
async del(key) {
|
||||
await this.redisClient.del(key);
|
||||
}
|
||||
async onModuleDestroy() {
|
||||
await this.redisClient.quit();
|
||||
}
|
||||
};
|
||||
exports.RedisService = RedisService;
|
||||
exports.RedisService = RedisService = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(0, (0, common_1.Inject)('REDIS_CLIENT')),
|
||||
__metadata("design:paramtypes", [Object])
|
||||
], RedisService);
|
||||
//# sourceMappingURL=redis.service.js.map
|
||||
-1
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"redis.service.js","sourceRoot":"","sources":["../../src/common/redis.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAqE;AAS9D,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEoB;IAD3C,YAC2C,WAA4B;QAA5B,gBAAW,GAAX,WAAW,CAAiB;IACpE,CAAC;IAKJ,SAAS;QACP,OAAO,IAAI,CAAC,WAAW,CAAC;IAC1B,CAAC;IAMD,KAAK,CAAC,GAAG,CAAC,GAAW,EAAE,KAAU,EAAE,UAAmB;QACpD,MAAM,WAAW,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;QAC9E,IAAI,UAAU,EAAE,CAAC;YACf,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,EAAE,EAAE,EAAE,UAAU,EAAE,CAAC,CAAC;QACnE,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,WAAW,CAAC,CAAC;QAC/C,CAAC;IACH,CAAC;IAMD,KAAK,CAAC,GAAG,CAAI,GAAW;QACtB,MAAM,KAAK,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QAC9C,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAC;QAExB,IAAI,CAAC;YACH,OAAO,IAAI,CAAC,KAAK,CAAC,KAAK,CAAM,CAAC;QAChC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAqB,CAAC;QAC/B,CAAC;IACH,CAAC;IAMD,KAAK,CAAC,GAAG,CAAC,GAAW;QACnB,MAAM,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAClC,CAAC;IAED,KAAK,CAAC,eAAe;QACnB,MAAM,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC;IAChC,CAAC;CACF,CAAA;AAnDY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;IAGR,WAAA,IAAA,eAAM,EAAC,cAAc,CAAC,CAAA;;GAFd,YAAY,CAmDxB"}
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class TelegramService {
|
||||
private configService;
|
||||
private readonly logger;
|
||||
private readonly botToken;
|
||||
private readonly chatId;
|
||||
constructor(configService: ConfigService);
|
||||
sendMessage(text: string): Promise<boolean>;
|
||||
sendIntelligenceReport(stats: {
|
||||
syncResult: number;
|
||||
updatedSegments: number;
|
||||
discoveredRoads: number;
|
||||
totalPoints: number;
|
||||
days: number;
|
||||
timeProfiles?: number;
|
||||
}): Promise<boolean>;
|
||||
}
|
||||
-76
@@ -1,76 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var TelegramService_1;
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TelegramService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
let TelegramService = TelegramService_1 = class TelegramService {
|
||||
configService;
|
||||
logger = new common_1.Logger(TelegramService_1.name);
|
||||
botToken;
|
||||
chatId;
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
this.botToken = this.configService.get('TELEGRAM_BOT_TOKEN') || '';
|
||||
this.chatId = this.configService.get('TELEGRAM_CHAT_ID') || '';
|
||||
}
|
||||
async sendMessage(text) {
|
||||
if (!this.botToken || !this.chatId || this.botToken.includes('YOUR_BOT_TOKEN')) {
|
||||
this.logger.warn('Telegram Bot Token or Chat ID not configured. Skipping notification.');
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
this.logger.log('Sending report to Telegram...');
|
||||
const url = `https://api.telegram.org/bot${this.botToken}/sendMessage`;
|
||||
await axios_1.default.post(url, {
|
||||
chat_id: this.chatId,
|
||||
text: text,
|
||||
parse_mode: 'HTML',
|
||||
});
|
||||
this.logger.log('✅ Telegram notification sent successfully.');
|
||||
return true;
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`❌ Failed to send Telegram message: ${error.message}`);
|
||||
if (error.response) {
|
||||
this.logger.error(`Status: ${error.response.status}, Data: ${JSON.stringify(error.response.data)}`);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async sendIntelligenceReport(stats) {
|
||||
const message = `
|
||||
🚀 <b>Intaleq Map Intelligence Report</b>
|
||||
━━━━━━━━━━━━━━━━━━
|
||||
📅 <b>Window:</b> Last ${stats.days} days
|
||||
📥 <b>Tracks Imported:</b> ${stats.syncResult} points
|
||||
🛣️ <b>Roads Updated:</b> ${stats.updatedSegments} segments
|
||||
✨ <b>New Roads Found:</b> ${stats.discoveredRoads} candidates
|
||||
🕒 <b>Time Profiles:</b> ${stats.timeProfiles || 0} buckets
|
||||
📍 <b>Database Total:</b> ${stats.totalPoints} points
|
||||
━━━━━━━━━━━━━━━━━━
|
||||
✅ <i>Deep Sync and Analysis Complete.</i>
|
||||
`;
|
||||
return this.sendMessage(message);
|
||||
}
|
||||
};
|
||||
exports.TelegramService = TelegramService;
|
||||
exports.TelegramService = TelegramService = TelegramService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof config_1.ConfigService !== "undefined" && config_1.ConfigService) === "function" ? _a : Object])
|
||||
], TelegramService);
|
||||
//# sourceMappingURL=telegram.service.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"telegram.service.js","sourceRoot":"","sources":["../../src/common/telegram.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2CAA+C;AAC/C,kDAA0B;AAGnB,IAAM,eAAe,uBAArB,MAAM,eAAe;IAKN;IAJH,MAAM,GAAG,IAAI,eAAM,CAAC,iBAAe,CAAC,IAAI,CAAC,CAAC;IAC1C,QAAQ,CAAS;IACjB,MAAM,CAAS;IAEhC,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;QAC9C,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,oBAAoB,CAAC,IAAI,EAAE,CAAC;QAC3E,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,kBAAkB,CAAC,IAAI,EAAE,CAAC;IACzE,CAAC;IAMD,KAAK,CAAC,WAAW,CAAC,IAAY;QAC5B,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,CAAC,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC/E,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,sEAAsE,CAAC,CAAC;YACzF,OAAO,KAAK,CAAC;QACf,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAC;YACjD,MAAM,GAAG,GAAG,+BAA+B,IAAI,CAAC,QAAQ,cAAc,CAAC;YACvE,MAAM,eAAK,CAAC,IAAI,CAAC,GAAG,EAAE;gBACpB,OAAO,EAAE,IAAI,CAAC,MAAM;gBACpB,IAAI,EAAE,IAAI;gBACV,UAAU,EAAE,MAAM;aACnB,CAAC,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,4CAA4C,CAAC,CAAC;YAC9D,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,sCAAsC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACzE,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,KAAK,CAAC,QAAQ,CAAC,MAAM,WAAW,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YACtG,CAAC;YACD,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAKD,KAAK,CAAC,sBAAsB,CAAC,KAO5B;QACC,MAAM,OAAO,GAAG;;;yBAGK,KAAK,CAAC,IAAI;6BACN,KAAK,CAAC,UAAU;4BACjB,KAAK,CAAC,eAAe;4BACrB,KAAK,CAAC,eAAe;2BACtB,KAAK,CAAC,YAAY,IAAI,CAAC;4BACtB,KAAK,CAAC,WAAW;;;KAGxC,CAAC;QACF,OAAO,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;IACnC,CAAC;CACF,CAAA;AAhEY,0CAAe;0BAAf,eAAe;IAD3B,IAAA,mBAAU,GAAE;yDAMwB,sBAAa,oBAAb,sBAAa;GALrC,eAAe,CAgE3B"}
|
||||
@@ -1,19 +0,0 @@
|
||||
import { Repository } from 'typeorm';
|
||||
import { AdminBoundary } from './entities/admin-boundary.entity';
|
||||
export declare class AdminBoundariesService {
|
||||
private adminBoundaryRepo;
|
||||
private readonly logger;
|
||||
constructor(adminBoundaryRepo: Repository<AdminBoundary>);
|
||||
importGeoJSON(targetCountryCode: string, geoJson: any): Promise<{
|
||||
success: boolean;
|
||||
imported: number;
|
||||
errors: number;
|
||||
skipped: number;
|
||||
}>;
|
||||
importFromFile(countryCode: string, filePath: string): Promise<{
|
||||
success: boolean;
|
||||
imported: number;
|
||||
errors: number;
|
||||
skipped: number;
|
||||
}>;
|
||||
}
|
||||
-138
@@ -1,138 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var AdminBoundariesService_1;
|
||||
var _a;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AdminBoundariesService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const admin_boundary_entity_1 = require("./entities/admin-boundary.entity");
|
||||
let AdminBoundariesService = AdminBoundariesService_1 = class AdminBoundariesService {
|
||||
adminBoundaryRepo;
|
||||
logger = new common_1.Logger(AdminBoundariesService_1.name);
|
||||
constructor(adminBoundaryRepo) {
|
||||
this.adminBoundaryRepo = adminBoundaryRepo;
|
||||
}
|
||||
async importGeoJSON(targetCountryCode, geoJson) {
|
||||
this.logger.log(`Importing admin boundaries for ${targetCountryCode}...`);
|
||||
let imported = 0;
|
||||
let errors = 0;
|
||||
let skipped = 0;
|
||||
if (!geoJson || !geoJson.features || !Array.isArray(geoJson.features)) {
|
||||
this.logger.error('Invalid GeoJSON format');
|
||||
return { success: false, imported: 0, errors: 1, skipped: 0 };
|
||||
}
|
||||
for (const feature of geoJson.features) {
|
||||
try {
|
||||
const props = feature.properties || {};
|
||||
const countryCode = props.country || props.iso_3166_1_alpha2;
|
||||
if (targetCountryCode && countryCode && countryCode.toUpperCase() !== targetCountryCode.toUpperCase()) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
const subtype = (props.subtype || '').toLowerCase();
|
||||
let adminLevel;
|
||||
switch (subtype) {
|
||||
case 'country':
|
||||
adminLevel = 2;
|
||||
break;
|
||||
case 'region':
|
||||
adminLevel = 4;
|
||||
break;
|
||||
case 'county':
|
||||
adminLevel = 6;
|
||||
break;
|
||||
case 'localadmin':
|
||||
adminLevel = 8;
|
||||
break;
|
||||
case 'locality':
|
||||
adminLevel = 8;
|
||||
break;
|
||||
case 'neighborhood':
|
||||
adminLevel = 10;
|
||||
break;
|
||||
case 'macrohood':
|
||||
adminLevel = 9;
|
||||
break;
|
||||
case 'microhood':
|
||||
adminLevel = 11;
|
||||
break;
|
||||
default:
|
||||
adminLevel = parseInt(props.admin_level || props.adminLevel, 10);
|
||||
}
|
||||
if (isNaN(adminLevel)) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
const nameAr = props['names']?.['primary'] || props['names']?.['common'] || props['name_ar'] || props['name'];
|
||||
const nameEn = props['names']?.['en'] || props['name_en'];
|
||||
const isArabic = (text) => /[\u0600-\u06FF]/.test(text);
|
||||
if (['JO', 'SY', 'EG'].includes(targetCountryCode.toUpperCase()) && nameAr && !isArabic(nameAr)) {
|
||||
const alternativeAr = Object.values(props['names'] || {}).find(v => typeof v === 'string' && isArabic(v));
|
||||
if (!alternativeAr) {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let geom = feature.geometry;
|
||||
if (geom.type === 'Polygon') {
|
||||
geom = { type: 'MultiPolygon', coordinates: [geom.coordinates] };
|
||||
}
|
||||
else if (geom.type !== 'MultiPolygon') {
|
||||
skipped++;
|
||||
continue;
|
||||
}
|
||||
const boundary = this.adminBoundaryRepo.create({
|
||||
country_code: targetCountryCode.toUpperCase(),
|
||||
admin_level: adminLevel,
|
||||
name_ar: nameAr,
|
||||
name_en: nameEn,
|
||||
geom: geom,
|
||||
});
|
||||
await this.adminBoundaryRepo.save(boundary);
|
||||
imported++;
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error(`Error importing feature: ${err.message}`);
|
||||
errors++;
|
||||
}
|
||||
}
|
||||
this.logger.log(`Import finished. Imported: ${imported}, Errors: ${errors}, Skipped: ${skipped}`);
|
||||
return { success: true, imported, errors, skipped };
|
||||
}
|
||||
async importFromFile(countryCode, filePath) {
|
||||
const fs = require('fs');
|
||||
if (!fs.existsSync(filePath)) {
|
||||
this.logger.error(`File not found: ${filePath}`);
|
||||
return { success: false, imported: 0, errors: 1, skipped: 0 };
|
||||
}
|
||||
try {
|
||||
const data = fs.readFileSync(filePath, 'utf8');
|
||||
const geoJson = JSON.parse(data);
|
||||
return this.importGeoJSON(countryCode, geoJson);
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error(`Failed to read or parse GeoJSON file: ${err.message}`);
|
||||
return { success: false, imported: 0, errors: 1, skipped: 0 };
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.AdminBoundariesService = AdminBoundariesService;
|
||||
exports.AdminBoundariesService = AdminBoundariesService = AdminBoundariesService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(0, (0, typeorm_1.InjectRepository)(admin_boundary_entity_1.AdminBoundary)),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _a : Object])
|
||||
], AdminBoundariesService);
|
||||
//# sourceMappingURL=admin-boundaries.service.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"admin-boundaries.service.js","sourceRoot":"","sources":["../../src/geocoding/admin-boundaries.service.ts"],"names":[],"mappings":"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"}
|
||||
@@ -1,42 +0,0 @@
|
||||
import { Repository, DataSource } from 'typeorm';
|
||||
import { NeighborhoodPoint } from './entities/neighborhood-point.entity';
|
||||
import { NeighborhoodPolygon } from './entities/neighborhood-polygon.entity';
|
||||
import { AdminBoundary } from './entities/admin-boundary.entity';
|
||||
export declare class AdministrativeLinkingService {
|
||||
private dataSource;
|
||||
private readonly neighborhoodPointRepo;
|
||||
private readonly neighborhoodPolygonRepo;
|
||||
private readonly adminBoundaryRepo;
|
||||
private readonly logger;
|
||||
constructor(dataSource: DataSource, neighborhoodPointRepo: Repository<NeighborhoodPoint>, neighborhoodPolygonRepo: Repository<NeighborhoodPolygon>, adminBoundaryRepo: Repository<AdminBoundary>);
|
||||
private readonly overpassMirrors;
|
||||
private fetchWithRetry;
|
||||
syncOsmNeighborhoodPoints(bbox?: string, country?: string): Promise<{
|
||||
detected_country: string;
|
||||
osm_fetched: any;
|
||||
country_points: number;
|
||||
total_points_in_db: number;
|
||||
linked_to_district: number;
|
||||
}>;
|
||||
generateVoronoiNeighborhoods(country?: string): Promise<{
|
||||
status: string;
|
||||
country: string;
|
||||
total_polygons: number;
|
||||
districts_processed: any;
|
||||
}>;
|
||||
linkPlaces(country: 'jordan' | 'syria' | 'egypt'): Promise<{
|
||||
status: string;
|
||||
country: "syria" | "jordan" | "egypt";
|
||||
total_places: number;
|
||||
linked_neighborhood: number;
|
||||
linked_district: number;
|
||||
linked_governorate: number;
|
||||
sample_masoum: any;
|
||||
rows_affected: any;
|
||||
} | {
|
||||
status: string;
|
||||
error: string;
|
||||
polygons: number;
|
||||
places: number;
|
||||
}>;
|
||||
}
|
||||
@@ -1,283 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
var __param = (this && this.__param) || function (paramIndex, decorator) {
|
||||
return function (target, key) { decorator(target, key, paramIndex); }
|
||||
};
|
||||
var __importDefault = (this && this.__importDefault) || function (mod) {
|
||||
return (mod && mod.__esModule) ? mod : { "default": mod };
|
||||
};
|
||||
var AdministrativeLinkingService_1;
|
||||
var _a, _b, _c, _d;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AdministrativeLinkingService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const neighborhood_point_entity_1 = require("./entities/neighborhood-point.entity");
|
||||
const neighborhood_polygon_entity_1 = require("./entities/neighborhood-polygon.entity");
|
||||
const admin_boundary_entity_1 = require("./entities/admin-boundary.entity");
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
let AdministrativeLinkingService = AdministrativeLinkingService_1 = class AdministrativeLinkingService {
|
||||
dataSource;
|
||||
neighborhoodPointRepo;
|
||||
neighborhoodPolygonRepo;
|
||||
adminBoundaryRepo;
|
||||
logger = new common_1.Logger(AdministrativeLinkingService_1.name);
|
||||
constructor(dataSource, neighborhoodPointRepo, neighborhoodPolygonRepo, adminBoundaryRepo) {
|
||||
this.dataSource = dataSource;
|
||||
this.neighborhoodPointRepo = neighborhoodPointRepo;
|
||||
this.neighborhoodPolygonRepo = neighborhoodPolygonRepo;
|
||||
this.adminBoundaryRepo = adminBoundaryRepo;
|
||||
}
|
||||
overpassMirrors = [
|
||||
'https://overpass-api.de/api/interpreter',
|
||||
'https://overpass.kumi.systems/api/interpreter',
|
||||
'https://z.overpass-api.de/api/interpreter'
|
||||
];
|
||||
async fetchWithRetry(data, retries = 3) {
|
||||
for (let i = 0; i <= retries; i++) {
|
||||
const url = this.overpassMirrors[i % this.overpassMirrors.length];
|
||||
try {
|
||||
const response = await axios_1.default.post(url, data, {
|
||||
timeout: 45000,
|
||||
headers: {
|
||||
'User-Agent': 'IntaleqMapBot/1.0',
|
||||
'Content-Type': 'application/x-www-form-urlencoded'
|
||||
}
|
||||
});
|
||||
return response.data;
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.warn(`Mirror ${url} failed: ${error.message}. Trying next mirror...`);
|
||||
if (i === retries)
|
||||
throw error;
|
||||
await new Promise(res => setTimeout(res, 2000));
|
||||
}
|
||||
}
|
||||
}
|
||||
async syncOsmNeighborhoodPoints(bbox = '31.86,35.94,32.22,36.25', country) {
|
||||
this.logger.log(`Syncing neighborhood points for country: ${country || 'auto'}, bbox: ${bbox}`);
|
||||
const query = `[out:json][timeout:900];node["place"~"neighbourhood|suburb|town"](${bbox});out;`;
|
||||
try {
|
||||
const data = await this.fetchWithRetry(`data=${encodeURIComponent(query)}`);
|
||||
const elements = data.elements;
|
||||
this.logger.log(`Found ${elements.length} points in OSM.`);
|
||||
let detectedCountry = country;
|
||||
if (!detectedCountry) {
|
||||
const firstLat = parseFloat(bbox.split(',')[0]);
|
||||
if (firstLat > 32.3)
|
||||
detectedCountry = 'syria';
|
||||
else if (firstLat < 31.0)
|
||||
detectedCountry = 'egypt';
|
||||
else
|
||||
detectedCountry = 'jordan';
|
||||
}
|
||||
for (const e of elements) {
|
||||
const nameAr = e.tags['name:ar'] || e.tags['name'];
|
||||
const nameEn = e.tags['name:en'] || e.tags['name'];
|
||||
const geometry = { type: 'Point', coordinates: [e.lon, e.lat] };
|
||||
await this.dataSource.query(`
|
||||
INSERT INTO neighborhood_points (osm_id, name_ar, name_en, place_type, geometry, country)
|
||||
VALUES ($1, $2, $3, $4, ST_SetSRID(ST_GeomFromGeoJSON($5), 4326), $6)
|
||||
ON CONFLICT (osm_id) DO UPDATE SET
|
||||
name_ar = EXCLUDED.name_ar,
|
||||
name_en = EXCLUDED.name_en,
|
||||
place_type = EXCLUDED.place_type,
|
||||
geometry = EXCLUDED.geometry,
|
||||
country = COALESCE(EXCLUDED.country, neighborhood_points.country);
|
||||
`, [e.id, nameAr, nameEn, e.tags['place'], JSON.stringify(geometry), detectedCountry]);
|
||||
}
|
||||
await this.dataSource.query(`
|
||||
UPDATE neighborhood_points np
|
||||
SET district_id = COALESCE(
|
||||
(
|
||||
SELECT id FROM admin_boundaries ab
|
||||
WHERE ab.admin_level IN (6, 8)
|
||||
AND ST_Contains(ab.geom::geometry, np.geometry::geometry)
|
||||
ORDER BY ab.admin_level DESC
|
||||
LIMIT 1
|
||||
),
|
||||
(
|
||||
SELECT id FROM admin_boundaries ab
|
||||
WHERE ab.admin_level IN (6, 8)
|
||||
ORDER BY ab.geom::geometry <-> np.geometry::geometry
|
||||
LIMIT 1
|
||||
)
|
||||
)
|
||||
WHERE district_id IS NULL AND (country = $1 OR $1 IS NULL);
|
||||
`, [detectedCountry]);
|
||||
const totalPoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points`);
|
||||
const linkedPoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points WHERE district_id IS NOT NULL`);
|
||||
const countryPoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points WHERE country = $1`, [detectedCountry]);
|
||||
const diagnostics = {
|
||||
detected_country: detectedCountry,
|
||||
osm_fetched: elements.length,
|
||||
country_points: parseInt(countryPoints[0].cnt),
|
||||
total_points_in_db: parseInt(totalPoints[0].cnt),
|
||||
linked_to_district: parseInt(linkedPoints[0].cnt),
|
||||
};
|
||||
this.logger.log(`Step 1 diagnostics: ${JSON.stringify(diagnostics)}`);
|
||||
return diagnostics;
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error(`Failed to sync OSM points: ${error.message}`, error.stack);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
async generateVoronoiNeighborhoods(country) {
|
||||
this.logger.log(`Generating Voronoi polygons for ${country || 'all'} neighborhoods...`);
|
||||
const whereClause = country ? `WHERE district_id IS NOT NULL AND country = '${country}'` : `WHERE district_id IS NOT NULL`;
|
||||
const prePoints = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_points ${whereClause}`);
|
||||
const preDistricts = await this.dataSource.query(`SELECT district_id, count(*) as cnt FROM neighborhood_points ${whereClause} GROUP BY district_id`);
|
||||
const deleteWhere = country ? `WHERE method = 'voronoi' AND country = '${country}'` : `WHERE method = 'voronoi'`;
|
||||
await this.dataSource.query(`DELETE FROM neighborhood_polygons ${deleteWhere}`);
|
||||
this.logger.log(`Deleted old voronoi polygons for ${country || 'all'}.`);
|
||||
let totalInserted = 0;
|
||||
for (const row of preDistricts) {
|
||||
const districtId = row.district_id;
|
||||
const pointCount = parseInt(row.cnt);
|
||||
try {
|
||||
if (pointCount >= 2) {
|
||||
const result = await this.dataSource.query(`
|
||||
INSERT INTO neighborhood_polygons (osm_id, name_ar, name_en, place_type, parent_id, method, country, geometry)
|
||||
WITH voronoi_cells AS (
|
||||
SELECT (ST_Dump(ST_VoronoiPolygons(
|
||||
ST_Collect(np.geometry::geometry),
|
||||
0.00001,
|
||||
d.geom::geometry
|
||||
))).geom as cell
|
||||
FROM neighborhood_points np
|
||||
JOIN admin_boundaries d ON d.id = np.district_id
|
||||
WHERE np.district_id = $1
|
||||
GROUP BY d.geom
|
||||
),
|
||||
matched AS (
|
||||
SELECT
|
||||
np.osm_id, np.name_ar, np.name_en, np.place_type, np.district_id, np.country,
|
||||
ST_Multi(ST_Intersection(vc.cell::geometry, d.geom::geometry)) as geometry
|
||||
FROM voronoi_cells vc
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT np2.*
|
||||
FROM neighborhood_points np2
|
||||
WHERE np2.district_id = $1
|
||||
ORDER BY np2.geometry::geometry <-> vc.cell::geometry
|
||||
LIMIT 1
|
||||
) np
|
||||
JOIN admin_boundaries d ON d.id = $1
|
||||
)
|
||||
SELECT osm_id, name_ar, name_en, place_type, district_id, 'voronoi', country, geometry
|
||||
FROM matched
|
||||
WHERE ST_IsValid(geometry) AND NOT ST_IsEmpty(geometry)
|
||||
`, [districtId]);
|
||||
totalInserted += (result?.length || result?.[1] || 0);
|
||||
}
|
||||
else if (pointCount === 1) {
|
||||
await this.dataSource.query(`
|
||||
INSERT INTO neighborhood_polygons (osm_id, name_ar, name_en, place_type, parent_id, method, country, geometry)
|
||||
SELECT np.osm_id, np.name_ar, np.name_en, np.place_type, np.district_id, 'voronoi', np.country, ST_Multi(d.geom::geometry)
|
||||
FROM neighborhood_points np
|
||||
JOIN admin_boundaries d ON d.id = np.district_id
|
||||
WHERE np.district_id = $1
|
||||
`, [districtId]);
|
||||
totalInserted++;
|
||||
}
|
||||
}
|
||||
catch (err) {
|
||||
this.logger.error(`Voronoi FAILED for district ${districtId}: ${err.message}`);
|
||||
}
|
||||
}
|
||||
const postCount = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_polygons ${country ? `WHERE country = '${country}'` : ''}`);
|
||||
return {
|
||||
status: 'success',
|
||||
country: country || 'all',
|
||||
total_polygons: parseInt(postCount[0].cnt),
|
||||
districts_processed: preDistricts.length
|
||||
};
|
||||
}
|
||||
async linkPlaces(country) {
|
||||
const tableName = `places_${country}`;
|
||||
const polyCount = await this.dataSource.query(`SELECT count(*) as cnt FROM neighborhood_polygons`);
|
||||
const placeCount = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE location IS NOT NULL`);
|
||||
this.logger.log(`linkPlaces pre-flight: ${polyCount[0].cnt} polygons, ${placeCount[0].cnt} places with location in ${tableName}`);
|
||||
if (parseInt(polyCount[0].cnt) === 0) {
|
||||
this.logger.error('ABORT: neighborhood_polygons is EMPTY. Run generate-voronoi first!');
|
||||
return {
|
||||
status: 'FAILED',
|
||||
error: 'neighborhood_polygons table is empty. Run generate-voronoi first.',
|
||||
polygons: 0,
|
||||
places: parseInt(placeCount[0].cnt)
|
||||
};
|
||||
}
|
||||
this.logger.log(`Linking administrative hierarchy for ${tableName} using KNN...`);
|
||||
const updateResult = await this.dataSource.query(`
|
||||
UPDATE ${tableName} p
|
||||
SET
|
||||
neighborhood_id = (
|
||||
SELECT np.id FROM neighborhood_polygons np
|
||||
WHERE (np.country = $1 OR np.country IS NULL)
|
||||
ORDER BY p.location::geometry <-> np.geometry::geometry
|
||||
LIMIT 1
|
||||
),
|
||||
sub_district_id = (
|
||||
SELECT ab.id FROM admin_boundaries ab
|
||||
WHERE ab.admin_level = 8
|
||||
AND ST_Within(p.location::geometry, ab.geom::geometry)
|
||||
LIMIT 1
|
||||
),
|
||||
district_id = (
|
||||
SELECT ab.id FROM admin_boundaries ab
|
||||
WHERE ab.admin_level = 6
|
||||
AND ST_Within(p.location::geometry, ab.geom::geometry)
|
||||
LIMIT 1
|
||||
),
|
||||
governorate_id = (
|
||||
SELECT ab.id FROM admin_boundaries ab
|
||||
WHERE ab.admin_level = 4
|
||||
AND ST_Within(p.location::geometry, ab.geom::geometry)
|
||||
LIMIT 1
|
||||
)
|
||||
WHERE p.location IS NOT NULL;
|
||||
`, [country]);
|
||||
const linkedNeighborhood = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE neighborhood_id IS NOT NULL`);
|
||||
const linkedDistrict = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE district_id IS NOT NULL`);
|
||||
const linkedGov = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName} WHERE governorate_id IS NOT NULL`);
|
||||
const totalPlaces = await this.dataSource.query(`SELECT count(*) as cnt FROM ${tableName}`);
|
||||
const samplePlace = await this.dataSource.query(`
|
||||
SELECT p.id, p.name_ar, p.neighborhood_id, p.district_id, p.governorate_id,
|
||||
n.name_ar as neighborhood_name
|
||||
FROM ${tableName} p
|
||||
LEFT JOIN neighborhood_polygons n ON p.neighborhood_id = n.id
|
||||
WHERE p.name_ar LIKE '%معصوم%'
|
||||
LIMIT 3
|
||||
`);
|
||||
const diagnostics = {
|
||||
status: 'completed',
|
||||
country,
|
||||
total_places: parseInt(totalPlaces[0].cnt),
|
||||
linked_neighborhood: parseInt(linkedNeighborhood[0].cnt),
|
||||
linked_district: parseInt(linkedDistrict[0].cnt),
|
||||
linked_governorate: parseInt(linkedGov[0].cnt),
|
||||
sample_masoum: samplePlace,
|
||||
rows_affected: updateResult?.[1] || 'unknown'
|
||||
};
|
||||
this.logger.log(`Step 3 diagnostics: ${JSON.stringify(diagnostics)}`);
|
||||
return diagnostics;
|
||||
}
|
||||
};
|
||||
exports.AdministrativeLinkingService = AdministrativeLinkingService;
|
||||
exports.AdministrativeLinkingService = AdministrativeLinkingService = AdministrativeLinkingService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(1, (0, typeorm_1.InjectRepository)(neighborhood_point_entity_1.NeighborhoodPoint)),
|
||||
__param(2, (0, typeorm_1.InjectRepository)(neighborhood_polygon_entity_1.NeighborhoodPolygon)),
|
||||
__param(3, (0, typeorm_1.InjectRepository)(admin_boundary_entity_1.AdminBoundary)),
|
||||
__metadata("design:paramtypes", [typeof (_a = typeof typeorm_2.DataSource !== "undefined" && typeorm_2.DataSource) === "function" ? _a : Object, typeof (_b = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _b : Object, typeof (_c = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _c : Object, typeof (_d = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _d : Object])
|
||||
], AdministrativeLinkingService);
|
||||
//# sourceMappingURL=administrative-linking.service.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -1,4 +0,0 @@
|
||||
export declare class ReverseGeocodeDto {
|
||||
lat: number;
|
||||
lng: number;
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ReverseGeocodeDto = void 0;
|
||||
const class_validator_1 = require("class-validator");
|
||||
const class_transformer_1 = require("class-transformer");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
class ReverseGeocodeDto {
|
||||
lat;
|
||||
lng;
|
||||
}
|
||||
exports.ReverseGeocodeDto = ReverseGeocodeDto;
|
||||
__decorate([
|
||||
(0, class_validator_1.IsNotEmpty)(),
|
||||
(0, class_validator_1.IsNumber)(),
|
||||
(0, class_validator_1.Min)(-90),
|
||||
(0, class_validator_1.Max)(90),
|
||||
(0, class_transformer_1.Type)(() => Number),
|
||||
(0, swagger_1.ApiProperty)({ description: 'Latitude' }),
|
||||
__metadata("design:type", Number)
|
||||
], ReverseGeocodeDto.prototype, "lat", void 0);
|
||||
__decorate([
|
||||
(0, class_validator_1.IsNotEmpty)(),
|
||||
(0, class_validator_1.IsNumber)(),
|
||||
(0, class_validator_1.Min)(-180),
|
||||
(0, class_validator_1.Max)(180),
|
||||
(0, class_transformer_1.Type)(() => Number),
|
||||
(0, swagger_1.ApiProperty)({ description: 'Longitude' }),
|
||||
__metadata("design:type", Number)
|
||||
], ReverseGeocodeDto.prototype, "lng", void 0);
|
||||
//# sourceMappingURL=reverse-geocode.dto.js.map
|
||||
@@ -1 +0,0 @@
|
||||
{"version":3,"file":"reverse-geocode.dto.js","sourceRoot":"","sources":["../../../src/geocoding/dto/reverse-geocode.dto.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qDAAiE;AACjE,yDAAyC;AACzC,6CAA8C;AAE9C,MAAa,iBAAiB;IAO5B,GAAG,CAAS;IAQZ,GAAG,CAAS;CACb;AAhBD,8CAgBC;AATC;IANC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,EAAE,CAAC;IACR,IAAA,qBAAG,EAAC,EAAE,CAAC;IACP,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,UAAU,EAAE,CAAC;;8CAC7B;AAQZ;IANC,IAAA,4BAAU,GAAE;IACZ,IAAA,0BAAQ,GAAE;IACV,IAAA,qBAAG,EAAC,CAAC,GAAG,CAAC;IACT,IAAA,qBAAG,EAAC,GAAG,CAAC;IACR,IAAA,wBAAI,EAAC,GAAG,EAAE,CAAC,MAAM,CAAC;IAClB,IAAA,qBAAW,EAAC,EAAE,WAAW,EAAE,WAAW,EAAE,CAAC;;8CAC9B"}
|
||||
@@ -1,7 +0,0 @@
|
||||
export declare class SearchQueryDto {
|
||||
q: string;
|
||||
lat?: number;
|
||||
lng?: number;
|
||||
radius?: number;
|
||||
country?: string;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user