Showing
19 changed files
with
350 additions
and
74 deletions
1 | // Weather broadcasting page | 1 | // Weather broadcasting page |
2 | -import axios from "axios"; | ||
3 | import { useState, useEffect } from "react"; | 2 | import { useState, useEffect } from "react"; |
3 | +import { Navigate } from "react-router-dom"; | ||
4 | +import weatherService from "./service/weather"; | ||
4 | // components | 5 | // components |
5 | import Bottombar from "./components/Bottombar"; | 6 | import Bottombar from "./components/Bottombar"; |
6 | import Topbar from "./components/Topbar"; | 7 | import Topbar from "./components/Topbar"; |
7 | -import ChatroomBox from "./components/ChatroomBox"; | ||
8 | 8 | ||
9 | function App() { | 9 | function App() { |
10 | + const token = sessionStorage.getItem("user-token"); | ||
11 | + const [weather, setWeather] = useState(); | ||
12 | + const [forecast, setForecast] = useState(); | ||
13 | + const [air, setAir] = useState(); | ||
14 | + | ||
15 | + const handleData = async (e) => { | ||
16 | + setWeather(null); | ||
17 | + setForecast(null); | ||
18 | + setAir(null); | ||
19 | + if (e.target.id === "weather") { | ||
20 | + const weatherData = await weatherService.getWeather(token); | ||
21 | + setWeather(weatherData); | ||
22 | + } else if (e.target.id === "forecast") { | ||
23 | + const forecasetData = await weatherService.getForecaset(token); | ||
24 | + setForecast(forecasetData); | ||
25 | + } else if (e.target.id === "air") { | ||
26 | + const airData = await weatherService.getAirPollution(token); | ||
27 | + setAir(airData); | ||
28 | + } | ||
29 | + }; | ||
30 | + | ||
31 | + if (!token) { | ||
32 | + return <Navigate to="/login" />; | ||
33 | + } | ||
34 | + | ||
10 | return ( | 35 | return ( |
11 | <div> | 36 | <div> |
12 | <Topbar /> | 37 | <Topbar /> |
13 | <h1>Weather Page (home)</h1> | 38 | <h1>Weather Page (home)</h1> |
14 | - <div></div> | 39 | + <div className="weather-buttons"> |
40 | + <button id="weather" onClick={(e) => handleData(e)}> | ||
41 | + Weather | ||
42 | + </button> | ||
43 | + <button id="forecast" onClick={(e) => handleData(e)}> | ||
44 | + Forecast | ||
45 | + </button> | ||
46 | + <button id="air" onClick={(e) => handleData(e)}> | ||
47 | + Air Pollution | ||
48 | + </button> | ||
49 | + </div> | ||
50 | + | ||
51 | + {!weather ? ( | ||
52 | + "" | ||
53 | + ) : ( | ||
54 | + <div> | ||
55 | + <h2> | ||
56 | + {weather.meta.city} ({weather.meta.country}) - {weather.description} | ||
57 | + </h2> | ||
58 | + <small>Time Zone : UTC {weather.meta.timezone}</small> | ||
59 | + <p> | ||
60 | + Temperature : {weather.temp.realCelcius} C / feels like{" "} | ||
61 | + {weather.temp.feelCelcius} C | ||
62 | + </p> | ||
63 | + <p>Wind : {weather.types.wind} m/s</p> | ||
64 | + <p>Cloud : {weather.types.clouds} %</p> | ||
65 | + <p>{weather.rain ? "rain : Yes" : "rain : No"}</p> | ||
66 | + <p>{weather.snow ? "snow : Yes" : "snow : No"}</p> | ||
67 | + </div> | ||
68 | + )} | ||
15 | 69 | ||
16 | <Bottombar /> | 70 | <Bottombar /> |
17 | </div> | 71 | </div> |
... | @@ -19,11 +73,3 @@ function App() { | ... | @@ -19,11 +73,3 @@ function App() { |
19 | } | 73 | } |
20 | 74 | ||
21 | export default App; | 75 | export default App; |
22 | - | ||
23 | -// {Array.isArray(chats) ? ( | ||
24 | -// chats.map((chat, i) => { | ||
25 | -// return <ChatroomBox key={i} chat={chat} />; | ||
26 | -// }) | ||
27 | -// ) : ( | ||
28 | -// <h2>No chatting room!</h2> | ||
29 | -// )} | ... | ... |
1 | import { Link } from "react-router-dom"; | 1 | import { Link } from "react-router-dom"; |
2 | // styles | 2 | // styles |
3 | import "../styles/bar.scss"; | 3 | import "../styles/bar.scss"; |
4 | -import { TiWeatherPartlySunny } from "react-icons/ti"; | 4 | +import { FcHome } from "react-icons/fc"; |
5 | import { AiFillSetting } from "react-icons/ai"; | 5 | import { AiFillSetting } from "react-icons/ai"; |
6 | 6 | ||
7 | function Topbar() { | 7 | function Topbar() { |
8 | return ( | 8 | return ( |
9 | <div className="topbar"> | 9 | <div className="topbar"> |
10 | <Link to="/" className="logo"> | 10 | <Link to="/" className="logo"> |
11 | - <TiWeatherPartlySunny /> | 11 | + <FcHome /> |
12 | </Link> | 12 | </Link> |
13 | <Link to="/settings" className="settings"> | 13 | <Link to="/settings" className="settings"> |
14 | <AiFillSetting /> | 14 | <AiFillSetting /> | ... | ... |
... | @@ -8,7 +8,7 @@ import Signup from "./routes/signup"; | ... | @@ -8,7 +8,7 @@ import Signup from "./routes/signup"; |
8 | import Login from "./routes/login"; | 8 | import Login from "./routes/login"; |
9 | import Users from "./routes/users"; | 9 | import Users from "./routes/users"; |
10 | import Settings from "./routes/settings"; | 10 | import Settings from "./routes/settings"; |
11 | -import Tell from "./routes/tell"; | 11 | +import Tweet from "./routes/tweet"; |
12 | import NotFound from "./routes/notfound"; | 12 | import NotFound from "./routes/notfound"; |
13 | // styles | 13 | // styles |
14 | import "./styles/layout.scss"; | 14 | import "./styles/layout.scss"; |
... | @@ -24,7 +24,7 @@ root.render( | ... | @@ -24,7 +24,7 @@ root.render( |
24 | <Route exact path="/signup" element={<Signup />} /> | 24 | <Route exact path="/signup" element={<Signup />} /> |
25 | <Route exact path="/users" element={<Users />} /> | 25 | <Route exact path="/users" element={<Users />} /> |
26 | <Route exact path="/settings" element={<Settings />} /> | 26 | <Route exact path="/settings" element={<Settings />} /> |
27 | - <Route exact path="/tell" element={<Tell />} /> | 27 | + <Route exact path="/tell" element={<Tweet />} /> |
28 | <Route path="*" element={<NotFound />} /> | 28 | <Route path="*" element={<NotFound />} /> |
29 | </Routes> | 29 | </Routes> |
30 | </BrowserRouter> | 30 | </BrowserRouter> | ... | ... |
... | @@ -15,7 +15,7 @@ function Login() { | ... | @@ -15,7 +15,7 @@ function Login() { |
15 | }); | 15 | }); |
16 | 16 | ||
17 | useEffect(() => { | 17 | useEffect(() => { |
18 | - if (localStorage.key("user-token")) { | 18 | + if (sessionStorage.key("user-token")) { |
19 | navigate("/", { replace: true }); | 19 | navigate("/", { replace: true }); |
20 | } | 20 | } |
21 | }, [loginSuccess, setLoginSuccess, navigate]); | 21 | }, [loginSuccess, setLoginSuccess, navigate]); |
... | @@ -29,6 +29,10 @@ function Login() { | ... | @@ -29,6 +29,10 @@ function Login() { |
29 | 29 | ||
30 | const handleSubmit = async (e) => { | 30 | const handleSubmit = async (e) => { |
31 | e.preventDefault(); | 31 | e.preventDefault(); |
32 | + if (!userinfo.email || !userinfo.password) { | ||
33 | + alert("Please fill all boxes"); | ||
34 | + return; | ||
35 | + } | ||
32 | const answer = await authService.handleLogin(userinfo); | 36 | const answer = await authService.handleLogin(userinfo); |
33 | setLoginSuccess(answer); | 37 | setLoginSuccess(answer); |
34 | }; | 38 | }; | ... | ... |
... | @@ -11,12 +11,13 @@ function Signup() { | ... | @@ -11,12 +11,13 @@ function Signup() { |
11 | const [userinfo, setUserInfo] = useState({ | 11 | const [userinfo, setUserInfo] = useState({ |
12 | username: "", | 12 | username: "", |
13 | country: "", | 13 | country: "", |
14 | + city: "", | ||
14 | email: "", | 15 | email: "", |
15 | password: "", | 16 | password: "", |
16 | }); | 17 | }); |
17 | 18 | ||
18 | useEffect(() => { | 19 | useEffect(() => { |
19 | - if (localStorage.key("user-token")) { | 20 | + if (sessionStorage.key("user-token")) { |
20 | navigate("/", { replace: true }); | 21 | navigate("/", { replace: true }); |
21 | } | 22 | } |
22 | }, [signupSuccess, setSignupSuccess, navigate]); | 23 | }, [signupSuccess, setSignupSuccess, navigate]); |
... | @@ -44,6 +45,11 @@ function Signup() { | ... | @@ -44,6 +45,11 @@ function Signup() { |
44 | 45 | ||
45 | <br /> | 46 | <br /> |
46 | 47 | ||
48 | + <p> | ||
49 | + <b>Country Code</b> and <b>City Name</b> is required for Weather | ||
50 | + service! | ||
51 | + </p> | ||
52 | + | ||
47 | <form className="authForm" onSubmit={(e) => handleSubmit(e)}> | 53 | <form className="authForm" onSubmit={(e) => handleSubmit(e)}> |
48 | <label htmlFor="username"> | 54 | <label htmlFor="username"> |
49 | <input | 55 | <input |
... | @@ -63,6 +69,15 @@ function Signup() { | ... | @@ -63,6 +69,15 @@ function Signup() { |
63 | id="country" | 69 | id="country" |
64 | /> | 70 | /> |
65 | </label> | 71 | </label> |
72 | + <label htmlFor="city"> | ||
73 | + <input | ||
74 | + placeholder="City Name" | ||
75 | + onChange={(e) => onChange(e)} | ||
76 | + value={userinfo.city} | ||
77 | + type="text" | ||
78 | + id="city" | ||
79 | + /> | ||
80 | + </label> | ||
66 | <label htmlFor="email"> | 81 | <label htmlFor="email"> |
67 | <input | 82 | <input |
68 | placeholder="email" | 83 | placeholder="email" | ... | ... |
client/src/routes/tell.jsx
deleted
100644 → 0
client/src/routes/tweet.jsx
0 → 100644
1 | const axios = require("axios").default; | 1 | const axios = require("axios").default; |
2 | 2 | ||
3 | -const handleSignup = async ({ username, country, email, password }) => { | 3 | +const handleSignup = async ({ username, country, city, email, password }) => { |
4 | try { | 4 | try { |
5 | const response = await axios.post("http://localhost:8080/api/users", { | 5 | const response = await axios.post("http://localhost:8080/api/users", { |
6 | username, | 6 | username, |
7 | country, | 7 | country, |
8 | + city, | ||
8 | email, | 9 | email, |
9 | password, | 10 | password, |
10 | }); | 11 | }); |
11 | if (response.data) { | 12 | if (response.data) { |
12 | - console.log("signup request sent..."); | 13 | + sessionStorage.setItem("user-token", JSON.stringify(response.data)); |
13 | - localStorage.setItem("user-token", JSON.stringify(response.data)); | ||
14 | return "success"; | 14 | return "success"; |
15 | } | 15 | } |
16 | } catch (err) { | 16 | } catch (err) { |
... | @@ -25,8 +25,7 @@ const handleLogin = async ({ email, password }) => { | ... | @@ -25,8 +25,7 @@ const handleLogin = async ({ email, password }) => { |
25 | password, | 25 | password, |
26 | }); | 26 | }); |
27 | if (response.data) { | 27 | if (response.data) { |
28 | - console.log("signin request sent..."); | 28 | + sessionStorage.setItem("user-token", JSON.stringify(response.data)); |
29 | - localStorage.setItem("user-token", JSON.stringify(response.data)); | ||
30 | return "success"; | 29 | return "success"; |
31 | } | 30 | } |
32 | } catch (err) { | 31 | } catch (err) { | ... | ... |
client/src/service/weather.js
0 → 100644
1 | +const axios = require("axios").default; | ||
2 | + | ||
3 | +const getWeather = async (user) => { | ||
4 | + if (!user) { | ||
5 | + return; | ||
6 | + } | ||
7 | + try { | ||
8 | + const response = await axios.post("http://localhost:8080/api/weather"); | ||
9 | + const data = response.data; | ||
10 | + const formattedData = { | ||
11 | + meta: { | ||
12 | + country: data.sys.country, | ||
13 | + city: data.name, | ||
14 | + timezone: data.timezone / 60 / 60, | ||
15 | + sunrise: data.sys.sunrise, | ||
16 | + sunset: data.sys.sunset, | ||
17 | + }, | ||
18 | + description: data.weather[0].description, | ||
19 | + temp: { | ||
20 | + realCelcius: (data.main.temp - 273.15).toFixed(2), | ||
21 | + feelCelcius: (data.main.feels_like - 273.15).toFixed(2), | ||
22 | + realFer: ((data.main.temp - 273.15) * 9) / 5 + 32, | ||
23 | + feelFer: ((data.main.feels_like - 273.15) * 9) / 5 + 32, | ||
24 | + }, | ||
25 | + types: { | ||
26 | + wind: data.wind.speed, | ||
27 | + clouds: data.clouds.all, | ||
28 | + rain: data.rain ? data.rain : null, | ||
29 | + snow: data.snow ? data.snow : null, | ||
30 | + }, | ||
31 | + }; | ||
32 | + return formattedData; | ||
33 | + } catch (err) { | ||
34 | + console.log(err); | ||
35 | + } | ||
36 | +}; | ||
37 | + | ||
38 | +const getForecaset = async () => { | ||
39 | + return; | ||
40 | +}; | ||
41 | + | ||
42 | +const getAirPollution = async () => { | ||
43 | + return; | ||
44 | +}; | ||
45 | + | ||
46 | +const weatherService = { | ||
47 | + getWeather, | ||
48 | + getForecaset, | ||
49 | + getAirPollution, | ||
50 | +}; | ||
51 | + | ||
52 | +export default weatherService; |
... | @@ -17,4 +17,18 @@ | ... | @@ -17,4 +17,18 @@ |
17 | input { | 17 | input { |
18 | width: 100%; | 18 | width: 100%; |
19 | } | 19 | } |
20 | + | ||
21 | + label { | ||
22 | + margin-top: 0.2rem; | ||
23 | + margin-bottom: 0.2rem; | ||
24 | + } | ||
25 | +} | ||
26 | + | ||
27 | +.weather-buttons { | ||
28 | + display: flex; | ||
29 | + flex-direction: row; | ||
30 | + button { | ||
31 | + margin-left: 0.2rem; | ||
32 | + margin-right: 0.2rem; | ||
33 | + } | ||
20 | } | 34 | } | ... | ... |
... | @@ -8,10 +8,10 @@ const { jwtGenerator } = require("../config/jwt"); | ... | @@ -8,10 +8,10 @@ const { jwtGenerator } = require("../config/jwt"); |
8 | // @route POST /api/users | 8 | // @route POST /api/users |
9 | // @access Public | 9 | // @access Public |
10 | const signupUser = asyncHandler(async (req, res) => { | 10 | const signupUser = asyncHandler(async (req, res) => { |
11 | - const { username, country, email, password } = req.body; | 11 | + const { username, country, city, email, password } = req.body; |
12 | - if (!username || !email || !password) { | 12 | + if (!username || !country || !city || !email || !password) { |
13 | res.status(400); | 13 | res.status(400); |
14 | - throw new Error("Please fill in required fields"); | 14 | + throw new Error("Please fill in all fields"); |
15 | } | 15 | } |
16 | 16 | ||
17 | // Check if user already exists | 17 | // Check if user already exists |
... | @@ -29,6 +29,7 @@ const signupUser = asyncHandler(async (req, res) => { | ... | @@ -29,6 +29,7 @@ const signupUser = asyncHandler(async (req, res) => { |
29 | const user = await User.create({ | 29 | const user = await User.create({ |
30 | username, | 30 | username, |
31 | country, | 31 | country, |
32 | + city, | ||
32 | email, | 33 | email, |
33 | password: hashedPassword, | 34 | password: hashedPassword, |
34 | }); | 35 | }); |
... | @@ -40,6 +41,7 @@ const signupUser = asyncHandler(async (req, res) => { | ... | @@ -40,6 +41,7 @@ const signupUser = asyncHandler(async (req, res) => { |
40 | _id: user.id, | 41 | _id: user.id, |
41 | username: user.username, | 42 | username: user.username, |
42 | country: user.country, | 43 | country: user.country, |
44 | + city: user.city, | ||
43 | email: user.email, | 45 | email: user.email, |
44 | token: jwtGenerator(user._id), | 46 | token: jwtGenerator(user._id), |
45 | }); | 47 | }); | ... | ... |
server/actions/weatherActions.js
0 → 100644
1 | +const bcrypt = require("bcryptjs"); | ||
2 | +const axios = require("axios").default; | ||
3 | +const User = require("../models/userModel"); | ||
4 | +// handles "exception" inside of async express routes | ||
5 | +const asyncHandler = require("express-async-handler"); | ||
6 | + | ||
7 | +// @desc Get current weather (API - Current Weather) | ||
8 | +// @route Get /api/weather | ||
9 | +// @access Public | ||
10 | +const getWeather = asyncHandler(async (req, res) => { | ||
11 | + const countryCode = "US"; | ||
12 | + const cityName = "los angeles"; | ||
13 | + const limit = 5; | ||
14 | + | ||
15 | + const metaGeoData = await axios.get( | ||
16 | + `http://api.openweathermap.org/geo/1.0/direct?q=${cityName},${countryCode}&limit=${limit}&appid=${process.env.OPENWEATHER_API_KEY}` | ||
17 | + ); | ||
18 | + const data = metaGeoData.data[0]; | ||
19 | + const geoData = { | ||
20 | + lat: data.lat, | ||
21 | + lon: data.lon, | ||
22 | + country: data.country, | ||
23 | + state: data.state, | ||
24 | + }; | ||
25 | + | ||
26 | + const metaData = await axios.get( | ||
27 | + `https://api.openweathermap.org/data/2.5/weather?lat=${geoData.lat}&lon=${geoData.lon}&appid=${process.env.OPENWEATHER_API_KEY}` | ||
28 | + ); | ||
29 | + const weatherData = metaData.data; | ||
30 | + | ||
31 | + res.json(weatherData); | ||
32 | +}); | ||
33 | + | ||
34 | +// @desc Get weather forecast (3-hour Forecast 5 days) | ||
35 | +// @route GET /api/weather/forecast | ||
36 | +// @access Public | ||
37 | +const getForecast = asyncHandler(async (req, res) => { | ||
38 | + const lat = 35; | ||
39 | + const lon = 139; | ||
40 | + const data = await axios.get( | ||
41 | + `https://pro.openweathermap.org/data/2.5/forecast/hourly?lat=${lat}&lon=${lon}&appid=${process.env.OPENWEATHER_API_KEY2}` | ||
42 | + ); | ||
43 | + console.log(data); | ||
44 | +}); | ||
45 | + | ||
46 | +// @desc Get air pollution (Air Pollution API) | ||
47 | +// @route GET /api/weather/airpollution | ||
48 | +// @access Public | ||
49 | +const getAirPollution = asyncHandler(async (req, res) => { | ||
50 | + res.json({}); | ||
51 | +}); | ||
52 | + | ||
53 | +module.exports = { | ||
54 | + getWeather, | ||
55 | + getForecast, | ||
56 | + getAirPollution, | ||
57 | +}; | ||
58 | + | ||
59 | +// use geocoding |
server/models/chatModel.js
deleted
100644 → 0
1 | -const mongoose = require("mongoose"); | ||
2 | - | ||
3 | -const chatSchema = mongoose.Schema({ | ||
4 | - room: { | ||
5 | - type: mongoose.Schema.Types.ObjectId, | ||
6 | - require: true, | ||
7 | - ref: "Chatroom", | ||
8 | - }, | ||
9 | - conversation: [ | ||
10 | - { | ||
11 | - message: { | ||
12 | - type: String, | ||
13 | - required: true, | ||
14 | - }, | ||
15 | - }, | ||
16 | - ], | ||
17 | -}); | ||
18 | - | ||
19 | -module.exports = mongoose.model("Chat", chatSchema); |
server/models/chatroomModel.js
deleted
100644 → 0
1 | -const mongoose = require("mongoose"); | ||
2 | - | ||
3 | -const chatroomSchema = mongoose.Schema( | ||
4 | - { | ||
5 | - roomName: { | ||
6 | - type: String, | ||
7 | - required: [true, "Please add chatroom name"], | ||
8 | - }, | ||
9 | - participants: [ | ||
10 | - { | ||
11 | - user: { | ||
12 | - type: mongoose.Schema.Types.ObjectId, | ||
13 | - required: true, | ||
14 | - ref: "User", | ||
15 | - }, | ||
16 | - }, | ||
17 | - ], | ||
18 | - }, | ||
19 | - { | ||
20 | - timestamps: true, | ||
21 | - } | ||
22 | -); | ||
23 | - | ||
24 | -module.exports = mongoose.model("Chatroom", chatroomSchema); |
... | @@ -8,7 +8,11 @@ const userSchema = mongoose.Schema( | ... | @@ -8,7 +8,11 @@ const userSchema = mongoose.Schema( |
8 | }, | 8 | }, |
9 | country: { | 9 | country: { |
10 | type: String, | 10 | type: String, |
11 | - required: false, | 11 | + required: true, |
12 | + }, | ||
13 | + city: { | ||
14 | + type: String, | ||
15 | + required: true, | ||
12 | }, | 16 | }, |
13 | email: { | 17 | email: { |
14 | type: String, | 18 | type: String, | ... | ... |
... | @@ -9,6 +9,7 @@ | ... | @@ -9,6 +9,7 @@ |
9 | "version": "1.0.0", | 9 | "version": "1.0.0", |
10 | "license": "MIT", | 10 | "license": "MIT", |
11 | "dependencies": { | 11 | "dependencies": { |
12 | + "axios": "^0.27.2", | ||
12 | "bcryptjs": "^2.4.3", | 13 | "bcryptjs": "^2.4.3", |
13 | "colors": "^1.4.0", | 14 | "colors": "^1.4.0", |
14 | "cors": "^2.8.5", | 15 | "cors": "^2.8.5", |
... | @@ -148,6 +149,20 @@ | ... | @@ -148,6 +149,20 @@ |
148 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | 149 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", |
149 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" | 150 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" |
150 | }, | 151 | }, |
152 | + "node_modules/asynckit": { | ||
153 | + "version": "0.4.0", | ||
154 | + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", | ||
155 | + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" | ||
156 | + }, | ||
157 | + "node_modules/axios": { | ||
158 | + "version": "0.27.2", | ||
159 | + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", | ||
160 | + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", | ||
161 | + "dependencies": { | ||
162 | + "follow-redirects": "^1.14.9", | ||
163 | + "form-data": "^4.0.0" | ||
164 | + } | ||
165 | + }, | ||
151 | "node_modules/balanced-match": { | 166 | "node_modules/balanced-match": { |
152 | "version": "1.0.2", | 167 | "version": "1.0.2", |
153 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", | 168 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", |
... | @@ -503,6 +518,17 @@ | ... | @@ -503,6 +518,17 @@ |
503 | "node": ">=0.1.90" | 518 | "node": ">=0.1.90" |
504 | } | 519 | } |
505 | }, | 520 | }, |
521 | + "node_modules/combined-stream": { | ||
522 | + "version": "1.0.8", | ||
523 | + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", | ||
524 | + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", | ||
525 | + "dependencies": { | ||
526 | + "delayed-stream": "~1.0.0" | ||
527 | + }, | ||
528 | + "engines": { | ||
529 | + "node": ">= 0.8" | ||
530 | + } | ||
531 | + }, | ||
506 | "node_modules/component-emitter": { | 532 | "node_modules/component-emitter": { |
507 | "version": "1.3.0", | 533 | "version": "1.3.0", |
508 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", | 534 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", |
... | @@ -679,6 +705,14 @@ | ... | @@ -679,6 +705,14 @@ |
679 | "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", | 705 | "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", |
680 | "dev": true | 706 | "dev": true |
681 | }, | 707 | }, |
708 | + "node_modules/delayed-stream": { | ||
709 | + "version": "1.0.0", | ||
710 | + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", | ||
711 | + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", | ||
712 | + "engines": { | ||
713 | + "node": ">=0.4.0" | ||
714 | + } | ||
715 | + }, | ||
682 | "node_modules/denque": { | 716 | "node_modules/denque": { |
683 | "version": "2.0.1", | 717 | "version": "2.0.1", |
684 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", | 718 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", |
... | @@ -929,6 +963,38 @@ | ... | @@ -929,6 +963,38 @@ |
929 | "node": ">= 0.8" | 963 | "node": ">= 0.8" |
930 | } | 964 | } |
931 | }, | 965 | }, |
966 | + "node_modules/follow-redirects": { | ||
967 | + "version": "1.15.1", | ||
968 | + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", | ||
969 | + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==", | ||
970 | + "funding": [ | ||
971 | + { | ||
972 | + "type": "individual", | ||
973 | + "url": "https://github.com/sponsors/RubenVerborgh" | ||
974 | + } | ||
975 | + ], | ||
976 | + "engines": { | ||
977 | + "node": ">=4.0" | ||
978 | + }, | ||
979 | + "peerDependenciesMeta": { | ||
980 | + "debug": { | ||
981 | + "optional": true | ||
982 | + } | ||
983 | + } | ||
984 | + }, | ||
985 | + "node_modules/form-data": { | ||
986 | + "version": "4.0.0", | ||
987 | + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", | ||
988 | + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", | ||
989 | + "dependencies": { | ||
990 | + "asynckit": "^0.4.0", | ||
991 | + "combined-stream": "^1.0.8", | ||
992 | + "mime-types": "^2.1.12" | ||
993 | + }, | ||
994 | + "engines": { | ||
995 | + "node": ">= 6" | ||
996 | + } | ||
997 | + }, | ||
932 | "node_modules/forwarded": { | 998 | "node_modules/forwarded": { |
933 | "version": "0.2.0", | 999 | "version": "0.2.0", |
934 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", | 1000 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", |
... | @@ -2769,6 +2835,20 @@ | ... | @@ -2769,6 +2835,20 @@ |
2769 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", | 2835 | "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", |
2770 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" | 2836 | "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" |
2771 | }, | 2837 | }, |
2838 | + "asynckit": { | ||
2839 | + "version": "0.4.0", | ||
2840 | + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", | ||
2841 | + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" | ||
2842 | + }, | ||
2843 | + "axios": { | ||
2844 | + "version": "0.27.2", | ||
2845 | + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", | ||
2846 | + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", | ||
2847 | + "requires": { | ||
2848 | + "follow-redirects": "^1.14.9", | ||
2849 | + "form-data": "^4.0.0" | ||
2850 | + } | ||
2851 | + }, | ||
2772 | "balanced-match": { | 2852 | "balanced-match": { |
2773 | "version": "1.0.2", | 2853 | "version": "1.0.2", |
2774 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", | 2854 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", |
... | @@ -3019,6 +3099,14 @@ | ... | @@ -3019,6 +3099,14 @@ |
3019 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", | 3099 | "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", |
3020 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" | 3100 | "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==" |
3021 | }, | 3101 | }, |
3102 | + "combined-stream": { | ||
3103 | + "version": "1.0.8", | ||
3104 | + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", | ||
3105 | + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", | ||
3106 | + "requires": { | ||
3107 | + "delayed-stream": "~1.0.0" | ||
3108 | + } | ||
3109 | + }, | ||
3022 | "component-emitter": { | 3110 | "component-emitter": { |
3023 | "version": "1.3.0", | 3111 | "version": "1.3.0", |
3024 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", | 3112 | "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", |
... | @@ -3151,6 +3239,11 @@ | ... | @@ -3151,6 +3239,11 @@ |
3151 | "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", | 3239 | "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==", |
3152 | "dev": true | 3240 | "dev": true |
3153 | }, | 3241 | }, |
3242 | + "delayed-stream": { | ||
3243 | + "version": "1.0.0", | ||
3244 | + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", | ||
3245 | + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==" | ||
3246 | + }, | ||
3154 | "denque": { | 3247 | "denque": { |
3155 | "version": "2.0.1", | 3248 | "version": "2.0.1", |
3156 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", | 3249 | "resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz", |
... | @@ -3349,6 +3442,21 @@ | ... | @@ -3349,6 +3442,21 @@ |
3349 | "unpipe": "~1.0.0" | 3442 | "unpipe": "~1.0.0" |
3350 | } | 3443 | } |
3351 | }, | 3444 | }, |
3445 | + "follow-redirects": { | ||
3446 | + "version": "1.15.1", | ||
3447 | + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz", | ||
3448 | + "integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==" | ||
3449 | + }, | ||
3450 | + "form-data": { | ||
3451 | + "version": "4.0.0", | ||
3452 | + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", | ||
3453 | + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", | ||
3454 | + "requires": { | ||
3455 | + "asynckit": "^0.4.0", | ||
3456 | + "combined-stream": "^1.0.8", | ||
3457 | + "mime-types": "^2.1.12" | ||
3458 | + } | ||
3459 | + }, | ||
3352 | "forwarded": { | 3460 | "forwarded": { |
3353 | "version": "0.2.0", | 3461 | "version": "0.2.0", |
3354 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", | 3462 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", | ... | ... |
... | @@ -16,6 +16,7 @@ | ... | @@ -16,6 +16,7 @@ |
16 | "author": "황선혁", | 16 | "author": "황선혁", |
17 | "license": "MIT", | 17 | "license": "MIT", |
18 | "dependencies": { | 18 | "dependencies": { |
19 | + "axios": "^0.27.2", | ||
19 | "bcryptjs": "^2.4.3", | 20 | "bcryptjs": "^2.4.3", |
20 | "colors": "^1.4.0", | 21 | "colors": "^1.4.0", |
21 | "cors": "^2.8.5", | 22 | "cors": "^2.8.5", | ... | ... |
server/routes/weatherRoutes.js
0 → 100644
1 | +const express = require("express"); | ||
2 | +const router = express.Router(); | ||
3 | +const { | ||
4 | + getWeather, | ||
5 | + getForecast, | ||
6 | + getAirPollution, | ||
7 | +} = require("../actions/weatherActions"); | ||
8 | + | ||
9 | +// "/api/weather/" | ||
10 | +router.post("/", getWeather); | ||
11 | +router.get("/forecast", getForecast); | ||
12 | +router.get("/airpollution", getAirPollution); | ||
13 | + | ||
14 | +module.exports = router; |
... | @@ -13,6 +13,7 @@ app.use(express.json()); | ... | @@ -13,6 +13,7 @@ app.use(express.json()); |
13 | app.use(express.urlencoded({ extended: false })); | 13 | app.use(express.urlencoded({ extended: false })); |
14 | 14 | ||
15 | app.use("/api/users", require("./routes/userRoutes")); | 15 | app.use("/api/users", require("./routes/userRoutes")); |
16 | +app.use("/api/weather", require("./routes/weatherRoutes")); | ||
16 | 17 | ||
17 | app.use(errorHandler); | 18 | app.use(errorHandler); |
18 | 19 | ... | ... |
-
Please register or login to post a comment