Compare commits
3 Commits
email_to_a
...
migrate-to
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e356f7a177 | ||
|
|
055ca54002 | ||
|
|
3f8e6438b2 |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1 +0,0 @@
|
||||
node_modules/
|
||||
@@ -1 +0,0 @@
|
||||
{}
|
||||
@@ -1,4 +1,8 @@
|
||||
const Sequelize = require("sequelize");
|
||||
const sequelize = new Sequelize(process.env.JAWSDB_URL);
|
||||
const sequelize = new Sequelize("sql7276322", "sql7276322", "RS53ihYlg9", {
|
||||
host: "sql7.freemysqlhosting.net",
|
||||
dialect: "mysql",
|
||||
operatorsAliases: false
|
||||
});
|
||||
|
||||
module.exports = sequelize;
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
SECRET_VARIABLE=mysecretval
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"key": "value",
|
||||
"key2": "value2",
|
||||
"other_key": "other_value"
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
{
|
||||
"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"
|
||||
}]
|
||||
}
|
||||
}
|
||||
}]
|
||||
}
|
||||
@@ -1,10 +1,10 @@
|
||||
let express = require("express");
|
||||
const path = require("path");
|
||||
const bodyParser = require("body-parser");
|
||||
const MarketAlert = require("./backend/MarketAlert");
|
||||
const sendNotification = require("./backend/utils/sendnotification");
|
||||
const scrapTheItems = require("./backend/utils/scraptheitems");
|
||||
const sequelize = require("./backend/db.js");
|
||||
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();
|
||||
@@ -49,17 +49,15 @@ app.get("/items/:url", async (req, res) => {
|
||||
app.post("/marketalerts", function(req, res) {
|
||||
const { email, last_date, olx_url } = req.body;
|
||||
console.log(email, last_date, olx_url);
|
||||
sequelize.sync().then(() => {
|
||||
MarketAlert.create({
|
||||
olx_url,
|
||||
last_date,
|
||||
email
|
||||
})
|
||||
.then(() => {
|
||||
res.json({ message: "Market Alert Created!" });
|
||||
})
|
||||
.catch(e => console.error(e));
|
||||
});
|
||||
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) {
|
||||
@@ -95,11 +93,4 @@ app.post("/payforalert", function(request, response) {
|
||||
});
|
||||
});
|
||||
|
||||
app.use(express.static(path.join(__dirname, 'frontend-react/build')));
|
||||
|
||||
// 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}!`));
|
||||
1168
backend/package-lock.json
generated
Normal file
1168
backend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
20
backend/package.json
Normal file
20
backend/package.json
Normal file
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,10 @@
|
||||
const scrapTheItems = require("./scraptheitems");
|
||||
const convertToDate = require("./convertToDate");
|
||||
const AWS = require('aws-sdk');
|
||||
AWS.config.update({region: 'eu-central-1'});
|
||||
|
||||
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;
|
||||
@@ -16,43 +18,16 @@ async function sendNotification(marketAlert) {
|
||||
(mes, item) => mes + `<strong>${item.url} i ${item.price}</strong>`,
|
||||
""
|
||||
);
|
||||
|
||||
// Create sendEmail params
|
||||
var params = {
|
||||
Destination: { /* required */
|
||||
CcAddresses: [
|
||||
],
|
||||
ToAddresses: [
|
||||
email
|
||||
]
|
||||
},
|
||||
Message: { /* required */
|
||||
Body: { /* required */
|
||||
Html: {
|
||||
Charset: "UTF-8",
|
||||
Data: message
|
||||
},
|
||||
Text: {
|
||||
Charset: "UTF-8",
|
||||
Data: message // TODO: convert to text
|
||||
}
|
||||
},
|
||||
Subject: {
|
||||
Charset: 'UTF-8',
|
||||
Data: 'Javimi alert'
|
||||
}
|
||||
},
|
||||
Source: 'info@saburly.com', /* required */
|
||||
ReplyToAddresses: [
|
||||
'info@saburly.com',
|
||||
],
|
||||
const msg = {
|
||||
to: email,
|
||||
from: "test@example.com",
|
||||
subject: "Market Alert",
|
||||
text: "New items on olx",
|
||||
html: message
|
||||
};
|
||||
|
||||
if (message) {
|
||||
const sendPromise = new AWS.SES({apiVersion: '2010-12-01'}).sendEmail(params).promise();
|
||||
await sendPromise;
|
||||
await sgMail.send(msg);
|
||||
return { id, date: String(convertToDate(lastDate)) };
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = sendNotification;
|
||||
|
||||
@@ -20,10 +20,6 @@ const appStyle = theme => ({
|
||||
zIndex: "1",
|
||||
backgroundColor: "#272727",
|
||||
backgroundImage: "linear-gradient(180deg,#272727, #21525f)"
|
||||
},
|
||||
itemsCountTitle: {
|
||||
textAlign: 'center',
|
||||
color: 'white'
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -2,19 +2,19 @@ import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import withStyles from "@material-ui/core/styles/withStyles";
|
||||
import { connect } from "react-redux";
|
||||
import { ITEMS_CHANGED, USER_DATA_CHANGED } from "../constants/actionTypes";
|
||||
import { areObjectEqual } from "../utils/helpers";
|
||||
import { createOlxLink } from "../utils/createOlxLink";
|
||||
import { ITEMS_CHANGED, USER_DATA_CHANGED } from "constants/actionTypes";
|
||||
import { areObjectEqual } from "utils/helpers";
|
||||
import { createOlxLink } from "utils/createOlxLink";
|
||||
import axios from "axios";
|
||||
|
||||
import image from "../assets/img/sidebar-1.jpg";
|
||||
import logo from "../assets/img/reactlogo.png";
|
||||
import image from "assets/img/sidebar-1.jpg";
|
||||
import logo from "assets/img/reactlogo.png";
|
||||
|
||||
import Sidebar from "../components/Sidebar.js";
|
||||
import Sidebar from "components/Sidebar.js";
|
||||
import ItemsContainer from "./items/itemscontainer/ItemsContainer";
|
||||
import NotificationModal from "./NotificationModal";
|
||||
|
||||
import dashboardStyle from "../assets/dashboardStyle.js";
|
||||
import dashboardStyle from "assets/dashboardStyle.js";
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
@@ -84,8 +84,9 @@ class App extends React.Component {
|
||||
return (
|
||||
<div className={classes.wrapper}>
|
||||
<Sidebar logoText={"Market Alarm"} logo={logo} image={image} />
|
||||
|
||||
<div className={classes.mainPanel}>
|
||||
{items.length && <h3 className={classes.itemsCountTitle}>Pronađeno {items.length} nekretnina. Napravite notifikaciju i primite vise detalja na vas emailu adresu.</h3>}
|
||||
<ItemsContainer />
|
||||
{items.length ? <NotificationModal /> : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,8 +5,8 @@ import DialogContent from "@material-ui/core/DialogContent";
|
||||
import DialogActions from "@material-ui/core/DialogActions";
|
||||
import Slide from "@material-ui/core/Slide";
|
||||
import withStyles from "@material-ui/core/styles/withStyles";
|
||||
import { notificationmodalwrapper } from "../utils/notificationmodalwrapper";
|
||||
import modalStyle from "../assets/modalStyle.js";
|
||||
import { notificationmodalwrapper } from "utils/notificationmodalwrapper";
|
||||
import modalStyle from "assets/modalStyle.js";
|
||||
import Button from "@material-ui/core/Button";
|
||||
import IconButton from "@material-ui/core/IconButton";
|
||||
import Close from "@material-ui/icons/Close";
|
||||
@@ -62,7 +62,7 @@ class NotificationModal extends React.Component {
|
||||
};
|
||||
|
||||
successCallback = data => {
|
||||
//token = data.response.token.token;
|
||||
token = data.response.token.token;
|
||||
const {
|
||||
userdata: { email, last_date, olx_url }
|
||||
} = this.props;
|
||||
@@ -73,10 +73,18 @@ class NotificationModal extends React.Component {
|
||||
last_date,
|
||||
olx_url
|
||||
})
|
||||
.then(response => {
|
||||
this.handleClose();
|
||||
alert("Market Alert Created");
|
||||
})
|
||||
.then(response =>
|
||||
axios
|
||||
.post("/payforalert", {
|
||||
email,
|
||||
token
|
||||
})
|
||||
.then(response => {
|
||||
this.handleClose();
|
||||
alert("Market Alert Created");
|
||||
})
|
||||
.catch(error => console.log(error))
|
||||
)
|
||||
.catch(error => console.log(error));
|
||||
};
|
||||
|
||||
@@ -106,8 +114,7 @@ class NotificationModal extends React.Component {
|
||||
};
|
||||
|
||||
handleSaveMarketAlert = () => {
|
||||
this.successCallback();
|
||||
//this.tokenRequest();
|
||||
this.tokenRequest();
|
||||
};
|
||||
|
||||
render() {
|
||||
@@ -154,22 +161,82 @@ class NotificationModal extends React.Component {
|
||||
id="classic-modal-slide-description"
|
||||
className={classes.modalBody}
|
||||
>
|
||||
<div>
|
||||
<Input
|
||||
className={classes.inputStyle}
|
||||
placeholder="Email"
|
||||
inputProps={{
|
||||
"aria-label": "Email"
|
||||
}}
|
||||
type="email"
|
||||
onChange={this.handleEmail}
|
||||
/>
|
||||
<Input
|
||||
className={classes.inputStyle}
|
||||
type="hidden"
|
||||
value={token}
|
||||
/>
|
||||
</div>
|
||||
<FormControlLabel
|
||||
className={classes.whiteText}
|
||||
control={
|
||||
<Checkbox
|
||||
className={classes.checkBoxStyle}
|
||||
checked={this.isChecked("emailChecked")}
|
||||
type={"checkbox"}
|
||||
value={""}
|
||||
onChange={() => this.optionChange("emailChecked")}
|
||||
/>
|
||||
}
|
||||
label={<Typography style={{ color: "white" }}>Email</Typography>}
|
||||
/>
|
||||
|
||||
{this.isChecked("emailChecked") ? (
|
||||
<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>
|
||||
<DialogActions className={classes.modalFooter}>
|
||||
{validEmail ? (
|
||||
|
||||
@@ -12,15 +12,16 @@ import ExpandMore from "@material-ui/icons/ExpandMore";
|
||||
import Collapse from "@material-ui/core/Collapse";
|
||||
import StarBorder from "@material-ui/icons/StarBorder";
|
||||
|
||||
import sidebarStyle from "../assets/sidebarStyle.js";
|
||||
import CollapseWrapperStyled from "../components/widgets/CollapseWrapperStyled";
|
||||
import sidebarStyle from "assets/sidebarStyle.js";
|
||||
import CollapseWrapperStyled from "components/widgets/CollapseWrapperStyled";
|
||||
import DeepCategoryWrapper from "./widgets/DeepCategoryWrapper";
|
||||
import { hoc } from "../utils/helpers";
|
||||
import { hoc } from "utils/helpers";
|
||||
|
||||
import * as Vozila from "./categories/Vozila";
|
||||
import * as Nekretnine from "./categories/Nekretnine";
|
||||
|
||||
import { connect } from "react-redux";
|
||||
import { CATEGORY_SELECT } from "../constants/actionTypes";
|
||||
import { CATEGORY_SELECT } from "constants/actionTypes";
|
||||
|
||||
const options = [
|
||||
{ value: "Nekretnine", label: "Nekretnine" }
|
||||
|
||||
20
frontend-react/src/components/categories/Vozila.js
Normal file
20
frontend-react/src/components/categories/Vozila.js
Normal file
@@ -0,0 +1,20 @@
|
||||
import React from "react";
|
||||
|
||||
import Automobili from "../subcategories/vozila/Automobili";
|
||||
import Motocikli from "../subcategories/vozila/Motocikli";
|
||||
|
||||
const options = [
|
||||
{ value: 18, label: "Automobili" },
|
||||
{ value: 21, label: "Motocikli" }
|
||||
];
|
||||
const depth = 0;
|
||||
const childrenComponents = {
|
||||
18: <Automobili />,
|
||||
21: <Motocikli />
|
||||
};
|
||||
|
||||
export const properties = {
|
||||
options,
|
||||
depth,
|
||||
childrenComponents
|
||||
};
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import * as Filters from "./AllFiltersDefined";
|
||||
import CheckboxAndRadioWrapper from "../../widgets/CheckboxAndRadioWrapper";
|
||||
import SelectDisplayCheckboxWrapper from "../../widgets/SelectDisplayCheckboxWrapper";
|
||||
import RangeWrapper from "../../widgets/RangeWrapper";
|
||||
import DropDownWrapper from "../../widgets/DropDownWrapper";
|
||||
import CheckboxAndRadioWrapper from "components/widgets/CheckboxAndRadioWrapper";
|
||||
import SelectDisplayCheckboxWrapper from "components/widgets/SelectDisplayCheckboxWrapper";
|
||||
import RangeWrapper from "components/widgets/RangeWrapper";
|
||||
import DropDownWrapper from "components/widgets/DropDownWrapper";
|
||||
|
||||
const Kanton = (
|
||||
<DropDownWrapper componentName="Lokacija" {...Filters.lokacijaOptions} />
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WrapAll } from "../../widgets/CollapseWrapperAll";
|
||||
import { WrapAll } from "components/widgets/CollapseWrapperAll";
|
||||
import {
|
||||
Kanton,
|
||||
Grad,
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Vrsta,
|
||||
Kvadratura,
|
||||
DodatnoZaKucu
|
||||
} from "../../filters/NekretnineFilter/index";
|
||||
} from "components/filters/NekretnineFilter/index";
|
||||
|
||||
const KuceFilters = [
|
||||
Kanton,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { WrapAll } from "../../widgets/CollapseWrapperAll";
|
||||
import { WrapAll } from "components/widgets/CollapseWrapperAll";
|
||||
import {
|
||||
Kanton,
|
||||
Grad,
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Vrsta,
|
||||
Kvadratura,
|
||||
DodatnoZaStan
|
||||
} from "../../filters/NekretnineFilter/index";
|
||||
} from "components/filters/NekretnineFilter/index";
|
||||
|
||||
const StanFilters = [
|
||||
Kanton,
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { WrapAll } from "components/widgets/CollapseWrapperAll";
|
||||
import {
|
||||
Proizvodac,
|
||||
Kanton,
|
||||
Grad,
|
||||
Cijena,
|
||||
Stanje,
|
||||
Vrsta,
|
||||
Godiste,
|
||||
KilometraMin,
|
||||
KilometraMax,
|
||||
Gorivo
|
||||
} from "components/filters/VozilaFilter/index";
|
||||
|
||||
const AutomobiliFilters = [
|
||||
Proizvodac,
|
||||
Kanton,
|
||||
Grad,
|
||||
Cijena,
|
||||
Stanje,
|
||||
Vrsta,
|
||||
Godiste,
|
||||
KilometraMin,
|
||||
KilometraMax,
|
||||
Gorivo
|
||||
];
|
||||
const Automobili = () => WrapAll(AutomobiliFilters);
|
||||
|
||||
export default Automobili;
|
||||
@@ -1,8 +1,8 @@
|
||||
import React from "react";
|
||||
import { optionchangewrapper } from "../../utils/optionchangewrapper";
|
||||
import { optionchangewrapper } from "utils/optionchangewrapper";
|
||||
import Checkbox from "@material-ui/core/Checkbox";
|
||||
import Radio from "@material-ui/core/Radio";
|
||||
import "../../assets/checkboxAndRadioStyle.css";
|
||||
import "assets/checkboxAndRadioStyle.css";
|
||||
|
||||
class CheckboxAndRadioWrapper extends React.Component {
|
||||
optionChange = (option, optionName, type) => {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import { OPTION_EXPAND_CHANGE } from "../../constants/actionTypes";
|
||||
import { OPTION_EXPAND_CHANGE } from "constants/actionTypes";
|
||||
|
||||
import withStyles from "@material-ui/core/styles/withStyles";
|
||||
import sidebarStyle from "../../assets/sidebarStyle.js";
|
||||
import sidebarStyle from "assets/sidebarStyle.js";
|
||||
import Collapse from "@material-ui/core/Collapse";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import React from "react";
|
||||
import { subcategorywrapper } from "../../utils/subcategorywrapper";
|
||||
import { hoc } from "../../utils/helpers";
|
||||
import { subcategorywrapper } from "utils/subcategorywrapper";
|
||||
import { hoc } from "utils/helpers";
|
||||
|
||||
import withStyles from "@material-ui/core/styles/withStyles";
|
||||
import sidebarStyle from "../../assets/sidebarStyle.js";
|
||||
import sidebarStyle from "assets/sidebarStyle.js";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import React from "react";
|
||||
import { optionchangewrapper } from "../../utils/optionchangewrapper";
|
||||
import { optionchangewrapper } from "utils/optionchangewrapper";
|
||||
|
||||
import withStyles from "@material-ui/core/styles/withStyles";
|
||||
import List from "@material-ui/core/List";
|
||||
import ListItem from "@material-ui/core/ListItem";
|
||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||
import ListItemText from "@material-ui/core/ListItemText";
|
||||
import sidebarStyle from "../../assets/sidebarStyle.js";
|
||||
import sidebarStyle from "assets/sidebarStyle.js";
|
||||
import StarBorder from "@material-ui/icons/StarBorder";
|
||||
|
||||
class DropdownWrapper extends React.Component {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import React from "react";
|
||||
import { Range } from "rc-slider";
|
||||
import { optionchangewrapper } from "../../utils/optionchangewrapper";
|
||||
import "../../assets/rangeStyle.css";
|
||||
import { optionchangewrapper } from "utils/optionchangewrapper";
|
||||
import "assets/rangeStyle.css";
|
||||
import "rc-slider/assets/index.css";
|
||||
import Input from "@material-ui/core/Input";
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import React from "react";
|
||||
import CheckboxAndRadioWrapper from "../../components/widgets/CheckboxAndRadioWrapper";
|
||||
import { optionchangewrapper } from "../../utils/optionchangewrapper";
|
||||
import CheckboxAndRadioWrapper from "components/widgets/CheckboxAndRadioWrapper";
|
||||
import { optionchangewrapper } from "utils/optionchangewrapper";
|
||||
|
||||
class SelectDisplayCheckboxWrapper extends React.Component {
|
||||
render() {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CATEGORY_SELECT } from "../constants/actionTypes";
|
||||
import { CATEGORY_SELECT } from "constants/actionTypes";
|
||||
|
||||
export default (state = null, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
SUBCATEGORY_SELECT,
|
||||
CATEGORY_SELECT,
|
||||
OPTION_CHANGE
|
||||
} from "../constants/actionTypes";
|
||||
} from "constants/actionTypes";
|
||||
|
||||
export default (state = [], action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { MODAL_CLOSE, MODAL_OPEN } from "../constants/actionTypes";
|
||||
import { MODAL_CLOSE, MODAL_OPEN } from "constants/actionTypes";
|
||||
|
||||
export default (state = false, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
SUBCATEGORY_SELECT,
|
||||
CATEGORY_SELECT,
|
||||
OPTION_CHANGE
|
||||
} from "../constants/actionTypes";
|
||||
} from "constants/actionTypes";
|
||||
|
||||
export default (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { SUBCATEGORY_SELECT, CATEGORY_SELECT } from "../constants/actionTypes";
|
||||
import { SUBCATEGORY_SELECT, CATEGORY_SELECT } from "constants/actionTypes";
|
||||
|
||||
export default (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
OPTION_EXPAND_CHANGE,
|
||||
CATEGORY_SELECT,
|
||||
SUBCATEGORY_SELECT
|
||||
} from "../constants/actionTypes";
|
||||
} from "constants/actionTypes";
|
||||
|
||||
export default (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { USER_DATA_CHANGED } from "../constants/actionTypes";
|
||||
import { USER_DATA_CHANGED } from "constants/actionTypes";
|
||||
|
||||
export default (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
MODAL_CLOSE,
|
||||
MODAL_OPEN,
|
||||
USER_DATA_CHANGED
|
||||
} from "../constants/actionTypes";
|
||||
} from "constants/actionTypes";
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { connect } from "react-redux";
|
||||
import { OPTION_CHANGE } from "../constants/actionTypes";
|
||||
import { OPTION_CHANGE } from "constants/actionTypes";
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { connect } from "react-redux";
|
||||
import { SUBCATEGORY_SELECT } from "../constants/actionTypes";
|
||||
import { SUBCATEGORY_SELECT } from "constants/actionTypes";
|
||||
|
||||
const mapStateToProps = state => {
|
||||
return {
|
||||
|
||||
6
ma-api/.gitignore
vendored
Normal file
6
ma-api/.gitignore
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
# package directories
|
||||
node_modules
|
||||
jspm_packages
|
||||
|
||||
# Serverless directories
|
||||
.serverless
|
||||
6
ma-api/README.md
Normal file
6
ma-api/README.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# How to deploy automatically:
|
||||
|
||||
1. set up aws cli with aws configure
|
||||
2. cd ma-api
|
||||
3. serverless deploy
|
||||
|
||||
83
ma-api/handler.js
Normal file
83
ma-api/handler.js
Normal file
@@ -0,0 +1,83 @@
|
||||
'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 };
|
||||
};
|
||||
|
||||
17
ma-api/lib/MarketAlert.js
Normal file
17
ma-api/lib/MarketAlert.js
Normal file
@@ -0,0 +1,17 @@
|
||||
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;
|
||||
8
ma-api/lib/arethereanynewitems.js
Normal file
8
ma-api/lib/arethereanynewitems.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const convertToDate = require("./convertToDate");
|
||||
function areThereAnyNewItems(lastItemDate, controlDate) {
|
||||
if (!lastItemDate) {
|
||||
return true;
|
||||
}
|
||||
return new Date(controlDate) < convertToDate(lastItemDate);
|
||||
}
|
||||
module.exports = areThereAnyNewItems;
|
||||
23
ma-api/lib/config/config.json
Normal file
23
ma-api/lib/config/config.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
13
ma-api/lib/convertToDate.js
Normal file
13
ma-api/lib/convertToDate.js
Normal file
@@ -0,0 +1,13 @@
|
||||
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;
|
||||
8
ma-api/lib/db.js
Normal file
8
ma-api/lib/db.js
Normal file
@@ -0,0 +1,8 @@
|
||||
const Sequelize = require("sequelize");
|
||||
const sequelize = new Sequelize("sql7276322", "sql7276322@localhost", "RS53ihYlg9", {
|
||||
host: "sql7.freemysqlhosting.net",
|
||||
dialect: "mysql",
|
||||
operatorsAliases: false
|
||||
});
|
||||
|
||||
module.exports = sequelize;
|
||||
96
ma-api/lib/index.js
Normal file
96
ma-api/lib/index.js
Normal file
@@ -0,0 +1,96 @@
|
||||
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}!`));
|
||||
27
ma-api/lib/migrations/20190310042110-create-market-alert.js
Normal file
27
ma-api/lib/migrations/20190310042110-create-market-alert.js
Normal file
@@ -0,0 +1,27 @@
|
||||
'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');
|
||||
}
|
||||
};
|
||||
37
ma-api/lib/models/index.js
Normal file
37
ma-api/lib/models/index.js
Normal 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;
|
||||
10
ma-api/lib/models/marketalert.js
Normal file
10
ma-api/lib/models/marketalert.js
Normal file
@@ -0,0 +1,10 @@
|
||||
'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;
|
||||
};
|
||||
42
ma-api/lib/scraptheitems.js
Normal file
42
ma-api/lib/scraptheitems.js
Normal file
@@ -0,0 +1,42 @@
|
||||
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;
|
||||
33
ma-api/lib/sendnotification.js
Normal file
33
ma-api/lib/sendnotification.js
Normal file
@@ -0,0 +1,33 @@
|
||||
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
Normal file
1918
ma-api/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
21
ma-api/package.json
Normal file
21
ma-api/package.json
Normal file
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
109
ma-api/serverless.yml
Normal file
109
ma-api/serverless.yml
Normal file
@@ -0,0 +1,109 @@
|
||||
# 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"
|
||||
1244
package-lock.json
generated
1244
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
30
package.json
30
package.json
@@ -1,30 +0,0 @@
|
||||
{
|
||||
"name": "marketalarm",
|
||||
"version": "1.0.1",
|
||||
"description": "Market Alarm",
|
||||
"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"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git@gitlab.com:saburly/marketalarm/web.git"
|
||||
},
|
||||
"author": "Saburlije",
|
||||
"license": "ISC",
|
||||
"engines": {
|
||||
"node": "11.10.x"
|
||||
},
|
||||
"dependencies": {
|
||||
"2checkout-node": "0.0.1",
|
||||
"@sendgrid/mail": "^6.3.1",
|
||||
"aws-sdk": "^2.422.0",
|
||||
"cheerio": "^1.0.0-rc.2",
|
||||
"express": "^4.16.4",
|
||||
"mysql2": "^1.6.4",
|
||||
"node-fetch": "^2.3.0",
|
||||
"sequelize": "^4.42.0"
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user