fix(tactical): resolve DTO types and TS1272 build error for isolatedModules

This commit is contained in:
Hamza-Ayed
2026-08-18 09:46:04 +03:00
parent bd287d155b
commit 941420f5c0
86 changed files with 3614 additions and 1556 deletions
+127 -16
View File
@@ -12,7 +12,7 @@ var __param = (this && this.__param) || function (paramIndex, decorator) {
return function (target, key) { decorator(target, key, paramIndex); }
};
var GeocodingService_1;
var _a, _b, _c, _d, _e;
var _a, _b, _c, _d, _e, _f;
Object.defineProperty(exports, "__esModule", { value: true });
exports.GeocodingService = void 0;
const common_1 = require("@nestjs/common");
@@ -22,26 +22,32 @@ const cache_manager_1 = require("@nestjs/cache-manager");
const place_syria_entity_1 = require("./entities/place-syria.entity");
const place_jordan_entity_1 = require("./entities/place-jordan.entity");
const place_egypt_entity_1 = require("./entities/place-egypt.entity");
const place_iraq_entity_1 = require("./entities/place-iraq.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 {
placesSyriaRepository;
placesJordanRepository;
placesEgyptRepository;
placesIraqRepository;
osmAreasRepository;
osmPointsRepository;
cacheManager;
logger = new common_1.Logger(GeocodingService_1.name);
DB_TIMEOUT_MS = 1100;
constructor(placesSyriaRepository, placesJordanRepository, placesEgyptRepository, osmAreasRepository, osmPointsRepository, cacheManager) {
constructor(placesSyriaRepository, placesJordanRepository, placesEgyptRepository, placesIraqRepository, osmAreasRepository, osmPointsRepository, cacheManager) {
this.placesSyriaRepository = placesSyriaRepository;
this.placesJordanRepository = placesJordanRepository;
this.placesEgyptRepository = placesEgyptRepository;
this.placesIraqRepository = placesIraqRepository;
this.osmAreasRepository = osmAreasRepository;
this.osmPointsRepository = osmPointsRepository;
this.cacheManager = cacheManager;
}
getRepositoryForCoords(lat, lng) {
if (lat >= 29 && lat <= 37.5 && lng >= 38.7 && lng <= 48.8) {
return this.placesIraqRepository;
}
if (lat >= 29 && lat <= 37.5 && lng >= 34.5 && lng <= 42.5) {
if (lat > 32.5 && lng > 35.8)
return this.placesSyriaRepository;
@@ -55,6 +61,9 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
identifyRegion(lat, lng) {
if (lat === undefined || lng === undefined)
return undefined;
if (lat >= 29 && lat <= 37.5 && lng >= 38.7 && lng <= 48.8) {
return 'iraq';
}
if (lat >= 29 && lat <= 37.5 && lng >= 34.5 && lng <= 42.5) {
if (lat > 32.5 && lng > 35.8)
return 'syria';
@@ -70,6 +79,8 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
return 'places_jordan';
if (repo === this.placesEgyptRepository)
return 'places_egypt';
if (repo === this.placesIraqRepository)
return 'places_iraq';
return 'places_syria';
}
async searchPlaces(query, lat, lon, radius = 20000, country) {
@@ -100,7 +111,7 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
queryParams.push(lat, lon, radius);
}
let regionCondition = '';
if (targetRegion && ['syria', 'jordan', 'egypt'].includes(targetRegion)) {
if (targetRegion && ['syria', 'jordan', 'egypt', 'iraq'].includes(targetRegion)) {
regionCondition = `AND (region = '${targetRegion}' OR region = 'global')`;
}
const sqlQuery = `
@@ -227,6 +238,8 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
getRepoByTableName(tableName) {
if (tableName === 'places_egypt')
return this.placesEgyptRepository;
if (tableName === 'places_iraq')
return this.placesIraqRepository;
if (tableName === 'places_jordan')
return this.placesJordanRepository;
return this.placesSyriaRepository;
@@ -263,7 +276,7 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
const normalizedQuery = normalizedQueryRes?.nq || cleanQuery.toLowerCase();
let queryParams = [`${normalizedQuery}%`];
let regionCondition = '';
if (targetRegion && ['syria', 'jordan', 'egypt'].includes(targetRegion)) {
if (targetRegion && ['syria', 'jordan', 'egypt', 'iraq'].includes(targetRegion)) {
regionCondition = `AND (region = '${targetRegion}' OR region = 'global')`;
}
const sqlQuery = `
@@ -297,10 +310,44 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
return { results: [] };
}
}
async findIraqiAddress(lat, lng) {
const MAX_DISTANCE_M = 150;
try {
const rows = await this.osmPointsRepository.query(`
WITH p AS (
SELECT ST_Transform(ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326), 3857) AS g3857,
ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326)::geography AS geog
)
SELECT l.name,
ST_Distance(ST_Transform(l.way, 4326)::geography, p.geog) AS distance
FROM planet_osm_line l, p
WHERE l.name ~ '^[0-9]{2,4}-[0-9]{1,3}$'
ORDER BY l.way <-> p.g3857
LIMIT 1
`, [lng, lat]);
const row = rows?.[0];
if (!row || Number(row.distance) > MAX_DISTANCE_M)
return null;
const [mahalla, zuqaq] = String(row.name).split('-');
return {
mahalla,
zuqaq,
distance: Number(row.distance),
text: `محلة ${mahalla}، زقاق ${zuqaq}`,
};
}
catch (e) {
this.logger.warn(`Iraqi address lookup failed: ${e.message}`);
return null;
}
}
async reverseGeocode(lat, lng) {
try {
const repo = this.getRepositoryForCoords(lat, lng);
const tableName = this.getTableNameForRepo(repo);
const iraqiAddressPromise = this.identifyRegion(lat, lng) === 'iraq'
? this.findIraqiAddress(lat, lng)
: Promise.resolve(null);
const queryPromises = [];
queryPromises.push(repo.query(`
SELECT
@@ -357,7 +404,20 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
WHERE location IS NOT NULL AND (names->>'primary' IS NOT NULL OR names->>'common' IS NOT NULL)
ORDER BY location <-> ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326) ASC LIMIT 5
`, [lng, lat]));
const results = await Promise.allSettled(queryPromises);
queryPromises.push(repo.query(`
SELECT
id::text, COALESCE(name, 'Street') as name, COALESCE(name, 'طريق معتمد') as name_ar, 'street' as category,
'' as neighbourhood, '' as district, '' as governorate,
ST_Y(ST_Centroid(geometry::geometry))::text as latitude, ST_X(ST_Centroid(geometry::geometry))::text as longitude, '' as address, 'approved_road' as source,
ST_DistanceSphere(geometry::geometry, ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326)) as distance
FROM approved_roads
WHERE geometry IS NOT NULL AND name IS NOT NULL AND trim(name) != ''
ORDER BY geometry::geometry <-> ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326) ASC LIMIT 5
`, [lng, lat]).catch(() => []));
const [results, iraqiAddress] = await Promise.all([
Promise.allSettled(queryPromises),
iraqiAddressPromise,
]);
let allResults = [];
results.forEach(res => {
if (res.status === 'fulfilled' && res.value)
@@ -368,7 +428,17 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
.slice(0, 5)
.map(r => {
const distance = Number(r.distance);
const fullAddressParts = [r.name_ar || r.name, r.address, r.neighbourhood, r.district, r.governorate].filter(Boolean);
const ZUQAQ_NAME_RE = /^[0-9]{2,4}-[0-9]{1,3}$/;
const rawName = r.name_ar || r.name;
const nameIsZuqaq = typeof rawName === 'string' && ZUQAQ_NAME_RE.test(rawName);
const fullAddressParts = [
nameIsZuqaq ? null : rawName,
r.address,
iraqiAddress?.text,
r.neighbourhood,
r.district,
r.governorate,
].filter(Boolean);
let humanReadable = r.name_ar || r.name;
if (distance <= 20) {
humanReadable = r.name_ar || r.name;
@@ -382,8 +452,14 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
const districtPart = r.neighbourhood || r.district || '';
humanReadable = [streetPart, districtPart].filter(Boolean).join('، ');
}
if (iraqiAddress) {
const landmark = distance <= 70 && !nameIsZuqaq ? rawName : null;
humanReadable = [iraqiAddress.text, landmark].filter(Boolean).join(' — ');
}
return {
...r,
mahalla: iraqiAddress?.mahalla,
zuqaq: iraqiAddress?.zuqaq,
latitude: parseFloat(r.latitude),
longitude: parseFloat(r.longitude),
human_readable_address: humanReadable,
@@ -396,9 +472,20 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
return [];
}
}
extractCoords(data) {
const lat = Number(data?.lat ?? data?.latitude);
const lng = Number(data?.lng ?? data?.longitude);
if (!Number.isFinite(lat) || !Number.isFinite(lng)) {
throw new common_1.HttpException('Invalid coordinates: provide numeric lat/lng (or latitude/longitude)', common_1.HttpStatus.BAD_REQUEST);
}
if (lat < -90 || lat > 90 || lng < -180 || lng > 180) {
throw new common_1.HttpException(`Coordinates out of range: lat=${lat}, lng=${lng}`, common_1.HttpStatus.BAD_REQUEST);
}
return { lat, lng };
}
async addPlace(data) {
try {
const lat = Number(data.latitude), lng = Number(data.longitude);
const { lat, lng } = this.extractCoords(data);
const repo = this.getRepositoryForCoords(lat, lng), tableName = this.getTableNameForRepo(repo);
const newPlace = repo.create({ ...data, latitude: lat, longitude: lng, created_at: new Date(), location: { type: 'Point', coordinates: [lng, lat] } });
const savedPlace = await repo.save(newPlace);
@@ -406,12 +493,14 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
return { ...savedPlace, latitude: lat, longitude: lng, location: `POINT(${lng} ${lat})` };
}
catch (error) {
if (error instanceof common_1.HttpException)
throw error;
throw new common_1.HttpException(error.message, common_1.HttpStatus.INTERNAL_SERVER_ERROR);
}
}
async upsertPlace(data) {
try {
const lat = Number(data.latitude), lng = Number(data.longitude);
const { lat, lng } = this.extractCoords(data);
const repo = this.getRepositoryForCoords(lat, lng), tableName = this.getTableNameForRepo(repo);
const existing = await repo.query(`SELECT id, name FROM ${tableName} WHERE ST_DistanceSphere(location, ST_SetSRID(ST_MakePoint($1::float, $2::float), 4326)) < 15 LIMIT 1`, [lng, lat]);
if (existing && existing.length > 0) {
@@ -427,20 +516,39 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
}
async upsertBatch(places) {
const results = [];
for (const place of places) {
const errors = [];
for (const [index, place] of places.entries()) {
try {
const r = await this.addPlace(place);
results.push({ id: r.id, action: 'created' });
}
catch (e) { }
catch (e) {
if (errors.length < 50) {
errors.push({
index,
name: place?.name,
reason: e instanceof common_1.HttpException ? e.message : (e?.message ?? 'unknown'),
});
}
}
}
return { total: places.length, processed: results.length, created: results.length, updated: 0 };
const failed = places.length - results.length;
return {
total: places.length,
processed: results.length,
created: results.length,
updated: 0,
failed,
errors,
errorsTruncated: failed > errors.length,
};
}
async getRecentPlaces(limit = 50) {
const s = await this.placesSyriaRepository.find({ order: { created_at: 'DESC' }, take: limit });
const j = await this.placesJordanRepository.find({ order: { created_at: 'DESC' }, take: limit });
const e = await this.placesEgyptRepository.find({ order: { created_at: 'DESC' }, take: limit });
return [...s, ...j, ...e].sort((a, b) => b.created_at.getTime() - a.created_at.getTime()).slice(0, limit);
const i = await this.placesIraqRepository.find({ order: { created_at: 'DESC' }, take: limit });
return [...s, ...j, ...e, ...i].sort((a, b) => b.created_at.getTime() - a.created_at.getTime()).slice(0, limit);
}
async getAllPlacesGeoJSON() {
try {
@@ -448,6 +556,7 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_syria
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_jordan
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_egypt
UNION ALL SELECT id::text, name_ar as name, category, latitude, longitude, address, 'user' as region FROM places_iraq
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Building') as name, 'building' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_building WHERE names->>'primary' IS NOT NULL LIMIT 500
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Street') as name, 'street' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_segment WHERE names->>'primary' IS NOT NULL LIMIT 500
UNION ALL SELECT id::text, COALESCE(names->>'primary', names->>'common', 'Place') as name, 'place' as category, ST_Y(ST_Centroid(location)) as latitude, ST_X(ST_Centroid(location)) as longitude, '' as address, 'overture' as region FROM overture_place WHERE names->>'primary' IS NOT NULL LIMIT 500
@@ -478,6 +587,7 @@ let GeocodingService = GeocodingService_1 = class GeocodingService {
case 'syria': return this.placesSyriaRepository;
case 'jordan': return this.placesJordanRepository;
case 'egypt': return this.placesEgyptRepository;
case 'iraq': return this.placesIraqRepository;
default: throw new common_1.HttpException('Invalid country: ' + country, common_1.HttpStatus.BAD_REQUEST);
}
}
@@ -488,9 +598,10 @@ exports.GeocodingService = GeocodingService = GeocodingService_1 = __decorate([
__param(0, (0, typeorm_1.InjectRepository)(place_syria_entity_1.PlaceSyria)),
__param(1, (0, typeorm_1.InjectRepository)(place_jordan_entity_1.PlaceJordan)),
__param(2, (0, typeorm_1.InjectRepository)(place_egypt_entity_1.PlaceEgypt)),
__param(3, (0, typeorm_1.InjectRepository)(osm_area_entity_1.OsmArea)),
__param(4, (0, typeorm_1.InjectRepository)(osm_point_with_area_entity_1.OsmPointWithArea)),
__param(5, (0, common_1.Inject)(cache_manager_1.CACHE_MANAGER)),
__metadata("design:paramtypes", [typeof (_a = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _a : Object, typeof (_b = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _b : Object, typeof (_c = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _c : Object, typeof (_d = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _d : Object, typeof (_e = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _e : Object, Object])
__param(3, (0, typeorm_1.InjectRepository)(place_iraq_entity_1.PlaceIraq)),
__param(4, (0, typeorm_1.InjectRepository)(osm_area_entity_1.OsmArea)),
__param(5, (0, typeorm_1.InjectRepository)(osm_point_with_area_entity_1.OsmPointWithArea)),
__param(6, (0, common_1.Inject)(cache_manager_1.CACHE_MANAGER)),
__metadata("design:paramtypes", [typeof (_a = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _a : Object, typeof (_b = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _b : Object, typeof (_c = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _c : Object, typeof (_d = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _d : Object, typeof (_e = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _e : Object, typeof (_f = typeof typeorm_2.Repository !== "undefined" && typeorm_2.Repository) === "function" ? _f : Object, Object])
], GeocodingService);
//# sourceMappingURL=geocoding.service.js.map