Compare commits

...

19 Commits

Author SHA1 Message Date
MirnaM
59723410b6 Update README 2019-04-30 09:47:50 +02:00
MirnaM
51ed3551c7 Use default postgres port 2019-04-30 09:19:52 +02:00
MirnaM
58177a8cce Add dockerfile 2019-04-30 09:06:46 +02:00
Senad Uka
17492eb52c City is now saved 2019-04-27 07:08:36 +02:00
Senad Uka
298c901759 Added migrations and saving real estate type correctly 2019-04-20 05:26:14 +02:00
Senad Uka
c534c1ee34 Added a new model - does not work yet 2019-04-16 06:27:11 +02:00
Senad Uka
2380c85122 Now posting the type of real estate 2019-04-15 06:56:03 +02:00
Senad Uka
0f7e9f9285 Type of real estate 2019-04-14 06:01:37 +02:00
Senad Uka
dee4df9bd8 Added compression 2019-04-13 10:38:25 +02:00
Senad Uka
4248e6304a Poruka 2019-04-13 10:27:35 +02:00
Senad Uka
9fd9fe8b82 Fixed css 2019-04-12 06:47:51 +02:00
Senad Uka
467d551857 Logo set up 2019-04-12 05:40:52 +02:00
Senad Uka
28f95b9c05 Logo and button - logo unfinished 2019-04-11 05:27:55 +02:00
Senad Uka
9aba66c273 Logo and button - logo unfinished 2019-04-11 05:27:34 +02:00
Senad Uka
d03e85a0dc Switched to materializecss + jquery 2019-04-10 05:17:39 +02:00
Senad Uka
9d49e72bb4 Fix mixed content 2019-04-10 05:01:19 +02:00
Senad Uka
efe2dd66a3 Heroku fix fix 2019-04-09 06:11:41 +02:00
Senad Uka
5f2fee504a Heroku postbuild fix 2019-04-09 06:08:22 +02:00
Senad Uka
add905c793 Saved what was unsaved 2019-04-09 06:04:17 +02:00
23 changed files with 1298 additions and 112 deletions

48
Dockerfile Normal file
View File

@@ -0,0 +1,48 @@
#
# example Dockerfile for https://docs.docker.com/engine/examples/postgresql_service/
#
FROM ubuntu:16.04
# Add the PostgreSQL PGP key to verify their Debian packages.
# It should be the same key as https://www.postgresql.org/media/keys/ACCC4CF8.asc
RUN apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 --recv-keys B97B0AFCAA1A47F044F244A07FCC7D46ACCC4CF8
# Add PostgreSQL's repository. It contains the most recent stable release
# of PostgreSQL, ``9.3``.
RUN echo "deb http://apt.postgresql.org/pub/repos/apt/ precise-pgdg main" > /etc/apt/sources.list.d/pgdg.list
# Install ``python-software-properties``, ``software-properties-common`` and PostgreSQL 9.3
# There are some warnings (in red) that show up during the build. You can hide
# them by prefixing each apt-get statement with DEBIAN_FRONTEND=noninteractive
RUN apt-get update && apt-get install -y python-software-properties software-properties-common postgresql-9.3 postgresql-client-9.3 postgresql-contrib-9.3
# Note: The official Debian and Ubuntu images automatically ``apt-get clean``
# after each ``apt-get``
# Run the rest of the commands as the ``postgres`` user created by the ``postgres-9.3`` package when it was ``apt-get installed``
USER postgres
# Create a PostgreSQL role named ``docker`` with ``docker`` as the password and
# then create a database `docker` owned by the ``docker`` role.
# Note: here we use ``&&\`` to run commands one after the other - the ``\``
# allows the RUN command to span multiple lines.
RUN /etc/init.d/postgresql start &&\
psql --command "CREATE USER docker WITH SUPERUSER PASSWORD 'docker';" &&\
createdb -O docker marketalerts
# Adjust PostgreSQL configuration so that remote connections to the
# database are possible.
RUN echo "host all all 0.0.0.0/0 md5" >> /etc/postgresql/9.3/main/pg_hba.conf
# And add ``listen_addresses`` to ``/etc/postgresql/9.3/main/postgresql.conf``
RUN echo "listen_addresses='*'" >> /etc/postgresql/9.3/main/postgresql.conf
# Expose the PostgreSQL port
EXPOSE 5432
# Add VOLUMEs to allow backup of config, logs and databases
VOLUME ["/etc/postgresql", "/var/log/postgresql", "/var/lib/postgresql"]
# Set the default command to run when starting the container
CMD ["/usr/lib/postgresql/9.3/bin/postgres", "-D", "/var/lib/postgresql/9.3/main", "-c", "config_file=/etc/postgresql/9.3/main/postgresql.conf"]

View File

@@ -3,10 +3,8 @@
The purpose of this project is to build a web application that enables subscribing to notifications when new products are published on various ad based marketplaces. The MVP will be only based on OLX.ba
ENV:
JAWSDB_URL='mysql://sq4dlf9mz49avli0:gqy5vzmzyhp0837x@tuy8t6uuvh43khkk.cbetxkdyhwsb.us-east-1.rds.amazonaws.com:3306/rxhzg1550441ftqk'
Run postgres image with:
docker run --name pg_test -d -p 5432:5432 marketalerts
Run with:
$ npm start

15
app/config/config.json Normal file
View File

@@ -0,0 +1,15 @@
{
"development": {
"username": "docker",
"password": "docker",
"database": "marketalerts",
"port": "5432",
"dialect": "postgres"
},
"test": {
"use_env_variable": "JAWSDB_URL"
},
"production": {
"use_env_variable": "JAWSDB_URL"
}
}

41
app/controllers/grad.js Normal file
View File

@@ -0,0 +1,41 @@
const db = require('../models/index');
const { currentRERequest } = require('../helpers/url');
const gradovi = [
{"ime":" Sarajevo","id":"sarajevo"},
{"ime":" Unsko-sanski","id":"unskosanski"},
{"ime":" Posavski","id":"posavski"},
{"ime":" Tuzlanski","id":"tuzlanski"},
{"ime":" Zeničko-dobojski","id":"zenickodobojski"},
{"ime":" Bosansko-podrinjski","id":"bosanskopodrinjski"},
{"ime":" Srednjobosanski","id":"srednjobosanski"},
{"ime":" Hercegovačko-neretvanski","id":"hercegovackoneretvanski"},
{"ime":" Zapadno-hercegovački","id":"zapadnohercegovacki"},
{"ime":" Livanjski","id":"livanjski"},
{"ime":" Banjalučka","id":"banjalučka"},
{"ime":" Dobojsko-Bijeljinska","id":"dobojskobijeljinska"},
{"ime":" Sarajevsko-Zvornička","id":"sarajevskozvornicka"},
{"ime":" Trebinjsko-Fočanska","id":"trebinjskofocanska"},
{"ime":"Distrikt Brčko","id":"distriktbrcko"},
];
const getGrad = (req,res) => {
const nextStep = req.query.nextStep || '/';
res.render('grad', {
nextStep,
gradovi
});
}
const postGrad = async (req, res) => {
let request = await currentRERequest(req);
request.city = req.body.grad;
await request.save();
res.send("Result is " + JSON.stringify(request));
}
module.exports = {
getGrad,
postGrad
};

View File

@@ -1,7 +1,35 @@
const db = require('../models/index');
const vrsteNekretnina = [
{ ime: "Kuća", id: "kuca" },
{ ime: "Stan", id: "stan" },
{ ime: "Vikendica", id: "vikendica" }
];
const getVrstaNekretnine = (req,res) => {
res.render('vrsta_nekretnine', { nextStep: '/' } );
const nextStep = req.query.nextStep;
res.render('vrsta_nekretnine', {
nextStep,
vrste: vrsteNekretnina
});
}
const postVrstaNekretnine = (req, res) => {
let nextStep = req.query.nextStep;
db.RealEstateRequest.create({
realEstateType: req.body.vrsta
}).then( (result) => {
nextStep = nextStep || `/grad/${result.uniqueId}`;
res.redirect(nextStep);
}).catch( (e) => {
res.send(e);
});
}
module.exports = {
getVrstaNekretnine
getVrstaNekretnine,
postVrstaNekretnine
};

View File

@@ -1,4 +0,0 @@
const Sequelize = require("sequelize");
const sequelize = new Sequelize(process.env.JAWSDB_URL);
module.exports = sequelize;

14
app/helpers/url.js Normal file
View File

@@ -0,0 +1,14 @@
const db = require('../models/index');
const currentRERequest = async (req) => {
const uniqueId = req.params['request_id'];
if(!uniqueId) return null;
const request = await db.RealEstateRequest.findOne({ where: {uniqueId} });
console.log("Request ", request);
return request;
}
module.exports = {
currentRERequest
}

View File

@@ -0,0 +1,34 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('MarketAlerts', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
olxUrl: {
type: Sequelize.STRING
},
lastDate: {
type: Sequelize.STRING
},
email: {
type: Sequelize.STRING,
allowNull: false
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('MarketAlerts');
}
};

View File

@@ -0,0 +1,34 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.createTable('RealEstateRequests', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
uniqueId: {
type: Sequelize.UUID
},
realEstateType: {
type: Sequelize.ENUM,
values: ['kuca','stan','vikendica','plac','poslovni_prostor','apartman','garaza']
},
email: {
type: Sequelize.STRING
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: (queryInterface, Sequelize) => {
return queryInterface.dropTable('RealEstateRequests');
}
};

View File

@@ -0,0 +1,18 @@
'use strict';
module.exports = {
up: (queryInterface, Sequelize) => {
return queryInterface.addColumn(
'RealEstateRequests',
'city',
Sequelize.STRING
);
},
down: (queryInterface, Sequelize) => {
return queryInterface.removeColumn(
'RealEstateRequests',
'city'
);
}
};

View File

@@ -1,17 +0,0 @@
const Sequelize = require("sequelize");
const sequelize = require("../db/db");
const MarketAlert = sequelize.define("market_alert", {
id: {
type: Sequelize.INTEGER,
autoIncrement: true,
primaryKey: true
},
olx_url: Sequelize.STRING,
last_date: Sequelize.STRING,
email: {
type: Sequelize.STRING,
allowNull: false
}
});
module.exports = MarketAlert;

37
app/models/index.js Normal file
View File

@@ -0,0 +1,37 @@
'use strict';
const fs = require('fs');
const path = require('path');
const Sequelize = require('sequelize');
const basename = path.basename(__filename);
const env = process.env.NODE_ENV || 'development';
const config = require(__dirname + '/../config/config.json')[env];
const db = {};
let sequelize;
if (config.use_env_variable) {
sequelize = new Sequelize(process.env[config.use_env_variable], config);
} else {
sequelize = new Sequelize(config.database, config.username, config.password, config);
}
fs
.readdirSync(__dirname)
.filter(file => {
return (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js');
})
.forEach(file => {
const model = sequelize['import'](path.join(__dirname, file));
db[model.name] = model;
});
Object.keys(db).forEach(modelName => {
if (db[modelName].associate) {
db[modelName].associate(db);
}
});
db.sequelize = sequelize;
db.Sequelize = Sequelize;
module.exports = db;

15
app/models/marketalert.js Normal file
View File

@@ -0,0 +1,15 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const MarketAlert = sequelize.define('MarketAlert', {
olxUrl: DataTypes.STRING,
lastDate: DataTypes.STRING,
email: {
type: DataTypes.STRING,
allowNul: false
}
}, {});
MarketAlert.associate = function(models) {
// associations can be defined here
};
return MarketAlert;
};

View File

@@ -0,0 +1,20 @@
'use strict';
module.exports = (sequelize, DataTypes) => {
const RealEstateRequest = sequelize.define('RealEstateRequest', {
uniqueId: {
type: DataTypes.UUID,
defaultValue: DataTypes.UUIDV4,
allowNull: false
},
realEstateType: {
type: DataTypes.ENUM,
values: ['kuca','stan','vikendica','plac','poslovni_prostor','apartman','garaza']
},
email: DataTypes.STRING,
city: DataTypes.STRING,
}, {});
RealEstateRequest.associate = function(models) {
// associations can be defined here
};
return RealEstateRequest;
};

BIN
app/public/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

11
app/public/main.css Normal file
View File

@@ -0,0 +1,11 @@
.dobrodosli-center-button {
width: 100%;
}
.dobrodosli-big-logo {
font-size: 200pt;
background-image: url(./images/logo.png);
background-size: contain;
background-repeat: no-repeat;
color: rgba(0, 0, 0, 0);
}

View File

@@ -1,3 +1,17 @@
<a href="<%= nextStep %>"><button>Kreni</button></a>
<!-- -->
<div class="row center-align">
<span class="dobrodosli-big-logo">🤙</span>
</div>
<div class="row center-align">
<div>Sve nekretnine dostupne u oglasima.</div>
<div> Na vaš email. </div>
<div> BESPLATNO </div>
</div>
<div class="row">
<div class="col s6 push-s3">
<a href="<%= nextStep %>" class="dobrodosli-center-button waves-effect waves-light btn">
Javi mi
</a>
</div>
</div>

25
app/views/grad.ejs Normal file
View File

@@ -0,0 +1,25 @@
<div class="row center-align">
<h2>U kojoj regiji tražite nekretninu?</h2>
</div>
<form method="POST" id="form-grad">
<div class="row center-align">
<ul class="collection with-header">
<% for(const grad of gradovi) { %>
<li class="collection-item" > <div id="<%= grad.id %>" ><%= grad.ime %><a href="#!" class="secondary-content"><i class="material-icons">send</i></a></div></li>
<% } %>
</ul>
<input type="hidden" name="grad" id="grad" />
</div>
</form>
<script>
$(document).ready( () => {
$(".collection-item").click( (e) => {
const clickedId = $(e.target).attr("id");
$("#grad").val(clickedId);
$("#form-grad").submit();
});
});
</script>

View File

@@ -1,14 +1,17 @@
<!doctype>
<html>
<head>
<link rel="stylesheet" href="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.css" />
<script src="http://code.jquery.com/jquery-1.11.1.min.js"></script>
<script src="http://code.jquery.com/mobile/1.4.5/jquery.mobile-1.4.5.min.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/css/materialize.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<script src="https://code.jquery.com/jquery-2.1.1.min.js"></script>
<meta charset="UTF-8" />
<link rel="stylesheet" href="/assets/main.css">
</head>
<body>
Something in your body:
<%-body%>
<div class="container">
<%-body%>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/materialize/1.0.0/js/materialize.min.js"></script>
</body>
</html>

View File

@@ -1,3 +1,29 @@
<div class="row center-align">
<h2>Koju nekretninu tražite?</h2>
</div>
<form method="POST" id="form-vrsta">
<div class="row center-align">
<ul class="collection with-header">
<% for(let vrsta of vrste) { %>
<li class="collection-item" > <div id="<%= vrsta.id %>" ><%= vrsta.ime %><a href="#!" class="secondary-content"><i class="material-icons">send</i></a></div></li>
<% } %>
</ul>
<input type="hidden" name="vrsta" id="vrsta" />
</div>
</form>
<script>
$(document).ready( () => {
$(".collection-item").click( (e) => {
const clickedId = $(e.target).attr("id");
$("#vrsta").val(clickedId);
$("#form-vrsta").submit();
});
});
</script>
<a href="<%= nextStep %>"> >>> </a>

View File

@@ -1,13 +1,14 @@
const dobrodosli = require('./app/controllers/dobrodosli').getDobrodosli;
const getVrstaNekretnine = require('./app/controllers/vrsta_nekretnine').getVrstaNekretnine;
const { getVrstaNekretnine, postVrstaNekretnine} = require('./app/controllers/vrsta_nekretnine');
const { getGrad, postGrad } = require('./app/controllers/grad');
let express = require("express");
const path = require("path");
const bodyParser = require("body-parser");
const MarketAlert = require("./app/models/MarketAlert");
const MarketAlert = require("./app/models/marketalert");
const sendNotification = require("./app/lib/sendnotification");
const scrapTheItems = require("./app/lib/scraptheitems");
const sequelize = require("./app/db/db");
const sequelize = require("./app/models/index").sequelize;
const Twocheckout = require("2checkout-node");
const layout = require('express-layout');
@@ -21,6 +22,8 @@ app.set('views', path.join(__dirname, '/app/views'));
app.set('view engine', 'ejs');
app.use(layout());
const compression = require('compression');
app.use(compression());
app.get("/api/sendnotifications", async function(req, res) {
let marketAlerts = await MarketAlert.findAll();
@@ -105,6 +108,15 @@ app.post("/api/payforalert", function(request, response) {
});
app.get('/', dobrodosli);
app.get('/vrstanekretnine/:request_id', getVrstaNekretnine);
app.get('/vrstanekretnine', getVrstaNekretnine);
app.post('/vrstanekretnine/:request_id', postVrstaNekretnine);
app.post('/vrstanekretnine', postVrstaNekretnine);
app.get('/grad/:request_id', getGrad);
app.post('/grad/:request_id', postGrad);
app.use('/assets', express.static('./app/public'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`));

946
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,8 +5,7 @@
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"start": "node ./index.js",
"heroku-postbuild": "cd frontend-react && npm install && npm run build"
"start": "node ./index.js"
},
"repository": {
"type": "git",
@@ -22,13 +21,16 @@
"@sendgrid/mail": "^6.3.1",
"aws-sdk": "^2.422.0",
"cheerio": "^1.0.0-rc.2",
"compression": "^1.7.4",
"dotenv": "^7.0.0",
"ejs": "^2.6.1",
"express": "^4.16.4",
"express-ejs-layouts": "^2.5.0",
"express-layout": "^0.1.0",
"mysql2": "^1.6.4",
"node-fetch": "^2.3.0",
"pg": "^7.10.0",
"react-step-wizard": "^5.1.0",
"sequelize": "^4.42.0"
"sequelize": "^4.43.2",
"sequelize-cli": "^5.4.0"
}
}