Compare commits

86 Commits
Author SHA1 Message Date
Hamza-Ayed cb91e5f0de fix: resolve MapLibre worker rendering regression by configuring custom worker and updating geometry type filters 2026-09-24 18:49:40 +03:00
Hamza-Ayed 043cd052b6 fix(tactical): resolve MapLibre line-dasharray crash and add self-healing layers for viewshed, LOS, and artillery 2026-09-24 13:23:38 +03:00
Hamza-Ayed 8dbb336e0a feat: add Jordan places migration scripts and enhance map controller with style-load safety and camera controls 2026-09-23 20:56:34 +03:00
Hamza-Ayed dc9a736e26 MENA region data enrichment scripts` -> Wait, let's keep it concise and clean.
*   *Option 3:* `feat: add tourist landmarks support with UI sheets, data models
2026-09-21 05:21:44 +03:00
Hamza-Ayed cff5a8e1ce chore: remove obsolete python patch scripts and update API key fallback handling in frontend views 2026-09-20 00:59:59 +03:00
Hamza-Ayed e8f5de585d fix: remove landing page nav tab and prevent crowdsource contribution modal from auto-opening on tourism map 2026-09-20 00:46:06 +03:00
Hamza-Ayed ce31d79ba6 feat: add place gate population scripts, map style assets, automated cron sync tasks, and expand dashboard and landing applications 2026-09-19 21:22:05 +03:00
Hamza-Ayed 82b1045f9c feat: bind Landing Page explicitly to #landing route and dedicated Landing Page nav button 2026-09-19 12:56:50 +03:00
Hamza-Ayed e819186201 feat: make Mapbox-grade Landing Page the default home portal with standalone landing.html page 2026-09-19 12:55:15 +03:00
Hamza-Ayed 126c94df63 feat: add Mapbox-grade sovereign portal landing page with interactive showcase, unit economics ROI simulator, and developer playground 2026-09-19 12:36:10 +03:00
Hamza-Ayed 335aa299da chore: update .gitignore to exclude sqlite routing packages 2026-09-19 12:34:35 +03:00
Hamza-Ayed 0bf13824c9 feat: add Jordan 3D topographic platform, Siro Map cinematic orbit studio, sun-shadow simulation, and sovereign intelligence security gating 2026-09-19 12:34:26 +03:00
Hamza-Ayed 91e9e71ce4 feat: introduce routing status tracking for approved roads and telemetry batch queue limits 2026-09-19 12:34:26 +03:00
Hamza-Ayed a8470eb76c feat: add device authentication, place gates support, and PiP navigation features 2026-09-19 12:34:26 +03:00
Hamza-Ayed 279c9fa510 feat: add Google Play store assets and documentation alongside UI copy updates 2026-09-19 12:34:26 +03:00
Hamza-Ayed ce896df30a feat: add device authentication, place gates support, and PiP navigation features 2026-09-19 12:34:26 +03:00
Hamza-Ayed 55830beee8 feat: add vehicle customization, Arabic search normalization, and smooth movement interpolation 2026-09-19 12:34:25 +03:00
Hamza-Ayed 43ad2e0ad4 chore: add build artifacts, map assets, and routing data 2026-09-19 12:34:24 +03:00
Hamza-Ayed aa2b9f131f fix(tactical): implement dynamic MGRS grid conversion, triangle of error resection accuracy, clean surveyed IDW elevation, and robust offline sync validation 2026-09-14 14:21:30 +03:00
Hamza-Ayed 17e8f8eeac refactor(tactical-app): modernize state management with GetX controllers, bindings, and clean MVP architecture 2026-08-22 15:27:53 +03:00
Hamza-Ayed 5e3be672ee feat(tactical-app): full sovereign military suite in Flutter with first-run onboarding, artillery ballistics, HLZ assessment, minefield breach, isochrone, symbols, and overlays 2026-08-22 15:24:09 +03:00
Hamza-Ayed c10fd2396c feat(tactical): sovereign military suite with 100% real satellite DEM intervisibility, viewshed 360, offline engines, and entitlements 2026-08-22 15:11:16 +03:00
Hamza-Ayed 4a7859f4c2 feat: integrate CocoaPods for macos tactical app and update environment configurations 2026-08-21 04:12:13 +03:00
Hamza-Ayed 7216d86740 feat(dem): Universal analytical Jordan DEM surface model for all routes across the Kingdom 2026-08-21 02:55:15 +03:00
Hamza-Ayed 96b22d1fc0 fix(elevation): Restore smooth non-zero continuous terrain differentials 2026-08-21 02:49:41 +03:00
Hamza-Ayed 8a0cf35116 fix(slope): Remove artificial high-frequency noise and calibrate gentle urban gradients 2026-08-21 02:46:02 +03:00
Hamza-Ayed ad4b5d991c feat(topography): Multi-scale micro-terrain and polyline sub-segment elevation tracker 2026-08-21 02:39:58 +03:00
Hamza-Ayed ff97821fd0 fix(elevation): Continuous smooth IDW DEM model for realistic gradients without jumps 2026-08-21 02:34:39 +03:00
Hamza-Ayed 398daceffb fix(routing): Friendly Arabic diagnostic messages for out-of-boundary / off-grid points 2026-08-21 02:26:38 +03:00
Hamza-Ayed 0849b7b53c feat(pricing): Automated Monthly Jordan Fuel Pricing Service via Gemini AI and Cron Schedule 2026-08-21 02:19:08 +03:00
Hamza-Ayed f14d40dac7 feat(routing): Dynamic traffic delay & descent-aware mechanical braking advice 2026-08-21 02:15:56 +03:00
Hamza-Ayed 7572d6b4c5 feat(api & web): Implement real-time Eco Routing, Fuel & Energy Consumption and Cost Engine 2026-08-21 01:58:00 +03:00
Hamza-Ayed e1e634fcb3 fix(docker): Inject VITE_API_KEY environment variable into web frontend container 2026-08-21 00:10:28 +03:00
Hamza-Ayed 429496dd3c fix(web): Add AlertTriangle import and remove hardcoded API key fallback 2026-08-21 00:09:16 +03:00
Hamza-Ayed d7119a53eb fix(security & web): Enforce strict ApiKeyGuard and fix missing Gauge import in App.tsx 2026-08-21 00:07:34 +03:00
Hamza-Ayed 50ecf9bfbf fix(api): Allow trusted portal domains to use demo master API key seamlessly 2026-08-21 00:02:54 +03:00
Hamza-Ayed 9e65afae59 fix(web): Add default master API key fallback for web route calculation 2026-08-21 00:01:11 +03:00
Hamza-Ayed 81d88ee770 fix(api): Use estimateElevation in tactical.service.ts 2026-08-20 23:56:41 +03:00
Hamza-Ayed 431d1f95ff feat: Add Intaleq Native Android Kotlin & iOS Swift SDKs, Incline slope routing, Topographic contours and modular tactical app 2026-08-20 23:54:58 +03:00
Hamza-Ayed 4f6595b1eb feat(showcase): add Autonomous Gemini Vision Map Verification section to executive showcase future roadmap 2026-08-19 17:00:54 +03:00
Hamza-Ayed 52a912380f fix(roads): split parameterized queries into individual statements to satisfy node-postgres prepared statement rule 2026-08-19 16:46:59 +03:00
Hamza-Ayed 06895d6720 fix(roads): replace ON CONFLICT with delete and insert for bulletproof candidate approval 2026-08-19 16:44:59 +03:00
Hamza-Ayed 313c2f641f fix(roads): explicit cast geometry::geometry on insert to approved_roads 2026-08-19 16:38:00 +03:00
Hamza-Ayed 956dae8e75 fix(roads): add ALTER TABLE migration for candidate_id column in approved_roads table 2026-08-19 16:34:42 +03:00
Hamza-Ayed b98ce56622 fix(roads): fix road approval & deletion persistence, add robust endpoints, delete candidate from DB upon reject/delete, and add clear all pending button 2026-08-19 16:29:24 +03:00
Hamza-Ayed 3d13d31101 feat(weather): add real-time animated cloud and precipitation radar layer, integrate early warning badges, refine showcase text, and remove floating CTA button 2026-08-19 16:14:33 +03:00
Hamza-Ayed 470d6d959d fix(showcase): resolve scrolling lock, inject Apple Arabic typography globally, add sticky quick-jump floating subnav, and format continuous executive presentation 2026-08-18 20:24:04 +03:00
Hamza-Ayed aa2967964f feat(showcase): upgrade strategic executive presentation with Apple typography, glassmorphic UI, live tactical telemetry simulator, 8 C4ISR modules, and 2026+ future vision roadmap 2026-08-18 20:15:20 +03:00
Hamza-Ayed ad231c6f20 feat(tactical): integrate cross-country terrain slope, basalt rocks, and off-road mobility analysis into Isochrone engine 2026-08-18 15:40:18 +03:00
Hamza-Ayed 9088af0534 feat(tactical): implement real road-network ray-casting routing for Isochrone reachability 2026-08-18 15:31:56 +03:00
Hamza-Ayed fc4be2b8fd fix: resolve isochroneLoading variable reference in TacticalDefenseView 2026-08-18 15:19:15 +03:00
Hamza-Ayed 3109d782d3 feat(tactical): add interactive Isochrone reachability analysis for hospitals and civil defense 2026-08-18 15:17:37 +03:00
Hamza-Ayed ac4a74d0e0 fix(deps): align @nestjs/common and @nestjs/core to 11.2.1 to resolve sse-signal decorator module error 2026-08-18 14:49:09 +03:00
Hamza-Ayed c28d7f236e feat(tactical): add isochrone emergency reachability endpoint and clean build config 2026-08-18 14:16:40 +03:00
Hamza-Ayed 0d21c1bfbf chore: update sync script file paths 2026-08-18 14:02:40 +03:00
Hamza-Ayed bab24892c7 chore(cleanup): archive one-time migrations, secure telemetry endpoint, and purge temporary files 2026-08-18 14:00:20 +03:00
Hamza-Ayed cb384765fb feat(security): implement dynamic tactical license gate, instant key revocation, and clean hardcoded credentials 2026-08-18 13:46:02 +03:00
Hamza-Ayed 8375773898 fix(web): position map source switcher below navbar and fix missing Flame icon import in ExecutiveShowcase 2026-08-18 13:05:36 +03:00
Hamza-Ayed 5d813f3a3f fix(web): use pre-seeded master API key by default so platform opens seamlessly without auth prompts 2026-08-18 13:02:00 +03:00
Hamza-Ayed f1e31cb721 refactor(tactical): remove ridge lines to keep map uncluttered and focused strictly on wadi flows 2026-08-18 12:54:35 +03:00
Hamza-Ayed 13ff137359 fix(tactical): implement scientific D8 hydrological flow routing for natural wadis and isolate from ridges 2026-08-18 12:44:58 +03:00
Hamza-Ayed c9b3249aa5 fix(web): add shared DemSource singleton, add ErrorBoundary, and conditional view rendering to prevent WebGL crash 2026-08-18 12:35:17 +03:00
Hamza-Ayed ba4ae15eab feat(routing): smart street extraction and distinctive naming for main and alternative routes 2026-08-18 12:25:10 +03:00
Hamza-Ayed 0a854c4302 feat(routing): pure Arabic localization for all navigation instructions (translate Continue onto, toward, keep right, etc.) 2026-08-18 12:18:06 +03:00
Hamza-Ayed 57e3da7ea4 feat(routing): enable elevation profile and steep slope/incline warnings in routing API and frontend 2026-08-18 12:10:32 +03:00
Hamza-Ayed 06543ee8e0 feat(executive): redesign executive strategic showcase with Apple Light theme, SF Pro typography, animations, and full C4ISR tactical modules 2026-08-18 11:57:24 +03:00
Hamza-Ayed ba2f211438 fix(tactical): remove label spam on terrain grid, refine realistic cliff gradient >= 28 deg, and soften overlay opacities 2026-08-18 11:43:28 +03:00
Hamza-Ayed c35f6e61f2 feat(tactical): high-resolution 21x21 DEM terrain study, bilinear interpolation, cliff/rock barrier detection, and 4-tier slope classification 2026-08-18 11:37:48 +03:00
Hamza-Ayed b039281972 feat(tactical): real DEM grid slope analysis, dynamic contour lines, and topography-driven wadi/ridge features 2026-08-18 11:15:56 +03:00
Hamza-Ayed b1ee482830 feat(tactical): add map visualization layers for peaks, valleys, slope sectors, wadis, roads, urban, and basalt hazards 2026-08-18 10:56:58 +03:00
Hamza-Ayed 6bd0bc0ce5 feat(tactical): add comprehensive Terrain Study (دراسة الأرض) with natural/manmade obstacles and OAKOC intelligence report 2026-08-18 10:42:04 +03:00
Hamza-Ayed 91c36d99c9 fix(tactical): resolve click stale closures, add direct coordinate inputs, and draggable markers 2026-08-18 10:24:26 +03:00
Hamza-Ayed 23dde3c8e3 feat(tactical): add interactive location picker, minefield quantities analysis, and organizational feasibility study 2026-08-18 10:15:15 +03:00
Hamza-Ayed 97b8b27ddf feat(tactical): enhance symbols palette, instructions, and fix header layout spacing 2026-08-18 09:55:52 +03:00
Hamza-Ayed 2691eb68eb fix(web): fix tactical map style.json and implement keep-alive views for instant tab switching 2026-08-18 09:49:58 +03:00
Hamza-Ayed 9247499f50 fix(tactical): sync complete tactical service with LOS and artillery methods 2026-08-18 09:47:04 +03:00
Hamza-Ayed 941420f5c0 fix(tactical): resolve DTO types and TS1272 build error for isolatedModules 2026-08-18 09:46:04 +03:00
Hamza-Ayed bd287d155b style(showcase): apple SF Pro typography and refined executive palette 2026-08-17 19:33:21 +03:00
Hamza-Ayed a0a5a09135 feat(tactical): add tactical line of sight API, elevation engine, dynamic step resolution, weather module and executive showcase 2026-08-17 19:25:39 +03:00
Hamza-Ayed 03cdc6fe9c feat: implement geometry snapping logic for manual road insertion and approved roads topology 2026-08-16 17:34:06 +03:00
Hamza-Ayed b3ffa7c71d fix: remove undefined getRefs call and clean up unused HERE checks in CompareView 2026-08-16 14:14:11 +03:00
Hamza-Ayed 844774bd1b feat: add Soviet Military Topo and Google Topo, remove HERE API dependency, relocate and restyle floating ViewNav 2026-08-16 14:09:02 +03:00
Hamza-Ayed 126761004c feat: enhance basemaps palette with Google Hybrid and Esri Clarity, remove carto, instant road approve update 2026-08-16 13:53:38 +03:00
Hamza-Ayed 8061f3e7a3 feat: add road refinement controller and HERE maps in CompareView 2026-08-16 13:35:09 +03:00
Hamza-Ayed 3136a2b7aa feat: sync scripts, scraper, and sql tools from server 2026-08-16 13:19:10 +03:00
Hamza-Ayed 32865e78df Backup before syncing server scripts 2026-08-16 13:17:56 +03:00
1020 changed files with 438844 additions and 117978 deletions
-9
View File
@@ -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
+4
View File
@@ -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
View File
@@ -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
+25
View File
@@ -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/
+13 -1
View File
@@ -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.
-6
View File
@@ -1,6 +0,0 @@
import { AppService } from './app.service';
export declare class AppController {
private readonly appService;
constructor(appService: AppService);
getHello(): string;
}
-35
View File
@@ -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
-1
View File
@@ -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"}
-2
View File
@@ -1,2 +0,0 @@
export declare class AppModule {
}
-65
View File
@@ -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
-1
View File
@@ -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"}
-3
View File
@@ -1,3 +0,0 @@
export declare class AppService {
getHello(): string;
}
-20
View File
@@ -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
-1
View File
@@ -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"}
-9
View File
@@ -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>;
}
-53
View File
@@ -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
View File
@@ -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"}
-24
View File
@@ -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>;
}
-180
View File
@@ -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
File diff suppressed because one or more lines are too long
-5
View File
@@ -1,5 +0,0 @@
export declare class CreateKeyDto {
name: string;
rateLimit?: number;
allowedOrigins?: string[];
}
-35
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"}
-10
View File
@@ -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>;
}
-54
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1,2 +0,0 @@
export declare class BillingModule {
}
-32
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -1 +0,0 @@
{"version":3,"file":"billing.service.js","sourceRoot":"","sources":["../../src/billing/billing.service.ts"],"names":[],"mappings":"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"}
-18
View File
@@ -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;
}
-82
View File
@@ -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"}
-23
View File
@@ -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;
}
-92
View File
@@ -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"}
-16
View File
@@ -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
View File
@@ -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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"}
-14
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"}
-8
View File
@@ -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;
-21
View File
@@ -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
View File
@@ -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"}
-4
View File
@@ -1,4 +0,0 @@
import { CanActivate, ExecutionContext } from '@nestjs/common';
export declare class AdminGuard implements CanActivate {
canActivate(context: ExecutionContext): boolean;
}
-29
View File
@@ -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
View File
@@ -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"}
-7
View File
@@ -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
View File
@@ -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
View File
@@ -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"}
-6
View File
@@ -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>;
}
-37
View File
@@ -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
View File
@@ -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
View File
@@ -1,2 +0,0 @@
export declare class MailModule {
}
-22
View File
@@ -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
View File
@@ -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
View File
@@ -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>;
}
-122
View File
@@ -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
View File
@@ -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
View File
@@ -1,2 +0,0 @@
export declare class RedisModule {
}
-36
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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"}
-19
View File
@@ -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
View File
@@ -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
-4
View File
@@ -1,4 +0,0 @@
export declare class ReverseGeocodeDto {
lat: number;
lng: number;
}
-39
View File
@@ -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"}
-7
View File
@@ -1,7 +0,0 @@
export declare class SearchQueryDto {
q: string;
lat?: number;
lng?: number;
radius?: number;
country?: string;
}
-58
View File
@@ -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);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.SearchQueryDto = void 0;
const class_validator_1 = require("class-validator");
const class_transformer_1 = require("class-transformer");
const swagger_1 = require("@nestjs/swagger");
class SearchQueryDto {
q;
lat;
lng;
radius = 20000;
country;
}
exports.SearchQueryDto = SearchQueryDto;
__decorate([
(0, class_validator_1.IsString)(),
(0, swagger_1.ApiPropertyOptional)({ description: 'Search query string' }),
__metadata("design:type", String)
], SearchQueryDto.prototype, "q", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiPropertyOptional)({ description: 'Latitude for proximity search' }),
__metadata("design:type", Number)
], SearchQueryDto.prototype, "lat", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiPropertyOptional)({ description: 'Longitude for proximity search' }),
__metadata("design:type", Number)
], SearchQueryDto.prototype, "lng", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsNumber)(),
(0, class_validator_1.Min)(0),
(0, class_validator_1.Max)(50000),
(0, class_transformer_1.Type)(() => Number),
(0, swagger_1.ApiPropertyOptional)({ description: 'Proximity radius in meters', default: 20000 }),
__metadata("design:type", Number)
], SearchQueryDto.prototype, "radius", void 0);
__decorate([
(0, class_validator_1.IsOptional)(),
(0, class_validator_1.IsEnum)(['jordan', 'syria', 'egypt']),
(0, swagger_1.ApiPropertyOptional)({ description: 'Country filter', enum: ['jordan', 'syria', 'egypt'] }),
__metadata("design:type", String)
], SearchQueryDto.prototype, "country", void 0);
//# sourceMappingURL=search-query.dto.js.map

Some files were not shown because too many files have changed in this diff Show More