Compare commits
7 Commits
migrate-to
...
heroku-por
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fc24add1f | ||
|
|
b1a08a7a57 | ||
|
|
1f7063f94e | ||
|
|
8a1e406f43 | ||
|
|
01b864d75b | ||
|
|
b7cb61b53b | ||
|
|
a957293029 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
node_modules/
|
||||||
1
backend/context.json
Normal file
1
backend/context.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{}
|
||||||
1
backend/deploy.env
Normal file
1
backend/deploy.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
SECRET_VARIABLE=mysecretval
|
||||||
5
backend/event.json
Normal file
5
backend/event.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"key": "value",
|
||||||
|
"key2": "value2",
|
||||||
|
"other_key": "other_value"
|
||||||
|
}
|
||||||
36
backend/event_sources.json
Normal file
36
backend/event_sources.json
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
{
|
||||||
|
"EventSourceMappings": [
|
||||||
|
{
|
||||||
|
"EventSourceArn": "your event source arn",
|
||||||
|
"StartingPosition": "LATEST",
|
||||||
|
"BatchSize": 100,
|
||||||
|
"Enabled": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"ScheduleEvents": [
|
||||||
|
{
|
||||||
|
"ScheduleName": "node-lambda-test-schedule",
|
||||||
|
"ScheduleState": "ENABLED",
|
||||||
|
"ScheduleExpression": "rate(1 hour)",
|
||||||
|
"Input":
|
||||||
|
{
|
||||||
|
"key1": "value",
|
||||||
|
"key2": "value"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"S3Events": [{
|
||||||
|
"Bucket": "BUCKET_NAME",
|
||||||
|
"Events": [
|
||||||
|
"s3:ObjectCreated:*"
|
||||||
|
],
|
||||||
|
"Filter": {
|
||||||
|
"Key": {
|
||||||
|
"FilterRules": [{
|
||||||
|
"Name": "prefix",
|
||||||
|
"Value": "STRING_VALUE"
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
}
|
||||||
@@ -49,15 +49,17 @@ app.get("/items/:url", async (req, res) => {
|
|||||||
app.post("/marketalerts", function(req, res) {
|
app.post("/marketalerts", function(req, res) {
|
||||||
const { email, last_date, olx_url } = req.body;
|
const { email, last_date, olx_url } = req.body;
|
||||||
console.log(email, last_date, olx_url);
|
console.log(email, last_date, olx_url);
|
||||||
res.json({ message: "Market Alert Created!" });
|
sequelize.sync().then(() => {
|
||||||
// sequelize.sync().then(() =>
|
MarketAlert.create({
|
||||||
// MarketAlert.create({
|
olx_url,
|
||||||
// olx_url,
|
last_date,
|
||||||
// last_date,
|
email
|
||||||
// email
|
})
|
||||||
// })
|
.then(() => {
|
||||||
// );
|
res.json({ message: "Market Alert Created!" });
|
||||||
// res.json({ message: "Market Alert Created!" });
|
})
|
||||||
|
.catch(e => console.error(e));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post("/payforalert", function(request, response) {
|
app.post("/payforalert", function(request, response) {
|
||||||
@@ -93,4 +95,10 @@ app.post("/payforalert", function(request, response) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Anything that doesn't match the above, send back index.html
|
||||||
|
app.get('*', (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname + '/../frontend-react/build/index.html'))
|
||||||
|
})
|
||||||
|
|
||||||
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
|
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
|
||||||
|
|||||||
@@ -20,6 +20,10 @@ const appStyle = theme => ({
|
|||||||
zIndex: "1",
|
zIndex: "1",
|
||||||
backgroundColor: "#272727",
|
backgroundColor: "#272727",
|
||||||
backgroundImage: "linear-gradient(180deg,#272727, #21525f)"
|
backgroundImage: "linear-gradient(180deg,#272727, #21525f)"
|
||||||
|
},
|
||||||
|
itemsCountTitle: {
|
||||||
|
textAlign: 'center',
|
||||||
|
color: 'white'
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -84,9 +84,8 @@ class App extends React.Component {
|
|||||||
return (
|
return (
|
||||||
<div className={classes.wrapper}>
|
<div className={classes.wrapper}>
|
||||||
<Sidebar logoText={"Market Alarm"} logo={logo} image={image} />
|
<Sidebar logoText={"Market Alarm"} logo={logo} image={image} />
|
||||||
|
|
||||||
<div className={classes.mainPanel}>
|
<div className={classes.mainPanel}>
|
||||||
<ItemsContainer />
|
{items.length && <h3 className={classes.itemsCountTitle}>Pronađeno {items.length} nekretnina. Napravite notifikaciju i primite vise detalja na vas emailu adresu.</h3>}
|
||||||
{items.length ? <NotificationModal /> : null}
|
{items.length ? <NotificationModal /> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ class NotificationModal extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
successCallback = data => {
|
successCallback = data => {
|
||||||
token = data.response.token.token;
|
//token = data.response.token.token;
|
||||||
const {
|
const {
|
||||||
userdata: { email, last_date, olx_url }
|
userdata: { email, last_date, olx_url }
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@@ -73,18 +73,10 @@ class NotificationModal extends React.Component {
|
|||||||
last_date,
|
last_date,
|
||||||
olx_url
|
olx_url
|
||||||
})
|
})
|
||||||
.then(response =>
|
.then(response => {
|
||||||
axios
|
this.handleClose();
|
||||||
.post("/payforalert", {
|
alert("Market Alert Created");
|
||||||
email,
|
})
|
||||||
token
|
|
||||||
})
|
|
||||||
.then(response => {
|
|
||||||
this.handleClose();
|
|
||||||
alert("Market Alert Created");
|
|
||||||
})
|
|
||||||
.catch(error => console.log(error))
|
|
||||||
)
|
|
||||||
.catch(error => console.log(error));
|
.catch(error => console.log(error));
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -114,7 +106,8 @@ class NotificationModal extends React.Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
handleSaveMarketAlert = () => {
|
handleSaveMarketAlert = () => {
|
||||||
this.tokenRequest();
|
this.successCallback();
|
||||||
|
//this.tokenRequest();
|
||||||
};
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
@@ -161,82 +154,22 @@ class NotificationModal extends React.Component {
|
|||||||
id="classic-modal-slide-description"
|
id="classic-modal-slide-description"
|
||||||
className={classes.modalBody}
|
className={classes.modalBody}
|
||||||
>
|
>
|
||||||
<FormControlLabel
|
<div>
|
||||||
className={classes.whiteText}
|
<Input
|
||||||
control={
|
className={classes.inputStyle}
|
||||||
<Checkbox
|
placeholder="Email"
|
||||||
className={classes.checkBoxStyle}
|
inputProps={{
|
||||||
checked={this.isChecked("emailChecked")}
|
"aria-label": "Email"
|
||||||
type={"checkbox"}
|
}}
|
||||||
value={""}
|
type="email"
|
||||||
onChange={() => this.optionChange("emailChecked")}
|
onChange={this.handleEmail}
|
||||||
/>
|
/>
|
||||||
}
|
<Input
|
||||||
label={<Typography style={{ color: "white" }}>Email</Typography>}
|
className={classes.inputStyle}
|
||||||
/>
|
type="hidden"
|
||||||
|
value={token}
|
||||||
{this.isChecked("emailChecked") ? (
|
/>
|
||||||
<div>
|
</div>
|
||||||
<Input
|
|
||||||
className={classes.inputStyle}
|
|
||||||
placeholder="Email"
|
|
||||||
inputProps={{
|
|
||||||
"aria-label": "Email"
|
|
||||||
}}
|
|
||||||
type="email"
|
|
||||||
onChange={this.handleEmail}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className={classes.inputStyle}
|
|
||||||
type="hidden"
|
|
||||||
value={token}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className={classes.inputStyle}
|
|
||||||
placeholder="Card Number"
|
|
||||||
inputProps={{
|
|
||||||
"aria-label": "Card Number"
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
autoComplete="off"
|
|
||||||
type="number"
|
|
||||||
onChange={e => this.handleInput(e, "ccNo")}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className={classes.inputStyle}
|
|
||||||
placeholder="Expiration Year"
|
|
||||||
inputProps={{
|
|
||||||
"aria-label": "Expiration Year"
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
autoComplete="off"
|
|
||||||
type="number"
|
|
||||||
onChange={e => this.handleInput(e, "expYear")}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className={classes.inputStyle}
|
|
||||||
placeholder="Expiration Month"
|
|
||||||
inputProps={{
|
|
||||||
"aria-label": "Expiration Month"
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
autoComplete="off"
|
|
||||||
type="number"
|
|
||||||
onChange={e => this.handleInput(e, "expMonth")}
|
|
||||||
/>
|
|
||||||
<Input
|
|
||||||
className={classes.inputStyle}
|
|
||||||
placeholder="CVV"
|
|
||||||
inputProps={{
|
|
||||||
"aria-label": "CVV"
|
|
||||||
}}
|
|
||||||
required
|
|
||||||
autoComplete="off"
|
|
||||||
type="number"
|
|
||||||
onChange={e => this.handleInput(e, "cvv")}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
) : null}
|
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions className={classes.modalFooter}>
|
<DialogActions className={classes.modalFooter}>
|
||||||
{validEmail ? (
|
{validEmail ? (
|
||||||
|
|||||||
6
ma-api/.gitignore
vendored
6
ma-api/.gitignore
vendored
@@ -1,6 +0,0 @@
|
|||||||
# package directories
|
|
||||||
node_modules
|
|
||||||
jspm_packages
|
|
||||||
|
|
||||||
# Serverless directories
|
|
||||||
.serverless
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
# How to deploy automatically:
|
|
||||||
|
|
||||||
1. set up aws cli with aws configure
|
|
||||||
2. cd ma-api
|
|
||||||
3. serverless deploy
|
|
||||||
|
|
||||||
@@ -1,83 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
const path = require("path");
|
|
||||||
const bodyParser = require("body-parser");
|
|
||||||
const MarketAlert = require("./lib/MarketAlert");
|
|
||||||
const sendNotification = require("./lib/sendnotification");
|
|
||||||
const scrapTheItems = require("./lib/scraptheitems");
|
|
||||||
const sequelize = require("./lib/db.js");
|
|
||||||
const Twocheckout = require("2checkout-node");
|
|
||||||
|
|
||||||
|
|
||||||
module.exports.sendnotification = async (event, context) => {
|
|
||||||
let marketAlerts = await MarketAlert.findAll();
|
|
||||||
|
|
||||||
let lastDateUpdate = await Promise.all(
|
|
||||||
marketAlerts
|
|
||||||
.map(marketAlert => {
|
|
||||||
const { id, email, olx_url, last_date } = marketAlert.dataValues;
|
|
||||||
return { id, email, olx_url, last_date };
|
|
||||||
})
|
|
||||||
.map(sendNotification)
|
|
||||||
);
|
|
||||||
|
|
||||||
lastDateUpdate = lastDateUpdate.filter(Boolean(dateUpdate));
|
|
||||||
lastDateUpdate.length &&
|
|
||||||
lastDateUpdate.forEach(dateUpdate =>
|
|
||||||
MarketAlert.update(
|
|
||||||
{ last_date: dateUpdate.date },
|
|
||||||
{ where: { id: dateUpdate.id } }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify({
|
|
||||||
message: 'Notifications sent',
|
|
||||||
input: event,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
|
|
||||||
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
module.exports.getitems = async (event, context) => {
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify({
|
|
||||||
message: 'Get Items',
|
|
||||||
input: event,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
|
|
||||||
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.marketalerts = async (event, context) => {
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify({
|
|
||||||
message: 'Market alerts',
|
|
||||||
input: event,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
|
|
||||||
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports.payforalert = async (event, context) => {
|
|
||||||
return {
|
|
||||||
statusCode: 200,
|
|
||||||
body: JSON.stringify({
|
|
||||||
message: 'Pay for alert',
|
|
||||||
input: event,
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
// Use this code if you don't use the http event with the LAMBDA-PROXY integration
|
|
||||||
// return { message: 'Go Serverless v1.0! Your function executed successfully!', event };
|
|
||||||
};
|
|
||||||
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
const Sequelize = require("sequelize");
|
|
||||||
const sequelize = require("./db.js");
|
|
||||||
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;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const convertToDate = require("./convertToDate");
|
|
||||||
function areThereAnyNewItems(lastItemDate, controlDate) {
|
|
||||||
if (!lastItemDate) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return new Date(controlDate) < convertToDate(lastItemDate);
|
|
||||||
}
|
|
||||||
module.exports = areThereAnyNewItems;
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"development": {
|
|
||||||
"username": "javimistaging",
|
|
||||||
"password": "10MinutaSvaki*Dan",
|
|
||||||
"database": "database_development",
|
|
||||||
"host": "javimi-staging.cluster-c52xdqogwrl2.eu-central-1.rds.amazonaws.com",
|
|
||||||
"dialect": "mysql"
|
|
||||||
},
|
|
||||||
"test": {
|
|
||||||
"username": "root",
|
|
||||||
"password": null,
|
|
||||||
"database": "database_test",
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"dialect": "mysql"
|
|
||||||
},
|
|
||||||
"production": {
|
|
||||||
"username": "root",
|
|
||||||
"password": null,
|
|
||||||
"database": "database_production",
|
|
||||||
"host": "127.0.0.1",
|
|
||||||
"dialect": "mysql"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
function convertToDate(date) {
|
|
||||||
const [dan, mjesec, godina] = date
|
|
||||||
.split(". u ")[0]
|
|
||||||
.split(".")
|
|
||||||
.map(el => Number(el));
|
|
||||||
const [sati, minute] = date
|
|
||||||
.split(". u ")[1]
|
|
||||||
.split(":")
|
|
||||||
.map(el => Number(el));
|
|
||||||
return new Date(godina, mjesec, dan, sati, minute);
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = convertToDate;
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
const Sequelize = require("sequelize");
|
|
||||||
const sequelize = new Sequelize("sql7276322", "sql7276322@localhost", "RS53ihYlg9", {
|
|
||||||
host: "sql7.freemysqlhosting.net",
|
|
||||||
dialect: "mysql",
|
|
||||||
operatorsAliases: false
|
|
||||||
});
|
|
||||||
|
|
||||||
module.exports = sequelize;
|
|
||||||
@@ -1,96 +0,0 @@
|
|||||||
let express = require("express");
|
|
||||||
const path = require("path");
|
|
||||||
const bodyParser = require("body-parser");
|
|
||||||
const MarketAlert = require("./MarketAlert");
|
|
||||||
const sendNotification = require("./utils/sendnotification");
|
|
||||||
const scrapTheItems = require("./utils/scraptheitems");
|
|
||||||
const sequelize = require("./db.js");
|
|
||||||
const Twocheckout = require("2checkout-node");
|
|
||||||
|
|
||||||
const app = express();
|
|
||||||
app.use(bodyParser.json());
|
|
||||||
app.use(bodyParser.urlencoded({ extended: true }));
|
|
||||||
|
|
||||||
const port = process.env.PORT || 5000;
|
|
||||||
|
|
||||||
app.get("/sendnotifications", async function(req, res) {
|
|
||||||
let marketAlerts = await MarketAlert.findAll();
|
|
||||||
|
|
||||||
let lastDateUpdate = await Promise.all(
|
|
||||||
marketAlerts
|
|
||||||
.map(marketAlert => {
|
|
||||||
const { id, email, olx_url, last_date } = marketAlert.dataValues;
|
|
||||||
return { id, email, olx_url, last_date };
|
|
||||||
})
|
|
||||||
.map(sendNotification)
|
|
||||||
);
|
|
||||||
lastDateUpdate = lastDateUpdate.filter(Boolean(dateUpdate));
|
|
||||||
lastDateUpdate.length &&
|
|
||||||
lastDateUpdate.forEach(dateUpdate =>
|
|
||||||
MarketAlert.update(
|
|
||||||
{ last_date: dateUpdate.date },
|
|
||||||
{ where: { id: dateUpdate.id } }
|
|
||||||
)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
app.get("/items/:url", async (req, res) => {
|
|
||||||
let url =
|
|
||||||
"https://www.olx.ba/pretraga?" +
|
|
||||||
req.params.url +
|
|
||||||
"&sort_order=desc&sort_po=datum";
|
|
||||||
let appts = await scrapTheItems(url);
|
|
||||||
res.json({
|
|
||||||
last_date: appts[0] && appts[0].date,
|
|
||||||
items: appts
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/marketalerts", function(req, res) {
|
|
||||||
const { email, last_date, olx_url } = req.body;
|
|
||||||
console.log(email, last_date, olx_url);
|
|
||||||
res.json({ message: "Market Alert Created!" });
|
|
||||||
// sequelize.sync().then(() =>
|
|
||||||
// MarketAlert.create({
|
|
||||||
// olx_url,
|
|
||||||
// last_date,
|
|
||||||
// email
|
|
||||||
// })
|
|
||||||
// );
|
|
||||||
// res.json({ message: "Market Alert Created!" });
|
|
||||||
});
|
|
||||||
|
|
||||||
app.post("/payforalert", function(request, response) {
|
|
||||||
let tco = new Twocheckout({
|
|
||||||
sellerId: "901402692",
|
|
||||||
privateKey: "A28DCE5F-9292-405C-8161-F84D8BB83AFC",
|
|
||||||
sandbox: true
|
|
||||||
});
|
|
||||||
|
|
||||||
let params = {
|
|
||||||
merchantOrderId: "123",
|
|
||||||
token: request.body.token,
|
|
||||||
currency: "USD",
|
|
||||||
total: "2.00",
|
|
||||||
billingAddr: {
|
|
||||||
name: "Testing Tester",
|
|
||||||
addrLine1: "123 Test St",
|
|
||||||
city: "Sarajevo",
|
|
||||||
state: "BiH",
|
|
||||||
zipCode: "71000",
|
|
||||||
country: "BiH",
|
|
||||||
email: request.body.email,
|
|
||||||
phoneNumber: "5555555555"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
tco.checkout.authorize(params, function(error, data) {
|
|
||||||
if (error) {
|
|
||||||
response.send(error.message);
|
|
||||||
} else {
|
|
||||||
response.send(data.response.responseMsg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
module.exports = {
|
|
||||||
up: (queryInterface, Sequelize) => {
|
|
||||||
return queryInterface.createTable('MarketAlerts', {
|
|
||||||
id: {
|
|
||||||
allowNull: false,
|
|
||||||
autoIncrement: true,
|
|
||||||
primaryKey: true,
|
|
||||||
type: Sequelize.INTEGER
|
|
||||||
},
|
|
||||||
olx_url: {
|
|
||||||
type: Sequelize.STRING
|
|
||||||
},
|
|
||||||
createdAt: {
|
|
||||||
allowNull: false,
|
|
||||||
type: Sequelize.DATE
|
|
||||||
},
|
|
||||||
updatedAt: {
|
|
||||||
allowNull: false,
|
|
||||||
type: Sequelize.DATE
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
down: (queryInterface, Sequelize) => {
|
|
||||||
return queryInterface.dropTable('MarketAlerts');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
'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;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
'use strict';
|
|
||||||
module.exports = (sequelize, DataTypes) => {
|
|
||||||
const MarketAlert = sequelize.define('MarketAlert', {
|
|
||||||
olx_url: DataTypes.STRING
|
|
||||||
}, {});
|
|
||||||
MarketAlert.associate = function(models) {
|
|
||||||
// associations can be defined here
|
|
||||||
};
|
|
||||||
return MarketAlert;
|
|
||||||
};
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
let fetch = require("node-fetch");
|
|
||||||
let cheerio = require("cheerio");
|
|
||||||
const areThereAnyNewItems = require("./arethereanynewitems");
|
|
||||||
|
|
||||||
async function scrapTheItems(url, controlDate, noNewItems = false) {
|
|
||||||
let items = [];
|
|
||||||
let response = await fetch(url);
|
|
||||||
const body = await response.text();
|
|
||||||
const $ = cheerio.load(body);
|
|
||||||
$("#rezultatipretrage")
|
|
||||||
.find(".listitem")
|
|
||||||
.each(async (index, elem) => {
|
|
||||||
if (noNewItems) return;
|
|
||||||
const itemDate = $(elem)
|
|
||||||
.find(".cijena > .datum > div")
|
|
||||||
.first()
|
|
||||||
.attr("data-cijelidatum");
|
|
||||||
|
|
||||||
if (controlDate && !areThereAnyNewItems(itemDate, controlDate)) {
|
|
||||||
noNewItems = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const id = $(elem)
|
|
||||||
.find("a")
|
|
||||||
.first()
|
|
||||||
.attr("href");
|
|
||||||
const cijena = $(elem)
|
|
||||||
.find(".cijena > .datum > span")
|
|
||||||
.first()
|
|
||||||
.text();
|
|
||||||
const image = $(elem)
|
|
||||||
.find("a > .slika > img")
|
|
||||||
.first()
|
|
||||||
.attr("src");
|
|
||||||
|
|
||||||
items.push({ url: id, price: cijena, image, date: itemDate });
|
|
||||||
});
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = scrapTheItems;
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
const scrapTheItems = require("./scraptheitems");
|
|
||||||
const convertToDate = require("./convertToDate");
|
|
||||||
const sgMail = require("@sendgrid/mail");
|
|
||||||
// should be process.env.SENDGRID_API_KEY
|
|
||||||
sgMail.setApiKey(
|
|
||||||
"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"
|
|
||||||
);
|
|
||||||
|
|
||||||
async function sendNotification(marketAlert) {
|
|
||||||
const { id, email, olx_url, last_date } = marketAlert;
|
|
||||||
let url =
|
|
||||||
"https://www.olx.ba/pretraga?" + olx_url + "&sort_order=desc&sort_po=datum";
|
|
||||||
let newItems = await scrapTheItems(url);
|
|
||||||
let lastDate = newItems.length && newItems[0].date;
|
|
||||||
let message =
|
|
||||||
newItems.length &&
|
|
||||||
newItems.reduce(
|
|
||||||
(mes, item) => mes + `<strong>${item.url} i ${item.price}</strong>`,
|
|
||||||
""
|
|
||||||
);
|
|
||||||
const msg = {
|
|
||||||
to: email,
|
|
||||||
from: "test@example.com",
|
|
||||||
subject: "Market Alert",
|
|
||||||
text: "New items on olx",
|
|
||||||
html: message
|
|
||||||
};
|
|
||||||
if (message) {
|
|
||||||
await sgMail.send(msg);
|
|
||||||
return { id, date: String(convertToDate(lastDate)) };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
module.exports = sendNotification;
|
|
||||||
1918
ma-api/package-lock.json
generated
1918
ma-api/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "backend",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "",
|
|
||||||
"main": "index.js",
|
|
||||||
"scripts": {
|
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
|
||||||
},
|
|
||||||
"author": "Ehvan Gradanin",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"2checkout-node": "0.0.1",
|
|
||||||
"@sendgrid/mail": "^6.3.1",
|
|
||||||
"cheerio": "^1.0.0-rc.2",
|
|
||||||
"express": "^4.16.4",
|
|
||||||
"mysql2": "^1.6.4",
|
|
||||||
"node-fetch": "^2.3.0",
|
|
||||||
"sequelize": "^4.42.0",
|
|
||||||
"sequelize-cli": "^5.4.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
# Welcome to Serverless!
|
|
||||||
#
|
|
||||||
# This file is the main config file for your service.
|
|
||||||
# It's very minimal at this point and uses default values.
|
|
||||||
# You can always add more config options for more control.
|
|
||||||
# We've included some commented out config examples here.
|
|
||||||
# Just uncomment any of them to get that config option.
|
|
||||||
#
|
|
||||||
# For full config options, check the docs:
|
|
||||||
# docs.serverless.com
|
|
||||||
#
|
|
||||||
# Happy Coding!
|
|
||||||
|
|
||||||
service: ma-api # NOTE: update this with your service name
|
|
||||||
|
|
||||||
# You can pin your service to only deploy with a specific Serverless version
|
|
||||||
# Check out our docs for more details
|
|
||||||
# frameworkVersion: "=X.X.X"
|
|
||||||
|
|
||||||
provider:
|
|
||||||
name: aws
|
|
||||||
runtime: nodejs8.10
|
|
||||||
|
|
||||||
# you can overwrite defaults here
|
|
||||||
# stage: dev
|
|
||||||
# region: us-east-1
|
|
||||||
|
|
||||||
# you can add statements to the Lambda function's IAM Role here
|
|
||||||
# iamRoleStatements:
|
|
||||||
# - Effect: "Allow"
|
|
||||||
# Action:
|
|
||||||
# - "s3:ListBucket"
|
|
||||||
# Resource: { "Fn::Join" : ["", ["arn:aws:s3:::", { "Ref" : "ServerlessDeploymentBucket" } ] ] }
|
|
||||||
# - Effect: "Allow"
|
|
||||||
# Action:
|
|
||||||
# - "s3:PutObject"
|
|
||||||
# Resource:
|
|
||||||
# Fn::Join:
|
|
||||||
# - ""
|
|
||||||
# - - "arn:aws:s3:::"
|
|
||||||
# - "Ref" : "ServerlessDeploymentBucket"
|
|
||||||
# - "/*"
|
|
||||||
|
|
||||||
# you can define service wide environment variables here
|
|
||||||
# environment:
|
|
||||||
# variable1: value1
|
|
||||||
|
|
||||||
# you can add packaging information here
|
|
||||||
#package:
|
|
||||||
# include:
|
|
||||||
# - include-me.js
|
|
||||||
# - include-me-dir/**
|
|
||||||
# exclude:
|
|
||||||
# - exclude-me.js
|
|
||||||
# - exclude-me-dir/**
|
|
||||||
|
|
||||||
functions:
|
|
||||||
sendnotification:
|
|
||||||
handler: handler.sendnotification
|
|
||||||
events:
|
|
||||||
- http:
|
|
||||||
path: notifications/send
|
|
||||||
method: post
|
|
||||||
|
|
||||||
# The following are a few example events you can configure
|
|
||||||
# NOTE: Please make sure to change your handler code to work with those events
|
|
||||||
# Check the event documentation for details
|
|
||||||
# events:
|
|
||||||
# - http:
|
|
||||||
# path: users/create
|
|
||||||
# method: get
|
|
||||||
# - websocket: $connect
|
|
||||||
# - s3: ${env:BUCKET}
|
|
||||||
# - schedule: rate(10 minutes)
|
|
||||||
# - sns: greeter-topic
|
|
||||||
# - stream: arn:aws:dynamodb:region:XXXXXX:table/foo/stream/1970-01-01T00:00:00.000
|
|
||||||
# - alexaSkill: amzn1.ask.skill.xx-xx-xx-xx
|
|
||||||
# - alexaSmartHome: amzn1.ask.skill.xx-xx-xx-xx
|
|
||||||
# - iot:
|
|
||||||
# sql: "SELECT * FROM 'some_topic'"
|
|
||||||
# - cloudwatchEvent:
|
|
||||||
# event:
|
|
||||||
# source:
|
|
||||||
# - "aws.ec2"
|
|
||||||
# detail-type:
|
|
||||||
# - "EC2 Instance State-change Notification"
|
|
||||||
# detail:
|
|
||||||
# state:
|
|
||||||
# - pending
|
|
||||||
# - cloudwatchLog: '/aws/lambda/hello'
|
|
||||||
# - cognitoUserPool:
|
|
||||||
# pool: MyUserPool
|
|
||||||
# trigger: PreSignUp
|
|
||||||
|
|
||||||
# Define function environment variables here
|
|
||||||
# environment:
|
|
||||||
# variable2: value2
|
|
||||||
|
|
||||||
# you can add CloudFormation resource templates here
|
|
||||||
#resources:
|
|
||||||
# Resources:
|
|
||||||
# NewResource:
|
|
||||||
# Type: AWS::S3::Bucket
|
|
||||||
# Properties:
|
|
||||||
# BucketName: my-new-bucket
|
|
||||||
# Outputs:
|
|
||||||
# NewOutput:
|
|
||||||
# Description: "Description for the output"
|
|
||||||
# Value: "Some output value"
|
|
||||||
1153
package-lock.json
generated
1153
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
25
package.json
Normal file
25
package.json
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "marketalarm",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Market Alarm",
|
||||||
|
"main": "./backend/index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git@gitlab.com:saburly/marketalarm/web.git"
|
||||||
|
},
|
||||||
|
"author": "Saburlije",
|
||||||
|
"license": "ISC",
|
||||||
|
"engines": {
|
||||||
|
"node": "11.10.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"start": "node ./backend/index.js",
|
||||||
|
"heroku-postbuild": "cd frontend-react && npm install && npm run build && cd .. && cd backend && npm install"
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user