diff --git a/.gitignore b/.gitignore index 6704566..dd46c96 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +.zip + # Logs logs *.log diff --git a/DIRECTIONS.md b/DIRECTIONS.md new file mode 100644 index 0000000..d885fc6 --- /dev/null +++ b/DIRECTIONS.md @@ -0,0 +1 @@ +![Directions for use](assets/Directions.jpg) \ No newline at end of file diff --git a/assets/Directions.jpg b/assets/Directions.jpg new file mode 100644 index 0000000..4ad5dbf Binary files /dev/null and b/assets/Directions.jpg differ diff --git a/events/.env.sample b/events/.env.sample new file mode 100644 index 0000000..856b6d2 --- /dev/null +++ b/events/.env.sample @@ -0,0 +1,7 @@ +DATABASE_URL=postgres://localhost:5432/ + +MAILGUN_KEY= + +MAILGUN_DOMAIN= + +TICKET_API= diff --git a/events/index.js b/events/index.js new file mode 100644 index 0000000..ba61bad --- /dev/null +++ b/events/index.js @@ -0,0 +1,185 @@ +require('dotenv').config(); + +const axios = require('axios'); +const Mailgun = require('mailgun.js'); +const formData = require('form-data'); +const { Sequelize, DataTypes } = require('sequelize'); + +const DATABASE_URL = process.env.DATABASE_URL; +const db = new Sequelize(DATABASE_URL); + +const subs = db.define('Subscriptions', { + username: { + type: DataTypes.STRING, + required: true, + }, + type: { + type: DataTypes.ENUM(['weather', 'events']), + required: true, + }, +}); + +const users = db.define('Users', { + username: { + type: DataTypes.STRING, + required: true, + primaryKey: true, + unique: true, + }, + password: { + type: DataTypes.STRING, + required: true, + }, + role: { + type: DataTypes.STRING, + required: true, + }, + name: { + type: DataTypes.STRING, + required: false, + }, + phone: { + type: DataTypes.STRING, + required: true, + }, + email: { + type: DataTypes.STRING, + required: true, + }, + zip: { + type: DataTypes.STRING, + required: true, + }, + city: { + type: DataTypes.STRING, + required: true, + }, + state: { + type: DataTypes.STRING, + required: true, + }, +}); + +users.hasMany(subs, { + foreignKey: 'username', +}); +subs.belongsTo(users, { + foreignKey: 'username', +}); + +const mailgun = new Mailgun(formData); +const client = mailgun.client({ + username: 'api', + key: process.env.MAILGUN_KEY, +}); + +async function getEvents(city, stateCode) { + try { + + const currentDate = new Date(); + const startDateTime = currentDate.toISOString().slice(0, -5) + 'Z'; + + const endDateTime = new Date(currentDate.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, -5) + 'Z'; + console.log(startDateTime); + console.log(endDateTime); + + + let apikey = process.env.TICKET_API; + let radius = '20'; + let unit = 'miles'; + + + const response = await axios.get( + `https://app.ticketmaster.com/discovery/v2/events.json?size=5&apikey=${apikey}&startDateTime=${startDateTime}&endDateTime=${endDateTime}&city=${city}&radius=${radius}&unit=${unit}&stateCode=${stateCode}`); + + const concerts = response.data; + + + const toStandardTime = function (time) { + + const [hours, minutes, seconds] = time.split(':'); + let hoursFix = parseInt(hours, 10); + let minutesFix = parseInt(minutes, 10); + let secondsFix = parseInt(seconds, 10); + + if (hoursFix >= 12) { + + hoursFix -= 12; + + + time = `${hoursFix.toString().padStart(2, '0')}:${minutesFix + .toString() + .padStart(2, '0')}:${secondsFix.toString().padStart(2, '0')} PM`; + } else { + + time = `${hoursFix.toString().padStart(2, '0')}:${minutesFix + .toString() + .padStart(2, '0')}:${secondsFix.toString().padStart(2, '0')} AM`; + } + + return time; + }; + let eventList = ''; + + for (let i = 0; i < 5; i++) { + const venueData = concerts._embedded.events[i]; + const venue = venueData._embedded.venues[0].name; + const eventName = concerts._embedded.events[i].name; + const eventUrl = concerts._embedded.events[i].url; + const eventDate = concerts._embedded.events[i].dates; + const eventStartTime = eventDate.start.localTime; + const eventStartDate = eventDate.start.localDate; + const newTime = toStandardTime(eventStartTime); + + eventList += `
  • ${eventName} at ${venue} on ${eventStartDate} at ${newTime}, Link: ${eventUrl}
  • `; + } + return eventList; + } catch (error) { + console.error(error); + } +} + +async function mailgunEvents(eventList, email) { + + const msg = { + to: email, + from: 'notifyme.us@gmail.com', + subject: 'Check Out These 5 events happening this week!', + html: `

    Five Upcoming Events in your Area:

    + `, + }; + + await client.messages.create(process.env.MAILGUN_DOMAIN, msg); + console.log('completed mailgun send'); +} + + +exports.handler = async () => { + const subsList = await subs.findAll({ + where: { + type: 'events', + }, + include: [{ + model: users, + required: true, + }], + }); + console.log(subsList); + + await Promise.allSettled(subsList.map(async sub => { + try { + console.log('THIS IS SUB -------------', sub.User.dataValues); + const { city, state, email } = sub.User.dataValues; + + const eventList = await getEvents(city, state); + await mailgunEvents( eventList, email); + console.log(`----- SUCCESS sending events summary to ${email} -----`); + } catch (e) { + console.log(e.message); + } + })); + console.log(`----- SUCCESS -----`); + +}; diff --git a/events/package-lock.json b/events/package-lock.json new file mode 100644 index 0000000..ba4d647 --- /dev/null +++ b/events/package-lock.json @@ -0,0 +1,500 @@ +{ + "name": "events", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "events", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.2.1", + "dotenv": "^16.0.3", + "form-data": "^4.0.0", + "mailgun.js": "^8.0.6", + "pg": "^8.8.0", + "sequelize": "^6.28.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "node_modules/@types/validator": { + "version": "13.7.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz", + "integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/dottie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", + "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mailgun.js": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/mailgun.js/-/mailgun.js-8.0.6.tgz", + "integrity": "sha512-b+c7QO1T4oFsudEcRB2H7oZKth8ZDeYRW4xjW12QQVNYDSJCVxqSQfps6ofcH8fqcCMJdzc76HVNGdnUZgBPCw==", + "dependencies": { + "axios": "^0.27.2", + "base-64": "^1.0.0", + "url-join": "^4.0.1" + } + }, + "node_modules/mailgun.js/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.40", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz", + "integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==", + "dependencies": { + "moment": ">= 2.9.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/retry-as-promised": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.3.tgz", + "integrity": "sha512-SEvMa4khHvpU/o6zgh7sK24qm6rxVgKnrSyzb5POeDvZx5N9Bf0s5sQsQ4Fl+HjRp0X+w2UzACGfUnXtx6cJ9Q==" + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sequelize": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.28.0.tgz", + "integrity": "sha512-+WHqvUQgTp19GLkt+gyQ+F6qg+FIEO2O5F9C0TOYV/PjZ2a/XwWvVkL1NCkS4VSIjVVvAUutiW6Wv9ofveGaVw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.7", + "@types/validator": "^13.7.1", + "debug": "^4.3.3", + "dottie": "^2.0.2", + "inflection": "^1.13.2", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "moment-timezone": "^0.5.34", + "pg-connection-string": "^2.5.0", + "retry-as-promised": "^7.0.3", + "semver": "^7.3.5", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.7.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/events/package.json b/events/package.json new file mode 100644 index 0000000..d2988f7 --- /dev/null +++ b/events/package.json @@ -0,0 +1,20 @@ +{ + "name": "events", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.2.1", + "dotenv": "^16.0.3", + "form-data": "^4.0.0", + "mailgun.js": "^8.0.6", + "pg": "^8.8.0", + "sequelize": "^6.28.0" + } +} diff --git a/events/test.js b/events/test.js new file mode 100644 index 0000000..e4167d3 --- /dev/null +++ b/events/test.js @@ -0,0 +1,6 @@ +const test = require('.').handler; + +test().then(() => { + console.log('complete'); +}); + diff --git a/eventsNotification.js b/eventsNotification.js new file mode 100644 index 0000000..e69de29 diff --git a/external-api copy.js b/external-api copy.js new file mode 100644 index 0000000..e9ecaaf --- /dev/null +++ b/external-api copy.js @@ -0,0 +1,348 @@ +const router = require('express').Router(); +const axios = require('axios'); +const Mailgun = require('mailgun-js'); + +require('dotenv').config(); +const mailgun = new Mailgun({ + apiKey: process.env.MAILGUN_KEY, + domain: process.env.MAILGUN_DOMAIN, +}); + +const getForecast = socket => ( async () => { + + let zip = '60513'; + + try { + + const API_KEY = process.env.WEATHER_KEY; + + let token = process.env.MAPBOX_TOKEN; + + + const latLon = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/postcode/${zip}.json?access_token=${token}&country=us`); + + + let lat = latLon.data.features[0].center[1]; + let lon = latLon.data.features[0].center[0]; + + const response = await axios.get( + `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${API_KEY}`, + ); + + const getForecastData = function (dayIndex) { + const forecast = response.data.list[dayIndex]; + const weather = forecast.weather[0].description; + const temperature = forecast.main.temp; + const fahrenheit = (temperature - 273.15) * 9 / 5 + 32; + const fahrenheitRounded = Math.round(fahrenheit * 100) / 100; + const town = response.data.city.name; + return { + weather: weather, + temp: fahrenheitRounded, + city: town, + }; + }; + + const forecastData1 = getForecastData(4); + const forecastData2 = getForecastData(12); + const forecastData3 = getForecastData(20); + const forecastData4 = getForecastData(28); + const forecastData5 = getForecastData(36); + + + const currentDate = new Date(); + const forecastDates = []; + + for (let i = 0; i < 5; i++) { + const forecastDate = new Date(currentDate); + forecastDate.setDate(currentDate.getDate() + i); + forecastDates.push(forecastDate); + } + + console.log(response.data); + + + + const msg = { + to: 'steveo732@gmail.com', + from: 'notifyme.us@gmail.com', + subject: 'Your 5 Day Weather Forecast', + text: `The weather forecast for the next 5 days in ${forecastData1.city} from NotifyMe-US: + ${forecastDates[0].toLocaleDateString()}: ${forecastData1.temp} °F with ${forecastData1.weather} + ${forecastDates[1].toLocaleDateString()}: ${forecastData2.temp} °F with ${forecastData2.weather} + ${forecastDates[2].toLocaleDateString()}: ${forecastData3.temp} °F with ${forecastData3.weather} + ${forecastDates[3].toLocaleDateString()}: ${forecastData4.temp} °F with ${forecastData4.weather} + ${forecastDates[4].toLocaleDateString()}: ${forecastData5.temp} °F with ${forecastData5.weather} + `, + html: `

    The weather forecast for the next 5 days in ${forecastData1.city} from NotifyMe-US: +

    +

    `, + }; + + + mailgun.messages().send(msg, (error, body) => { + if (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } else { + res.send({ message: 'Email sent successfully', msg }); + console.log(msg.text); + } + }); + + } catch (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } +}); + + +const getWeather = socket => (async () => { + + let zip = '60513'; + try { + + const API_KEY = process.env.WEATHER_KEY; + let token = process.env.MAPBOX_TOKEN; + + + const latLon = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/postcode/${zip}.json?access_token=${token}&country=us`); + + + let lat = latLon.data.features[0].center[1]; + let lon = latLon.data.features[0].center[0]; + + const response = await axios.get( + `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}`); + + + const forecast = response.data; + const weather = forecast.weather[0].description; + const temperatureHigh = forecast.main.temp_max; + const fahrenheitHigh = (temperatureHigh - 273.15) * 9 / 5 + 32; + const fahrenheitRoundedHigh = Math.round(fahrenheitHigh * 100) / 100; + const temperatureLow = forecast.main.temp_min; + const fahrenheitLow = (temperatureLow - 273.15) * 9 / 5 + 32; + const fahrenheitRoundedLow = Math.round(fahrenheitLow * 100) / 100; + const humidity = forecast.main.humidity; + const windspeed = forecast.wind.speed; + const windMph = windspeed * 2.236936; + const windMphFormatted = windMph.toFixed(2); + const cloudCoverage = forecast.clouds.all; + const town = forecast.name; + + const msg = { + to: 'steveo732@gmail.com', + from: 'notifyme.us@gmail.com', + subject: 'Your Daily Weather Forecast', + text: `"Here is your custom weather forecast for today from NotifyMe-US: + + The weather in ${town} is currently ${weather}. The high temperature for today will be ${fahrenheitRoundedHigh} degrees Fahrenheit, and the low temperature will be ${fahrenheitRoundedLow} degrees Fahrenheit. The humidity will be ${humidity}%, and the wind speed will be ${windMphFormatted} mph. There will be ${cloudCoverage}% cloud coverage."`, + + + html: `

    + Here is your custom weather forecast for today from NotifyMe-US:: +

    +

    `, + }; + + mailgun.messages().send(msg, (error, body) => { + if (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } else { + console.log(msg.text); + + res.send({ message: 'Email sent successfully', msg }); + } + }); + + } catch (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } +}); + + +const getDirections = socket => ( async () => { + let token = process.env.MAPBOX_TOKEN; + + let addressOne = encodeURIComponent('4026 sunnyside ave 60513'); + let addressTwo = encodeURIComponent('4255 N Knox Ave 60641'); + + try { + + const latLonOne = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/address/${addressOne}.json?types=address%2Cplace&access_token=${token}&country=us`); + + + const latLonTwo = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/address/${addressTwo}.json?access_token=${token}&country=us`); + + + let addressOneCoor = latLonOne.data.features[0].center; + let addressTwoCoor = latLonTwo.data.features[0].center; + + let coordinates = `${addressOneCoor[0]},${addressOneCoor[1]};${addressTwoCoor[0]},${addressTwoCoor[1]}`; + + + const response = await axios.get(`https://api.mapbox.com/directions/v5/mapbox/driving-traffic/${coordinates}?geometries=polyline&overview=simplified&steps=true&access_token=${token}`); + + const directions = response.data; + const step = directions.routes[0].legs[0].steps; + + const instructionsAndDistance = step.map(step => ({ + instruction: step.maneuver.instruction, + distance: parseFloat((step.distance / 1609.344).toFixed(2)), + })); + + const instructions = instructionsAndDistance.map(step => `${step.instruction} (${step.distance} miles)`).join('\n'); + const distance = instructionsAndDistance.reduce((acc, step) => acc + step.distance, 0); + const roundedDistance = distance.toFixed(2); + + const duration = Math.ceil(directions.routes[0].duration / 60); + + const msg = { + to: 'steveo732@gmail.com', + from: 'notifyme.us@gmail.com', + subject: 'Your Daily Commute from NotifyMe-US', + text: `Your Daily Commute from NotifyMe-US for ${new Date().toLocaleDateString()} + Total distance: ${roundedDistance} miles + Total duration: ${duration} minutes, + + Best Route: + ${instructions}`, + + html: `

    Your Daily Commute from NotifyMe-US for ${new Date().toLocaleDateString()}

    +

    Total distance: ${roundedDistance} miles

    +

    Total duration: ${duration} minutes

    +

    Best Route:

    + +
      + ${instructionsAndDistance.map(step => `
    1. ${step.instruction} (${step.distance} miles)
    2. `).join('')} +
    + `, + + }; + + + + mailgun.messages().send(msg, (error, body) => { + if (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } else { + console.log(msg.text); + res.send({ message: 'Email sent successfully', msg }); + + } + }); + } catch (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } +}); + +const getEvents = socket => ( async () => { + try { + + const currentDate = new Date(); + const startDateTime = currentDate.toISOString().slice(0, -5) + 'Z'; + + const endDateTime = new Date(currentDate.getTime() + 7 * 24 * 60 * 60 * 1000).toISOString().slice(0, -5) + 'Z'; + console.log(startDateTime); + console.log(endDateTime); + + + let apikey = process.env.TICKET_API; + let city = 'seattle'; + let stateCode = 'wa'; + let radius = '20'; + let unit = 'miles'; + + + const response = await axios.get( + `https://app.ticketmaster.com/discovery/v2/events.json?size=5&apikey=${apikey}&startDateTime=${startDateTime}&endDateTime=${endDateTime}&city=${city}&radius=${radius}&unit=${unit}&stateCode=${stateCode}`); + + const concerts = response.data; + + + const toStandardTime = function (time) { + + const [hours, minutes, seconds] = time.split(':'); + let hoursFix = parseInt(hours, 10); + let minutesFix = parseInt(minutes, 10); + let secondsFix = parseInt(seconds, 10); + + if (hoursFix >= 12) { + + hoursFix -= 12; + + + time = `${hoursFix.toString().padStart(2, '0')}:${minutesFix + .toString() + .padStart(2, '0')}:${secondsFix.toString().padStart(2, '0')} PM`; + } else { + + time = `${hoursFix.toString().padStart(2, '0')}:${minutesFix + .toString() + .padStart(2, '0')}:${secondsFix.toString().padStart(2, '0')} AM`; + } + + return time; + }; + let eventList = ''; + + for (let i = 0; i < 5; i++) { + const venueData = concerts._embedded.events[i]; + const venue = venueData._embedded.venues[0].name; + const eventName = concerts._embedded.events[i].name; + const eventUrl = concerts._embedded.events[i].url; + const eventDate = concerts._embedded.events[i].dates; + const eventStartTime = eventDate.start.localTime; + const eventStartDate = eventDate.start.localDate; + const newTime = toStandardTime(eventStartTime); + + eventList += `
  • ${eventName} at ${venue} on ${eventStartDate} at ${newTime}, Link: ${eventUrl}
  • `; + } + + const msg = { + to: 'steveo732@gmail.com', + from: 'notifyme.us@gmail.com', + subject: 'Check Out These 5 events happening this week!', + text: `Five Upcoming Events in your Area: ${eventList}`, + html: `

    Five Upcoming Events in your Area:

    + `, + }; + + mailgun.messages().send(msg, (error, body) => { + if (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } else { + console.log(msg.text); + socket.emit('API_RESULT', 'success'); // TODO - replace 'success' with actual api result + } + }); + } catch (error) { + console.error(error); + socket.emit('API_ERROR', 'An error has occured with your request'); + } +}); diff --git a/weather/.DS_Store b/weather/.DS_Store new file mode 100644 index 0000000..a3c6fd6 Binary files /dev/null and b/weather/.DS_Store differ diff --git a/weather/.env.sample b/weather/.env.sample new file mode 100644 index 0000000..2d2c206 --- /dev/null +++ b/weather/.env.sample @@ -0,0 +1,9 @@ +DATABASE_URL=postgres://localhost:5432/ + +MAILGUN_KEY= + +MAILGUN_DOMAIN= + +WEATHER_KEY= + +MAPBOX_TOKEN= diff --git a/weather/index.js b/weather/index.js new file mode 100644 index 0000000..91daded --- /dev/null +++ b/weather/index.js @@ -0,0 +1,230 @@ +require('dotenv').config(); + +const axios = require('axios'); +const Mailgun = require('mailgun.js'); +const formData = require('form-data'); +const { Sequelize, DataTypes } = require('sequelize'); + +const DATABASE_URL = process.env.DATABASE_URL; +const db = new Sequelize(DATABASE_URL); + +const subs = db.define('Subscriptions', { + username: { + type: DataTypes.STRING, + required: true, + }, + type: { + type: DataTypes.ENUM(['weather', 'events']), + required: true, + }, +}); + +const users = db.define('Users', { + username: { + type: DataTypes.STRING, + required: true, + primaryKey: true, + unique: true, + }, + password: { + type: DataTypes.STRING, + required: true, + }, + role: { + type: DataTypes.STRING, + required: true, + }, + name: { + type: DataTypes.STRING, + required: false, + }, + phone: { + type: DataTypes.STRING, + required: true, + }, + email: { + type: DataTypes.STRING, + required: true, + }, + zip: { + type: DataTypes.STRING, + required: true, + }, +}); + +users.hasMany(subs, { + foreignKey: 'username', +}); +subs.belongsTo(users, { + foreignKey: 'username', +}); + +const mailgun = new Mailgun(formData); +const client = mailgun.client({ + username: 'api', + key: process.env.MAILGUN_KEY, +}); + +const getCurrentWeather = async (zip) => { + try { + const API_KEY = process.env.WEATHER_KEY; + let token = process.env.MAPBOX_TOKEN; + + const latLon = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/postcode/${zip}.json?access_token=${token}&country=us`); + + let lat = latLon.data.features[0].center[1]; + let lon = latLon.data.features[0].center[0]; + + const response = await axios.get( + `https://api.openweathermap.org/data/2.5/weather?lat=${lat}&lon=${lon}&appid=${API_KEY}`); + + const forecast = response.data; + const temperatureHigh = forecast.main.temp_max; + const fahrenheitHigh = (temperatureHigh - 273.15) * 9 / 5 + 32; + const temperatureLow = forecast.main.temp_min; + const fahrenheitLow = (temperatureLow - 273.15) * 9 / 5 + 32; + const windspeed = forecast.wind.speed; + const windMph = windspeed * 2.236936; + + const output = { + town: forecast.name, + weather: forecast.weather[0].description, + fahrenheitRoundedHigh: Math.round(fahrenheitHigh * 100) / 100, + fahrenheitRoundedLow: Math.round(fahrenheitLow * 100) / 100, + humidity: forecast.main.humidity, + windMphFormatted: windMph.toFixed(2), + cloudCoverage: forecast.clouds.all, + }; + + return output; + } catch (error) { + console.error(error); + throw 'An error has occured with your request (current)'; + } +}; + +const getForecast = async (zip) => { + try { + const API_KEY = process.env.WEATHER_KEY; + let token = process.env.MAPBOX_TOKEN; + + + const latLon = await axios.get(`https://api.mapbox.com/geocoding/v5/mapbox.places/postcode/${zip}.json?access_token=${token}&country=us`); + + let lat = latLon.data.features[0].center[1]; + let lon = latLon.data.features[0].center[0]; + + const response = await axios.get( + `https://api.openweathermap.org/data/2.5/forecast?lat=${lat}&lon=${lon}&appid=${API_KEY}`, + ); + + const getForecastData = function (dayIndex) { + const forecast = response.data.list[dayIndex]; + const weather = forecast.weather[0].description; + const temperature = forecast.main.temp; + const fahrenheit = (temperature - 273.15) * 9 / 5 + 32; + const fahrenheitRounded = Math.round(fahrenheit * 100) / 100; + const town = response.data.city.name; + return { + weather: weather, + temp: fahrenheitRounded, + city: town, + }; + }; + + const forecast = { + data: [], + dates: [], + }; + + for (let i = 0; i < 5; i++) { + let j = i * 8 + 4; + forecast.data[i] = getForecastData(j); + const forecastDate = new Date(); + forecastDate.setDate(forecastDate.getDate() + i); + forecast.dates[i] = forecastDate; + } + return forecast; + + } catch (error) { + console.error(error); + throw 'An error has occured with your request (forecast)'; + } +}; + +async function mailgunForecast(current, forecast, email) { + const { data, dates } = forecast; + + const msg = { + to: email, + from: 'notifyme.us@gmail.com', + subject: 'Your Daily Weather Summary', + html: `
    +

    + Here is your custom weather forecast for ${current.town} from NotifyMe-US: +

      +
    • + The weather is currently ${current.weather}. The high temperature for today will be ${current.fahrenheitRoundedHigh} degrees Fahrenheit, and the low temperature will be ${current.fahrenheitRoundedLow} degrees Fahrenheit. +
    • +
    • + The humidity will be ${current.humidity}%, and the wind speed will be ${current.windMphFormatted} mph. There will be ${current.cloudCoverage}% cloud coverage. +
    • +
    +

    + +

    + The weather forecast for the next 5 days: +

      +
    • + ${dates[0].toLocaleDateString()}: ${data[0].temp} °F with ${data[0].weather} +
    • +
    • + ${dates[1].toLocaleDateString()}: ${data[1].temp} °F with ${data[1].weather} +
    • +
    • + ${dates[2].toLocaleDateString()}: ${data[2].temp} °F with ${data[2].weather} +
    • +
    • + ${dates[3].toLocaleDateString()}: ${data[3].temp} °F with ${data[3].weather} +
    • +
    • + ${dates[4].toLocaleDateString()}: ${data[4].temp} °F with ${data[4].weather} +
    • +
    +

    +
    `, + }; + + + await client.messages.create(process.env.MAILGUN_DOMAIN, msg); + console.log('completed mailgun send'); +} + + +exports.handler = async () => { + const subsList = await subs.findAll({ + where: { + type: 'weather', + }, + include: [{ + model: users, + required: true, + }], + }); + console.log(subsList); + + await Promise.allSettled(subsList.map(async sub => { + try { + console.log('THIS IS SUB -------------', sub.User.dataValues); + const { zip, email } = sub.User.dataValues; + const currentWeather = await getCurrentWeather(zip); + const forecast = await getForecast(zip); + await mailgunForecast(currentWeather, forecast, email); + console.log(`----- SUCCESS sending weather summary to ${email} -----`); + } catch(e) { + console.log(e.message); + } + })); + console.log(`----- SUCCESS -----`); + +}; diff --git a/weather/package-lock.json b/weather/package-lock.json new file mode 100644 index 0000000..e6725b7 --- /dev/null +++ b/weather/package-lock.json @@ -0,0 +1,500 @@ +{ + "name": "lambdas", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "lambdas", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "axios": "^1.2.1", + "dotenv": "^16.0.3", + "form-data": "^4.0.0", + "mailgun.js": "^8.0.6", + "pg": "^8.8.0", + "sequelize": "^6.28.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.7.tgz", + "integrity": "sha512-9AonUzyTjXXhEOa0DnqpzZi6VHlqKMswga9EXjpXnnqxwLtdvPPtlO8evrI5D9S6asFRCQ6v+wpiUKbw+vKqyg==", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/ms": { + "version": "0.7.31", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.31.tgz", + "integrity": "sha512-iiUgKzV9AuaEkZqkOLDIvlQiL6ltuZd9tGcW3gwpnX8JbuiuhFlEGmmFXEXkN50Cvq7Os88IY2v0dkDqXYWVgA==" + }, + "node_modules/@types/node": { + "version": "18.11.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.11.18.tgz", + "integrity": "sha512-DHQpWGjyQKSHj3ebjFI/wRKcqQcdR+MoFBygntYOZytCqNfkd2ZC4ARDJ2DQqhjH5p85Nnd3jhUJIXrszFX/JA==" + }, + "node_modules/@types/validator": { + "version": "13.7.10", + "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.7.10.tgz", + "integrity": "sha512-t1yxFAR2n0+VO6hd/FJ9F2uezAZVWHLmpmlJzm1eX03+H7+HsuTAp7L8QJs+2pQCfWkP1+EXsGK9Z9v7o/qPVQ==" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" + }, + "node_modules/axios": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.2.1.tgz", + "integrity": "sha512-I88cFiGu9ryt/tfVEi4kX2SITsvDddTajXTOFmt2uK1ZVA8LytjtdeyefdQWEf5PU8w+4SSJDoYnggflB5tW4A==", + "dependencies": { + "follow-redirects": "^1.15.0", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/base-64": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base-64/-/base-64-1.0.0.tgz", + "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==" + }, + "node_modules/buffer-writer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/buffer-writer/-/buffer-writer-2.0.0.tgz", + "integrity": "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dotenv": { + "version": "16.0.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.0.3.tgz", + "integrity": "sha512-7GO6HghkA5fYG9TYnNxi14/7K9f5occMlp3zXAuSxn7CKCxt9xbNWG7yF8hTCSUchlfWSe3uLmlPfigevRItzQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/dottie": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/dottie/-/dottie-2.0.2.tgz", + "integrity": "sha512-fmrwR04lsniq/uSr8yikThDTrM7epXHBAAjH9TbeH3rEA8tdCO7mRzB9hdmdGyJCxF8KERo9CITcm3kGuoyMhg==" + }, + "node_modules/follow-redirects": { + "version": "1.15.2", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.2.tgz", + "integrity": "sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/mailgun.js": { + "version": "8.0.6", + "resolved": "https://registry.npmjs.org/mailgun.js/-/mailgun.js-8.0.6.tgz", + "integrity": "sha512-b+c7QO1T4oFsudEcRB2H7oZKth8ZDeYRW4xjW12QQVNYDSJCVxqSQfps6ofcH8fqcCMJdzc76HVNGdnUZgBPCw==", + "dependencies": { + "axios": "^0.27.2", + "base-64": "^1.0.0", + "url-join": "^4.0.1" + } + }, + "node_modules/mailgun.js/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, + "node_modules/moment-timezone": { + "version": "0.5.40", + "resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.40.tgz", + "integrity": "sha512-tWfmNkRYmBkPJz5mr9GVDn9vRlVZOTe6yqY92rFxiOdWXbjaR0+9LwQnZGGuNR63X456NqmEkbskte8tWL5ePg==", + "dependencies": { + "moment": ">= 2.9.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/packet-reader": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/packet-reader/-/packet-reader-1.0.0.tgz", + "integrity": "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==" + }, + "node_modules/pg": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/pg/-/pg-8.8.0.tgz", + "integrity": "sha512-UXYN0ziKj+AeNNP7VDMwrehpACThH7LUl/p8TDFpEUuSejCUIwGSfxpHsPvtM6/WXFy6SU4E5RG4IJV/TZAGjw==", + "dependencies": { + "buffer-writer": "2.0.0", + "packet-reader": "1.0.0", + "pg-connection-string": "^2.5.0", + "pg-pool": "^3.5.2", + "pg-protocol": "^1.5.0", + "pg-types": "^2.1.0", + "pgpass": "1.x" + }, + "engines": { + "node": ">= 8.0.0" + }, + "peerDependencies": { + "pg-native": ">=3.0.1" + }, + "peerDependenciesMeta": { + "pg-native": { + "optional": true + } + } + }, + "node_modules/pg-connection-string": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.5.0.tgz", + "integrity": "sha512-r5o/V/ORTA6TmUnyWZR9nCj1klXCO2CEKNRlVuJptZe85QuhFayC7WeMic7ndayT5IRIR0S0xFxFi2ousartlQ==" + }, + "node_modules/pg-int8": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz", + "integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/pg-pool": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.5.2.tgz", + "integrity": "sha512-His3Fh17Z4eg7oANLob6ZvH8xIVen3phEZh2QuyrIl4dQSDVEabNducv6ysROKpDNPSD+12tONZVWfSgMvDD9w==", + "peerDependencies": { + "pg": ">=8.0" + } + }, + "node_modules/pg-protocol": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.5.0.tgz", + "integrity": "sha512-muRttij7H8TqRNu/DxrAJQITO4Ac7RmX3Klyr/9mJEOBeIpgnF8f9jAfRz5d3XwQZl5qBjF9gLsUtMPJE0vezQ==" + }, + "node_modules/pg-types": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz", + "integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==", + "dependencies": { + "pg-int8": "1.0.1", + "postgres-array": "~2.0.0", + "postgres-bytea": "~1.0.0", + "postgres-date": "~1.0.4", + "postgres-interval": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/pgpass": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz", + "integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==", + "dependencies": { + "split2": "^4.1.0" + } + }, + "node_modules/postgres-array": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz", + "integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==", + "engines": { + "node": ">=4" + } + }, + "node_modules/postgres-bytea": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz", + "integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-date": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz", + "integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/postgres-interval": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz", + "integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==", + "dependencies": { + "xtend": "^4.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" + }, + "node_modules/retry-as-promised": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/retry-as-promised/-/retry-as-promised-7.0.3.tgz", + "integrity": "sha512-SEvMa4khHvpU/o6zgh7sK24qm6rxVgKnrSyzb5POeDvZx5N9Bf0s5sQsQ4Fl+HjRp0X+w2UzACGfUnXtx6cJ9Q==" + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/semver/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, + "node_modules/sequelize": { + "version": "6.28.0", + "resolved": "https://registry.npmjs.org/sequelize/-/sequelize-6.28.0.tgz", + "integrity": "sha512-+WHqvUQgTp19GLkt+gyQ+F6qg+FIEO2O5F9C0TOYV/PjZ2a/XwWvVkL1NCkS4VSIjVVvAUutiW6Wv9ofveGaVw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/sequelize" + } + ], + "dependencies": { + "@types/debug": "^4.1.7", + "@types/validator": "^13.7.1", + "debug": "^4.3.3", + "dottie": "^2.0.2", + "inflection": "^1.13.2", + "lodash": "^4.17.21", + "moment": "^2.29.1", + "moment-timezone": "^0.5.34", + "pg-connection-string": "^2.5.0", + "retry-as-promised": "^7.0.3", + "semver": "^7.3.5", + "sequelize-pool": "^7.1.0", + "toposort-class": "^1.0.1", + "uuid": "^8.3.2", + "validator": "^13.7.0", + "wkx": "^0.5.0" + }, + "engines": { + "node": ">=10.0.0" + }, + "peerDependenciesMeta": { + "ibm_db": { + "optional": true + }, + "mariadb": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "oracledb": { + "optional": true + }, + "pg": { + "optional": true + }, + "pg-hstore": { + "optional": true + }, + "snowflake-sdk": { + "optional": true + }, + "sqlite3": { + "optional": true + }, + "tedious": { + "optional": true + } + } + }, + "node_modules/sequelize-pool": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/sequelize-pool/-/sequelize-pool-7.1.0.tgz", + "integrity": "sha512-G9c0qlIWQSK29pR/5U2JF5dDQeqqHRragoyahj/Nx4KOOQ3CPPfzxnfqFPCSB7x5UgjOgnZ61nSxz+fjDpRlJg==", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/sequelize/node_modules/inflection": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/inflection/-/inflection-1.13.4.tgz", + "integrity": "sha512-6I/HUDeYFfuNCVS3td055BaXBwKYuzw7K3ExVMStBowKo9oOAMJIXIHvdyR3iboTCp1b+1i5DSkIZTcwIktuDw==", + "engines": [ + "node >= 0.4.0" + ] + }, + "node_modules/split2": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.1.0.tgz", + "integrity": "sha512-VBiJxFkxiXRlUIeyMQi8s4hgvKCSjtknJv/LVYbrgALPwf5zSKmEwV9Lst25AkvMDnvxODugjdl6KZgwKM1WYQ==", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/toposort-class": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toposort-class/-/toposort-class-1.0.1.tgz", + "integrity": "sha512-OsLcGGbYF3rMjPUf8oKktyvCiUxSbqMMS39m33MAjLTC1DVIH6x3WSt63/M77ihI09+Sdfk1AXvfhCEeUmC7mg==" + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/validator": { + "version": "13.7.0", + "resolved": "https://registry.npmjs.org/validator/-/validator-13.7.0.tgz", + "integrity": "sha512-nYXQLCBkpJ8X6ltALua9dRrZDHVYxjJ1wgskNt1lH9fzGjs3tgojGSCBjmEPwkWS1y29+DrizMTW19Pr9uB2nw==", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/wkx": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/wkx/-/wkx-0.5.0.tgz", + "integrity": "sha512-Xng/d4Ichh8uN4l0FToV/258EjMGU9MGcA0HV2d9B/ZpZB3lqQm7nkOdZdm5GhKtLLhAE7PiVQwN4eN+2YJJUg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "engines": { + "node": ">=0.4" + } + } + } +} diff --git a/weather/package.json b/weather/package.json new file mode 100644 index 0000000..8f4c42f --- /dev/null +++ b/weather/package.json @@ -0,0 +1,20 @@ +{ + "name": "lambdas", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "axios": "^1.2.1", + "dotenv": "^16.0.3", + "form-data": "^4.0.0", + "mailgun.js": "^8.0.6", + "pg": "^8.8.0", + "sequelize": "^6.28.0" + } +} diff --git a/weather/test.js b/weather/test.js new file mode 100644 index 0000000..e4167d3 --- /dev/null +++ b/weather/test.js @@ -0,0 +1,6 @@ +const test = require('.').handler; + +test().then(() => { + console.log('complete'); +}); +