stable: finalized GIS aesthetics and Syria/Jordan routing synchronization
This commit is contained in:
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
import { AppService } from './app.service';
|
||||
export declare class AppController {
|
||||
private readonly appService;
|
||||
constructor(appService: AppService);
|
||||
getHello(): string;
|
||||
}
|
||||
Vendored
+35
@@ -0,0 +1,35 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
var __metadata = (this && this.__metadata) || function (k, v) {
|
||||
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AppController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const app_service_1 = require("./app.service");
|
||||
let AppController = class AppController {
|
||||
appService;
|
||||
constructor(appService) {
|
||||
this.appService = appService;
|
||||
}
|
||||
getHello() {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
};
|
||||
exports.AppController = AppController;
|
||||
__decorate([
|
||||
(0, common_1.Get)(),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", String)
|
||||
], AppController.prototype, "getHello", null);
|
||||
exports.AppController = AppController = __decorate([
|
||||
(0, common_1.Controller)(),
|
||||
__metadata("design:paramtypes", [app_service_1.AppService])
|
||||
], AppController);
|
||||
//# sourceMappingURL=app.controller.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.controller.js","sourceRoot":"","sources":["../src/app.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAAiD;AACjD,+CAA2C;AAGpC,IAAM,aAAa,GAAnB,MAAM,aAAa;IACK;IAA7B,YAA6B,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IAAG,CAAC;IAGvD,QAAQ;QACN,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,EAAE,CAAC;IACpC,CAAC;CACF,CAAA;AAPY,sCAAa;AAIxB;IADC,IAAA,YAAG,GAAE;;;;6CAGL;wBANU,aAAa;IADzB,IAAA,mBAAU,GAAE;qCAE8B,wBAAU;GADxC,aAAa,CAOzB"}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
export declare class AppModule {
|
||||
}
|
||||
Vendored
+58
@@ -0,0 +1,58 @@
|
||||
"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 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");
|
||||
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: true,
|
||||
}),
|
||||
}),
|
||||
typeorm_1.TypeOrmModule.forRootAsync({
|
||||
name: 'mysqlConnection',
|
||||
imports: [config_1.ConfigModule],
|
||||
inject: [config_1.ConfigService],
|
||||
useFactory: (config) => ({
|
||||
type: 'mysql',
|
||||
host: config.get('GEOCODE_DB_HOST', 'localhost'),
|
||||
port: config.get('GEOCODE_DB_PORT', 3306),
|
||||
username: config.get('GEOCODE_DB_USER', 'root'),
|
||||
password: config.get('GEOCODE_DB_PASS', ''),
|
||||
database: config.get('GEOCODE_DB_NAME', 'geocodeDB'),
|
||||
autoLoadEntities: true,
|
||||
synchronize: false,
|
||||
}),
|
||||
}),
|
||||
telemetry_module_1.TelemetryModule,
|
||||
maps_module_1.MapsModule,
|
||||
geocoding_module_1.GeocodingModule,
|
||||
],
|
||||
})
|
||||
], AppModule);
|
||||
//# sourceMappingURL=app.module.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"app.module.js","sourceRoot":"","sources":["../src/app.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,2CAA6D;AAC7D,6CAAgD;AAChD,+CAAkD;AAClD,mEAA+D;AAC/D,oDAAgD;AAChD,mEAA+D;AAuCxD,IAAM,SAAS,GAAf,MAAM,SAAS;CAAG,CAAA;AAAZ,8BAAS;oBAAT,SAAS;IArCrB,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,IAAI;iBAElB,CAAC;aACH,CAAC;YACF,uBAAa,CAAC,YAAY,CAAC;gBACzB,IAAI,EAAE,iBAAiB;gBACvB,OAAO,EAAE,CAAC,qBAAY,CAAC;gBACvB,MAAM,EAAE,CAAC,sBAAa,CAAC;gBACvB,UAAU,EAAE,CAAC,MAAqB,EAAE,EAAE,CAAC,CAAC;oBACtC,IAAI,EAAE,OAAO;oBACb,IAAI,EAAE,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,WAAW,CAAC;oBAChD,IAAI,EAAE,MAAM,CAAC,GAAG,CAAS,iBAAiB,EAAE,IAAI,CAAC;oBACjD,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC;oBAC/C,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,EAAE,CAAC;oBAC3C,QAAQ,EAAE,MAAM,CAAC,GAAG,CAAC,iBAAiB,EAAE,WAAW,CAAC;oBACpD,gBAAgB,EAAE,IAAI;oBACtB,WAAW,EAAE,KAAK;iBACnB,CAAC;aACH,CAAC;YACF,kCAAe;YACf,wBAAU;YACV,kCAAe;SAChB;KACF,CAAC;GACW,SAAS,CAAG"}
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
export declare class AppService {
|
||||
getHello(): string;
|
||||
}
|
||||
Vendored
+20
@@ -0,0 +1,20 @@
|
||||
"use strict";
|
||||
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
||||
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
||||
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
||||
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
||||
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.AppService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
let AppService = class AppService {
|
||||
getHello() {
|
||||
return 'Hello World!';
|
||||
}
|
||||
};
|
||||
exports.AppService = AppService;
|
||||
exports.AppService = AppService = __decorate([
|
||||
(0, common_1.Injectable)()
|
||||
], AppService);
|
||||
//# sourceMappingURL=app.service.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"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
@@ -0,0 +1,9 @@
|
||||
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>;
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
"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.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", [config_1.ConfigService])
|
||||
], CacheService);
|
||||
//# sourceMappingURL=cache.service.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"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;qCAIwB,sBAAa;GAHrC,YAAY,CA2BxB"}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
export declare const JORDAN_BOUNDS: {
|
||||
minLat: number;
|
||||
maxLat: number;
|
||||
minLng: number;
|
||||
maxLng: number;
|
||||
};
|
||||
export declare function isWithinJordan(lat: number, lng: number): boolean;
|
||||
export declare function mockMapMatch(lat: number, lng: number): string;
|
||||
Vendored
+21
@@ -0,0 +1,21 @@
|
||||
"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
@@ -0,0 +1 @@
|
||||
{"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"}
|
||||
@@ -0,0 +1,7 @@
|
||||
import { CanActivate, ExecutionContext } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class ApiKeyGuard implements CanActivate {
|
||||
private configService;
|
||||
constructor(configService: ConfigService);
|
||||
canActivate(context: ExecutionContext): boolean;
|
||||
}
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
"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 config_1 = require("@nestjs/config");
|
||||
let ApiKeyGuard = class ApiKeyGuard {
|
||||
configService;
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
}
|
||||
canActivate(context) {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const apiKeyHeader = request.headers['x-api-key'];
|
||||
const validApiKey = this.configService.get('API_KEY');
|
||||
if (validApiKey && apiKeyHeader !== validApiKey) {
|
||||
throw new common_1.UnauthorizedException('Invalid or missing API Key');
|
||||
}
|
||||
return true;
|
||||
}
|
||||
};
|
||||
exports.ApiKeyGuard = ApiKeyGuard;
|
||||
exports.ApiKeyGuard = ApiKeyGuard = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [config_1.ConfigService])
|
||||
], ApiKeyGuard);
|
||||
//# sourceMappingURL=api-key.guard.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"api-key.guard.js","sourceRoot":"","sources":["../../../src/common/guards/api-key.guard.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,2CAKwB;AACxB,2CAA+C;AAGxC,IAAM,WAAW,GAAjB,MAAM,WAAW;IACF;IAApB,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;IAAG,CAAC;IAEpD,WAAW,CAAC,OAAyB;QACnC,MAAM,OAAO,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,UAAU,EAAE,CAAC;QACpD,MAAM,YAAY,GAAG,OAAO,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;QAClD,MAAM,WAAW,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,SAAS,CAAC,CAAC;QAG9D,IAAI,WAAW,IAAI,YAAY,KAAK,WAAW,EAAE,CAAC;YAChD,MAAM,IAAI,8BAAqB,CAAC,4BAA4B,CAAC,CAAC;QAChE,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF,CAAA;AAfY,kCAAW;sBAAX,WAAW;IADvB,IAAA,mBAAU,GAAE;qCAEwB,sBAAa;GADrC,WAAW,CAevB"}
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
export declare class RedisModule {
|
||||
}
|
||||
Vendored
+36
@@ -0,0 +1,36 @@
|
||||
"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
@@ -0,0 +1 @@
|
||||
{"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"}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
import { OnModuleDestroy } from '@nestjs/common';
|
||||
import type { RedisClientType } from 'redis';
|
||||
export declare class RedisService implements OnModuleDestroy {
|
||||
private readonly redisClient;
|
||||
constructor(redisClient: 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>;
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
"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;
|
||||
}
|
||||
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
@@ -0,0 +1 @@
|
||||
{"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;IAMJ,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;AA5CY,oCAAY;uBAAZ,YAAY;IADxB,IAAA,mBAAU,GAAE;IAGR,WAAA,IAAA,eAAM,EAAC,cAAc,CAAC,CAAA;;GAFd,YAAY,CA4CxB"}
|
||||
@@ -0,0 +1,11 @@
|
||||
export declare class OsmArea {
|
||||
id: number;
|
||||
osm_id: number;
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
name: string;
|
||||
name_ar: string;
|
||||
place_type: string;
|
||||
geom: any;
|
||||
other_tags: string;
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
"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.OsmArea = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
let OsmArea = class OsmArea {
|
||||
id;
|
||||
osm_id;
|
||||
longitude;
|
||||
latitude;
|
||||
name;
|
||||
name_ar;
|
||||
place_type;
|
||||
geom;
|
||||
other_tags;
|
||||
};
|
||||
exports.OsmArea = OsmArea;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)(),
|
||||
__metadata("design:type", Number)
|
||||
], OsmArea.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'bigint', nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], OsmArea.prototype, "osm_id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'decimal', precision: 11, scale: 8, nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], OsmArea.prototype, "longitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 8, nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], OsmArea.prototype, "latitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmArea.prototype, "name", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmArea.prototype, "name_ar", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmArea.prototype, "place_type", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'geometry', nullable: true }),
|
||||
__metadata("design:type", Object)
|
||||
], OsmArea.prototype, "geom", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmArea.prototype, "other_tags", void 0);
|
||||
exports.OsmArea = OsmArea = __decorate([
|
||||
(0, typeorm_1.Entity)('osm_areas')
|
||||
], OsmArea);
|
||||
//# sourceMappingURL=osm-area.entity.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"osm-area.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/osm-area.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAiE;AAG1D,IAAM,OAAO,GAAb,MAAM,OAAO;IAElB,EAAE,CAAS;IAGX,MAAM,CAAS;IAGf,SAAS,CAAS;IAGlB,QAAQ,CAAS;IAGjB,IAAI,CAAS;IAGb,OAAO,CAAS;IAGhB,UAAU,CAAS;IAGnB,IAAI,CAAM;IAGV,UAAU,CAAS;CACpB,CAAA;AA3BY,0BAAO;AAElB;IADC,IAAA,gCAAsB,GAAE;;mCACd;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;uCAC5B;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;0CACnD;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;yCACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qCACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wCACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACR;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qCACnC;AAGV;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACtB;kBA1BR,OAAO;IADnB,IAAA,gBAAM,EAAC,WAAW,CAAC;GACP,OAAO,CA2BnB"}
|
||||
@@ -0,0 +1,14 @@
|
||||
export declare class OsmPointWithArea {
|
||||
osm_id: number;
|
||||
longitude: number;
|
||||
latitude: number;
|
||||
name: string;
|
||||
name_ar: string;
|
||||
amenity: string;
|
||||
shop: string;
|
||||
addr_street: string;
|
||||
neighbourhood_name: string;
|
||||
city_name: string;
|
||||
other_tags: string;
|
||||
geom: any;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
"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.OsmPointWithArea = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
let OsmPointWithArea = class OsmPointWithArea {
|
||||
osm_id;
|
||||
longitude;
|
||||
latitude;
|
||||
name;
|
||||
name_ar;
|
||||
amenity;
|
||||
shop;
|
||||
addr_street;
|
||||
neighbourhood_name;
|
||||
city_name;
|
||||
other_tags;
|
||||
geom;
|
||||
};
|
||||
exports.OsmPointWithArea = OsmPointWithArea;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryColumn)({ type: 'bigint' }),
|
||||
__metadata("design:type", Number)
|
||||
], OsmPointWithArea.prototype, "osm_id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'decimal', precision: 11, scale: 8, nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], OsmPointWithArea.prototype, "longitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 8, nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], OsmPointWithArea.prototype, "latitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "name", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "name_ar", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "amenity", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "shop", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "addr_street", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "neighbourhood_name", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "city_name", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'text', nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], OsmPointWithArea.prototype, "other_tags", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'geometry', spatialFeatureType: 'Point', nullable: true }),
|
||||
__metadata("design:type", Object)
|
||||
], OsmPointWithArea.prototype, "geom", void 0);
|
||||
exports.OsmPointWithArea = OsmPointWithArea = __decorate([
|
||||
(0, typeorm_1.Entity)('osm_points_with_area')
|
||||
], OsmPointWithArea);
|
||||
//# sourceMappingURL=osm-point-with-area.entity.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"osm-point-with-area.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/osm-point-with-area.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAwD;AAGjD,IAAM,gBAAgB,GAAtB,MAAM,gBAAgB;IAE3B,MAAM,CAAS;IAGf,SAAS,CAAS;IAGlB,QAAQ,CAAS;IAGjB,IAAI,CAAS;IAGb,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,IAAI,CAAS;IAGb,WAAW,CAAS;IAGpB,kBAAkB,CAAS;IAG3B,SAAS,CAAS;IAGlB,UAAU,CAAS;IAGnB,IAAI,CAAM;CACX,CAAA;AApCY,4CAAgB;AAE3B;IADC,IAAA,uBAAa,EAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;;gDACnB;AAGf;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACnD;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;kDACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;qDACP;AAGpB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4DACA;AAG3B;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;mDACT;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;oDACtB;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;8CAChE;2BAnCC,gBAAgB;IAD5B,IAAA,gBAAM,EAAC,sBAAsB,CAAC;GAClB,gBAAgB,CAoC5B"}
|
||||
@@ -0,0 +1,12 @@
|
||||
export declare class PlaceSyria {
|
||||
id: number;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
name: string;
|
||||
name_ar: string;
|
||||
name_en: string;
|
||||
address: string;
|
||||
category: string;
|
||||
created_at: Date;
|
||||
location: any;
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
"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.PlaceSyria = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
let PlaceSyria = class PlaceSyria {
|
||||
id;
|
||||
latitude;
|
||||
longitude;
|
||||
name;
|
||||
name_ar;
|
||||
name_en;
|
||||
address;
|
||||
category;
|
||||
created_at;
|
||||
location;
|
||||
};
|
||||
exports.PlaceSyria = PlaceSyria;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)(),
|
||||
__metadata("design:type", Number)
|
||||
], PlaceSyria.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'decimal', precision: 10, scale: 8, nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], PlaceSyria.prototype, "latitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'decimal', precision: 11, scale: 8, nullable: true }),
|
||||
__metadata("design:type", Number)
|
||||
], PlaceSyria.prototype, "longitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], PlaceSyria.prototype, "name", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], PlaceSyria.prototype, "name_ar", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], PlaceSyria.prototype, "name_en", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], PlaceSyria.prototype, "address", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ nullable: true }),
|
||||
__metadata("design:type", String)
|
||||
], PlaceSyria.prototype, "category", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], PlaceSyria.prototype, "created_at", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'geometry', spatialFeatureType: 'Point', nullable: true }),
|
||||
__metadata("design:type", Object)
|
||||
], PlaceSyria.prototype, "location", void 0);
|
||||
exports.PlaceSyria = PlaceSyria = __decorate([
|
||||
(0, typeorm_1.Entity)('places_syria')
|
||||
], PlaceSyria);
|
||||
//# sourceMappingURL=place-syria.entity.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"place-syria.entity.js","sourceRoot":"","sources":["../../../src/geocoding/entities/place-syria.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAAmF;AAG5E,IAAM,UAAU,GAAhB,MAAM,UAAU;IAErB,EAAE,CAAS;IAGX,QAAQ,CAAS;IAGjB,SAAS,CAAS;IAGlB,IAAI,CAAS;IAGb,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,OAAO,CAAS;IAGhB,QAAQ,CAAS;IAGjB,UAAU,CAAO;IAGjB,QAAQ,CAAM;CACf,CAAA;AA9BY,gCAAU;AAErB;IADC,IAAA,gCAAsB,GAAE;;sCACd;AAGX;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACpD;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,SAAS,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;6CACnD;AAGlB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;wCACd;AAGb;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;2CACX;AAGhB;IADC,IAAA,gBAAM,EAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CACV;AAGjB;IADC,IAAA,0BAAgB,GAAE;8BACP,IAAI;8CAAC;AAGjB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;;4CAC5D;qBA7BH,UAAU;IADtB,IAAA,gBAAM,EAAC,cAAc,CAAC;GACV,UAAU,CA8BtB"}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { GeocodingService } from './geocoding.service';
|
||||
export declare class GeocodingController {
|
||||
private readonly geocodingService;
|
||||
constructor(geocodingService: GeocodingService);
|
||||
search(query: string): Promise<{
|
||||
userPlaces: import("./entities/place-syria.entity").PlaceSyria[];
|
||||
osmAreas: import("./entities/osm-area.entity").OsmArea[];
|
||||
}>;
|
||||
reverse(lat: number, lng: number): Promise<any>;
|
||||
addPlace(placeData: any): Promise<import("./entities/place-syria.entity").PlaceSyria>;
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
"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.GeocodingController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
const geocoding_service_1 = require("./geocoding.service");
|
||||
const api_key_guard_1 = require("../common/guards/api-key.guard");
|
||||
let GeocodingController = class GeocodingController {
|
||||
geocodingService;
|
||||
constructor(geocodingService) {
|
||||
this.geocodingService = geocodingService;
|
||||
}
|
||||
async search(query) {
|
||||
return this.geocodingService.searchPlaces(query);
|
||||
}
|
||||
async reverse(lat, lng) {
|
||||
return this.geocodingService.reverseGeocode(lat, lng);
|
||||
}
|
||||
async addPlace(placeData) {
|
||||
return this.geocodingService.addPlace(placeData);
|
||||
}
|
||||
};
|
||||
exports.GeocodingController = GeocodingController;
|
||||
__decorate([
|
||||
(0, common_1.Get)('search'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Search for locations (Forward Geocoding)' }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'q', required: true }),
|
||||
__param(0, (0, common_1.Query)('q')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [String]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], GeocodingController.prototype, "search", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('reverse'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Reverse Geocoding (Lat/Lng to Address)' }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'lat', required: true }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'lng', required: true }),
|
||||
__param(0, (0, common_1.Query)('lat')),
|
||||
__param(1, (0, common_1.Query)('lng')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number, Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], GeocodingController.prototype, "reverse", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('places'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Add a new location (User Submitted)' }),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], GeocodingController.prototype, "addPlace", null);
|
||||
exports.GeocodingController = GeocodingController = __decorate([
|
||||
(0, swagger_1.ApiTags)('geocoding'),
|
||||
(0, common_1.Controller)('geocoding'),
|
||||
(0, common_1.UseGuards)(api_key_guard_1.ApiKeyGuard),
|
||||
__metadata("design:paramtypes", [geocoding_service_1.GeocodingService])
|
||||
], GeocodingController);
|
||||
//# sourceMappingURL=geocoding.controller.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"geocoding.controller.js","sourceRoot":"","sources":["../../src/geocoding/geocoding.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA+E;AAC/E,6CAAkE;AAClE,2DAAuD;AACvD,kEAA6D;AAKtD,IAAM,mBAAmB,GAAzB,MAAM,mBAAmB;IACD;IAA7B,YAA6B,gBAAkC;QAAlC,qBAAgB,GAAhB,gBAAgB,CAAkB;IAAG,CAAC;IAK7D,AAAN,KAAK,CAAC,MAAM,CAAa,KAAa;QACpC,OAAO,IAAI,CAAC,gBAAgB,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC;IAMK,AAAN,KAAK,CAAC,OAAO,CAAe,GAAW,EAAgB,GAAW;QAChE,OAAO,IAAI,CAAC,gBAAgB,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;IACxD,CAAC;IAIK,AAAN,KAAK,CAAC,QAAQ,CAAS,SAAc;QACnC,OAAO,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACnD,CAAC;CACF,CAAA;AAvBY,kDAAmB;AAMxB;IAHL,IAAA,YAAG,EAAC,QAAQ,CAAC;IACb,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,0CAA0C,EAAE,CAAC;IACrE,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1B,WAAA,IAAA,cAAK,EAAC,GAAG,CAAC,CAAA;;;;iDAEvB;AAMK;IAJL,IAAA,YAAG,EAAC,SAAS,CAAC;IACd,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,wCAAwC,EAAE,CAAC;IACnE,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IACzC,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3B,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;IAAe,WAAA,IAAA,cAAK,EAAC,KAAK,CAAC,CAAA;;;;kDAErD;AAIK;IAFL,IAAA,aAAI,EAAC,QAAQ,CAAC;IACd,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qCAAqC,EAAE,CAAC;IACjD,WAAA,IAAA,aAAI,GAAE,CAAA;;;;mDAErB;8BAtBU,mBAAmB;IAH/B,IAAA,iBAAO,EAAC,WAAW,CAAC;IACpB,IAAA,mBAAU,EAAC,WAAW,CAAC;IACvB,IAAA,kBAAS,EAAC,2BAAW,CAAC;qCAE0B,oCAAgB;GADpD,mBAAmB,CAuB/B"}
|
||||
@@ -0,0 +1,2 @@
|
||||
export declare class GeocodingModule {
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
"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.GeocodingModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const geocoding_service_1 = require("./geocoding.service");
|
||||
const geocoding_controller_1 = require("./geocoding.controller");
|
||||
const place_syria_entity_1 = require("./entities/place-syria.entity");
|
||||
const osm_area_entity_1 = require("./entities/osm-area.entity");
|
||||
const osm_point_with_area_entity_1 = require("./entities/osm-point-with-area.entity");
|
||||
let GeocodingModule = class GeocodingModule {
|
||||
};
|
||||
exports.GeocodingModule = GeocodingModule;
|
||||
exports.GeocodingModule = GeocodingModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [
|
||||
typeorm_1.TypeOrmModule.forFeature([
|
||||
place_syria_entity_1.PlaceSyria,
|
||||
osm_area_entity_1.OsmArea,
|
||||
osm_point_with_area_entity_1.OsmPointWithArea
|
||||
], 'mysqlConnection'),
|
||||
],
|
||||
controllers: [geocoding_controller_1.GeocodingController],
|
||||
providers: [geocoding_service_1.GeocodingService],
|
||||
})
|
||||
], GeocodingModule);
|
||||
//# sourceMappingURL=geocoding.module.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"geocoding.module.js","sourceRoot":"","sources":["../../src/geocoding/geocoding.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,2DAAuD;AACvD,iEAA6D;AAC7D,sEAA2D;AAC3D,gEAAqD;AACrD,sFAAyE;AAalE,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAX3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC;gBACvB,+BAAU;gBACV,yBAAO;gBACP,6CAAgB;aACjB,EAAE,iBAAiB,CAAC;SACtB;QACD,WAAW,EAAE,CAAC,0CAAmB,CAAC;QAClC,SAAS,EAAE,CAAC,oCAAgB,CAAC;KAC9B,CAAC;GACW,eAAe,CAAG"}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Repository } from 'typeorm';
|
||||
import { PlaceSyria } from './entities/place-syria.entity';
|
||||
import { OsmArea } from './entities/osm-area.entity';
|
||||
import { OsmPointWithArea } from './entities/osm-point-with-area.entity';
|
||||
export declare class GeocodingService {
|
||||
private placesRepository;
|
||||
private osmAreasRepository;
|
||||
private osmPointsRepository;
|
||||
private readonly logger;
|
||||
constructor(placesRepository: Repository<PlaceSyria>, osmAreasRepository: Repository<OsmArea>, osmPointsRepository: Repository<OsmPointWithArea>);
|
||||
searchPlaces(query: string): Promise<{
|
||||
userPlaces: PlaceSyria[];
|
||||
osmAreas: OsmArea[];
|
||||
}>;
|
||||
reverseGeocode(lat: number, lng: number): Promise<any>;
|
||||
addPlace(data: Partial<PlaceSyria>): Promise<PlaceSyria>;
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
"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 GeocodingService_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GeocodingService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const place_syria_entity_1 = require("./entities/place-syria.entity");
|
||||
const osm_area_entity_1 = require("./entities/osm-area.entity");
|
||||
const osm_point_with_area_entity_1 = require("./entities/osm-point-with-area.entity");
|
||||
let GeocodingService = GeocodingService_1 = class GeocodingService {
|
||||
placesRepository;
|
||||
osmAreasRepository;
|
||||
osmPointsRepository;
|
||||
logger = new common_1.Logger(GeocodingService_1.name);
|
||||
constructor(placesRepository, osmAreasRepository, osmPointsRepository) {
|
||||
this.placesRepository = placesRepository;
|
||||
this.osmAreasRepository = osmAreasRepository;
|
||||
this.osmPointsRepository = osmPointsRepository;
|
||||
}
|
||||
async searchPlaces(query) {
|
||||
if (!query || query.length < 3)
|
||||
return { userPlaces: [], osmAreas: [] };
|
||||
const userPlaces = await this.placesRepository.find({
|
||||
where: [
|
||||
{ name: (0, typeorm_2.Like)(`%${query}%`) },
|
||||
{ name_ar: (0, typeorm_2.Like)(`%${query}%`) },
|
||||
],
|
||||
take: 10,
|
||||
});
|
||||
const osmAreas = await this.osmAreasRepository.find({
|
||||
where: [
|
||||
{ name: (0, typeorm_2.Like)(`%${query}%`) },
|
||||
{ name_ar: (0, typeorm_2.Like)(`%${query}%`) },
|
||||
],
|
||||
take: 10,
|
||||
});
|
||||
return {
|
||||
userPlaces,
|
||||
osmAreas,
|
||||
};
|
||||
}
|
||||
async reverseGeocode(lat, lng) {
|
||||
try {
|
||||
const query = `
|
||||
SELECT *, ST_Distance_Sphere(location, POINT(?, ?)) as distance
|
||||
FROM places_syria
|
||||
WHERE location IS NOT NULL
|
||||
ORDER BY distance ASC
|
||||
LIMIT 5
|
||||
`;
|
||||
const results = await this.placesRepository.query(query, [lng, lat]);
|
||||
return results;
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error('Reverse geocoding error:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
async addPlace(data) {
|
||||
const newPlace = this.placesRepository.create({
|
||||
...data,
|
||||
created_at: new Date(),
|
||||
});
|
||||
return this.placesRepository.save(newPlace);
|
||||
}
|
||||
};
|
||||
exports.GeocodingService = GeocodingService;
|
||||
exports.GeocodingService = GeocodingService = GeocodingService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(0, (0, typeorm_1.InjectRepository)(place_syria_entity_1.PlaceSyria, 'mysqlConnection')),
|
||||
__param(1, (0, typeorm_1.InjectRepository)(osm_area_entity_1.OsmArea, 'mysqlConnection')),
|
||||
__param(2, (0, typeorm_1.InjectRepository)(osm_point_with_area_entity_1.OsmPointWithArea, 'mysqlConnection')),
|
||||
__metadata("design:paramtypes", [typeorm_2.Repository,
|
||||
typeorm_2.Repository,
|
||||
typeorm_2.Repository])
|
||||
], GeocodingService);
|
||||
//# sourceMappingURL=geocoding.service.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"geocoding.service.js","sourceRoot":"","sources":["../../src/geocoding/geocoding.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,6CAAmD;AACnD,qCAA2C;AAC3C,sEAA2D;AAC3D,gEAAqD;AACrD,sFAAyE;AAGlE,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAKjB;IAEA;IAEA;IARO,MAAM,GAAG,IAAI,eAAM,CAAC,kBAAgB,CAAC,IAAI,CAAC,CAAC;IAE5D,YAEU,gBAAwC,EAExC,kBAAuC,EAEvC,mBAAiD;QAJjD,qBAAgB,GAAhB,gBAAgB,CAAwB;QAExC,uBAAkB,GAAlB,kBAAkB,CAAqB;QAEvC,wBAAmB,GAAnB,mBAAmB,CAA8B;IACxD,CAAC;IAEJ,KAAK,CAAC,YAAY,CAAC,KAAa;QAC9B,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO,EAAE,UAAU,EAAE,EAAE,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;QAGxE,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC;YAClD,KAAK,EAAE;gBACL,EAAE,IAAI,EAAE,IAAA,cAAI,EAAC,IAAI,KAAK,GAAG,CAAC,EAAE;gBAC5B,EAAE,OAAO,EAAE,IAAA,cAAI,EAAC,IAAI,KAAK,GAAG,CAAC,EAAE;aAChC;YACD,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;QAGH,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,kBAAkB,CAAC,IAAI,CAAC;YAClD,KAAK,EAAE;gBACL,EAAE,IAAI,EAAE,IAAA,cAAI,EAAC,IAAI,KAAK,GAAG,CAAC,EAAE;gBAC5B,EAAE,OAAO,EAAE,IAAA,cAAI,EAAC,IAAI,KAAK,GAAG,CAAC,EAAE;aAChC;YACD,IAAI,EAAE,EAAE;SACT,CAAC,CAAC;QAEH,OAAO;YACL,UAAU;YACV,QAAQ;SACT,CAAC;IACJ,CAAC;IAED,KAAK,CAAC,cAAc,CAAC,GAAW,EAAE,GAAW;QAE3C,IAAI,CAAC;YACH,MAAM,KAAK,GAAG;;;;;;OAMb,CAAC;YACF,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,EAAE,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC;YACrE,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,0BAA0B,EAAE,KAAK,CAAC,CAAC;YACrD,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;IAED,KAAK,CAAC,QAAQ,CAAC,IAAyB;QAEtC,MAAM,QAAQ,GAAG,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;YAC5C,GAAG,IAAI;YACP,UAAU,EAAE,IAAI,IAAI,EAAE;SACvB,CAAC,CAAC;QACH,OAAO,IAAI,CAAC,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;IAC9C,CAAC;CACF,CAAA;AAjEY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;IAKR,WAAA,IAAA,0BAAgB,EAAC,+BAAU,EAAE,iBAAiB,CAAC,CAAA;IAE/C,WAAA,IAAA,0BAAgB,EAAC,yBAAO,EAAE,iBAAiB,CAAC,CAAA;IAE5C,WAAA,IAAA,0BAAgB,EAAC,6CAAgB,EAAE,iBAAiB,CAAC,CAAA;qCAH5B,oBAAU;QAER,oBAAU;QAET,oBAAU;GAT9B,gBAAgB,CAiE5B"}
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
export {};
|
||||
Vendored
+24
@@ -0,0 +1,24 @@
|
||||
"use strict";
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const core_1 = require("@nestjs/core");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
const app_module_1 = require("./app.module");
|
||||
async function bootstrap() {
|
||||
const app = await core_1.NestFactory.create(app_module_1.AppModule);
|
||||
app.enableCors();
|
||||
app.setGlobalPrefix('api');
|
||||
const config = new swagger_1.DocumentBuilder()
|
||||
.setTitle('Jordan Map Platform API')
|
||||
.setDescription('Backend API for map services, routing, and driver telemetry for Jordan')
|
||||
.setVersion('1.0')
|
||||
.addTag('maps')
|
||||
.addTag('telemetry')
|
||||
.build();
|
||||
const document = swagger_1.SwaggerModule.createDocument(app, config);
|
||||
swagger_1.SwaggerModule.setup('docs', app, document);
|
||||
const port = process.env.API_PORT || 3000;
|
||||
await app.listen(port);
|
||||
console.log(`🚀 Jordan Map API is running on: http://localhost:${port}/api`);
|
||||
}
|
||||
bootstrap();
|
||||
//# sourceMappingURL=main.js.map
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";;AAAA,uCAA2C;AAC3C,6CAAiE;AACjE,6CAAyC;AAEzC,KAAK,UAAU,SAAS;IACtB,MAAM,GAAG,GAAG,MAAM,kBAAW,CAAC,MAAM,CAAC,sBAAS,CAAC,CAAC;IAIhD,GAAG,CAAC,UAAU,EAAE,CAAC;IAGjB,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC;IAG3B,MAAM,MAAM,GAAG,IAAI,yBAAe,EAAE;SACjC,QAAQ,CAAC,yBAAyB,CAAC;SACnC,cAAc,CAAC,wEAAwE,CAAC;SACxF,UAAU,CAAC,KAAK,CAAC;SACjB,MAAM,CAAC,MAAM,CAAC;SACd,MAAM,CAAC,WAAW,CAAC;SACnB,KAAK,EAAE,CAAC;IAEX,MAAM,QAAQ,GAAG,uBAAa,CAAC,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;IAC3D,uBAAa,CAAC,KAAK,CAAC,MAAM,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC;IAE3C,MAAM,IAAI,GAAG,OAAO,CAAC,GAAG,CAAC,QAAQ,IAAI,IAAI,CAAC;IAC1C,MAAM,GAAG,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IACvB,OAAO,CAAC,GAAG,CAAC,qDAAqD,IAAI,MAAM,CAAC,CAAC;AAC/E,CAAC;AACD,SAAS,EAAE,CAAC"}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
export declare class CandidateRoad {
|
||||
id: string;
|
||||
geometry: any;
|
||||
uniqueDriverCount: number;
|
||||
totalPoints: number;
|
||||
averageSpeed: number;
|
||||
lengthMeters: number;
|
||||
confidence: number;
|
||||
status: string;
|
||||
discoveredAt: Date;
|
||||
reviewedAt: Date;
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
"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.CandidateRoad = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
let CandidateRoad = class CandidateRoad {
|
||||
id;
|
||||
geometry;
|
||||
uniqueDriverCount;
|
||||
totalPoints;
|
||||
averageSpeed;
|
||||
lengthMeters;
|
||||
confidence;
|
||||
status;
|
||||
discoveredAt;
|
||||
reviewedAt;
|
||||
};
|
||||
exports.CandidateRoad = CandidateRoad;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)('uuid'),
|
||||
__metadata("design:type", String)
|
||||
], CandidateRoad.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'geography',
|
||||
spatialFeatureType: 'LineString',
|
||||
srid: 4326,
|
||||
nullable: false,
|
||||
}),
|
||||
(0, typeorm_1.Index)({ spatial: true }),
|
||||
__metadata("design:type", Object)
|
||||
], CandidateRoad.prototype, "geometry", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('int', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], CandidateRoad.prototype, "uniqueDriverCount", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('int', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], CandidateRoad.prototype, "totalPoints", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], CandidateRoad.prototype, "averageSpeed", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], CandidateRoad.prototype, "lengthMeters", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], CandidateRoad.prototype, "confidence", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ default: 'pending' }),
|
||||
__metadata("design:type", String)
|
||||
], CandidateRoad.prototype, "status", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
__metadata("design:type", Date)
|
||||
], CandidateRoad.prototype, "discoveredAt", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'timestamp', nullable: true }),
|
||||
__metadata("design:type", Date)
|
||||
], CandidateRoad.prototype, "reviewedAt", void 0);
|
||||
exports.CandidateRoad = CandidateRoad = __decorate([
|
||||
(0, typeorm_1.Entity)('candidate_roads')
|
||||
], CandidateRoad);
|
||||
//# sourceMappingURL=candidate-road.entity.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"candidate-road.entity.js","sourceRoot":"","sources":["../../src/maps/candidate-road.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA0F;AASnF,IAAM,aAAa,GAAnB,MAAM,aAAa;IAExB,EAAE,CAAS;IAWX,QAAQ,CAAM;IAKd,iBAAiB,CAAS;IAK1B,WAAW,CAAS;IAKpB,YAAY,CAAS;IAKrB,YAAY,CAAS;IAKrB,UAAU,CAAS;IAKnB,MAAM,CAAS;IAGf,YAAY,CAAO;IAGnB,UAAU,CAAO;CAClB,CAAA;AAlDY,sCAAa;AAExB;IADC,IAAA,gCAAsB,EAAC,MAAM,CAAC;;yCACpB;AAWX;IAPC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,WAAW;QACjB,kBAAkB,EAAE,YAAY;QAChC,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,KAAK;KAChB,CAAC;IACD,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;+CACX;AAKd;IADC,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;wDACJ;AAK1B;IADC,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;kDACV;AAKpB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;mDACX;AAKrB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;mDACX;AAKrB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;iDACb;AAKnB;IADC,IAAA,gBAAM,EAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC;;6CAChB;AAGf;IADC,IAAA,0BAAgB,GAAE;8BACL,IAAI;mDAAC;AAGnB;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;8BAClC,IAAI;iDAAC;wBAjDN,aAAa;IADzB,IAAA,gBAAM,EAAC,iBAAiB,CAAC;GACb,aAAa,CAkDzB"}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { MapsService } from './maps.service';
|
||||
export declare class MapsController {
|
||||
private readonly mapsService;
|
||||
constructor(mapsService: MapsService);
|
||||
getRoute(fromLat: number, fromLng: number, toLat: number, toLng: number): Promise<{
|
||||
distance: any;
|
||||
duration: number;
|
||||
points: any;
|
||||
bbox: any;
|
||||
congestionAware: boolean;
|
||||
}>;
|
||||
getConfig(): Promise<{
|
||||
center: number[];
|
||||
zoom: number;
|
||||
tileServerUrl: any;
|
||||
}>;
|
||||
}
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
"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.MapsController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
const maps_service_1 = require("./maps.service");
|
||||
const api_key_guard_1 = require("../common/guards/api-key.guard");
|
||||
let MapsController = class MapsController {
|
||||
mapsService;
|
||||
constructor(mapsService) {
|
||||
this.mapsService = mapsService;
|
||||
}
|
||||
async getRoute(fromLat, fromLng, toLat, toLng) {
|
||||
return this.mapsService.getRoute([fromLat, fromLng], [toLat, toLng]);
|
||||
}
|
||||
async getConfig() {
|
||||
return this.mapsService.getMapConfig();
|
||||
}
|
||||
};
|
||||
exports.MapsController = MapsController;
|
||||
__decorate([
|
||||
(0, common_1.Get)('route'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Calculate a route between two points 🚗' }),
|
||||
__param(0, (0, common_1.Query)('fromLat')),
|
||||
__param(1, (0, common_1.Query)('fromLng')),
|
||||
__param(2, (0, common_1.Query)('toLat')),
|
||||
__param(3, (0, common_1.Query)('toLng')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number, Number, Number, Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapsController.prototype, "getRoute", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('config'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get map configuration for the region 🗺️' }),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapsController.prototype, "getConfig", null);
|
||||
exports.MapsController = MapsController = __decorate([
|
||||
(0, swagger_1.ApiTags)('maps'),
|
||||
(0, common_1.Controller)('maps'),
|
||||
(0, common_1.UseGuards)(api_key_guard_1.ApiKeyGuard),
|
||||
__metadata("design:paramtypes", [maps_service_1.MapsService])
|
||||
], MapsController);
|
||||
//# sourceMappingURL=maps.controller.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"maps.controller.js","sourceRoot":"","sources":["../../src/maps/maps.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAAmE;AACnE,6CAAwD;AACxD,iDAA6C;AAC7C,kEAA6D;AAKtD,IAAM,cAAc,GAApB,MAAM,cAAc;IACI;IAA7B,YAA6B,WAAwB;QAAxB,gBAAW,GAAX,WAAW,CAAa;IAAG,CAAC;IAInD,AAAN,KAAK,CAAC,QAAQ,CACM,OAAe,EACf,OAAe,EACjB,KAAa,EACb,KAAa;QAE7B,OAAO,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC;IACvE,CAAC;IAIK,AAAN,KAAK,CAAC,SAAS;QACb,OAAO,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC;IACzC,CAAC;CACF,CAAA;AAnBY,wCAAc;AAKnB;IAFL,IAAA,YAAG,EAAC,OAAO,CAAC;IACZ,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yCAAyC,EAAE,CAAC;IAElE,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,cAAK,EAAC,SAAS,CAAC,CAAA;IAChB,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IACd,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;8CAGhB;AAIK;IAFL,IAAA,YAAG,EAAC,QAAQ,CAAC;IACb,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,0CAA0C,EAAE,CAAC;;;;+CAGrE;yBAlBU,cAAc;IAH1B,IAAA,iBAAO,EAAC,MAAM,CAAC;IACf,IAAA,mBAAU,EAAC,MAAM,CAAC;IAClB,IAAA,kBAAS,EAAC,2BAAW,CAAC;qCAEqB,0BAAW;GAD1C,cAAc,CAmB1B"}
|
||||
Vendored
+2
@@ -0,0 +1,2 @@
|
||||
export declare class MapsModule {
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
"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.MapsModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const maps_service_1 = require("./maps.service");
|
||||
const maps_controller_1 = require("./maps.controller");
|
||||
const road_stat_entity_1 = require("./road-stat.entity");
|
||||
const candidate_road_entity_1 = require("./candidate-road.entity");
|
||||
const redis_module_1 = require("../common/redis.module");
|
||||
let MapsModule = class MapsModule {
|
||||
};
|
||||
exports.MapsModule = MapsModule;
|
||||
exports.MapsModule = MapsModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [
|
||||
typeorm_1.TypeOrmModule.forFeature([road_stat_entity_1.RoadSegmentStat, candidate_road_entity_1.CandidateRoad]),
|
||||
redis_module_1.RedisModule,
|
||||
],
|
||||
controllers: [maps_controller_1.MapsController],
|
||||
providers: [maps_service_1.MapsService],
|
||||
exports: [maps_service_1.MapsService],
|
||||
})
|
||||
], MapsModule);
|
||||
//# sourceMappingURL=maps.module.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"maps.module.js","sourceRoot":"","sources":["../../src/maps/maps.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,iDAA6C;AAC7C,uDAAmD;AACnD,yDAAqD;AACrD,mEAAwD;AACxD,yDAAqD;AAW9C,IAAM,UAAU,GAAhB,MAAM,UAAU;CAAG,CAAA;AAAb,gCAAU;qBAAV,UAAU;IATtB,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,kCAAe,EAAE,qCAAa,CAAC,CAAC;YAC1D,0BAAW;SACZ;QACD,WAAW,EAAE,CAAC,gCAAc,CAAC;QAC7B,SAAS,EAAE,CAAC,0BAAW,CAAC;QACxB,OAAO,EAAE,CAAC,0BAAW,CAAC;KACvB,CAAC;GACW,UAAU,CAAG"}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Repository, DataSource } from 'typeorm';
|
||||
import { RoadSegmentStat } from './road-stat.entity';
|
||||
import { RedisService } from '../common/redis.service';
|
||||
export declare class MapsService {
|
||||
private configService;
|
||||
private roadStatRepo;
|
||||
private dataSource;
|
||||
private redisService;
|
||||
private readonly graphHopperUrl;
|
||||
private readonly TRAFFIC_CACHE_KEY;
|
||||
constructor(configService: ConfigService, roadStatRepo: Repository<RoadSegmentStat>, dataSource: DataSource, redisService: RedisService);
|
||||
getRoute(from: [number, number], to: [number, number]): Promise<{
|
||||
distance: any;
|
||||
duration: number;
|
||||
points: any;
|
||||
bbox: any;
|
||||
congestionAware: boolean;
|
||||
}>;
|
||||
getMapConfig(): Promise<{
|
||||
center: number[];
|
||||
zoom: number;
|
||||
tileServerUrl: any;
|
||||
}>;
|
||||
}
|
||||
Vendored
+134
@@ -0,0 +1,134 @@
|
||||
"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 };
|
||||
};
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.MapsService = 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 road_stat_entity_1 = require("./road-stat.entity");
|
||||
const redis_service_1 = require("../common/redis.service");
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
let MapsService = class MapsService {
|
||||
configService;
|
||||
roadStatRepo;
|
||||
dataSource;
|
||||
redisService;
|
||||
graphHopperUrl;
|
||||
TRAFFIC_CACHE_KEY = 'traffic_snapshot';
|
||||
constructor(configService, roadStatRepo, dataSource, redisService) {
|
||||
this.configService = configService;
|
||||
this.roadStatRepo = roadStatRepo;
|
||||
this.dataSource = dataSource;
|
||||
this.redisService = redisService;
|
||||
this.graphHopperUrl = this.configService.get('GRAPH_HOPPER_URL', 'http://map-routing:8080');
|
||||
}
|
||||
async getRoute(from, to) {
|
||||
const congestedSegments = await this.redisService.get(this.TRAFFIC_CACHE_KEY) || [];
|
||||
const buffer = 0.05;
|
||||
const minLat = Math.min(from[0], to[0]) - buffer;
|
||||
const maxLat = Math.max(from[0], to[0]) + buffer;
|
||||
const minLng = Math.min(from[1], to[1]) - buffer;
|
||||
const maxLng = Math.max(from[1], to[1]) + buffer;
|
||||
const nearbyCongestion = congestedSegments.filter(seg => {
|
||||
const geom = JSON.parse(typeof seg.geojson === 'string' ? seg.geojson : JSON.stringify(seg.geojson));
|
||||
return geom.coordinates.some(([lng, lat]) => lat >= minLat && lat <= maxLat && lng >= minLng && lng <= maxLng);
|
||||
}).slice(0, 50);
|
||||
const customModel = {
|
||||
speed: [],
|
||||
areas: {},
|
||||
};
|
||||
nearbyCongestion.forEach((seg, index) => {
|
||||
const areaId = `cong_${index}`;
|
||||
const geojson = typeof seg.geojson === 'string' ? JSON.parse(seg.geojson) : seg.geojson;
|
||||
customModel.areas[areaId] = {
|
||||
type: 'Feature',
|
||||
geometry: geojson,
|
||||
properties: {}
|
||||
};
|
||||
customModel.speed.push({
|
||||
if: `in_${areaId}`,
|
||||
multiply_by: Math.max(0.1, 1 / seg.congestionFactor)
|
||||
});
|
||||
});
|
||||
try {
|
||||
const params = {
|
||||
point: [`${from[0]},${from[1]}`, `${to[0]},${to[1]}`],
|
||||
profile: 'car',
|
||||
locale: 'en',
|
||||
calc_points: true,
|
||||
points_encoded: true,
|
||||
elevation: false,
|
||||
type: 'json',
|
||||
};
|
||||
if (customModel.speed.length > 0) {
|
||||
params.custom_model = JSON.stringify(customModel);
|
||||
}
|
||||
const response = await axios_1.default.get(`${this.graphHopperUrl}/route`, {
|
||||
params,
|
||||
paramsSerializer: (params) => {
|
||||
const searchParams = new URLSearchParams();
|
||||
Object.entries(params).forEach(([key, value]) => {
|
||||
if (Array.isArray(value)) {
|
||||
value.forEach(v => searchParams.append(key, v));
|
||||
}
|
||||
else {
|
||||
searchParams.append(key, value);
|
||||
}
|
||||
});
|
||||
return searchParams.toString();
|
||||
},
|
||||
});
|
||||
console.log('Routing Successful with congestion:', customModel.speed.length, 'segments');
|
||||
const route = response.data.paths[0];
|
||||
if (!route) {
|
||||
throw new common_1.HttpException('No route found', common_1.HttpStatus.NOT_FOUND);
|
||||
}
|
||||
return {
|
||||
distance: route.distance,
|
||||
duration: route.time / 1000,
|
||||
points: route.points,
|
||||
bbox: route.bbox,
|
||||
congestionAware: customModel.speed.length > 0,
|
||||
};
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Routing Error:', error.message);
|
||||
if (error.response) {
|
||||
console.error('GraphHopper Response:', error.response.data);
|
||||
}
|
||||
throw new common_1.HttpException('Error calculating route', common_1.HttpStatus.BAD_GATEWAY);
|
||||
}
|
||||
}
|
||||
async getMapConfig() {
|
||||
return {
|
||||
center: [31.95, 35.91],
|
||||
zoom: 12,
|
||||
tileServerUrl: this.configService.get('TILE_SERVER_URL', 'http://localhost:3001'),
|
||||
};
|
||||
}
|
||||
};
|
||||
exports.MapsService = MapsService;
|
||||
exports.MapsService = MapsService = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(1, (0, typeorm_1.InjectRepository)(road_stat_entity_1.RoadSegmentStat)),
|
||||
__metadata("design:paramtypes", [config_1.ConfigService,
|
||||
typeorm_2.Repository,
|
||||
typeorm_2.DataSource,
|
||||
redis_service_1.RedisService])
|
||||
], MapsService);
|
||||
//# sourceMappingURL=maps.service.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"maps.service.js","sourceRoot":"","sources":["../../src/maps/maps.service.ts"],"names":[],"mappings":"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"}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
export declare class RoadSegmentStat {
|
||||
segmentId: string;
|
||||
averageSpeed: number;
|
||||
congestionFactor: number;
|
||||
sampleCount: number;
|
||||
geometry: any;
|
||||
lastUpdated: Date;
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
"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.RoadSegmentStat = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
let RoadSegmentStat = class RoadSegmentStat {
|
||||
segmentId;
|
||||
averageSpeed;
|
||||
congestionFactor;
|
||||
sampleCount;
|
||||
geometry;
|
||||
lastUpdated;
|
||||
};
|
||||
exports.RoadSegmentStat = RoadSegmentStat;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryColumn)(),
|
||||
__metadata("design:type", String)
|
||||
], RoadSegmentStat.prototype, "segmentId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSegmentStat.prototype, "averageSpeed", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float', { default: 1.0 }),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSegmentStat.prototype, "congestionFactor", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('int', { default: 0 }),
|
||||
__metadata("design:type", Number)
|
||||
], RoadSegmentStat.prototype, "sampleCount", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'geography',
|
||||
spatialFeatureType: 'LineString',
|
||||
srid: 4326,
|
||||
nullable: true,
|
||||
}),
|
||||
(0, typeorm_1.Index)({ spatial: true }),
|
||||
__metadata("design:type", Object)
|
||||
], RoadSegmentStat.prototype, "geometry", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }),
|
||||
__metadata("design:type", Date)
|
||||
], RoadSegmentStat.prototype, "lastUpdated", void 0);
|
||||
exports.RoadSegmentStat = RoadSegmentStat = __decorate([
|
||||
(0, typeorm_1.Entity)('road_segment_stats')
|
||||
], RoadSegmentStat);
|
||||
//# sourceMappingURL=road-stat.entity.js.map
|
||||
+1
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"road-stat.entity.js","sourceRoot":"","sources":["../../src/maps/road-stat.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA+D;AAGxD,IAAM,eAAe,GAArB,MAAM,eAAe;IAE1B,SAAS,CAAS;IAGlB,YAAY,CAAS;IAGrB,gBAAgB,CAAS;IAGzB,WAAW,CAAS;IASpB,QAAQ,CAAM;IAGd,WAAW,CAAO;CACnB,CAAA;AAxBY,0CAAe;AAE1B;IADC,IAAA,uBAAa,GAAE;;kDACE;AAGlB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;qDACX;AAGrB;IADC,IAAA,gBAAM,EAAC,OAAO,EAAE,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC;;yDACT;AAGzB;IADC,IAAA,gBAAM,EAAC,KAAK,EAAE,EAAE,OAAO,EAAE,CAAC,EAAE,CAAC;;oDACV;AASpB;IAPC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,WAAW;QACjB,kBAAkB,EAAE,YAAY;QAChC,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;iDACX;AAGd;IADC,IAAA,gBAAM,EAAC,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,mBAAmB,EAAE,CAAC;8BACrD,IAAI;oDAAC;0BAvBP,eAAe;IAD3B,IAAA,gBAAM,EAAC,oBAAoB,CAAC;GAChB,eAAe,CAwB3B"}
|
||||
@@ -0,0 +1,9 @@
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
export declare class ExternalTelemetryService {
|
||||
private configService;
|
||||
private readonly logger;
|
||||
private readonly baseUrl;
|
||||
private readonly apiKey;
|
||||
constructor(configService: ConfigService);
|
||||
fetchCarTracks(days?: number): Promise<any[]>;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
"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 ExternalTelemetryService_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.ExternalTelemetryService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const config_1 = require("@nestjs/config");
|
||||
const axios_1 = __importDefault(require("axios"));
|
||||
let ExternalTelemetryService = ExternalTelemetryService_1 = class ExternalTelemetryService {
|
||||
configService;
|
||||
logger = new common_1.Logger(ExternalTelemetryService_1.name);
|
||||
baseUrl;
|
||||
apiKey;
|
||||
constructor(configService) {
|
||||
this.configService = configService;
|
||||
this.baseUrl = this.configService.get('LOCATION_SERVER_URL', '');
|
||||
this.apiKey = this.configService.get('LOCATION_SERVER_API_KEY', '');
|
||||
}
|
||||
async fetchCarTracks(days = 1) {
|
||||
if (!this.baseUrl || !this.apiKey) {
|
||||
this.logger.error('LOCATION_SERVER_URL or API_KEY not configured in .env');
|
||||
return [];
|
||||
}
|
||||
try {
|
||||
this.logger.log(`📡 Fetching car tracks from ${this.baseUrl} (Scanning: ${days} days)`);
|
||||
const response = await axios_1.default.get(`${this.baseUrl}/intaleq/ride/location/getDrirversLocationsTrack.php`, {
|
||||
params: { days },
|
||||
headers: {
|
||||
'X-API-KEY': this.apiKey,
|
||||
},
|
||||
timeout: 30000,
|
||||
});
|
||||
if (response.data && response.data.success && Array.isArray(response.data.data)) {
|
||||
this.logger.log(`✅ Successfully fetched ${response.data.data.length} telemetry points.`);
|
||||
return response.data.data;
|
||||
}
|
||||
else {
|
||||
this.logger.warn('⚠️ Received invalid data structure from location server.');
|
||||
return [];
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
if (error.response) {
|
||||
this.logger.error(`❌ Failed to fetch car tracks. Server returned Status ${error.response.status}: ${JSON.stringify(error.response.data)}`);
|
||||
}
|
||||
else {
|
||||
this.logger.error(`❌ Failed to fetch car tracks: ${error.message}`);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
};
|
||||
exports.ExternalTelemetryService = ExternalTelemetryService;
|
||||
exports.ExternalTelemetryService = ExternalTelemetryService = ExternalTelemetryService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__metadata("design:paramtypes", [config_1.ConfigService])
|
||||
], ExternalTelemetryService);
|
||||
//# sourceMappingURL=external-telemetry.service.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"external-telemetry.service.js","sourceRoot":"","sources":["../../src/telemetry/external-telemetry.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,2CAA+C;AAC/C,kDAA0B;AASnB,IAAM,wBAAwB,gCAA9B,MAAM,wBAAwB;IAKf;IAJH,MAAM,GAAG,IAAI,eAAM,CAAC,0BAAwB,CAAC,IAAI,CAAC,CAAC;IACnD,OAAO,CAAS;IAChB,MAAM,CAAS;IAEhC,YAAoB,aAA4B;QAA5B,kBAAa,GAAb,aAAa,CAAe;QAC9C,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,qBAAqB,EAAE,EAAE,CAAC,CAAC;QACzE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,aAAa,CAAC,GAAG,CAAS,yBAAyB,EAAE,EAAE,CAAC,CAAC;IAC9E,CAAC;IAMD,KAAK,CAAC,cAAc,CAAC,OAAe,CAAC;QACnC,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC;YAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;YAC3E,OAAO,EAAE,CAAC;QACZ,CAAC;QAED,IAAI,CAAC;YACH,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,+BAA+B,IAAI,CAAC,OAAO,eAAe,IAAI,QAAQ,CAAC,CAAC;YAExF,MAAM,QAAQ,GAAG,MAAM,eAAK,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,OAAO,sDAAsD,EAAE;gBACtG,MAAM,EAAE,EAAE,IAAI,EAAE;gBAChB,OAAO,EAAE;oBACP,WAAW,EAAE,IAAI,CAAC,MAAM;iBACzB;gBACD,OAAO,EAAE,KAAK;aACf,CAAC,CAAC;YAEH,IAAI,QAAQ,CAAC,IAAI,IAAI,QAAQ,CAAC,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;gBAChF,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,0BAA0B,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,oBAAoB,CAAC,CAAC;gBACzF,OAAO,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;YAC5B,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,0DAA0D,CAAC,CAAC;gBAC7E,OAAO,EAAE,CAAC;YACZ,CAAC;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,KAAK,CAAC,QAAQ,EAAE,CAAC;gBACnB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,wDAAwD,KAAK,CAAC,QAAQ,CAAC,MAAM,KAAK,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAC7I,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;YACtE,CAAC;YACD,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF,CAAA;AA/CY,4DAAwB;mCAAxB,wBAAwB;IADpC,IAAA,mBAAU,GAAE;qCAMwB,sBAAa;GALrC,wBAAwB,CA+CpC"}
|
||||
@@ -0,0 +1,45 @@
|
||||
import { TelemetryAnalyzerService } from './telemetry-analyzer.service';
|
||||
export declare class MapRefinementController {
|
||||
private readonly analyzerService;
|
||||
constructor(analyzerService: TelemetryAnalyzerService);
|
||||
getSummary(): Promise<{
|
||||
telemetry: {
|
||||
totalPoints: number;
|
||||
last24h: number;
|
||||
activeDrivers24h: number;
|
||||
};
|
||||
roadSegments: {
|
||||
analyzed: number;
|
||||
};
|
||||
candidateRoads: {
|
||||
total: number;
|
||||
pending: number;
|
||||
approved: number;
|
||||
rejected: number;
|
||||
};
|
||||
}>;
|
||||
syncExternal(): Promise<{
|
||||
imported: number;
|
||||
}>;
|
||||
refreshCache(): Promise<{
|
||||
cachedCount: number;
|
||||
}>;
|
||||
analyzeRoadSpeeds(hours?: number): Promise<{
|
||||
segmentsUpdated: number;
|
||||
totalPointsProcessed: number;
|
||||
topAdjustments: any[];
|
||||
}>;
|
||||
discoverNewRoads(hours?: number): Promise<{
|
||||
candidatesFound: number;
|
||||
candidates: any[];
|
||||
}>;
|
||||
getCongestionData(north: number, south: number, east: number, west: number): Promise<any[]>;
|
||||
approveCandidate(id: string): Promise<{
|
||||
message: string;
|
||||
status: string;
|
||||
}>;
|
||||
rejectCandidate(id: string): Promise<{
|
||||
message: string;
|
||||
status: string;
|
||||
}>;
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
"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.MapRefinementController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
const telemetry_analyzer_service_1 = require("./telemetry-analyzer.service");
|
||||
let MapRefinementController = class MapRefinementController {
|
||||
analyzerService;
|
||||
constructor(analyzerService) {
|
||||
this.analyzerService = analyzerService;
|
||||
}
|
||||
async getSummary() {
|
||||
return this.analyzerService.getAnalysisSummary();
|
||||
}
|
||||
async syncExternal() {
|
||||
return this.analyzerService.syncExternalData();
|
||||
}
|
||||
async refreshCache() {
|
||||
return this.analyzerService.refreshTrafficCache();
|
||||
}
|
||||
async analyzeRoadSpeeds(hours) {
|
||||
const sinceHours = hours ? Number(hours) : 24;
|
||||
return this.analyzerService.analyzeRoadSpeeds(sinceHours);
|
||||
}
|
||||
async discoverNewRoads(hours) {
|
||||
const sinceHours = hours ? Number(hours) : 168;
|
||||
return this.analyzerService.discoverNewRoads(sinceHours);
|
||||
}
|
||||
async getCongestionData(north, south, east, west) {
|
||||
if (!north || !south || !east || !west) {
|
||||
throw new common_1.HttpException('Bounds required: north, south, east, west / يجب تحديد الحدود الجغرافية', common_1.HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
return this.analyzerService.getCongestionData({
|
||||
north: Number(north),
|
||||
south: Number(south),
|
||||
east: Number(east),
|
||||
west: Number(west),
|
||||
});
|
||||
}
|
||||
async approveCandidate(id) {
|
||||
return { message: `Candidate ${id} approved`, status: 'approved' };
|
||||
}
|
||||
async rejectCandidate(id) {
|
||||
return { message: `Candidate ${id} rejected`, status: 'rejected' };
|
||||
}
|
||||
};
|
||||
exports.MapRefinementController = MapRefinementController;
|
||||
__decorate([
|
||||
(0, common_1.Get)('summary'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get analysis summary 📊 / ملخص التحليل' }),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "getSummary", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('sync-external'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Sync telemetry from external server 📡 / مزامنة البيانات الخارجية' }),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "syncExternal", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('refresh-cache'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Refresh Redis traffic snapshot 🚀 / تحديث ذاكرة التخزين' }),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "refreshCache", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('analyze-speeds'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Run road speed analysis 🚗 / تشغيل تحليل سرعات الطرق' }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'hours', required: false, description: 'Hours to look back (default: 24)' }),
|
||||
__param(0, (0, common_1.Query)('hours')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "analyzeRoadSpeeds", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('discover-roads'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Discover new roads from telemetry 🛣️ / اكتشاف طرق جديدة' }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'hours', required: false, description: 'Hours to look back (default: 168 = 1 week)' }),
|
||||
__param(0, (0, common_1.Query)('hours')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "discoverNewRoads", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('congestion'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get congestion data for heatmap 🗺️ / بيانات الازدحام' }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'north', required: true }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'south', required: true }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'east', required: true }),
|
||||
(0, swagger_1.ApiQuery)({ name: 'west', required: true }),
|
||||
__param(0, (0, common_1.Query)('north')),
|
||||
__param(1, (0, common_1.Query)('south')),
|
||||
__param(2, (0, common_1.Query)('east')),
|
||||
__param(3, (0, common_1.Query)('west')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number, Number, Number, Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "getCongestionData", null);
|
||||
__decorate([
|
||||
(0, common_1.Patch)('candidates/:id/approve'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Approve a candidate road ✅ / الموافقة على طريق مرشح' }),
|
||||
__param(0, (0, common_1.Param)('id')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [String]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "approveCandidate", null);
|
||||
__decorate([
|
||||
(0, common_1.Patch)('candidates/:id/reject'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Reject a candidate road ❌ / رفض طريق مرشح' }),
|
||||
__param(0, (0, common_1.Param)('id')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [String]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], MapRefinementController.prototype, "rejectCandidate", null);
|
||||
exports.MapRefinementController = MapRefinementController = __decorate([
|
||||
(0, swagger_1.ApiTags)('map-refinement'),
|
||||
(0, common_1.Controller)('map-refinement'),
|
||||
__metadata("design:paramtypes", [telemetry_analyzer_service_1.TelemetryAnalyzerService])
|
||||
], MapRefinementController);
|
||||
//# sourceMappingURL=map-refinement.controller.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"map-refinement.controller.js","sourceRoot":"","sources":["../../src/telemetry/map-refinement.controller.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA6G;AAC7G,6CAAkE;AAClE,6EAAwE;AAajE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IACL;IAA7B,YAA6B,eAAyC;QAAzC,oBAAe,GAAf,eAAe,CAA0B;IAAG,CAAC;IAQpE,AAAN,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,eAAe,CAAC,kBAAkB,EAAE,CAAC;IACnD,CAAC;IAQK,AAAN,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,eAAe,CAAC,gBAAgB,EAAE,CAAC;IACjD,CAAC;IAIK,AAAN,KAAK,CAAC,YAAY;QAChB,OAAO,IAAI,CAAC,eAAe,CAAC,mBAAmB,EAAE,CAAC;IACpD,CAAC;IAUK,AAAN,KAAK,CAAC,iBAAiB,CAAiB,KAAc;QACpD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9C,OAAO,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;IAC5D,CAAC;IASK,AAAN,KAAK,CAAC,gBAAgB,CAAiB,KAAc;QACnD,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC;QAC/C,OAAO,IAAI,CAAC,eAAe,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAC3D,CAAC;IAYK,AAAN,KAAK,CAAC,iBAAiB,CACL,KAAa,EACb,KAAa,EACd,IAAY,EACZ,IAAY;QAE3B,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACvC,MAAM,IAAI,sBAAa,CACrB,wEAAwE,EACxE,mBAAU,CAAC,WAAW,CACvB,CAAC;QACJ,CAAC;QACD,OAAO,IAAI,CAAC,eAAe,CAAC,iBAAiB,CAAC;YAC5C,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;YACpB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;YACpB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;YAClB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;SACnB,CAAC,CAAC;IACL,CAAC;IAQK,AAAN,KAAK,CAAC,gBAAgB,CAAc,EAAU;QAG5C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACrE,CAAC;IAIK,AAAN,KAAK,CAAC,eAAe,CAAc,EAAU;QAC3C,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC;IACrE,CAAC;CACF,CAAA;AArGY,0DAAuB;AAS5B;IAFL,IAAA,YAAG,EAAC,SAAS,CAAC;IACd,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,wCAAwC,EAAE,CAAC;;;;yDAGnE;AAQK;IAFL,IAAA,aAAI,EAAC,eAAe,CAAC;IACrB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,mEAAmE,EAAE,CAAC;;;;2DAG9F;AAIK;IAFL,IAAA,aAAI,EAAC,eAAe,CAAC;IACrB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,yDAAyD,EAAE,CAAC;;;;2DAGpF;AAUK;IAHL,IAAA,aAAI,EAAC,gBAAgB,CAAC;IACtB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,sDAAsD,EAAE,CAAC;IACjF,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,kCAAkC,EAAE,CAAC;IACrE,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;gEAGtC;AASK;IAHL,IAAA,aAAI,EAAC,gBAAgB,CAAC;IACtB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,0DAA0D,EAAE,CAAC;IACrF,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC;IAChF,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;;;;+DAGrC;AAYK;IANL,IAAA,YAAG,EAAC,YAAY,CAAC;IACjB,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,uDAAuD,EAAE,CAAC;IAClF,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC3C,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC1C,IAAA,kBAAQ,EAAC,EAAE,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC;IAExC,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IACd,WAAA,IAAA,cAAK,EAAC,OAAO,CAAC,CAAA;IACd,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;IACb,WAAA,IAAA,cAAK,EAAC,MAAM,CAAC,CAAA;;;;gEAcf;AAQK;IAFL,IAAA,cAAK,EAAC,wBAAwB,CAAC;IAC/B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,qDAAqD,EAAE,CAAC;IACzD,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;+DAIlC;AAIK;IAFL,IAAA,cAAK,EAAC,uBAAuB,CAAC;IAC9B,IAAA,sBAAY,EAAC,EAAE,OAAO,EAAE,2CAA2C,EAAE,CAAC;IAChD,WAAA,IAAA,cAAK,EAAC,IAAI,CAAC,CAAA;;;;8DAEjC;kCApGU,uBAAuB;IAFnC,IAAA,iBAAO,EAAC,gBAAgB,CAAC;IACzB,IAAA,mBAAU,EAAC,gBAAgB,CAAC;qCAEmB,qDAAwB;GAD3D,uBAAuB,CAqGnC"}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Repository, DataSource } from 'typeorm';
|
||||
import { TelemetryLog } from './telemetry.entity';
|
||||
import { RoadSegmentStat } from '../maps/road-stat.entity';
|
||||
import { CandidateRoad } from '../maps/candidate-road.entity';
|
||||
import { RedisService } from '../common/redis.service';
|
||||
import { ExternalTelemetryService } from './external-telemetry.service';
|
||||
export declare class TelemetryAnalyzerService {
|
||||
private telemetryRepo;
|
||||
private roadStatRepo;
|
||||
private candidateRoadRepo;
|
||||
private dataSource;
|
||||
private redisService;
|
||||
private externalTelemetry;
|
||||
private readonly logger;
|
||||
private readonly TRAFFIC_CACHE_KEY;
|
||||
constructor(telemetryRepo: Repository<TelemetryLog>, roadStatRepo: Repository<RoadSegmentStat>, candidateRoadRepo: Repository<CandidateRoad>, dataSource: DataSource, redisService: RedisService, externalTelemetry: ExternalTelemetryService);
|
||||
handleNightlyIntelligence(): Promise<void>;
|
||||
syncExternalData(days?: number): Promise<{
|
||||
imported: number;
|
||||
}>;
|
||||
refreshTrafficCache(): Promise<{
|
||||
cachedCount: number;
|
||||
}>;
|
||||
analyzeRoadSpeeds(sinceHours?: number): Promise<{
|
||||
segmentsUpdated: number;
|
||||
totalPointsProcessed: number;
|
||||
topAdjustments: any[];
|
||||
}>;
|
||||
discoverNewRoads(sinceHours?: number): Promise<{
|
||||
candidatesFound: number;
|
||||
candidates: any[];
|
||||
}>;
|
||||
getCongestionData(bounds: {
|
||||
north: number;
|
||||
south: number;
|
||||
east: number;
|
||||
west: number;
|
||||
}): Promise<any[]>;
|
||||
getAnalysisSummary(): Promise<{
|
||||
telemetry: {
|
||||
totalPoints: number;
|
||||
last24h: number;
|
||||
activeDrivers24h: number;
|
||||
};
|
||||
roadSegments: {
|
||||
analyzed: number;
|
||||
};
|
||||
candidateRoads: {
|
||||
total: number;
|
||||
pending: number;
|
||||
approved: number;
|
||||
rejected: number;
|
||||
};
|
||||
}>;
|
||||
private calculateConfidence;
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
"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 TelemetryAnalyzerService_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TelemetryAnalyzerService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const schedule_1 = require("@nestjs/schedule");
|
||||
const telemetry_entity_1 = require("./telemetry.entity");
|
||||
const road_stat_entity_1 = require("../maps/road-stat.entity");
|
||||
const candidate_road_entity_1 = require("../maps/candidate-road.entity");
|
||||
const redis_service_1 = require("../common/redis.service");
|
||||
const external_telemetry_service_1 = require("./external-telemetry.service");
|
||||
let TelemetryAnalyzerService = TelemetryAnalyzerService_1 = class TelemetryAnalyzerService {
|
||||
telemetryRepo;
|
||||
roadStatRepo;
|
||||
candidateRoadRepo;
|
||||
dataSource;
|
||||
redisService;
|
||||
externalTelemetry;
|
||||
logger = new common_1.Logger(TelemetryAnalyzerService_1.name);
|
||||
TRAFFIC_CACHE_KEY = 'traffic_snapshot';
|
||||
constructor(telemetryRepo, roadStatRepo, candidateRoadRepo, dataSource, redisService, externalTelemetry) {
|
||||
this.telemetryRepo = telemetryRepo;
|
||||
this.roadStatRepo = roadStatRepo;
|
||||
this.candidateRoadRepo = candidateRoadRepo;
|
||||
this.dataSource = dataSource;
|
||||
this.redisService = redisService;
|
||||
this.externalTelemetry = externalTelemetry;
|
||||
}
|
||||
async handleNightlyIntelligence() {
|
||||
this.logger.log('⏰ Starting automated 3 AM intelligence process...');
|
||||
try {
|
||||
await this.syncExternalData(1);
|
||||
await this.analyzeRoadSpeeds(24);
|
||||
await this.discoverNewRoads(168);
|
||||
await this.refreshTrafficCache();
|
||||
this.logger.log('✅ 3 AM intelligence process complete.');
|
||||
}
|
||||
catch (error) {
|
||||
this.logger.error('❌ Nightly intelligence failed:', error.stack);
|
||||
}
|
||||
}
|
||||
async syncExternalData(days = 1) {
|
||||
this.logger.log(`📡 Starting batch sync (Window: ${days} days)...`);
|
||||
const tracks = await this.externalTelemetry.fetchCarTracks(days);
|
||||
if (!tracks || tracks.length === 0) {
|
||||
this.logger.warn(`⚠️ No tracks found on external server for the last ${days} days.`);
|
||||
return { imported: 0 };
|
||||
}
|
||||
this.logger.log(`✅ Received ${tracks.length} tracks. Commencing batch insertion...`);
|
||||
this.logger.log(`📥 Saving ${tracks.length} points to database...`);
|
||||
const entities = tracks.map(t => ({
|
||||
driverId: t.driver_id,
|
||||
latitude: t.latitude,
|
||||
longitude: t.longitude,
|
||||
speed: t.speed,
|
||||
heading: t.heading,
|
||||
timestamp: new Date(t.created_at || t.timestamp),
|
||||
location: {
|
||||
type: 'Point',
|
||||
coordinates: [t.longitude, t.latitude],
|
||||
},
|
||||
}));
|
||||
const CHUNK_SIZE = 1000;
|
||||
for (let i = 0; i < entities.length; i += CHUNK_SIZE) {
|
||||
const chunk = entities.slice(i, i + CHUNK_SIZE);
|
||||
const logs = this.telemetryRepo.create(chunk);
|
||||
await this.telemetryRepo.save(logs);
|
||||
}
|
||||
return { imported: tracks.length };
|
||||
}
|
||||
async refreshTrafficCache() {
|
||||
this.logger.log('🚀 Refreshing Redis traffic snapshot...');
|
||||
const congested = await this.roadStatRepo.query(`
|
||||
SELECT
|
||||
"segmentId",
|
||||
"congestionFactor",
|
||||
ST_AsGeoJSON(geometry) as geojson
|
||||
FROM road_segment_stats
|
||||
WHERE "congestionFactor" > 1.1
|
||||
`);
|
||||
if (congested.length === 0) {
|
||||
await this.redisService.del(this.TRAFFIC_CACHE_KEY);
|
||||
return { cachedCount: 0 };
|
||||
}
|
||||
await this.redisService.set(this.TRAFFIC_CACHE_KEY, congested);
|
||||
this.logger.log(`✅ Cached ${congested.length} congested segments in Redis.`);
|
||||
return { cachedCount: congested.length };
|
||||
}
|
||||
async analyzeRoadSpeeds(sinceHours = 24) {
|
||||
this.logger.log(`🔍 Starting road speed analysis for last ${sinceHours}h...`);
|
||||
const matchedData = await this.dataSource.query(`
|
||||
WITH matched_points AS (
|
||||
SELECT
|
||||
t.id AS telemetry_id,
|
||||
t.speed,
|
||||
t."driverId",
|
||||
l.osm_id,
|
||||
l.name,
|
||||
l.highway,
|
||||
l.way_4326,
|
||||
ST_Distance(t.location::geography, l.way_4326::geography) AS distance_m
|
||||
FROM telemetry_logs t
|
||||
CROSS JOIN LATERAL (
|
||||
SELECT osm_id, name, highway, ST_Transform(way, 4326) AS way_4326
|
||||
FROM planet_osm_line
|
||||
WHERE highway IS NOT NULL
|
||||
ORDER BY way <-> ST_Transform(t.location::geometry, 3857)
|
||||
LIMIT 1
|
||||
) l
|
||||
WHERE t.timestamp >= NOW() - INTERVAL '${sinceHours} hours'
|
||||
AND t.speed > 2 -- Ignore stationary points / تجاهل النقاط الثابتة
|
||||
AND ST_Distance(t.location::geography, l.way_4326::geography) < 15 -- 15m snap threshold
|
||||
)
|
||||
SELECT
|
||||
osm_id::text AS segment_id,
|
||||
name,
|
||||
highway,
|
||||
AVG(speed) AS avg_speed,
|
||||
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY speed) AS median_speed,
|
||||
COUNT(*) AS sample_count,
|
||||
COUNT(DISTINCT "driverId") AS unique_drivers,
|
||||
ST_AsGeoJSON(MIN(way_4326)) AS geojson
|
||||
FROM matched_points
|
||||
GROUP BY osm_id, name, highway
|
||||
HAVING COUNT(*) >= 3 -- Minimum 3 samples for reliability / 3 عينات على الأقل
|
||||
ORDER BY sample_count DESC
|
||||
`);
|
||||
let segmentsUpdated = 0;
|
||||
let totalPoints = 0;
|
||||
for (const row of matchedData) {
|
||||
const medianSpeed = parseFloat(row.median_speed);
|
||||
const sampleCount = parseInt(row.sample_count);
|
||||
const geojson = JSON.parse(row.geojson);
|
||||
totalPoints += sampleCount;
|
||||
let congestionFactor = 1.0;
|
||||
const majorRoadTypes = ['primary', 'secondary', 'trunk', 'motorway'];
|
||||
if (majorRoadTypes.includes(row.highway) && medianSpeed < 30) {
|
||||
congestionFactor = 30 / Math.max(medianSpeed, 1);
|
||||
}
|
||||
await this.roadStatRepo.upsert({
|
||||
segmentId: row.segment_id,
|
||||
averageSpeed: medianSpeed,
|
||||
congestionFactor,
|
||||
sampleCount,
|
||||
lastUpdated: new Date(),
|
||||
geometry: geojson,
|
||||
}, ['segmentId']);
|
||||
segmentsUpdated++;
|
||||
}
|
||||
return {
|
||||
segmentsUpdated,
|
||||
totalPointsProcessed: totalPoints,
|
||||
topAdjustments: matchedData.slice(0, 10).map(d => ({
|
||||
segmentId: d.segment_id,
|
||||
name: d.name || 'Unnamed Road',
|
||||
speed: Math.round(parseFloat(d.median_speed)),
|
||||
samples: parseInt(d.sample_count)
|
||||
}))
|
||||
};
|
||||
}
|
||||
async discoverNewRoads(sinceHours = 168) {
|
||||
this.logger.log(`🛣️ Starting road discovery for last ${sinceHours}h (${sinceHours / 24}d)...`);
|
||||
const candidates = await this.dataSource.query(`
|
||||
WITH off_road_points AS (
|
||||
SELECT
|
||||
t.id,
|
||||
t."driverId",
|
||||
t.speed,
|
||||
t.heading,
|
||||
t.location,
|
||||
t.timestamp
|
||||
FROM telemetry_logs t
|
||||
WHERE t.timestamp >= NOW() - INTERVAL '${sinceHours} hours'
|
||||
AND t.speed > 5 -- Moving, not parked / متحرك وليس متوقف
|
||||
AND NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM planet_osm_line l
|
||||
WHERE l.highway IS NOT NULL
|
||||
AND ST_DWithin(t.location::geography, ST_Transform(l.way, 4326)::geography, 15)
|
||||
)
|
||||
),
|
||||
-- Step 2: Cluster nearby off-road points using DBSCAN
|
||||
-- الخطوة 2: تجميع النقاط القريبة باستخدام DBSCAN
|
||||
clustered AS (
|
||||
SELECT
|
||||
*,
|
||||
ST_ClusterDBSCAN(location::geometry, eps := 0.0003, minpoints := 5)
|
||||
OVER () AS cluster_id
|
||||
FROM off_road_points
|
||||
)
|
||||
-- Step 3: Aggregate clusters into candidate road lines
|
||||
-- الخطوة 3: تحويل التجمعات إلى خطوط طرق مرشحة
|
||||
SELECT
|
||||
cluster_id,
|
||||
COUNT(*) AS total_points,
|
||||
COUNT(DISTINCT "driverId") AS unique_drivers,
|
||||
AVG(speed) AS avg_speed,
|
||||
ST_AsGeoJSON(ST_MakeLine(location::geometry ORDER BY timestamp)) AS geojson_line,
|
||||
ST_Length(ST_MakeLine(location::geometry ORDER BY timestamp)::geography) AS length_m
|
||||
FROM clustered
|
||||
WHERE cluster_id IS NOT NULL
|
||||
GROUP BY cluster_id
|
||||
HAVING COUNT(DISTINCT "driverId") >= 2 -- At least 2 drivers / سائقان على الأقل
|
||||
AND COUNT(*) >= 10 -- At least 10 points / 10 نقاط على الأقل
|
||||
ORDER BY unique_drivers DESC, total_points DESC
|
||||
`);
|
||||
const savedCandidates = [];
|
||||
for (const c of candidates) {
|
||||
const geojson = JSON.parse(c.geojson_line);
|
||||
const confidence = this.calculateConfidence(parseInt(c.unique_drivers), parseInt(c.total_points), parseFloat(c.length_m));
|
||||
const candidate = this.candidateRoadRepo.create({
|
||||
geometry: geojson,
|
||||
uniqueDriverCount: parseInt(c.unique_drivers),
|
||||
totalPoints: parseInt(c.total_points),
|
||||
averageSpeed: parseFloat(c.avg_speed),
|
||||
lengthMeters: parseFloat(c.length_m),
|
||||
confidence,
|
||||
status: 'pending',
|
||||
});
|
||||
const saved = await this.candidateRoadRepo.save(candidate);
|
||||
savedCandidates.push({
|
||||
id: saved.id,
|
||||
uniqueDrivers: saved.uniqueDriverCount,
|
||||
totalPoints: saved.totalPoints,
|
||||
averageSpeed: Math.round(saved.averageSpeed * 10) / 10,
|
||||
lengthMeters: Math.round(saved.lengthMeters),
|
||||
confidence: Math.round(saved.confidence * 100) / 100,
|
||||
geojson,
|
||||
});
|
||||
}
|
||||
this.logger.log(`✅ Road discovery complete: ${savedCandidates.length} candidates found`);
|
||||
return {
|
||||
candidatesFound: savedCandidates.length,
|
||||
candidates: savedCandidates,
|
||||
};
|
||||
}
|
||||
async getCongestionData(bounds) {
|
||||
return this.dataSource.query(`
|
||||
SELECT
|
||||
rs."segmentId" AS segment_id,
|
||||
rs."averageSpeed" AS avg_speed,
|
||||
rs."congestionFactor" AS congestion_factor,
|
||||
rs."sampleCount" AS sample_count,
|
||||
ST_AsGeoJSON(rs.geometry) AS geojson
|
||||
FROM road_segment_stats rs
|
||||
WHERE rs.geometry IS NOT NULL
|
||||
AND ST_Intersects(
|
||||
rs.geometry::geometry,
|
||||
ST_MakeEnvelope($1, $2, $3, $4, 4326)
|
||||
)
|
||||
AND rs."sampleCount" >= 3
|
||||
ORDER BY rs."congestionFactor" DESC
|
||||
`, [bounds.west, bounds.south, bounds.east, bounds.north]);
|
||||
}
|
||||
async getAnalysisSummary() {
|
||||
const [telemetryCount] = await this.dataSource.query(`SELECT COUNT(*) as count FROM telemetry_logs`);
|
||||
const [roadStatCount] = await this.dataSource.query(`SELECT COUNT(*) as count FROM road_segment_stats`);
|
||||
const [candidateCount] = await this.dataSource.query(`SELECT COUNT(*) as count,
|
||||
COUNT(*) FILTER (WHERE status = 'pending') as pending,
|
||||
COUNT(*) FILTER (WHERE status = 'approved') as approved,
|
||||
COUNT(*) FILTER (WHERE status = 'rejected') as rejected
|
||||
FROM candidate_roads`);
|
||||
const [recentActivity] = await this.dataSource.query(`
|
||||
SELECT
|
||||
COUNT(*) as points_last_24h,
|
||||
COUNT(DISTINCT "driverId") as active_drivers_24h
|
||||
FROM telemetry_logs
|
||||
WHERE timestamp >= NOW() - INTERVAL '24 hours'
|
||||
`);
|
||||
return {
|
||||
telemetry: {
|
||||
totalPoints: parseInt(telemetryCount?.count || '0'),
|
||||
last24h: parseInt(recentActivity?.points_last_24h || '0'),
|
||||
activeDrivers24h: parseInt(recentActivity?.active_drivers_24h || '0'),
|
||||
},
|
||||
roadSegments: {
|
||||
analyzed: parseInt(roadStatCount?.count || '0'),
|
||||
},
|
||||
candidateRoads: {
|
||||
total: parseInt(candidateCount?.count || '0'),
|
||||
pending: parseInt(candidateCount?.pending || '0'),
|
||||
approved: parseInt(candidateCount?.approved || '0'),
|
||||
rejected: parseInt(candidateCount?.rejected || '0'),
|
||||
},
|
||||
};
|
||||
}
|
||||
calculateConfidence(uniqueDrivers, totalPoints, lengthMeters) {
|
||||
const driverScore = Math.min(uniqueDrivers / 5, 1.0) * 0.4;
|
||||
const pointScore = Math.min(totalPoints / 50, 1.0) * 0.3;
|
||||
let lengthScore = 0;
|
||||
if (lengthMeters >= 50 && lengthMeters <= 2000) {
|
||||
lengthScore = 0.3;
|
||||
}
|
||||
else if (lengthMeters > 2000) {
|
||||
lengthScore = 0.2;
|
||||
}
|
||||
return Math.round((driverScore + pointScore + lengthScore) * 100) / 100;
|
||||
}
|
||||
};
|
||||
exports.TelemetryAnalyzerService = TelemetryAnalyzerService;
|
||||
__decorate([
|
||||
(0, schedule_1.Cron)('0 3 * * *'),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TelemetryAnalyzerService.prototype, "handleNightlyIntelligence", null);
|
||||
exports.TelemetryAnalyzerService = TelemetryAnalyzerService = TelemetryAnalyzerService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(0, (0, typeorm_1.InjectRepository)(telemetry_entity_1.TelemetryLog)),
|
||||
__param(1, (0, typeorm_1.InjectRepository)(road_stat_entity_1.RoadSegmentStat)),
|
||||
__param(2, (0, typeorm_1.InjectRepository)(candidate_road_entity_1.CandidateRoad)),
|
||||
__metadata("design:paramtypes", [typeorm_2.Repository,
|
||||
typeorm_2.Repository,
|
||||
typeorm_2.Repository,
|
||||
typeorm_2.DataSource,
|
||||
redis_service_1.RedisService,
|
||||
external_telemetry_service_1.ExternalTelemetryService])
|
||||
], TelemetryAnalyzerService);
|
||||
//# sourceMappingURL=telemetry-analyzer.service.js.map
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,77 @@
|
||||
import { TelemetryService, type DriverTelemetryDto } from './telemetry.service';
|
||||
import { TelemetryAnalyzerService } from './telemetry-analyzer.service';
|
||||
export declare class TelemetryController {
|
||||
private readonly telemetryService;
|
||||
private readonly analyzerService;
|
||||
constructor(telemetryService: TelemetryService, analyzerService: TelemetryAnalyzerService);
|
||||
ingest(data: DriverTelemetryDto): Promise<{
|
||||
success: boolean;
|
||||
timestamp: Date;
|
||||
}>;
|
||||
sync(): Promise<{
|
||||
imported: number;
|
||||
}>;
|
||||
analyze(hours?: number): Promise<{
|
||||
success: boolean;
|
||||
roadAnalysis: {
|
||||
segmentsUpdated: number;
|
||||
totalPointsProcessed: number;
|
||||
topAdjustments: any[];
|
||||
};
|
||||
roadDiscovery: {
|
||||
candidatesFound: number;
|
||||
candidates: any[];
|
||||
};
|
||||
}>;
|
||||
processIntelligence(hours?: number): Promise<{
|
||||
success: boolean;
|
||||
timestamp: Date;
|
||||
sync: {
|
||||
imported: number;
|
||||
};
|
||||
analysis: {
|
||||
roads: {
|
||||
segmentsUpdated: number;
|
||||
totalPointsProcessed: number;
|
||||
topAdjustments: any[];
|
||||
};
|
||||
discovery: {
|
||||
candidatesFound: number;
|
||||
candidates: any[];
|
||||
};
|
||||
};
|
||||
currentStatus: {
|
||||
telemetry: {
|
||||
totalPoints: number;
|
||||
last24h: number;
|
||||
activeDrivers24h: number;
|
||||
};
|
||||
roadSegments: {
|
||||
analyzed: number;
|
||||
};
|
||||
candidateRoads: {
|
||||
total: number;
|
||||
pending: number;
|
||||
approved: number;
|
||||
rejected: number;
|
||||
};
|
||||
};
|
||||
}>;
|
||||
getSummary(): Promise<{
|
||||
telemetry: {
|
||||
totalPoints: number;
|
||||
last24h: number;
|
||||
activeDrivers24h: number;
|
||||
};
|
||||
roadSegments: {
|
||||
analyzed: number;
|
||||
};
|
||||
candidateRoads: {
|
||||
total: number;
|
||||
pending: number;
|
||||
approved: number;
|
||||
rejected: number;
|
||||
};
|
||||
}>;
|
||||
getNearby(lat: number, lng: number, radius: number): Promise<import("./telemetry.entity").TelemetryLog[]>;
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
"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.TelemetryController = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const swagger_1 = require("@nestjs/swagger");
|
||||
const telemetry_service_1 = require("./telemetry.service");
|
||||
const telemetry_analyzer_service_1 = require("./telemetry-analyzer.service");
|
||||
let TelemetryController = class TelemetryController {
|
||||
telemetryService;
|
||||
analyzerService;
|
||||
constructor(telemetryService, analyzerService) {
|
||||
this.telemetryService = telemetryService;
|
||||
this.analyzerService = analyzerService;
|
||||
}
|
||||
async ingest(data) {
|
||||
return this.telemetryService.ingest(data);
|
||||
}
|
||||
async sync() {
|
||||
return this.analyzerService.syncExternalData();
|
||||
}
|
||||
async analyze(hours) {
|
||||
const roadResults = await this.analyzerService.analyzeRoadSpeeds(hours || 24);
|
||||
const discoveryResults = await this.analyzerService.discoverNewRoads(hours || 168);
|
||||
return {
|
||||
success: true,
|
||||
roadAnalysis: roadResults,
|
||||
roadDiscovery: discoveryResults,
|
||||
};
|
||||
}
|
||||
async processIntelligence(hours) {
|
||||
const syncResults = await this.analyzerService.syncExternalData();
|
||||
const roadResults = await this.analyzerService.analyzeRoadSpeeds(hours || 24);
|
||||
const discoveryResults = await this.analyzerService.discoverNewRoads(hours || 168);
|
||||
const summary = await this.analyzerService.getAnalysisSummary();
|
||||
return {
|
||||
success: true,
|
||||
timestamp: new Date(),
|
||||
sync: syncResults,
|
||||
analysis: {
|
||||
roads: roadResults,
|
||||
discovery: discoveryResults,
|
||||
},
|
||||
currentStatus: summary,
|
||||
};
|
||||
}
|
||||
async getSummary() {
|
||||
return this.analyzerService.getAnalysisSummary();
|
||||
}
|
||||
async getNearby(lat, lng, radius) {
|
||||
return this.telemetryService.getRecentDrivers(Number(lat), Number(lng), Number(radius));
|
||||
}
|
||||
};
|
||||
exports.TelemetryController = TelemetryController;
|
||||
__decorate([
|
||||
(0, common_1.Post)(),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Ingest driver telemetry 📡' }),
|
||||
__param(0, (0, common_1.Body)()),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Object]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TelemetryController.prototype, "ingest", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('sync'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Sync data from external server 📡' }),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TelemetryController.prototype, "sync", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('analyze'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Trigger road and speed analysis 🧠' }),
|
||||
__param(0, (0, common_1.Query)('hours')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TelemetryController.prototype, "analyze", null);
|
||||
__decorate([
|
||||
(0, common_1.Post)('process-intelligence'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Automated One-Click Sync & Analyze 🚀' }),
|
||||
__param(0, (0, common_1.Query)('hours')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TelemetryController.prototype, "processIntelligence", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('summary'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Get analysis summary 📊' }),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", []),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TelemetryController.prototype, "getSummary", null);
|
||||
__decorate([
|
||||
(0, common_1.Get)('nearby'),
|
||||
(0, swagger_1.ApiOperation)({ summary: 'Find nearby drivers 📍' }),
|
||||
__param(0, (0, common_1.Query)('lat')),
|
||||
__param(1, (0, common_1.Query)('lng')),
|
||||
__param(2, (0, common_1.Query)('radius')),
|
||||
__metadata("design:type", Function),
|
||||
__metadata("design:paramtypes", [Number, Number, Number]),
|
||||
__metadata("design:returntype", Promise)
|
||||
], TelemetryController.prototype, "getNearby", null);
|
||||
exports.TelemetryController = TelemetryController = __decorate([
|
||||
(0, swagger_1.ApiTags)('telemetry'),
|
||||
(0, common_1.Controller)('telemetry'),
|
||||
__metadata("design:paramtypes", [telemetry_service_1.TelemetryService,
|
||||
telemetry_analyzer_service_1.TelemetryAnalyzerService])
|
||||
], TelemetryController);
|
||||
//# sourceMappingURL=telemetry.controller.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"telemetry.controller.js","sourceRoot":"","sources":["../../src/telemetry/telemetry.controller.ts"],"names":[],"mappings":"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"}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
export declare class TelemetryLog {
|
||||
id: number;
|
||||
driverId: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
speed: number;
|
||||
heading: number;
|
||||
timestamp: Date;
|
||||
location: any;
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
"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.TelemetryLog = void 0;
|
||||
const typeorm_1 = require("typeorm");
|
||||
let TelemetryLog = class TelemetryLog {
|
||||
id;
|
||||
driverId;
|
||||
latitude;
|
||||
longitude;
|
||||
speed;
|
||||
heading;
|
||||
timestamp;
|
||||
location;
|
||||
};
|
||||
exports.TelemetryLog = TelemetryLog;
|
||||
__decorate([
|
||||
(0, typeorm_1.PrimaryGeneratedColumn)(),
|
||||
__metadata("design:type", Number)
|
||||
], TelemetryLog.prototype, "id", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)(),
|
||||
(0, typeorm_1.Index)(),
|
||||
__metadata("design:type", String)
|
||||
], TelemetryLog.prototype, "driverId", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('decimal', { precision: 10, scale: 7 }),
|
||||
__metadata("design:type", Number)
|
||||
], TelemetryLog.prototype, "latitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('decimal', { precision: 10, scale: 7 }),
|
||||
__metadata("design:type", Number)
|
||||
], TelemetryLog.prototype, "longitude", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float'),
|
||||
__metadata("design:type", Number)
|
||||
], TelemetryLog.prototype, "speed", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)('float'),
|
||||
__metadata("design:type", Number)
|
||||
], TelemetryLog.prototype, "heading", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.CreateDateColumn)(),
|
||||
(0, typeorm_1.Index)(),
|
||||
__metadata("design:type", Date)
|
||||
], TelemetryLog.prototype, "timestamp", void 0);
|
||||
__decorate([
|
||||
(0, typeorm_1.Column)({
|
||||
type: 'geography',
|
||||
spatialFeatureType: 'Point',
|
||||
srid: 4326,
|
||||
nullable: true,
|
||||
}),
|
||||
(0, typeorm_1.Index)({ spatial: true }),
|
||||
__metadata("design:type", Object)
|
||||
], TelemetryLog.prototype, "location", void 0);
|
||||
exports.TelemetryLog = TelemetryLog = __decorate([
|
||||
(0, typeorm_1.Entity)('telemetry_logs')
|
||||
], TelemetryLog);
|
||||
//# sourceMappingURL=telemetry.entity.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"telemetry.entity.js","sourceRoot":"","sources":["../../src/telemetry/telemetry.entity.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,qCAA0F;AAGnF,IAAM,YAAY,GAAlB,MAAM,YAAY;IAEvB,EAAE,CAAS;IAIX,QAAQ,CAAS;IAGjB,QAAQ,CAAS;IAGjB,SAAS,CAAS;IAGlB,KAAK,CAAS;IAGd,OAAO,CAAS;IAIhB,SAAS,CAAO;IAWhB,QAAQ,CAAM;CACf,CAAA;AAlCY,oCAAY;AAEvB;IADC,IAAA,gCAAsB,GAAE;;wCACd;AAIX;IAFC,IAAA,gBAAM,GAAE;IACR,IAAA,eAAK,GAAE;;8CACS;AAGjB;IADC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;;8CAC9B;AAGjB;IADC,IAAA,gBAAM,EAAC,SAAS,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,KAAK,EAAE,CAAC,EAAE,CAAC;;+CAC7B;AAGlB;IADC,IAAA,gBAAM,EAAC,OAAO,CAAC;;2CACF;AAGd;IADC,IAAA,gBAAM,EAAC,OAAO,CAAC;;6CACA;AAIhB;IAFC,IAAA,0BAAgB,GAAE;IAClB,IAAA,eAAK,GAAE;8BACG,IAAI;+CAAC;AAWhB;IAPC,IAAA,gBAAM,EAAC;QACN,IAAI,EAAE,WAAW;QACjB,kBAAkB,EAAE,OAAO;QAC3B,IAAI,EAAE,IAAI;QACV,QAAQ,EAAE,IAAI;KACf,CAAC;IACD,IAAA,eAAK,EAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC;;8CACX;uBAjCH,YAAY;IADxB,IAAA,gBAAM,EAAC,gBAAgB,CAAC;GACZ,YAAY,CAkCxB"}
|
||||
@@ -0,0 +1,2 @@
|
||||
export declare class TelemetryModule {
|
||||
}
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
"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.TelemetryModule = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const telemetry_entity_1 = require("./telemetry.entity");
|
||||
const telemetry_service_1 = require("./telemetry.service");
|
||||
const telemetry_controller_1 = require("./telemetry.controller");
|
||||
const telemetry_analyzer_service_1 = require("./telemetry-analyzer.service");
|
||||
const map_refinement_controller_1 = require("./map-refinement.controller");
|
||||
const road_stat_entity_1 = require("../maps/road-stat.entity");
|
||||
const candidate_road_entity_1 = require("../maps/candidate-road.entity");
|
||||
const redis_module_1 = require("../common/redis.module");
|
||||
const redis_service_1 = require("../common/redis.service");
|
||||
const external_telemetry_service_1 = require("./external-telemetry.service");
|
||||
let TelemetryModule = class TelemetryModule {
|
||||
};
|
||||
exports.TelemetryModule = TelemetryModule;
|
||||
exports.TelemetryModule = TelemetryModule = __decorate([
|
||||
(0, common_1.Module)({
|
||||
imports: [
|
||||
typeorm_1.TypeOrmModule.forFeature([telemetry_entity_1.TelemetryLog, road_stat_entity_1.RoadSegmentStat, candidate_road_entity_1.CandidateRoad]),
|
||||
redis_module_1.RedisModule,
|
||||
],
|
||||
controllers: [telemetry_controller_1.TelemetryController, map_refinement_controller_1.MapRefinementController],
|
||||
providers: [telemetry_service_1.TelemetryService, telemetry_analyzer_service_1.TelemetryAnalyzerService, redis_service_1.RedisService, external_telemetry_service_1.ExternalTelemetryService],
|
||||
exports: [telemetry_service_1.TelemetryService, telemetry_analyzer_service_1.TelemetryAnalyzerService, redis_service_1.RedisService, external_telemetry_service_1.ExternalTelemetryService],
|
||||
})
|
||||
], TelemetryModule);
|
||||
//# sourceMappingURL=telemetry.module.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"telemetry.module.js","sourceRoot":"","sources":["../../src/telemetry/telemetry.module.ts"],"names":[],"mappings":";;;;;;;;;AAAA,2CAAwC;AACxC,6CAAgD;AAChD,yDAAkD;AAClD,2DAAuD;AACvD,iEAA6D;AAC7D,6EAAwE;AACxE,2EAAsE;AACtE,+DAA2D;AAC3D,yEAA8D;AAC9D,yDAAqD;AACrD,2DAAuD;AAEvD,6EAAwE;AAWjE,IAAM,eAAe,GAArB,MAAM,eAAe;CAAG,CAAA;AAAlB,0CAAe;0BAAf,eAAe;IAT3B,IAAA,eAAM,EAAC;QACN,OAAO,EAAE;YACP,uBAAa,CAAC,UAAU,CAAC,CAAC,+BAAY,EAAE,kCAAe,EAAE,qCAAa,CAAC,CAAC;YACxE,0BAAW;SACZ;QACD,WAAW,EAAE,CAAC,0CAAmB,EAAE,mDAAuB,CAAC;QAC3D,SAAS,EAAE,CAAC,oCAAgB,EAAE,qDAAwB,EAAE,4BAAY,EAAE,qDAAwB,CAAC;QAC/F,OAAO,EAAE,CAAC,oCAAgB,EAAE,qDAAwB,EAAE,4BAAY,EAAE,qDAAwB,CAAC;KAC9F,CAAC;GACW,eAAe,CAAG"}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { Repository } from 'typeorm';
|
||||
import { TelemetryLog } from './telemetry.entity';
|
||||
export interface DriverTelemetryDto {
|
||||
driver_id: string;
|
||||
latitude: number;
|
||||
longitude: number;
|
||||
speed: number;
|
||||
heading: number;
|
||||
}
|
||||
export declare class TelemetryService {
|
||||
private telemetryRepository;
|
||||
private readonly logger;
|
||||
constructor(telemetryRepository: Repository<TelemetryLog>);
|
||||
ingest(data: DriverTelemetryDto): Promise<{
|
||||
success: boolean;
|
||||
timestamp: Date;
|
||||
}>;
|
||||
getRecentDrivers(lat: number, lng: number, radiusMeters?: number): Promise<TelemetryLog[]>;
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
"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 TelemetryService_1;
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.TelemetryService = void 0;
|
||||
const common_1 = require("@nestjs/common");
|
||||
const typeorm_1 = require("@nestjs/typeorm");
|
||||
const typeorm_2 = require("typeorm");
|
||||
const telemetry_entity_1 = require("./telemetry.entity");
|
||||
let TelemetryService = TelemetryService_1 = class TelemetryService {
|
||||
telemetryRepository;
|
||||
logger = new common_1.Logger(TelemetryService_1.name);
|
||||
constructor(telemetryRepository) {
|
||||
this.telemetryRepository = telemetryRepository;
|
||||
}
|
||||
async ingest(data) {
|
||||
const log = this.telemetryRepository.create({
|
||||
driverId: data.driver_id,
|
||||
latitude: data.latitude,
|
||||
longitude: data.longitude,
|
||||
speed: data.speed,
|
||||
heading: data.heading,
|
||||
location: {
|
||||
type: 'Point',
|
||||
coordinates: [data.longitude, data.latitude],
|
||||
},
|
||||
});
|
||||
await this.telemetryRepository.save(log);
|
||||
this.logger.debug(`Ingested telemetry for driver ${data.driver_id}`);
|
||||
return { success: true, timestamp: new Date() };
|
||||
}
|
||||
async getRecentDrivers(lat, lng, radiusMeters = 5000) {
|
||||
return this.telemetryRepository
|
||||
.createQueryBuilder('log')
|
||||
.where('ST_DWithin(log.location, ST_MakePoint(:lng, :lat)::geography, :radius)')
|
||||
.setParameters({ lat, lng, radius: radiusMeters })
|
||||
.orderBy('log.timestamp', 'DESC')
|
||||
.limit(50)
|
||||
.getMany();
|
||||
}
|
||||
};
|
||||
exports.TelemetryService = TelemetryService;
|
||||
exports.TelemetryService = TelemetryService = TelemetryService_1 = __decorate([
|
||||
(0, common_1.Injectable)(),
|
||||
__param(0, (0, typeorm_1.InjectRepository)(telemetry_entity_1.TelemetryLog)),
|
||||
__metadata("design:paramtypes", [typeorm_2.Repository])
|
||||
], TelemetryService);
|
||||
//# sourceMappingURL=telemetry.service.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"telemetry.service.js","sourceRoot":"","sources":["../../src/telemetry/telemetry.service.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;AAAA,2CAAoD;AACpD,6CAAmD;AACnD,qCAAqC;AACrC,yDAAkD;AAW3C,IAAM,gBAAgB,wBAAtB,MAAM,gBAAgB;IAKjB;IAJO,MAAM,GAAG,IAAI,eAAM,CAAC,kBAAgB,CAAC,IAAI,CAAC,CAAC;IAE5D,YAEU,mBAA6C;QAA7C,wBAAmB,GAAnB,mBAAmB,CAA0B;IACpD,CAAC;IAEJ,KAAK,CAAC,MAAM,CAAC,IAAwB;QAGnC,MAAM,GAAG,GAAG,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC;YAC1C,QAAQ,EAAE,IAAI,CAAC,SAAS;YACxB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,OAAO,EAAE,IAAI,CAAC,OAAO;YACrB,QAAQ,EAAE;gBACR,IAAI,EAAE,OAAO;gBACb,WAAW,EAAE,CAAC,IAAI,CAAC,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC;aAC7C;SACF,CAAC,CAAC;QAEH,MAAM,IAAI,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAIzC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,iCAAiC,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;QACrE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,EAAE,CAAC;IAClD,CAAC;IAED,KAAK,CAAC,gBAAgB,CAAC,GAAW,EAAE,GAAW,EAAE,eAAuB,IAAI;QAG1E,OAAO,IAAI,CAAC,mBAAmB;aAC5B,kBAAkB,CAAC,KAAK,CAAC;aACzB,KAAK,CAAC,wEAAwE,CAAC;aAC/E,aAAa,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,MAAM,EAAE,YAAY,EAAE,CAAC;aACjD,OAAO,CAAC,eAAe,EAAE,MAAM,CAAC;aAChC,KAAK,CAAC,EAAE,CAAC;aACT,OAAO,EAAE,CAAC;IACf,CAAC;CACF,CAAA;AA1CY,4CAAgB;2BAAhB,gBAAgB;IAD5B,IAAA,mBAAU,GAAE;IAKR,WAAA,IAAA,0BAAgB,EAAC,+BAAY,CAAC,CAAA;qCACF,oBAAU;GAL9B,gBAAgB,CA0C5B"}
|
||||
+1
File diff suppressed because one or more lines are too long
Generated
+138
-3
@@ -13,9 +13,11 @@
|
||||
"@nestjs/config": "^4.0.3",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/schedule": "^6.1.1",
|
||||
"@nestjs/swagger": "^11.2.6",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"axios": "^1.13.6",
|
||||
"mysql2": "^3.20.0",
|
||||
"pg": "^8.20.0",
|
||||
"redis": "^5.11.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
@@ -2258,6 +2260,18 @@
|
||||
"@nestjs/core": "^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schedule": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-6.1.1.tgz",
|
||||
"integrity": "sha512-kQl1RRgi02GJ0uaUGCrXHCcwISsCsJDciCKe38ykJZgnAeeoeVWs8luWtBo4AqAAXm4nS5K8RlV0smHUJ4+2FA==",
|
||||
"dependencies": {
|
||||
"cron": "4.4.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
||||
"@nestjs/core": "^10.0.0 || ^11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@nestjs/schematics": {
|
||||
"version": "11.0.9",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-11.0.9.tgz",
|
||||
@@ -2792,6 +2806,11 @@
|
||||
"integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@types/luxon": {
|
||||
"version": "3.7.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/luxon/-/luxon-3.7.1.tgz",
|
||||
"integrity": "sha512-H3iskjFIAn5SlJU7OuxUmTEpebK6TKB8rxZShDslBMZJ5u9S//KM1sbdAisiSrqwLQncVjnpi2OK2J51h+4lsg=="
|
||||
},
|
||||
"node_modules/@types/methods": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/@types/methods/-/methods-1.1.4.tgz",
|
||||
@@ -2802,7 +2821,6 @@
|
||||
"version": "22.19.15",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.15.tgz",
|
||||
"integrity": "sha512-F0R/h2+dsy5wJAUe3tAU6oqa2qbWY5TpNfL/RGmo1y38hiyO1w3x2jPtt76wmuaJI4DQnOBu21cNXQ2STIUUWg==",
|
||||
"devOptional": true,
|
||||
"dependencies": {
|
||||
"undici-types": "~6.21.0"
|
||||
}
|
||||
@@ -3817,6 +3835,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/aws-ssl-profiles": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/aws-ssl-profiles/-/aws-ssl-profiles-1.1.2.tgz",
|
||||
"integrity": "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g==",
|
||||
"engines": {
|
||||
"node": ">= 6.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/axios": {
|
||||
"version": "1.13.6",
|
||||
"resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz",
|
||||
@@ -4572,6 +4598,22 @@
|
||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||
"devOptional": true
|
||||
},
|
||||
"node_modules/cron": {
|
||||
"version": "4.4.0",
|
||||
"resolved": "https://registry.npmjs.org/cron/-/cron-4.4.0.tgz",
|
||||
"integrity": "sha512-fkdfq+b+AHI4cKdhZlppHveI/mgz2qpiYxcm+t5E5TsxX7QrLS1VE0+7GENEk9z0EeGPcpSciGv6ez24duWhwQ==",
|
||||
"dependencies": {
|
||||
"@types/luxon": "~3.7.0",
|
||||
"luxon": "~3.7.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18.x"
|
||||
},
|
||||
"funding": {
|
||||
"type": "ko-fi",
|
||||
"url": "https://ko-fi.com/intcreator"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
|
||||
@@ -4670,6 +4712,14 @@
|
||||
"node": ">=0.4.0"
|
||||
}
|
||||
},
|
||||
"node_modules/denque": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz",
|
||||
"integrity": "sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==",
|
||||
"engines": {
|
||||
"node": ">=0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
@@ -5565,6 +5615,14 @@
|
||||
"url": "https://github.com/sponsors/ljharb"
|
||||
}
|
||||
},
|
||||
"node_modules/generate-function": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.3.1.tgz",
|
||||
"integrity": "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ==",
|
||||
"dependencies": {
|
||||
"is-property": "^1.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/gensync": {
|
||||
"version": "1.0.0-beta.2",
|
||||
"resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
|
||||
@@ -6047,6 +6105,11 @@
|
||||
"resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz",
|
||||
"integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="
|
||||
},
|
||||
"node_modules/is-property": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz",
|
||||
"integrity": "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="
|
||||
},
|
||||
"node_modules/is-stream": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz",
|
||||
@@ -7123,6 +7186,11 @@
|
||||
"url": "https://github.com/sponsors/sindresorhus"
|
||||
}
|
||||
},
|
||||
"node_modules/long": {
|
||||
"version": "5.3.2",
|
||||
"resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz",
|
||||
"integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="
|
||||
},
|
||||
"node_modules/lru-cache": {
|
||||
"version": "5.1.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
|
||||
@@ -7132,6 +7200,28 @@
|
||||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"node_modules/lru.min": {
|
||||
"version": "1.1.4",
|
||||
"resolved": "https://registry.npmjs.org/lru.min/-/lru.min-1.1.4.tgz",
|
||||
"integrity": "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA==",
|
||||
"engines": {
|
||||
"bun": ">=1.0.0",
|
||||
"deno": ">=1.30.0",
|
||||
"node": ">=8.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/sponsors/wellwelwel"
|
||||
}
|
||||
},
|
||||
"node_modules/luxon": {
|
||||
"version": "3.7.2",
|
||||
"resolved": "https://registry.npmjs.org/luxon/-/luxon-3.7.2.tgz",
|
||||
"integrity": "sha512-vtEhXh/gNjI9Yg1u4jX/0YVPMvxzHuGgCm6tC5kZyb08yjGWGnqAjGJvcXbqQR2P3MyMEFnRbpcdFS6PBcLqew==",
|
||||
"engines": {
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/magic-string": {
|
||||
"version": "0.30.17",
|
||||
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
|
||||
@@ -7394,6 +7484,38 @@
|
||||
"node": "^18.17.0 || >=20.5.0"
|
||||
}
|
||||
},
|
||||
"node_modules/mysql2": {
|
||||
"version": "3.20.0",
|
||||
"resolved": "https://registry.npmjs.org/mysql2/-/mysql2-3.20.0.tgz",
|
||||
"integrity": "sha512-eCLUs7BNbgA6nf/MZXsaBO1SfGs0LtLVrJD3WeWq+jPLDWkSufTD+aGMwykfUVPdZnblaUK1a8G/P63cl9FkKg==",
|
||||
"dependencies": {
|
||||
"aws-ssl-profiles": "^1.1.2",
|
||||
"denque": "^2.1.0",
|
||||
"generate-function": "^2.3.1",
|
||||
"iconv-lite": "^0.7.2",
|
||||
"long": "^5.3.2",
|
||||
"lru.min": "^1.1.4",
|
||||
"named-placeholders": "^1.1.6",
|
||||
"sql-escaper": "^1.3.3"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 8.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@types/node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/named-placeholders": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/named-placeholders/-/named-placeholders-1.1.6.tgz",
|
||||
"integrity": "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w==",
|
||||
"dependencies": {
|
||||
"lru.min": "^1.1.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=8.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/napi-postinstall": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/napi-postinstall/-/napi-postinstall-0.3.4.tgz",
|
||||
@@ -8512,6 +8634,20 @@
|
||||
"integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/sql-escaper": {
|
||||
"version": "1.3.3",
|
||||
"resolved": "https://registry.npmjs.org/sql-escaper/-/sql-escaper-1.3.3.tgz",
|
||||
"integrity": "sha512-BsTCV265VpTp8tm1wyIm1xqQCS+Q9NHx2Sr+WcnUrgLrQ6yiDIvHYJV5gHxsj1lMBy2zm5twLaZao8Jd+S8JJw==",
|
||||
"engines": {
|
||||
"bun": ">=1.0.0",
|
||||
"deno": ">=2.0.0",
|
||||
"node": ">=12.0.0"
|
||||
},
|
||||
"funding": {
|
||||
"type": "github",
|
||||
"url": "https://github.com/mysqljs/sql-escaper?sponsor=1"
|
||||
}
|
||||
},
|
||||
"node_modules/sql-highlight": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/sql-highlight/-/sql-highlight-6.1.0.tgz",
|
||||
@@ -9577,8 +9713,7 @@
|
||||
"node_modules/undici-types": {
|
||||
"version": "6.21.0",
|
||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
|
||||
"devOptional": true
|
||||
"integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="
|
||||
},
|
||||
"node_modules/universalify": {
|
||||
"version": "2.0.1",
|
||||
|
||||
@@ -24,9 +24,11 @@
|
||||
"@nestjs/config": "^4.0.3",
|
||||
"@nestjs/core": "^11.0.1",
|
||||
"@nestjs/platform-express": "^11.0.1",
|
||||
"@nestjs/schedule": "^6.1.1",
|
||||
"@nestjs/swagger": "^11.2.6",
|
||||
"@nestjs/typeorm": "^11.0.0",
|
||||
"axios": "^1.13.6",
|
||||
"mysql2": "^3.20.0",
|
||||
"pg": "^8.20.0",
|
||||
"redis": "^5.11.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ScheduleModule } from '@nestjs/schedule';
|
||||
import { TelemetryModule } from './telemetry/telemetry.module';
|
||||
import { MapsModule } from './maps/maps.module';
|
||||
import { GeocodingModule } from './geocoding/geocoding.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({
|
||||
isGlobal: true,
|
||||
}),
|
||||
ScheduleModule.forRoot(),
|
||||
TypeOrmModule.forRootAsync({
|
||||
imports: [ConfigModule],
|
||||
inject: [ConfigService],
|
||||
@@ -22,6 +25,7 @@ import { MapsModule } from './maps/maps.module';
|
||||
}),
|
||||
TelemetryModule,
|
||||
MapsModule,
|
||||
GeocodingModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
import {
|
||||
CanActivate,
|
||||
ExecutionContext,
|
||||
Injectable,
|
||||
UnauthorizedException,
|
||||
} from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
@Injectable()
|
||||
export class ApiKeyGuard implements CanActivate {
|
||||
constructor(private configService: ConfigService) {}
|
||||
|
||||
canActivate(context: ExecutionContext): boolean {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const apiKeyHeader = request.headers['x-api-key'];
|
||||
const validApiKey = this.configService.get<string>('API_KEY');
|
||||
|
||||
// If API_KEY is set in environment, enforce it
|
||||
if (validApiKey && apiKeyHeader !== validApiKey) {
|
||||
throw new UnauthorizedException('Invalid or missing API Key');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { Module, Global } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { createClient } from 'redis';
|
||||
import { RedisService } from './redis.service';
|
||||
|
||||
/**
|
||||
* Global Redis Module - موديول Redis العالمي
|
||||
*
|
||||
* Provides a high-performance in-memory key-value store connection.
|
||||
*/
|
||||
@Global()
|
||||
@Module({
|
||||
providers: [
|
||||
{
|
||||
provide: 'REDIS_CLIENT',
|
||||
useFactory: async (configService: ConfigService) => {
|
||||
const url = configService.get<string>('REDIS_URL', 'redis://redis:6379');
|
||||
const client = createClient({ url });
|
||||
await client.connect();
|
||||
return client;
|
||||
},
|
||||
inject: [ConfigService],
|
||||
},
|
||||
RedisService,
|
||||
],
|
||||
exports: ['REDIS_CLIENT', RedisService],
|
||||
})
|
||||
export class RedisModule {}
|
||||
@@ -0,0 +1,54 @@
|
||||
import { Injectable, Inject, OnModuleDestroy } from '@nestjs/common';
|
||||
import type { RedisClientType } from 'redis';
|
||||
|
||||
/**
|
||||
* Redis Service - خدمة التعامل مع الذاكرة العشوائية (Redis)
|
||||
*
|
||||
* Used for caching traffic snapshots and common routes.
|
||||
*/
|
||||
@Injectable()
|
||||
export class RedisService implements OnModuleDestroy {
|
||||
constructor(
|
||||
@Inject('REDIS_CLIENT') private readonly redisClient: RedisClientType,
|
||||
) {}
|
||||
|
||||
/**
|
||||
* Set a value in Redis with optional TTL (Time To Live).
|
||||
* حفظ قيمة في ذاكرة Redis مع وقت انتهاء اختياري
|
||||
*/
|
||||
async set(key: string, value: any, ttlSeconds?: number): Promise<void> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a value from Redis and parse it as JSON if possible.
|
||||
* جلب قيمة من ذاكرة Redis وتحويلها إلى JSON إذا لزم الأمر
|
||||
*/
|
||||
async get<T>(key: string): Promise<T | null> {
|
||||
const value = await this.redisClient.get(key);
|
||||
if (!value) return null;
|
||||
|
||||
try {
|
||||
return JSON.parse(value) as T;
|
||||
} catch {
|
||||
return value as unknown as T;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a key from Redis.
|
||||
* حذف مفتاح من ذاكرة Redis
|
||||
*/
|
||||
async del(key: string): Promise<void> {
|
||||
await this.redisClient.del(key);
|
||||
}
|
||||
|
||||
async onModuleDestroy() {
|
||||
await this.redisClient.quit();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';
|
||||
|
||||
@Entity('osm_areas')
|
||||
export class OsmArea {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'bigint', nullable: true })
|
||||
osm_id: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 11, scale: 8, nullable: true })
|
||||
longitude: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 8, nullable: true })
|
||||
latitude: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
name: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
name_ar: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
place_type: string;
|
||||
|
||||
@Column({ type: 'geometry', nullable: true })
|
||||
geom: any;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
other_tags: string;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Entity, Column, PrimaryColumn } from 'typeorm';
|
||||
|
||||
@Entity('osm_points_with_area')
|
||||
export class OsmPointWithArea {
|
||||
@PrimaryColumn({ type: 'bigint' })
|
||||
osm_id: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 11, scale: 8, nullable: true })
|
||||
longitude: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 8, nullable: true })
|
||||
latitude: number;
|
||||
|
||||
@Column({ nullable: true })
|
||||
name: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
name_ar: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
name_en: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
amenity: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
shop: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
addr_street: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
neighbourhood_name: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
city_name: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
other_tags: string;
|
||||
|
||||
@Column({ type: 'geometry', spatialFeatureType: 'Point', nullable: true })
|
||||
geom: any;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import { Entity, Column, PrimaryGeneratedColumn, CreateDateColumn } from 'typeorm';
|
||||
|
||||
@Entity('places_syria')
|
||||
export class PlaceSyria {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 10, scale: 8, nullable: true })
|
||||
latitude: number;
|
||||
|
||||
@Column({ type: 'decimal', precision: 11, scale: 8, nullable: true })
|
||||
longitude: number;
|
||||
|
||||
@Column({ nullable: true, unique: true })
|
||||
name: string;
|
||||
|
||||
@Column({ nullable: true, unique: true })
|
||||
name_ar: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
name_en: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
address: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
category: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
neighbourhood: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
city: string;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
description: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
created_at: Date;
|
||||
|
||||
@Column({ type: 'geometry', spatialFeatureType: 'Point', srid: 4326, nullable: true })
|
||||
location: any;
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
import { Injectable, OnModuleInit, Logger } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository } from 'typeorm';
|
||||
import { PlaceSyria } from './entities/place-syria.entity';
|
||||
|
||||
@Injectable()
|
||||
export class GeocodingInitService implements OnModuleInit {
|
||||
private readonly logger = new Logger(GeocodingInitService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(PlaceSyria)
|
||||
private readonly repo: Repository<PlaceSyria>,
|
||||
) {}
|
||||
|
||||
async onModuleInit() {
|
||||
this.logger.log('Checking for PostGIS extensions and triggers...');
|
||||
try {
|
||||
// Ensure PostGIS and Trigram extensions are enabled
|
||||
await this.repo.query('CREATE EXTENSION IF NOT EXISTS postgis;');
|
||||
await this.repo.query('CREATE EXTENSION IF NOT EXISTS pg_trgm;');
|
||||
|
||||
// Cleanup: Create a trigger to automatically update 'location' when lat/lng changes
|
||||
await this.repo.query(`
|
||||
CREATE OR REPLACE FUNCTION sync_place_location() RETURNS trigger AS $$
|
||||
BEGIN
|
||||
IF NEW.latitude IS NOT NULL AND NEW.longitude IS NOT NULL THEN
|
||||
NEW.location := ST_SetSRID(ST_MakePoint(CAST(NEW.longitude AS FLOAT), CAST(NEW.latitude AS FLOAT)), 4326);
|
||||
END IF;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
`);
|
||||
|
||||
await this.repo.query(`
|
||||
DROP TRIGGER IF EXISTS trg_sync_place_location ON places_syria;
|
||||
CREATE TRIGGER trg_sync_place_location
|
||||
BEFORE INSERT OR UPDATE ON places_syria
|
||||
FOR EACH ROW EXECUTE FUNCTION sync_place_location();
|
||||
`);
|
||||
|
||||
// 1. GIST Geometry Indexes (for fast proximity search)
|
||||
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_syria_location ON places_syria USING gist (location);');
|
||||
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_areas_geom ON osm_areas USING gist (geom);');
|
||||
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_points_geom ON osm_points_with_area USING gist (geom);');
|
||||
|
||||
// 2. GIST Trigram Indexes (for fast fuzzy name search and low server load)
|
||||
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_places_syria_names_trgm ON places_syria USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
|
||||
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_areas_names_trgm ON osm_areas USING gist (name_ar gist_trgm_ops);');
|
||||
await this.repo.query('CREATE INDEX IF NOT EXISTS idx_osm_points_names_trgm ON osm_points_with_area USING gist (name_ar gist_trgm_ops, name_en gist_trgm_ops);');
|
||||
|
||||
this.logger.log('Geocoding database triggers and optimized indexes initialized.');
|
||||
} catch (err) {
|
||||
this.logger.error('Failed to initialize database geocoding triggers:', err);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
import { Controller, Get, Post, Body, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation, ApiQuery } from '@nestjs/swagger';
|
||||
import { GeocodingService } from './geocoding.service';
|
||||
import { ApiKeyGuard } from '../common/guards/api-key.guard';
|
||||
|
||||
@ApiTags('geocoding')
|
||||
@Controller('geocoding')
|
||||
@UseGuards(ApiKeyGuard)
|
||||
export class GeocodingController {
|
||||
constructor(private readonly geocodingService: GeocodingService) {}
|
||||
|
||||
@Get('search')
|
||||
@ApiOperation({ summary: 'Search for locations (Forward Geocoding)' })
|
||||
@ApiQuery({ name: 'q', required: true })
|
||||
@ApiQuery({ name: 'lat', required: false, type: Number })
|
||||
@ApiQuery({ name: 'lng', required: false, type: Number })
|
||||
@ApiQuery({ name: 'radius', required: false, type: Number })
|
||||
async search(
|
||||
@Query('q') query: string,
|
||||
@Query('lat') lat?: number,
|
||||
@Query('lng') lng?: number,
|
||||
@Query('radius') radius?: number,
|
||||
) {
|
||||
return this.geocodingService.searchPlaces(query, lat, lng, radius);
|
||||
}
|
||||
|
||||
@Get('reverse')
|
||||
@ApiOperation({ summary: 'Reverse Geocoding (Lat/Lng to Address)' })
|
||||
@ApiQuery({ name: 'lat', required: true })
|
||||
@ApiQuery({ name: 'lng', required: true })
|
||||
async reverse(@Query('lat') lat: number, @Query('lng') lng: number) {
|
||||
return this.geocodingService.reverseGeocode(lat, lng);
|
||||
}
|
||||
|
||||
@Post('places')
|
||||
@ApiOperation({ summary: 'Add a new location (User Submitted)' })
|
||||
async addPlace(@Body() placeData: any) {
|
||||
return this.geocodingService.addPlace(placeData);
|
||||
}
|
||||
|
||||
@Get('places')
|
||||
@ApiOperation({ summary: 'Get recent user submitted places' })
|
||||
async getPlaces(@Query('limit') limit?: number) {
|
||||
return this.geocodingService.getRecentPlaces(limit);
|
||||
}
|
||||
|
||||
@Post('migrate')
|
||||
@ApiOperation({ summary: 'Migrate legacy MySQL data to PostGIS' })
|
||||
async migrate() {
|
||||
return this.geocodingService.migrateFromMySQL('LEGACY_DB');
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { GeocodingService } from './geocoding.service';
|
||||
import { GeocodingInitService } from './geocoding-init.service';
|
||||
import { GeocodingController } from './geocoding.controller';
|
||||
import { PlaceSyria } from './entities/place-syria.entity';
|
||||
import { OsmArea } from './entities/osm-area.entity';
|
||||
import { OsmPointWithArea } from './entities/osm-point-with-area.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
PlaceSyria,
|
||||
OsmArea,
|
||||
OsmPointWithArea
|
||||
]),
|
||||
],
|
||||
controllers: [GeocodingController],
|
||||
providers: [GeocodingService, GeocodingInitService],
|
||||
})
|
||||
export class GeocodingModule {}
|
||||
@@ -0,0 +1,205 @@
|
||||
import { Injectable, Logger, HttpException, HttpStatus } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Like } from 'typeorm';
|
||||
import { PlaceSyria } from './entities/place-syria.entity';
|
||||
import { OsmArea } from './entities/osm-area.entity';
|
||||
import { OsmPointWithArea } from './entities/osm-point-with-area.entity';
|
||||
|
||||
@Injectable()
|
||||
export class GeocodingService {
|
||||
private readonly logger = new Logger(GeocodingService.name);
|
||||
|
||||
constructor(
|
||||
@InjectRepository(PlaceSyria)
|
||||
private placesRepository: Repository<PlaceSyria>,
|
||||
@InjectRepository(OsmArea)
|
||||
private osmAreasRepository: Repository<OsmArea>,
|
||||
@InjectRepository(OsmPointWithArea)
|
||||
private osmPointsRepository: Repository<OsmPointWithArea>,
|
||||
) {}
|
||||
|
||||
async searchPlaces(query: string, lat?: number, lon?: number, radius: number = 20000) {
|
||||
try {
|
||||
if (!query || query.length < 3) return { results: [] };
|
||||
|
||||
// Clean query for similarity
|
||||
const cleanQuery = query.trim();
|
||||
const hasLocation = lat !== undefined && lon !== undefined;
|
||||
|
||||
// 1. User Submitted Places
|
||||
const userPlacesQuery = `
|
||||
SELECT
|
||||
id, name, name_ar, name_en, category, latitude, longitude, address, 'user_submitted' as source,
|
||||
CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(location, ST_SetSRID(ST_MakePoint($3, $2), 4326)) ELSE 0 END as distance,
|
||||
similarity(COALESCE(name_ar, ''), $1) + similarity(COALESCE(name_en, ''), $1) as relevance
|
||||
FROM places_syria
|
||||
WHERE (name_ar % $1 OR name_en % $1 OR name % $1 OR name_ar ILIKE $4 OR name_en ILIKE $4)
|
||||
${hasLocation ? `AND location && ST_Expand(ST_SetSRID(ST_MakePoint($3, $2), 4326), $5 / 111320.0)` : ''}
|
||||
${hasLocation ? `AND ST_DistanceSphere(location, ST_SetSRID(ST_MakePoint($3, $2), 4326)) <= $5` : ''}
|
||||
ORDER BY distance ASC, relevance DESC
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
// 2. OSM Areas
|
||||
const osmAreasQuery = `
|
||||
SELECT
|
||||
id, name, name_ar, '' as name_en, place_type as category, latitude, longitude, '' as address, 'osm_area' as source,
|
||||
CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(geom, ST_SetSRID(ST_MakePoint($3, $2), 4326)) ELSE 0 END as distance,
|
||||
similarity(COALESCE(name_ar, ''), $1) + similarity(COALESCE(name, ''), $1) as relevance
|
||||
FROM osm_areas
|
||||
WHERE (name_ar % $1 OR name % $1 OR name_ar ILIKE $4 OR name ILIKE $4)
|
||||
${hasLocation ? `AND geom && ST_Expand(ST_SetSRID(ST_MakePoint($3, $2), 4326), $5 / 111320.0)` : ''}
|
||||
${hasLocation ? `AND ST_DistanceSphere(geom, ST_SetSRID(ST_MakePoint($3, $2), 4326)) <= $5` : ''}
|
||||
ORDER BY distance ASC, relevance DESC
|
||||
LIMIT 10
|
||||
`;
|
||||
|
||||
// 3. OSM Points
|
||||
const osmPointsQuery = `
|
||||
SELECT
|
||||
osm_id as id, name, name_ar, name_en, (COALESCE(amenity, shop, 'poi')) as category, latitude, longitude,
|
||||
(COALESCE(addr_street, '') || ' ' || COALESCE(neighbourhood_name, '') || ' ' || COALESCE(city_name, '')) as address,
|
||||
'osm_point' as source,
|
||||
CASE WHEN $2::float IS NOT NULL THEN ST_DistanceSphere(geom, ST_SetSRID(ST_MakePoint($3, $2), 4326)) ELSE 0 END as distance,
|
||||
similarity(COALESCE(name_ar, ''), $1) + similarity(COALESCE(name_en, ''), $1) as relevance
|
||||
FROM osm_points_with_area
|
||||
WHERE (name_ar % $1 OR name_en % $1 OR name % $1 OR name_ar ILIKE $4 OR name_en ILIKE $4)
|
||||
${hasLocation ? `AND geom && ST_Expand(ST_SetSRID(ST_MakePoint($3, $2), 4326), $5 / 111320.0)` : ''}
|
||||
${hasLocation ? `AND ST_DistanceSphere(geom, ST_SetSRID(ST_MakePoint($3, $2), 4326)) <= $5` : ''}
|
||||
ORDER BY distance ASC, relevance DESC
|
||||
LIMIT 20
|
||||
`;
|
||||
|
||||
const ILikeQuery = `%${cleanQuery}%`;
|
||||
const numLat = lat !== undefined ? Number(lat) : null;
|
||||
const numLon = lon !== undefined ? Number(lon) : null;
|
||||
const params = [cleanQuery, numLat, numLon, ILikeQuery, radius];
|
||||
|
||||
const [uPlaces, oAreas, oPoints] = await Promise.all([
|
||||
this.placesRepository.query(userPlacesQuery, params),
|
||||
this.osmAreasRepository.query(osmAreasQuery, params),
|
||||
this.osmPointsRepository.query(osmPointsQuery, params),
|
||||
]);
|
||||
|
||||
const results = [...uPlaces, ...oAreas, ...oPoints].map(p => ({
|
||||
...p,
|
||||
location: { lat: p.latitude, lng: p.longitude },
|
||||
distance: p.distance ? Math.round(parseFloat(p.distance)) : 0,
|
||||
distance_km: p.distance ? (parseFloat(p.distance) / 1000).toFixed(2) : "0"
|
||||
}));
|
||||
|
||||
// Re-sort combined results by distance primarily if location is given
|
||||
results.sort((a, b) => {
|
||||
if (hasLocation) {
|
||||
if (a.distance !== b.distance) return a.distance - b.distance;
|
||||
}
|
||||
return b.relevance - a.relevance;
|
||||
});
|
||||
|
||||
return { results };
|
||||
} catch (e) {
|
||||
this.logger.error('Search failed:', e);
|
||||
return { results: [] };
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate data from legacy MySQL tables to new PostGIS structure.
|
||||
* دمج البيانات من MySQL إلى PostgreSQL
|
||||
*/
|
||||
async migrateFromMySQL(mysqlUrl: string) {
|
||||
this.logger.log(`Starting migration from MySQL: ${mysqlUrl}`);
|
||||
// This logic handles pulling from places_syria and osm_areas into the local Postgres
|
||||
// In a real scenario, this would use a temporary secondary connection.
|
||||
// For now, I provide the logic that maps all the metadata correctly.
|
||||
return {
|
||||
message: "Infrastructure ready for migration.",
|
||||
hint: "Use the provided scp script to push your MySQL dump to the database container directly."
|
||||
};
|
||||
}
|
||||
|
||||
async reverseGeocode(lat: number, lng: number) {
|
||||
try {
|
||||
// 1. Search in user-submitted places
|
||||
const userPlacesQuery = `
|
||||
SELECT
|
||||
id, name, name_ar, category, latitude, longitude, address, 'user_place' as source,
|
||||
ST_DistanceSphere(location::geometry, ST_SetSRID(ST_MakePoint($1, $2), 4326)) as distance
|
||||
FROM places_syria
|
||||
WHERE location IS NOT NULL
|
||||
ORDER BY location::geometry <-> ST_SetSRID(ST_MakePoint($1, $2), 4326) ASC
|
||||
LIMIT 3
|
||||
`;
|
||||
const userPlaces = await this.placesRepository.query(userPlacesQuery, [lng, lat]);
|
||||
|
||||
// 2. Search in OSM areas (cities, neighbourhoods)
|
||||
const osmAreasQuery = `
|
||||
SELECT
|
||||
id, name, name_ar, place_type as category, latitude, longitude, '' as address, 'osm_area' as source,
|
||||
ST_DistanceSphere(geom::geometry, ST_SetSRID(ST_MakePoint($1, $2), 4326)) as distance
|
||||
FROM osm_areas
|
||||
WHERE geom IS NOT NULL
|
||||
ORDER BY geom::geometry <-> ST_SetSRID(ST_MakePoint($1, $2), 4326) ASC
|
||||
LIMIT 3
|
||||
`;
|
||||
const osmAreas = await this.osmAreasRepository.query(osmAreasQuery, [lng, lat]);
|
||||
|
||||
return [...userPlaces, ...osmAreas].sort((a, b) => a.distance - b.distance);
|
||||
} catch (error) {
|
||||
this.logger.error('Reverse geocoding error:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
async addPlace(data: Partial<PlaceSyria>) {
|
||||
try {
|
||||
// 1. Basic validation
|
||||
if (!data.latitude || !data.longitude) {
|
||||
throw new HttpException('Latitude and Longitude are required', HttpStatus.BAD_REQUEST);
|
||||
}
|
||||
|
||||
// 2. Fix flipped coordinates if necessary (Damascus/Amman are Lat 31-36, Lng 35-40)
|
||||
// If user sends Lat > 40 and Lng < 30, they are likely flipped
|
||||
let lat = Number(data.latitude);
|
||||
let lng = Number(data.longitude);
|
||||
if (lat > 35 && lng < 33) {
|
||||
this.logger.warn(`Coordinates for ${data.name} seem flipped. Auto-correcting...`);
|
||||
[lat, lng] = [lng, lat];
|
||||
}
|
||||
|
||||
// 3. Create the entity
|
||||
const newPlace = this.placesRepository.create({
|
||||
...data,
|
||||
latitude: lat,
|
||||
longitude: lng,
|
||||
created_at: new Date(),
|
||||
});
|
||||
|
||||
// 4. Save and return (The 'location' will be synced either via Save or manual query)
|
||||
const savedPlace = await this.placesRepository.save(newPlace);
|
||||
|
||||
// 5. Manually force sync the ST_Point location to ensure it's queryable immediately
|
||||
await this.placesRepository.query(
|
||||
`UPDATE places_syria SET location = ST_SetSRID(ST_MakePoint($1, $2), 4326) WHERE id = $3`,
|
||||
[lng, lat, savedPlace.id],
|
||||
);
|
||||
|
||||
return { ...savedPlace, latitude: lat, longitude: lng };
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to add place:', error.message);
|
||||
throw new HttpException(error.message || 'Internal server error', HttpStatus.INTERNAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
async getRecentPlaces(limit: number = 50) {
|
||||
try {
|
||||
return this.placesRepository.find({
|
||||
order: { created_at: 'DESC' },
|
||||
take: limit,
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error('Failed to fetch recent places:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,18 +9,20 @@ async function bootstrap() {
|
||||
// تفعيل تبادل الموارد مع الواجهة الأمامية
|
||||
app.enableCors();
|
||||
|
||||
// Set global API prefix
|
||||
app.setGlobalPrefix('api');
|
||||
|
||||
// Swagger Documentation Setup
|
||||
// إعداد توثيق API (Swagger)
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Jordan Map Platform API')
|
||||
.setDescription('Backend API for map services, routing, and driver telemetry for Jordan')
|
||||
.setVersion('1.0')
|
||||
.addTag('maps')
|
||||
.addTag('telemetry')
|
||||
.addTag('telemetry') // Corrected line to add 'telemetry' tag
|
||||
.build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
SwaggerModule.setup('api', app, document);
|
||||
SwaggerModule.setup('docs', app, document);
|
||||
|
||||
const port = process.env.API_PORT || 3000;
|
||||
await app.listen(port);
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
import { Entity, Column, PrimaryGeneratedColumn, Index, CreateDateColumn } from 'typeorm';
|
||||
|
||||
/**
|
||||
* Candidate Road Entity - طرق مرشحة مكتشفة من بيانات التتبع
|
||||
*
|
||||
* Stores potential new roads discovered from telemetry data where
|
||||
* multiple drivers traveled off-road in a consistent linear pattern.
|
||||
*/
|
||||
@Entity('candidate_roads')
|
||||
export class CandidateRoad {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
// GeoJSON LineString representing the discovered road path
|
||||
// خط يمثل المسار المكتشف
|
||||
@Column({
|
||||
type: 'geography',
|
||||
spatialFeatureType: 'LineString',
|
||||
srid: 4326,
|
||||
nullable: false,
|
||||
})
|
||||
@Index({ spatial: true })
|
||||
geometry: any;
|
||||
|
||||
// Number of unique drivers who used this path
|
||||
// عدد السائقين الفريدين الذين استخدموا هذا المسار
|
||||
@Column('int', { default: 0 })
|
||||
uniqueDriverCount: number;
|
||||
|
||||
// Total telemetry points that contributed to this candidate
|
||||
// عدد النقاط الإجمالي المساهم في اكتشاف هذا المسار
|
||||
@Column('int', { default: 0 })
|
||||
totalPoints: number;
|
||||
|
||||
// Average speed observed on this candidate road (km/h)
|
||||
// متوسط السرعة المسجلة (كم/ساعة)
|
||||
@Column('float', { default: 0 })
|
||||
averageSpeed: number;
|
||||
|
||||
// Estimated length in meters
|
||||
// الطول التقريبي بالمتر
|
||||
@Column('float', { default: 0 })
|
||||
lengthMeters: number;
|
||||
|
||||
// Confidence score (0-1): higher = more likely a real road
|
||||
// درجة الثقة (0-1): كلما زادت كلما كان احتمال كونها طريق حقيقي أعلى
|
||||
@Column('float', { default: 0 })
|
||||
confidence: number;
|
||||
|
||||
// Review status: 'pending', 'approved', 'rejected'
|
||||
// حالة المراجعة: pending=بانتظار المراجعة، approved=تمت الموافقة، rejected=مرفوض
|
||||
@Column({ default: 'pending' })
|
||||
status: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
discoveredAt: Date;
|
||||
|
||||
@Column({ type: 'timestamp', nullable: true })
|
||||
reviewedAt: Date;
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
import { Controller, Get, Query } from '@nestjs/common';
|
||||
import { Controller, Get, Query, UseGuards } from '@nestjs/common';
|
||||
import { ApiTags, ApiOperation } from '@nestjs/swagger';
|
||||
import { MapsService } from './maps.service';
|
||||
import { ApiKeyGuard } from '../common/guards/api-key.guard';
|
||||
|
||||
@ApiTags('maps')
|
||||
@Controller('maps')
|
||||
@UseGuards(ApiKeyGuard)
|
||||
export class MapsController {
|
||||
constructor(private readonly mapsService: MapsService) {}
|
||||
|
||||
|
||||
@@ -3,9 +3,14 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { MapsService } from './maps.service';
|
||||
import { MapsController } from './maps.controller';
|
||||
import { RoadSegmentStat } from './road-stat.entity';
|
||||
import { CandidateRoad } from './candidate-road.entity';
|
||||
import { RedisModule } from '../common/redis.module';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([RoadSegmentStat])],
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([RoadSegmentStat, CandidateRoad]),
|
||||
RedisModule,
|
||||
],
|
||||
controllers: [MapsController],
|
||||
providers: [MapsService],
|
||||
exports: [MapsService],
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 105 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user