308 lines
6.8 KiB
JavaScript
308 lines
6.8 KiB
JavaScript
import express from 'express';
|
|
import bodyParser from 'body-parser';
|
|
import distanceInWordsToNow from 'date-fns/distance_in_words_to_now';
|
|
import parseDate from 'date-fns/format';
|
|
import moment from 'moment';
|
|
|
|
import {STATUS_NORMAL, STATUS_RESERVED, STATUS_SOLD} from '../common/enums';
|
|
|
|
var hr = require ('date-fns/locale/hr');
|
|
|
|
var MongoClient = require ('mongodb').MongoClient;
|
|
var ObjectID = require ('mongodb').ObjectID;
|
|
|
|
var url = 'mongodb://localhost:27017/kivi';
|
|
|
|
require ('babel-polyfill');
|
|
|
|
const router = express.Router ({mergeParams: true});
|
|
|
|
const PORT = process.env.PORT || 3001;
|
|
const AGENTURA_KEY = process.env.AGENTURA_KEY || '1somethingverysecret';
|
|
|
|
let db;
|
|
|
|
router.post ('/contact/:listingId', async (req, res, next) => {
|
|
try {
|
|
const listingId = req.params.listingId;
|
|
const body = req.body;
|
|
|
|
const contactRequests = db.collection ('contact_requests');
|
|
|
|
if (!body.email) {
|
|
res.status (422);
|
|
res.end ('Email is required');
|
|
return;
|
|
}
|
|
|
|
if (!body.name) {
|
|
res.status (422);
|
|
res.end ('Name is required');
|
|
return;
|
|
}
|
|
|
|
const result = await contactRequests.insertOne ({
|
|
name: body.name,
|
|
email: body.email,
|
|
listingId,
|
|
message: body.message,
|
|
phone: body.phone,
|
|
alert: body.alert,
|
|
});
|
|
|
|
res.status (200);
|
|
res.end ();
|
|
} catch (e) {
|
|
console.log ('error:', e);
|
|
next (e);
|
|
}
|
|
});
|
|
|
|
router.get ('/search/listings/:id', async (req, res, next) => {
|
|
try {
|
|
const id = req.params.id;
|
|
|
|
const listings = db.collection ('listings');
|
|
const listing = await listings.findOne ({_id: new ObjectID (id)});
|
|
if (listing) {
|
|
res.json (listing);
|
|
} else {
|
|
res.status (404);
|
|
}
|
|
|
|
res.end ();
|
|
} catch (e) {
|
|
console.log ('error:', e);
|
|
next (e);
|
|
}
|
|
});
|
|
|
|
router.get ('/search/listings', async (req, res, next) => {
|
|
try {
|
|
console.log ('Search listings');
|
|
const bounds = req.query.bounds || '';
|
|
const minPrice = req.query.minPrice;
|
|
const maxPrice = req.query.maxPrice;
|
|
const minSize = req.query.minSize;
|
|
const maxSize = req.query.maxSize;
|
|
const rooms = req.query.rooms;
|
|
const adType = req.query.adType;
|
|
const category = req.query.category;
|
|
const sort = req.query.sort;
|
|
const page = req.query.page || 0;
|
|
const pins = req.query.pins || false;
|
|
|
|
const properties = db.collection ('listings');
|
|
let query = {};
|
|
|
|
//Get only ads with location
|
|
query = Object.assign (query, {
|
|
has_map: true,
|
|
});
|
|
|
|
//AND
|
|
|
|
//Do not show sold or reserved properity
|
|
query = Object.assign (query, {
|
|
status: STATUS_NORMAL,
|
|
});
|
|
|
|
//AND
|
|
|
|
//Show ads that fall inside visible map
|
|
if (bounds) {
|
|
const [lat1, lng1, lat2, lng2] = bounds.split (',').map (parseFloat);
|
|
const box = [[lat1, lng1], [lat2, lng2]];
|
|
|
|
query = Object.assign (query, {
|
|
loc: {
|
|
$geoWithin: {
|
|
$box: box,
|
|
},
|
|
},
|
|
});
|
|
}
|
|
|
|
//AND
|
|
|
|
//Show only selected type of ads (selling or renting)
|
|
if (adType) {
|
|
query = Object.assign (query, {
|
|
adType: parseInt (adType),
|
|
});
|
|
}
|
|
|
|
//AND
|
|
|
|
//Match price
|
|
if (minPrice || maxPrice) {
|
|
const price = {};
|
|
if (minPrice) {
|
|
price['$gte'] = parseFloat (minPrice);
|
|
}
|
|
|
|
if (maxPrice) {
|
|
price['$lte'] = parseFloat (maxPrice);
|
|
}
|
|
|
|
query = Object.assign (query, {
|
|
price,
|
|
});
|
|
}
|
|
|
|
//AND
|
|
|
|
//Match number of rooms
|
|
if (rooms) {
|
|
const roomCount = [];
|
|
let fourPlus = false;
|
|
|
|
const allRooms = rooms.split (',');
|
|
allRooms.map (val => {
|
|
if (parseInt (val) !== 4) {
|
|
roomCount.push (parseInt (val));
|
|
} else {
|
|
fourPlus = true;
|
|
}
|
|
});
|
|
|
|
if (fourPlus) {
|
|
query = Object.assign (query, {
|
|
rooms: {$gte: 4},
|
|
});
|
|
} else {
|
|
query = Object.assign (query, {
|
|
rooms: {$in: roomCount},
|
|
});
|
|
}
|
|
}
|
|
|
|
//AND
|
|
|
|
//Match size
|
|
if (minSize || maxSize) {
|
|
const size = {};
|
|
if (minSize) {
|
|
size['$gte'] = parseFloat (minSize);
|
|
}
|
|
|
|
if (maxSize) {
|
|
size['$lte'] = parseFloat (maxSize);
|
|
}
|
|
|
|
query = Object.assign (query, {
|
|
size,
|
|
});
|
|
}
|
|
|
|
//AND
|
|
|
|
//Match category
|
|
if (category) {
|
|
const categoryCount = [];
|
|
|
|
const allCategories = category.split (',').map (val => {
|
|
categoryCount.push (parseInt (val));
|
|
});
|
|
|
|
query = Object.assign (query, {
|
|
category: {$in: categoryCount},
|
|
});
|
|
}
|
|
|
|
console.log ('QUERY: ', query);
|
|
const cnt = await properties.find (query).count ();
|
|
|
|
res.header ('X-Total-Count', cnt);
|
|
|
|
const getSort = () => {
|
|
if (sort === 'price-min') {
|
|
return [['price', 'asc']];
|
|
} else if (sort === 'price-max') {
|
|
return [['price', 'desc']];
|
|
} else if (sort === 'newest') {
|
|
return [['_id', 'desc']];
|
|
} else if (sort === 'relevance') {
|
|
// TODO: figure out what the relevance is
|
|
return [];
|
|
}
|
|
};
|
|
|
|
let all = properties.find (query, {
|
|
//"sort": [['field1','asc'], ['field2','desc']]
|
|
sort: getSort (),
|
|
});
|
|
|
|
const isPins = pins === 'true';
|
|
|
|
if (!isPins) {
|
|
all = await all.skip (20 * page).limit (20).toArray ();
|
|
} else {
|
|
all = await all.toArray ();
|
|
}
|
|
|
|
if (all.length > 0) {
|
|
res.header ('X-Last-Record-Id', [...all].pop ()._id);
|
|
}
|
|
|
|
if (isPins) {
|
|
res.json (
|
|
all.map (val => {
|
|
return {
|
|
_id: val._id,
|
|
loc: val.loc,
|
|
};
|
|
})
|
|
);
|
|
} else {
|
|
res.json (
|
|
all.map (({_id, address, images, price, rooms, size, time}) => ({
|
|
_id,
|
|
address,
|
|
images: [images[0]],
|
|
price,
|
|
rooms,
|
|
size,
|
|
time: distanceInWordsToNow (moment (time, 'DD.MM.YYYY'), {
|
|
locale: hr,
|
|
}),
|
|
realTime: time,
|
|
}))
|
|
);
|
|
}
|
|
|
|
res.end ();
|
|
} catch (e) {
|
|
console.log ('error:', e);
|
|
next (e);
|
|
}
|
|
});
|
|
|
|
const app = express ();
|
|
app.use (bodyParser.json ());
|
|
|
|
app.use (function (req, res, next) {
|
|
res.header ('Access-Control-Allow-Origin', '*');
|
|
res.header (
|
|
'Access-Control-Allow-Headers',
|
|
'Origin, X-Requested-With, Content-Type, Accept, X-Last-Record-Id, X-Total-Count'
|
|
);
|
|
res.header (
|
|
'Access-Control-Expose-Headers',
|
|
'X-Last-Record-Id, X-Total-Count'
|
|
);
|
|
res.header ('Access-Control-Allow-Methods', 'GET, POST, OPTIONS');
|
|
res.header ('Access-Control-Allow-Credentials', 'true');
|
|
next ();
|
|
});
|
|
|
|
app.use ('/api', router);
|
|
|
|
MongoClient.connect (url).then (database => {
|
|
db = database;
|
|
db.collection ('listings').createIndex ({loc: '2d'});
|
|
app.listen (PORT, () =>
|
|
console.log ('Express server running at localhost: ' + PORT)
|
|
);
|
|
});
|