Skip to content

Commit 13c8e08

Browse files
committed
modified: README.md
1 parent 9afade5 commit 13c8e08

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

index.copy.js

Lines changed: 335 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,335 @@
1+
import * as dotenv from 'dotenv';
2+
let WEATHERBIT_KEY = "";
3+
let WEATHERBIT_URI = "";
4+
5+
dotenv.config();
6+
7+
// dotenv.config()
8+
import request from 'request';
9+
import express from 'express';
10+
import bodyParser from 'body-parser';
11+
import {encryptAES, decryptAES} from './crypto.js';
12+
13+
14+
import awssdk from 'aws-sdk';
15+
16+
17+
18+
19+
if (process.env.NODE_ENV == 'production') {
20+
// Code for AWS Production Mode
21+
getAwsSecrets();
22+
} else if (process.env.NODE_ENV === 'awsdeploy') {
23+
WEATHERBIT_URI="https://api.weatherbit.io/v2.0/";
24+
WEATHERBIT_KEY="U2FsdGVkX18HMV5UUT9rJN76hOtIHDw1bH0beQYWH8a6E7uzKqskdgHvc6Nq2lO6O+GAb2vrcL+X8ZDqcGPuLw==";
25+
console.log("AWSDEPLOY mode!");
26+
} else {
27+
// Code for Development Mode
28+
WEATHERBIT_KEY = process.env.WEATHERBIT_KEY;
29+
WEATHERBIT_URI = process.env.WEATHERBIT_URI;
30+
}
31+
32+
33+
// Create network routing
34+
const app = express();
35+
36+
// EJS is accessed by default in the views directory.
37+
app.set('view engine', 'ejs');
38+
39+
// Allow access to 'public' folder where resources are available to this app
40+
app.use(express.static('public'));
41+
42+
app.use(bodyParser.urlencoded({ extended: true }));
43+
44+
// get the locale from the client-side via the ejs form
45+
app.get('/', (req, res) => {
46+
// console.dir(req.params);
47+
// console.dir(req.body);
48+
let apikey = encryptAES(WEATHERBIT_KEY);
49+
res.render('index', {xkey: apikey});
50+
})
51+
52+
53+
54+
app.get('/weatherbit', (req, res) => {
55+
// console.log('render get weatherbit:');
56+
// console.dir(req.params)
57+
// console.dir(req.body);
58+
res.render('pages/weatherbit');
59+
})
60+
61+
// Posting data to the client-side requires two API calls.
62+
// We implement the Promise.all() below to call and wait for all data to come back.
63+
64+
app.post('/weatherbit', (req, res) => {
65+
let city = req.body.locale;
66+
let coords = [ req.body.lat, req.body.lng ];
67+
let promisedData;
68+
// console.log(coords[0], coords[1]);
69+
if (city.length > 0) {
70+
// get data by address
71+
promisedData = gatherWeatheBits(city);
72+
} else if (typeof(coords[0]) === "string") {
73+
// get data by latitude/longitude
74+
promisedData = gatherWeatheBits(coords);
75+
}
76+
77+
promisedData.then( (data) => {
78+
res.render('pages/weatherbit', data);
79+
})
80+
});
81+
82+
83+
/*
84+
* function get AWS environment variables
85+
*/
86+
function getAwsSecrets() {
87+
var AWS = awssdk,
88+
region = "us-east-1",
89+
secretName = "techrolemi_weather_secret",
90+
secret,
91+
decodedBinarySecret;
92+
93+
// Create a Secrets Manager client
94+
var client = new AWS.SecretsManager({
95+
region: region
96+
});
97+
98+
99+
client.getSecretValue({SecretId: secretName}, function(err, data) {
100+
if (err) {
101+
if (err.code === 'DecryptionFailureException')
102+
// Secrets Manager can't decrypt the protected secret text using the provided KMS key.
103+
// Deal with the exception here, and/or rethrow at your discretion.
104+
throw err;
105+
else if (err.code === 'InternalServiceErrorException')
106+
// An error occurred on the server side.
107+
// Deal with the exception here, and/or rethrow at your discretion.
108+
throw err;
109+
else if (err.code === 'InvalidParameterException')
110+
// You provided an invalid value for a parameter.
111+
// Deal with the exception here, and/or rethrow at your discretion.
112+
throw err;
113+
else if (err.code === 'InvalidRequestException')
114+
// You provided a parameter value that is not valid for the current state of the resource.
115+
// Deal with the exception here, and/or rethrow at your discretion.
116+
throw err;
117+
else if (err.code === 'ResourceNotFoundException')
118+
// We can't find the resource that you asked for.
119+
// Deal with the exception here, and/or rethrow at your discretion.
120+
throw err;
121+
}
122+
else {
123+
// Decrypts secret using the associated KMS key.
124+
// Depending on whether the secret is a string or binary, one of these fields will be populated.
125+
// console.log(data);
126+
if ('SecretString' in data) {
127+
secret = data.SecretString;
128+
var aesParams = JSON.parse(secret);
129+
// console.log(data.Name + " : " + data.SecretString);
130+
Object.entries(aesParams).forEach((entry) => {
131+
var [key, value] = entry;
132+
if (`${key}` === "WEATHERBIT_KEY") {
133+
WEATHERBIT_KEY = `${value}`;
134+
console.log("WEATHERBIT_KEY="+ WEATHERBIT_KEY);
135+
}
136+
else if (`${key}` === "WEATHERBIT_URI") {
137+
WEATHERBIT_URI = `${value}`;
138+
console.log("WEATHERBIT_URI="+WEATHERBIT_URI);
139+
}
140+
});
141+
142+
} else {
143+
let buff = new Buffer(data.SecretBinary, 'base64');
144+
decodedBinarySecret = buff.toString('ascii');
145+
console.log("decodedBinarySecret: " + decodedBinarySecret);
146+
}
147+
}
148+
})
149+
}
150+
151+
152+
153+
/*
154+
* Function retrieves weatherbit.io current conditions.
155+
* Used in the Promise call below.
156+
*/
157+
function getWeatherBitCurrentConditions(city){
158+
return new Promise(resolve => {
159+
if (Array.isArray(city) === true && typeof(city[0]) === "string") {
160+
city = "&lat=" + city[0] + "&lon=" + city[1];
161+
} else {
162+
city = "&city=" + city;
163+
}
164+
165+
var Xcode = "";
166+
if (process.env.NODE_ENV === 'awsdeploy') {
167+
Xcode = JSON.parse(decryptAES(WEATHERBIT_KEY)).text;
168+
} else {
169+
Xcode = WEATHERBIT_KEY;
170+
}
171+
172+
setTimeout(() => {
173+
174+
let uriWeatherBitStr = `${WEATHERBIT_URI}current?units=I${city}&key=${Xcode}`;
175+
let retCode;
176+
if (WEATHERBIT_URI.length === 0) {
177+
console.log('Failed to get aws secrets!');
178+
return null;
179+
} else {
180+
console.log(uriWeatherBitStr);
181+
}
182+
try {
183+
request(uriWeatherBitStr, async function (err, response, body) {
184+
console.log(response.statusCode);
185+
if (response.statusCode == 429) {
186+
console.log("WARNING: You have exceeded your API call limit with weatherbit.io!");
187+
resolve(null);
188+
}
189+
if (response.statusCode == 200) {
190+
let weather = await JSON.parse(body).data[0];
191+
resolve(weather);
192+
} else {
193+
resolve(null);
194+
}
195+
})
196+
} catch (err) {
197+
console.log(err);
198+
}
199+
})
200+
}, 300);
201+
}
202+
203+
204+
/*
205+
* Function retrieves weatherbit.io daily forecast.
206+
* Used in the Promise call below.
207+
*/
208+
function getWeatherBitDailyForecast(city){
209+
return new Promise(resolve => {
210+
if (Array.isArray(city) === true && typeof(city[0]) === "string") {
211+
city = "&lat=" + city[0] + "&lon=" + city[1];
212+
} else {
213+
city = "&city=" + city;
214+
}
215+
216+
var Xcode = "";
217+
if (process.env.NODE_ENV === 'awsdeploy') {
218+
Xcode = JSON.parse(decryptAES(WEATHERBIT_KEY)).text;
219+
} else {
220+
Xcode = WEATHERBIT_KEY;
221+
}
222+
223+
setTimeout(() => {
224+
let uriWeatherBitStr = `${WEATHERBIT_URI}forecast/daily?units=I${city}&key=${Xcode}`;
225+
let retCode;
226+
// console.log(uriWeatherBitStr);
227+
try {
228+
request(uriWeatherBitStr, async function (err, response, body) {
229+
console.log(response.statusCode);
230+
231+
if (response.statusCode == 429) {
232+
console.log("WARNING: You have exceeded your API call limit with weatherbit.io!");
233+
resolve(null);
234+
}
235+
if (response.statusCode == 200) {
236+
retCode = await JSON.parse(body);
237+
resolve(retCode);
238+
} else {
239+
resolve(null);
240+
}
241+
})
242+
} catch (err) {
243+
console.log(err);
244+
}
245+
}, 500);
246+
})
247+
}
248+
249+
function getWeatherBitAirQuality(city) {
250+
/* API_URL = https://api.weatherbit.io/v2.0/history/airquality?city=${city}&start_date=2022-10-03&end_date=2022-10-04&tz=local&key=${apikey} */
251+
return new Promise(resolve => {
252+
if (Array.isArray(city) === true && typeof(city[0]) === "string") {
253+
city = "?lat=" + city[0] + "&lon=" + city[1];
254+
} else {
255+
city = "?city=" + city;
256+
}
257+
258+
var Xcode = "";
259+
if (process.env.NODE_ENV === 'awsdeploy') {
260+
Xcode = JSON.parse(decryptAES(WEATHERBIT_KEY)).text;
261+
} else {
262+
Xcode = WEATHERBIT_KEY;
263+
}
264+
265+
setTimeout(() => {
266+
let e = new Date().toISOString().slice(0, 16).replace('T', ' ')
267+
let enddate = e.split(' ')[0];
268+
// add 1 day to enddate
269+
let s = new Date(enddate);
270+
s.setDate(s.getDate() - 1);
271+
s = s.toISOString().slice(0, 16).replace('T', ' ');
272+
let startdate = s.split(' ')[0];
273+
274+
let retCode;
275+
let uriWeatherBitAPIStr = `${WEATHERBIT_URI}history/airquality${city}&start_date=${startdate}&end_date=${enddate}&key=${Xcode}`;
276+
277+
// console.log(uriWeatherBitAPIStr);
278+
try {
279+
request(uriWeatherBitAPIStr, async function (err, response, body) {
280+
console.log(response.statusCode);
281+
282+
if (response.statusCode == 429) {
283+
console.log("WARNING: You have exceeded your API call limit with weatherbit.io!");
284+
resolve(null);
285+
}
286+
if (response.statusCode == 200) {
287+
retCode = await JSON.parse(body);
288+
resolve(retCode);
289+
} else {
290+
resolve(null);
291+
}
292+
})
293+
} catch (err) {
294+
console.log(err);
295+
}
296+
}, 300);
297+
298+
})
299+
}
300+
301+
302+
/*
303+
* Return multiple promises consists of currentConditions and dailyForecast data.
304+
*/
305+
async function gatherWeatheBits(city) {
306+
const [dailyForecast, currentConditions, airQuality,] = await Promise.all([
307+
getWeatherBitDailyForecast(city),
308+
getWeatherBitCurrentConditions(city),
309+
getWeatherBitAirQuality(city)
310+
]);
311+
312+
313+
// Make sure all promisses fulfilled.
314+
if (dailyForecast !== null && airQuality !== null && currentConditions !== null ) {
315+
let currentHour = new Date().getHours();
316+
// combine 3 promises into a huge rendering passing paramters:
317+
let combinedData = { locale: city, curStatus: 200, curData: currentConditions, foreStatus: 200, foreData: dailyForecast, airqStatus: 200, airqData: airQuality.data[currentHour], error: null };
318+
return combinedData;
319+
} else {
320+
return { locale: city, curStatus: 400, curData: null, foreStatus: 400, foreData: null, airqStatus: 400, airqData: null, error: null };
321+
}
322+
}
323+
324+
// about page
325+
app.get('/about', function(req, res) {
326+
res.render('pages/about');
327+
});
328+
329+
let port = process.env.PORT || 3000;
330+
331+
// creating a server that is listening on ${port} for connections.
332+
app.listen(port, () => {
333+
console.log(`TechRolEmi weather report is listening on port ${port}`);
334+
});
335+

0 commit comments

Comments
 (0)