Skip to content

Commit d11fd02

Browse files
committed
modified: .gitignore
modified: README.md new file: crypto.js modified: index.js modified: package-lock.json modified: package.json new file: public/PermitGeolocation.JPG new file: public/PermitGeolocation.PNG modified: public/Runtime.PNG new file: public/geolocation.js modified: views/index.ejs renamed: views/about.ejs -> views/pages/about.ejs new file: views/pages/weatherbit.ejs modified: views/partials/header.ejs modified: views/partials/header2.ejs deleted: views/weatherbit.ejs
1 parent 0081fdc commit d11fd02

File tree

16 files changed

+663
-543
lines changed

16 files changed

+663
-543
lines changed

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
node_modules
22
.env
3-
public/ePub
3+
public/ePub
4+
views/header2.ejs
5+
index-temp.ejs

README.md

Lines changed: 27 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,10 @@ It is a typical NodeJS project layout.
9696

9797
`npm start`
9898

99+
>Initially, the browser might pop up asking if you would allow it to detect your location. A 'Allow' will enable GPS pinpoint weather report. A 'Block' will disable GPS for current and subsequent session.
100+
101+
![Browser popup](./public/PermitGeolocation.PNG)
102+
99103
<br />
100104

101105

@@ -111,22 +115,27 @@ The Docker container is built and saved to current working directory. Replace t
111115
`docker build -t hurricanemark/localweather:1.0 .`
112116

113117
```c
114-
[+] Building 139.3s (11/11) FINISHED
115-
=> [internal] load build definition from Dockerfile 0.1s
116-
=> => transferring dockerfile: 210B 0.1s
117-
=> [internal] load .dockerignore
118-
...
119-
=> [internal] load build context 0.8s
120-
=> => transferring context: 143.24kB 0.6s
121-
=> [2/5] WORKDIR /app 1.2s
122-
=> [3/5] COPY package.json ./ 0.1s
123-
=> [4/5] RUN npm install 6.6s
124-
=> [5/5] COPY . . 0.2s
125-
=> exporting to image 0.4s
126-
=> => exporting layers 0.3s
127-
=> => writing image sha256:bb89b0646be41055287fdac18ea1e405a2a19ef4b2919a0a02c213b9dc947b34 0.0s
128-
=> => naming to docker.io/hurricanemark/localweather:1.0
129-
```
118+
docker build -t marknre/techrolemiweatherapp:2.0 .
119+
120+
[+] Building 3.1s (11/11) FINISHED
121+
=> [internal] load build definition from Dockerfile 0.0s
122+
=> => transferring dockerfile: 32B 0.0s
123+
=> [internal] load .dockerignore 0.0s
124+
=> => transferring context: 2B 0.0s
125+
=> [internal] load metadata for docker.io/library/node:16.17.0 1.1s
126+
=> [auth] library/node:pull token for registry-1.docker.io 0.0s
127+
=> [internal] load build context 0.4s
128+
=> => transferring context: 635.22kB 0.3s
129+
=> [1/5] FROM docker.io/library/node:16.17.0@sha256:a5d9200d3b8c17f0f3d7717034a9c215015b7aae70cb2a9 0.0s
130+
=> CACHED [2/5] WORKDIR /app 0.0s
131+
=> CACHED [3/5] COPY package.json ./ 0.0s
132+
=> CACHED [4/5] RUN npm install 0.0s
133+
=> [5/5] COPY . . 0.9s
134+
=> exporting to image 0.7s
135+
=> => exporting layers 0.6s
136+
=> => writing image sha256:1c4db40ce8799665d8226aea4ae5759b3dc17c0538992b95e59b2e74375da1c7 0.0s
137+
=> => naming to docker.io/marknre/techrolemiweatherapp:2.0 0.0s
138+
```
130139

131140
**List the image**
132141

@@ -147,11 +156,11 @@ A docker image built with this `git-tag`:`Phase1-Extened-Weather-Forecasts` is a
147156

148157
### Run docker
149158

150-
Notice that environment variables (secret keys) required to run the app is not being included in the Dockerfile. These secret keys will be stated with the container. eg. ` ... -e WEATHER_VISUALCROSSING_API_KEY=XXXXX -e WEATHERBIT_KEY=XXXXXXX`
159+
Notice that environment variables (secret keys) required to run the app is not being included in the Dockerfile. These secret keys will be stated with the container. eg. ` ... -e WEATHERBIT_KEY=XXXXXXX`
151160

152161
Notice also that Dockerfile exposes port 8080. This needs to be forwarded to a port on your local machine. i.e. `LOCAL_PORT:CONTAINER_PORT` for example *4321:8080*
153162

154-
`docker run -p 4321:8080 bb89b0646be4 -e WEATHER_VISUALCROSSING_API_KEY=Actual_Secret_Key_for_VisualCrossing -e WEATHERBIT_KEY=Actual_Secret_Key_for_Weatherbit`
163+
`docker run -p 4321:8080 bb89b0646be4 -e WEATHERBIT_KEY=Actual_Secret_Key_for_Weatherbit`
155164

156165

157166

crypto.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
2+
import CryptoJS from 'crypto-js';
3+
const encrypt = (text) => {
4+
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(text));
5+
};
6+
7+
const decrypt = (data) => {
8+
return CryptoJS.enc.Base64.parse(data).toString(CryptoJS.enc.Utf8);
9+
};
10+
11+
function test() {
12+
let enStr = encrypt("e88e1a8096cd4897b79b230a9c49b243");
13+
console.log("Encrypted Key: " + enStr);
14+
console.log("Decrypted key: " + decrypt(enStr));
15+
16+
}
17+
18+
// test();
19+
export {encrypt, decrypt};

index.js

Lines changed: 105 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ dotenv.config()
33
import request from 'request';
44
import express from 'express';
55
import bodyParser from 'body-parser';
6+
import {encrypt, decrypt} from './crypto.js'
67

78
// Create network routing
89
const app = express();
@@ -17,20 +18,39 @@ app.use(bodyParser.urlencoded({ extended: true }));
1718

1819
// get the locale from the client-side via the ejs form
1920
app.get('/', (req, res) => {
20-
res.render('index');
21+
// console.dir(req.params);
22+
// console.dir(req.body);
23+
let apikey = encrypt(process.env.WEATHERBIT_KEY);
24+
res.render('index1', {xkey: apikey});
2125
})
2226

27+
28+
2329
app.get('/weatherbit', (req, res) => {
24-
res.render('weatherbit');
30+
// console.log('render get weatherbit:');
31+
// console.dir(req.params)
32+
// console.dir(req.body);
33+
let apikey = encrypt(process.env.WEATHERBIT_KEY);
34+
res.render('pages/weatherbit', { key: apikey});
2535
})
2636

2737
// Posting data to the client-side requires two API calls.
2838
// We implement the Promise.all() below to call and wait for all data to come back.
39+
2940
app.post('/weatherbit', (req, res) => {
3041
let city = req.body.locale;
31-
let promisedData = gatherWeatheBits(city);
42+
let coords = [ req.body.lat, req.body.lng ];
43+
let promisedData;
44+
if (city.length > 0) {
45+
// get data by address
46+
promisedData = gatherWeatheBits(city);
47+
} else if (typeof(coords[0]) === "string") {
48+
// get data by latitude/longitude
49+
promisedData = gatherWeatheBits(coords);
50+
}
51+
3252
promisedData.then( (data) => {
33-
res.render('weatherbit', data);
53+
res.render('pages/weatherbit', data);
3454
})
3555
});
3656

@@ -40,12 +60,17 @@ app.post('/weatherbit', (req, res) => {
4060
*/
4161
function getWeatherBitCurrentConditions(city){
4262
return new Promise(resolve => {
63+
if (Array.isArray(city) === true && typeof(city[0]) === "string") {
64+
city = "&lat=" + city[0] + "&lon=" + city[1];
65+
} else {
66+
city = "&city=" + city;
67+
}
4368
setTimeout(() => {
4469
let apiKey = process.env.WEATHERBIT_KEY;
4570
let url = process.env.WEATHERBIT_URI;
46-
let uriWeatherBitStr = `${url}current?units=I&city=${city}&key=${apiKey}`;
71+
let uriWeatherBitStr = `${url}current?units=I${city}&key=${apiKey}`;
4772
let retCode;
48-
console.log(uriWeatherBitStr);
73+
// console.log(uriWeatherBitStr);
4974
try {
5075
request(uriWeatherBitStr, async function (err, response, body) {
5176
console.log(response.statusCode);
@@ -74,10 +99,15 @@ function getWeatherBitCurrentConditions(city){
7499
*/
75100
function getWeatherBitDailyForecast(city){
76101
return new Promise(resolve => {
102+
if (Array.isArray(city) === true && typeof(city[0]) === "string") {
103+
city = "&lat=" + city[0] + "&lon=" + city[1];
104+
} else {
105+
city = "&city=" + city;
106+
}
77107
setTimeout(() => {
78108
let apiKey = process.env.WEATHERBIT_KEY;
79109
let url = process.env.WEATHERBIT_URI;
80-
let uriWeatherBitStr = `${url}forecast/daily?units=I&city=${city}&key=${apiKey}`;
110+
let uriWeatherBitStr = `${url}forecast/daily?units=I${city}&key=${apiKey}`;
81111
let retCode;
82112
// console.log(uriWeatherBitStr);
83113
try {
@@ -102,28 +132,86 @@ function getWeatherBitDailyForecast(city){
102132
})
103133
}
104134

135+
function getWeatherBitAirQuality(city) {
136+
/* 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} */
137+
return new Promise(resolve => {
138+
if (Array.isArray(city) === true && typeof(city[0]) === "string") {
139+
city = "?lat=" + city[0] + "&lon=" + city[1];
140+
} else {
141+
city = "?city=" + city;
142+
}
143+
setTimeout(() => {
144+
let apiKey = process.env.WEATHERBIT_KEY;
145+
let url = process.env.WEATHERBIT_URI;
146+
let e = new Date().toISOString().slice(0, 16).replace('T', ' ')
147+
148+
let enddate = e.split(' ')[0];
149+
// add 1 day to enddate
150+
let s = new Date(enddate);
151+
s.setDate(s.getDate() - 1);
152+
s = s.toISOString().slice(0, 16).replace('T', ' ');
153+
let startdate = s.split(' ')[0];
154+
155+
let retCode;
156+
let uriWeatherBitAPIStr = `${url}history/airquality${city}&start_date=${startdate}&end_date=${enddate}&key=${apiKey}`;
157+
158+
// console.log(uriWeatherBitAPIStr);
159+
try {
160+
request(uriWeatherBitAPIStr, async function (err, response, body) {
161+
console.log(response.statusCode);
162+
163+
if (response.statusCode == 429) {
164+
console.log("WARNING: You have exceeded your API call limit with weatherbit.io!");
165+
resolve(null);
166+
}
167+
if (response.statusCode == 200) {
168+
retCode = await JSON.parse(body);
169+
resolve(retCode);
170+
} else {
171+
resolve(null);
172+
}
173+
})
174+
} catch (err) {
175+
console.log(err);
176+
}
177+
}, 300);
178+
179+
})
180+
}
181+
182+
183+
105184
/*
106185
* Return multiple promises consists of currentConditions and dailyForecast data.
107186
*/
108187
async function gatherWeatheBits(city) {
109-
const [currentConditions, dailyForecast] = await Promise.all([
188+
const [dailyForecast, currentConditions, airQuality,] = await Promise.all([
189+
getWeatherBitDailyForecast(city),
110190
getWeatherBitCurrentConditions(city),
111-
getWeatherBitDailyForecast(city)
191+
getWeatherBitAirQuality(city)
112192
]);
113193

114-
// Since daily forecast might take longer, check its promise here.
115-
if (dailyForecast !== null) {
116-
// combine 2 promises:
117-
let combinedData = { locale: city, curStatus: 200, curData: currentConditions, foreStatus: 200, foreData: dailyForecast, error: null };
194+
195+
// Make sure all promisses fulfilled.
196+
if (dailyForecast !== null && airQuality !== null && currentConditions !== null ) {
197+
let currentHour = new Date().getHours();
198+
// combine 3 promises into a huge rendering passing paramters:
199+
let combinedData = { locale: city, curStatus: 200, curData: currentConditions, foreStatus: 200, foreData: dailyForecast, airqStatus: 200, airqData: airQuality.data[currentHour], error: null };
118200
return combinedData;
119201
} else {
120-
return { locale: city, curStatus: 400, curData: null, foreStatus: 400, foreData: null, error: null };
202+
return { locale: city, curStatus: 400, curData: null, foreStatus: 400, foreData: null, airqStatus: 400, airqData: null, error: null };
121203
}
122204
}
123205

124206
// post weather data to the client-side
125207
app.post('/', (req, res) => {
126-
console.log(req.body.locale);
208+
// currentLoc();
209+
// console.log(currentLoc);
210+
// console.log("Lng: " + longitude + ", Lat: " + latitude);
211+
console.dir(req.params);
212+
console.dir(req.body);
213+
214+
return;
127215
// call weatherAPI for data
128216
let apiKey = process.env.WEATHER_VISUALCROSSING_API_KEY;
129217
let city = req.body.locale;
@@ -154,14 +242,14 @@ app.post('/', (req, res) => {
154242
});
155243

156244
} catch (err) {
157-
res.render('index', { locale: city, data: null, forecast: null, error: err.message });
245+
res.render('index1', { locale: city, data: null, forecast: null, error: err.message });
158246
}
159247
});
160248

161249

162250
// about page
163251
app.get('/about', function(req, res) {
164-
res.render('about');
252+
res.render('pages/about');
165253
});
166254

167255
let port = process.env.PORT || 3210;

package-lock.json

Lines changed: 11 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"license": "ISC",
1515
"dependencies": {
1616
"body-parser": "^1.20.0",
17+
"crypto-js": "^4.1.1",
1718
"dotenv": "^16.0.3",
1819
"ejs": "^3.1.8",
1920
"express": "^4.18.1",

public/PermitGeolocation.JPG

13.3 KB
Loading

public/PermitGeolocation.PNG

4.67 KB
Loading

public/Runtime.PNG

183 Bytes
Loading

0 commit comments

Comments
 (0)