180 lines
6.5 KiB
TypeScript
180 lines
6.5 KiB
TypeScript
import axios from "axios";
|
|
import { inject, injectable } from "inversify";
|
|
import { startSession } from "mongoose";
|
|
|
|
import { AddressRepo, CityRepo, ProvinceRepo } from "./address.repository";
|
|
import { SaveSellerAddressDTO, SaveUserAddressDTO } from "./DTO/userAddress.dto";
|
|
import { INominatimResponse } from "./interfaces/ILocation";
|
|
import { AddressMessage, CommonMessage, ShopMessage, UserMessage } from "../../common/enums/message.enum";
|
|
import { BadRequestError, InternalError } from "../../core/app/app.errors";
|
|
import { IOCTYPES } from "../../IOC/ioc.types";
|
|
import { UserRepository } from "../user/user.repository";
|
|
import { ICity } from "./models/city.model";
|
|
import { IProvince } from "./models/province.model";
|
|
import { OwnerRef } from "../shop/models/Abstraction/IShop";
|
|
import { ShopRepo } from "../shop/shop.repository";
|
|
|
|
@injectable()
|
|
class AddressService {
|
|
private NOMINATIM_URL = "https://nominatim.openstreetmap.org/reverse";
|
|
// https://nominatim.openstreetmap.org/reverse?lat=34.198770&lon=49.663600&format=json&accept-language=fa
|
|
// private MAP_IR_URL = "https://map.ir/reverse";
|
|
// private MAP_IR_API_KEY = process.env.MAP_API_KEY;
|
|
// private NEHSNAN_URL = "https://api.neshan.org/v5/reverse";
|
|
// private NESHAN_API_KEY = process.env.NESHAN_API_KEY;
|
|
@inject(IOCTYPES.UserRepository) private userRepository: UserRepository;
|
|
@inject(IOCTYPES.AddressRepo) addressRepo: AddressRepo;
|
|
@inject(IOCTYPES.CityRepo) private cityRepo: CityRepo;
|
|
@inject(IOCTYPES.ProvinceRepo) private provinceRepo: ProvinceRepo;
|
|
@inject(IOCTYPES.ShopRepo) shopRepo: ShopRepo;
|
|
|
|
//#############################################################
|
|
//#############################################################
|
|
async getLocationWithGeo(geo: { lat: string; lon: string }) {
|
|
const location = await this.getAddressFromCoordinates(geo.lat, geo.lon);
|
|
let province: IProvince | null;
|
|
let city: ICity | null;
|
|
|
|
//TODO:fix this
|
|
const provinceName =
|
|
location?.address?.province?.replace(/^استان\s*/, "").trim() ?? location?.address?.state?.replace(/^استان\s*/, "").trim();
|
|
const cityName = location.address?.town ? location.address?.town?.replace(/^شهر\s*/, "").trim() : location?.address?.city;
|
|
|
|
if (!provinceName || !cityName) throw new BadRequestError(AddressMessage.InvalidLocation);
|
|
|
|
province = await this.provinceRepo.model.findOne({ name: provinceName });
|
|
if (!province) province = await this.provinceRepo.model.create({ name: provinceName });
|
|
|
|
city = await this.cityRepo.model.findOne({ name: cityName });
|
|
if (!city) city = await this.cityRepo.model.create({ name: cityName, province: province._id });
|
|
|
|
const displayNameArray = location.display_name.split(",").map((item) => item.trim());
|
|
|
|
const filteredDisplayNameArray = location.address.postcode
|
|
? displayNameArray.filter((item) => !item.includes(location.address.postcode as string))
|
|
: displayNameArray;
|
|
|
|
const address = filteredDisplayNameArray.reverse().join(",").trim();
|
|
|
|
return {
|
|
province: provinceName,
|
|
provinceId: province._id,
|
|
city: city.name,
|
|
cityId: city._id,
|
|
address,
|
|
region: location.address.suburb,
|
|
};
|
|
}
|
|
//#############################################################
|
|
//#############################################################
|
|
|
|
async getUserAddress(addressId: string) {
|
|
const userAddress = await this.addressRepo.findById(addressId);
|
|
if (!userAddress) throw new BadRequestError([AddressMessage.UserShouldHaveAddress, AddressMessage.NotFound]);
|
|
return userAddress;
|
|
}
|
|
|
|
//#############################################################
|
|
//#############################################################
|
|
|
|
async setUserAddress(userId: string, addressDto: SaveUserAddressDTO) {
|
|
const session = await startSession();
|
|
session.startTransaction();
|
|
try {
|
|
const address = await this.addressRepo.model.create(
|
|
[
|
|
{
|
|
address: addressDto.address,
|
|
city: addressDto.cityId,
|
|
province: addressDto.provinceId,
|
|
plaque: addressDto.plaque,
|
|
postalCode: addressDto.postalCode,
|
|
lat: addressDto.lat,
|
|
lon: addressDto.lon,
|
|
},
|
|
],
|
|
{ session },
|
|
);
|
|
|
|
const updatedUser = await this.userRepository.model.findByIdAndUpdate(userId, { address: address[0]._id }, { session });
|
|
if (!updatedUser) throw new BadRequestError(CommonMessage.NotValid);
|
|
|
|
await session.commitTransaction();
|
|
|
|
return {
|
|
message: UserMessage.UserUpdated,
|
|
address: address[0],
|
|
};
|
|
//
|
|
} catch (error) {
|
|
await session.abortTransaction();
|
|
throw error;
|
|
//
|
|
} finally {
|
|
await session.endSession();
|
|
}
|
|
}
|
|
|
|
//
|
|
async setSellerShopAddress(sellerId: string, addressDto: SaveSellerAddressDTO) {
|
|
const session = await startSession();
|
|
session.startTransaction();
|
|
try {
|
|
const shop = await this.shopRepo.model.findOne({ owner: sellerId, ownerRef: OwnerRef.SELLER }).session(session);
|
|
if (!shop) throw new BadRequestError(ShopMessage.ShopNotFound);
|
|
const address = await this.addressRepo.model.create(
|
|
[
|
|
{
|
|
address: addressDto.address,
|
|
city: addressDto.cityId,
|
|
province: addressDto.provinceId,
|
|
plaque: addressDto.plaque,
|
|
// postalCode: addressDto.postalCode,
|
|
lat: addressDto.lat,
|
|
lon: addressDto.lon,
|
|
},
|
|
],
|
|
{ session },
|
|
);
|
|
shop.address = address[0]._id;
|
|
await shop.save({ session });
|
|
|
|
await session.commitTransaction();
|
|
|
|
return {
|
|
message: AddressMessage.Updated,
|
|
address: address[0],
|
|
};
|
|
} catch (error) {
|
|
await session.abortTransaction();
|
|
throw error;
|
|
} finally {
|
|
await session.endSession();
|
|
}
|
|
}
|
|
|
|
//helpers
|
|
//##########################
|
|
private async getAddressFromCoordinates(lat: string, lon: string) {
|
|
try {
|
|
const response = await axios.get(this.NOMINATIM_URL, {
|
|
params: { lat, lon, format: "json", "accept-language": "fa" },
|
|
headers: {
|
|
"User-Agent": "Mozilla/5.0",
|
|
},
|
|
});
|
|
|
|
const location = response.data as INominatimResponse;
|
|
|
|
if (!location) throw new BadRequestError("Address not found for the given coordinates.");
|
|
|
|
return location;
|
|
} catch (error) {
|
|
console.log(error);
|
|
throw new InternalError([`Failed to get address from coordinates: ${error}`]);
|
|
}
|
|
}
|
|
}
|
|
|
|
export { AddressService };
|