Eric Whale

Add Get current Weather API

// Weather broadcasting page
import axios from "axios";
import { useState, useEffect } from "react";
import { Navigate } from "react-router-dom";
import weatherService from "./service/weather";
// components
import Bottombar from "./components/Bottombar";
import Topbar from "./components/Topbar";
import ChatroomBox from "./components/ChatroomBox";
function App() {
const token = sessionStorage.getItem("user-token");
const [weather, setWeather] = useState();
const [forecast, setForecast] = useState();
const [air, setAir] = useState();
const handleData = async (e) => {
setWeather(null);
setForecast(null);
setAir(null);
if (e.target.id === "weather") {
const weatherData = await weatherService.getWeather(token);
setWeather(weatherData);
} else if (e.target.id === "forecast") {
const forecasetData = await weatherService.getForecaset(token);
setForecast(forecasetData);
} else if (e.target.id === "air") {
const airData = await weatherService.getAirPollution(token);
setAir(airData);
}
};
if (!token) {
return <Navigate to="/login" />;
}
return (
<div>
<Topbar />
<h1>Weather Page (home)</h1>
<div></div>
<div className="weather-buttons">
<button id="weather" onClick={(e) => handleData(e)}>
Weather
</button>
<button id="forecast" onClick={(e) => handleData(e)}>
Forecast
</button>
<button id="air" onClick={(e) => handleData(e)}>
Air Pollution
</button>
</div>
{!weather ? (
""
) : (
<div>
<h2>
{weather.meta.city} ({weather.meta.country}) - {weather.description}
</h2>
<small>Time Zone : UTC {weather.meta.timezone}</small>
<p>
Temperature : {weather.temp.realCelcius} C / feels like{" "}
{weather.temp.feelCelcius} C
</p>
<p>Wind : {weather.types.wind} m/s</p>
<p>Cloud : {weather.types.clouds} %</p>
<p>{weather.rain ? "rain : Yes" : "rain : No"}</p>
<p>{weather.snow ? "snow : Yes" : "snow : No"}</p>
</div>
)}
<Bottombar />
</div>
......@@ -19,11 +73,3 @@ function App() {
}
export default App;
// {Array.isArray(chats) ? (
// chats.map((chat, i) => {
// return <ChatroomBox key={i} chat={chat} />;
// })
// ) : (
// <h2>No chatting room!</h2>
// )}
......
import { Link } from "react-router-dom";
// styles
import "../styles/bar.scss";
import { TiWeatherPartlySunny } from "react-icons/ti";
import { FcHome } from "react-icons/fc";
import { AiFillSetting } from "react-icons/ai";
function Topbar() {
return (
<div className="topbar">
<Link to="/" className="logo">
<TiWeatherPartlySunny />
<FcHome />
</Link>
<Link to="/settings" className="settings">
<AiFillSetting />
......
......@@ -8,7 +8,7 @@ import Signup from "./routes/signup";
import Login from "./routes/login";
import Users from "./routes/users";
import Settings from "./routes/settings";
import Tell from "./routes/tell";
import Tweet from "./routes/tweet";
import NotFound from "./routes/notfound";
// styles
import "./styles/layout.scss";
......@@ -24,7 +24,7 @@ root.render(
<Route exact path="/signup" element={<Signup />} />
<Route exact path="/users" element={<Users />} />
<Route exact path="/settings" element={<Settings />} />
<Route exact path="/tell" element={<Tell />} />
<Route exact path="/tell" element={<Tweet />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>
......
......@@ -15,7 +15,7 @@ function Login() {
});
useEffect(() => {
if (localStorage.key("user-token")) {
if (sessionStorage.key("user-token")) {
navigate("/", { replace: true });
}
}, [loginSuccess, setLoginSuccess, navigate]);
......@@ -29,6 +29,10 @@ function Login() {
const handleSubmit = async (e) => {
e.preventDefault();
if (!userinfo.email || !userinfo.password) {
alert("Please fill all boxes");
return;
}
const answer = await authService.handleLogin(userinfo);
setLoginSuccess(answer);
};
......
......@@ -11,12 +11,13 @@ function Signup() {
const [userinfo, setUserInfo] = useState({
username: "",
country: "",
city: "",
email: "",
password: "",
});
useEffect(() => {
if (localStorage.key("user-token")) {
if (sessionStorage.key("user-token")) {
navigate("/", { replace: true });
}
}, [signupSuccess, setSignupSuccess, navigate]);
......@@ -44,6 +45,11 @@ function Signup() {
<br />
<p>
<b>Country Code</b> and <b>City Name</b> is required for Weather
service!
</p>
<form className="authForm" onSubmit={(e) => handleSubmit(e)}>
<label htmlFor="username">
<input
......@@ -63,6 +69,15 @@ function Signup() {
id="country"
/>
</label>
<label htmlFor="city">
<input
placeholder="City Name"
onChange={(e) => onChange(e)}
value={userinfo.city}
type="text"
id="city"
/>
</label>
<label htmlFor="email">
<input
placeholder="email"
......
function Tell() {
return <div>tell</div>;
}
export default Tell;
function Tweet() {
return <div>Tweet</div>;
}
export default Tweet;
const axios = require("axios").default;
const handleSignup = async ({ username, country, email, password }) => {
const handleSignup = async ({ username, country, city, email, password }) => {
try {
const response = await axios.post("http://localhost:8080/api/users", {
username,
country,
city,
email,
password,
});
if (response.data) {
console.log("signup request sent...");
localStorage.setItem("user-token", JSON.stringify(response.data));
sessionStorage.setItem("user-token", JSON.stringify(response.data));
return "success";
}
} catch (err) {
......@@ -25,8 +25,7 @@ const handleLogin = async ({ email, password }) => {
password,
});
if (response.data) {
console.log("signin request sent...");
localStorage.setItem("user-token", JSON.stringify(response.data));
sessionStorage.setItem("user-token", JSON.stringify(response.data));
return "success";
}
} catch (err) {
......
const axios = require("axios").default;
const getWeather = async (user) => {
if (!user) {
return;
}
try {
const response = await axios.post("http://localhost:8080/api/weather");
const data = response.data;
const formattedData = {
meta: {
country: data.sys.country,
city: data.name,
timezone: data.timezone / 60 / 60,
sunrise: data.sys.sunrise,
sunset: data.sys.sunset,
},
description: data.weather[0].description,
temp: {
realCelcius: (data.main.temp - 273.15).toFixed(2),
feelCelcius: (data.main.feels_like - 273.15).toFixed(2),
realFer: ((data.main.temp - 273.15) * 9) / 5 + 32,
feelFer: ((data.main.feels_like - 273.15) * 9) / 5 + 32,
},
types: {
wind: data.wind.speed,
clouds: data.clouds.all,
rain: data.rain ? data.rain : null,
snow: data.snow ? data.snow : null,
},
};
return formattedData;
} catch (err) {
console.log(err);
}
};
const getForecaset = async () => {
return;
};
const getAirPollution = async () => {
return;
};
const weatherService = {
getWeather,
getForecaset,
getAirPollution,
};
export default weatherService;
......@@ -17,4 +17,18 @@
input {
width: 100%;
}
label {
margin-top: 0.2rem;
margin-bottom: 0.2rem;
}
}
.weather-buttons {
display: flex;
flex-direction: row;
button {
margin-left: 0.2rem;
margin-right: 0.2rem;
}
}
......
......@@ -8,10 +8,10 @@ const { jwtGenerator } = require("../config/jwt");
// @route POST /api/users
// @access Public
const signupUser = asyncHandler(async (req, res) => {
const { username, country, email, password } = req.body;
if (!username || !email || !password) {
const { username, country, city, email, password } = req.body;
if (!username || !country || !city || !email || !password) {
res.status(400);
throw new Error("Please fill in required fields");
throw new Error("Please fill in all fields");
}
// Check if user already exists
......@@ -29,6 +29,7 @@ const signupUser = asyncHandler(async (req, res) => {
const user = await User.create({
username,
country,
city,
email,
password: hashedPassword,
});
......@@ -40,6 +41,7 @@ const signupUser = asyncHandler(async (req, res) => {
_id: user.id,
username: user.username,
country: user.country,
city: user.city,
email: user.email,
token: jwtGenerator(user._id),
});
......
const bcrypt = require("bcryptjs");
const axios = require("axios").default;
const User = require("../models/userModel");
// handles "exception" inside of async express routes
const asyncHandler = require("express-async-handler");
// @desc Get current weather (API - Current Weather)
// @route Get /api/weather
// @access Public
const getWeather = asyncHandler(async (req, res) => {
const countryCode = "US";
const cityName = "los angeles";
const limit = 5;
const metaGeoData = await axios.get(
`http://api.openweathermap.org/geo/1.0/direct?q=${cityName},${countryCode}&limit=${limit}&appid=${process.env.OPENWEATHER_API_KEY}`
);
const data = metaGeoData.data[0];
const geoData = {
lat: data.lat,
lon: data.lon,
country: data.country,
state: data.state,
};
const metaData = await axios.get(
`https://api.openweathermap.org/data/2.5/weather?lat=${geoData.lat}&lon=${geoData.lon}&appid=${process.env.OPENWEATHER_API_KEY}`
);
const weatherData = metaData.data;
res.json(weatherData);
});
// @desc Get weather forecast (3-hour Forecast 5 days)
// @route GET /api/weather/forecast
// @access Public
const getForecast = asyncHandler(async (req, res) => {
const lat = 35;
const lon = 139;
const data = await axios.get(
`https://pro.openweathermap.org/data/2.5/forecast/hourly?lat=${lat}&lon=${lon}&appid=${process.env.OPENWEATHER_API_KEY2}`
);
console.log(data);
});
// @desc Get air pollution (Air Pollution API)
// @route GET /api/weather/airpollution
// @access Public
const getAirPollution = asyncHandler(async (req, res) => {
res.json({});
});
module.exports = {
getWeather,
getForecast,
getAirPollution,
};
// use geocoding
const mongoose = require("mongoose");
const chatSchema = mongoose.Schema({
room: {
type: mongoose.Schema.Types.ObjectId,
require: true,
ref: "Chatroom",
},
conversation: [
{
message: {
type: String,
required: true,
},
},
],
});
module.exports = mongoose.model("Chat", chatSchema);
const mongoose = require("mongoose");
const chatroomSchema = mongoose.Schema(
{
roomName: {
type: String,
required: [true, "Please add chatroom name"],
},
participants: [
{
user: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: "User",
},
},
],
},
{
timestamps: true,
}
);
module.exports = mongoose.model("Chatroom", chatroomSchema);
......@@ -8,7 +8,11 @@ const userSchema = mongoose.Schema(
},
country: {
type: String,
required: false,
required: true,
},
city: {
type: String,
required: true,
},
email: {
type: String,
......
......@@ -9,6 +9,7 @@
"version": "1.0.0",
"license": "MIT",
"dependencies": {
"axios": "^0.27.2",
"bcryptjs": "^2.4.3",
"colors": "^1.4.0",
"cors": "^2.8.5",
......@@ -148,6 +149,20 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"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": "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/balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
......@@ -503,6 +518,17 @@
"node": ">=0.1.90"
}
},
"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/component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
......@@ -679,6 +705,14 @@
"integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
"dev": 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/denque": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
......@@ -929,6 +963,38 @@
"node": ">= 0.8"
}
},
"node_modules/follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA==",
"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/forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
......@@ -2769,6 +2835,20 @@
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
"integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI="
},
"asynckit": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
"integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
},
"axios": {
"version": "0.27.2",
"resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz",
"integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==",
"requires": {
"follow-redirects": "^1.14.9",
"form-data": "^4.0.0"
}
},
"balanced-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
......@@ -3019,6 +3099,14 @@
"resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz",
"integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA=="
},
"combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
"requires": {
"delayed-stream": "~1.0.0"
}
},
"component-emitter": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz",
......@@ -3151,6 +3239,11 @@
"integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==",
"dev": true
},
"delayed-stream": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
"integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="
},
"denque": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/denque/-/denque-2.0.1.tgz",
......@@ -3349,6 +3442,21 @@
"unpipe": "~1.0.0"
}
},
"follow-redirects": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.1.tgz",
"integrity": "sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA=="
},
"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==",
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
},
"forwarded": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
......
......@@ -16,6 +16,7 @@
"author": "황선혁",
"license": "MIT",
"dependencies": {
"axios": "^0.27.2",
"bcryptjs": "^2.4.3",
"colors": "^1.4.0",
"cors": "^2.8.5",
......
const express = require("express");
const router = express.Router();
const {
getWeather,
getForecast,
getAirPollution,
} = require("../actions/weatherActions");
// "/api/weather/"
router.post("/", getWeather);
router.get("/forecast", getForecast);
router.get("/airpollution", getAirPollution);
module.exports = router;
......@@ -13,6 +13,7 @@ app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use("/api/users", require("./routes/userRoutes"));
app.use("/api/weather", require("./routes/weatherRoutes"));
app.use(errorHandler);
......