feat: add nginx to serve report

This commit is contained in:
Sosokker 2025-02-02 07:45:01 +07:00
parent 525cccf344
commit 69f57767bb
6 changed files with 357 additions and 0 deletions

15
cmd/Dockerfile Normal file
View File

@ -0,0 +1,15 @@
FROM golang:1.23.5-alpine
WORKDIR /usr/share/openweather-dashboard
COPY go.mod .
COPY go.sum .
RUN go mod download
COPY ../. .
RUN go build -o main .
CMD ["./main"]

24
docker-compose.yml Normal file
View File

@ -0,0 +1,24 @@
services:
backend:
container_name: backend
build: ./cmd
networks:
- internal-net
environment:
- OPENWEATHERMAP_API_KEY=34fec0bf50b5e8cc6c2070005ea3d5b0
nginx:
build:
context: nginx
container_name: nginx
volumes:
- ./nginx:/etc/nginx/conf.d/
networks:
- internal-net
ports:
- "8080:80"
depends_on:
- backend
networks:
internal-net:

14
nginx/Dockerfile Normal file
View File

@ -0,0 +1,14 @@
FROM nginx:alpine
WORKDIR /etc/nginx
COPY ./nginx.conf ./conf.d/default.conf
COPY ./web/index.html ./html/report/index.html
COPY ./web/index.css ./html/report/index.css
EXPOSE 80
ENTRYPOINT [ "nginx" ]
CMD [ "-g", "daemon off;" ]

34
nginx/nginx.conf Normal file
View File

@ -0,0 +1,34 @@
upstream api {
server backend:8080;
}
server {
listen 80;
server_name localhost;
location / {
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Max-Age' 1728000;
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,
X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
add_header 'Content-Type' 'application/json';
add_header 'Content-Length' 0;
return 204;
}
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Headers' 'Authorization,Accept,Origin,DNT,X-CustomHeader,Keep-Alive,User-Agent,
X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range';
add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT,DELETE,PATCH';
proxy_pass http://backend:8080;
}
location /report {
root html;
index index.html;
}
}

57
nginx/web/index.css Normal file
View File

@ -0,0 +1,57 @@
#main-section {
display: flex;
flex-direction: row;
margin: 2.5rem
}
#statistic {
flex: 1;
min-width: 300px;
background: #ffffff;
border-radius: 8px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
padding: 1.5rem;
text-align: center;
}
#table-section {
overflow-x: auto;
}
#map {
display: flex;
flex-direction: column;
gap: 2rem;
flex: 2;
min-width: 600px;
}
// from https://codepen.io/zass-udd/pen/NWqKmdE
table {
border-collapse: collapse;
width: 100%;
}
td, th {
border: 1px solid #dddddd;
text-align: center;
padding:6px 20px;
}
tr th {
border: 1px solid #dddddd;
text-align: center;
padding:6px 20px;
background-color: #ad1e23;
color:#fff;
}
tr:hover {
background-color: #dddddd;
cursor: pointer;
}
.content td, .content th {
border-top: 1px solid transparent;
padding: 2px 10px 2px 15px;
}

213
nginx/web/index.html Normal file
View File

@ -0,0 +1,213 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Graph</title>
<script src="https://cdn.plot.ly/plotly-3.0.0.min.js" charset="utf-8"></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link
href="https://fonts.googleapis.com/css2?family=Outfit:wght@100..900&family=Roboto:ital,wght@0,100..900;1,100..900&display=swap"
rel="stylesheet">
<link rel="stylesheet" href="./index.css">
<style>
body {
font-family: "Roboto", sans-serif;
}
</style>
</head>
<body>
<div id="main-section">
<div id="statistic">
<h2>Rainfall Statistics</h2>
<p>Today rain/hr.</p>
<div id="table-section">
<table>
<tbody>
<tr>
<th rowspan="2"><strong>Location</strong></th>
<th colspan="2"><strong>Coordinate</strong></th>
<th rowspan="2"><strong>Rain Per Hour</strong></th>
</tr>
<tr>
<th>Latitude</th>
<th>Longitude</th>
</tr>
<tr>
<td>8</td>
<td>0.395</td>
<td>±8</td>
<td>50.27</td>
</tr>
<tr>
<td>10</td>
<td>0.617</td>
<td>±6</td>
<td>78.54</td>
</tr>
<tr>
<td>12</td>
<td>0.888</td>
<td>±6</td>
<td>113.10</td>
</tr>
<tr>
<td>16</td>
<td>1.580</td>
<td>±5</td>
<td>201.06</td>
</tr>
<tr>
<td>20</td>
<td>2.470</td>
<td>±5</td>
<td>314.16</td>
</tr>
<tr>
<td>22</td>
<td>2.984</td>
<td>±4</td>
<td>380.13</td>
</tr>
<tr>
<td>25</td>
<td>3.850</td>
<td>±4</td>
<td>490.88</td>
</tr>
<tr>
<td>28</td>
<td>4.840</td>
<td>±4</td>
<td>615.75</td>
</tr>
<tr>
<td>32</td>
<td>6.310</td>
<td>±4</td>
<td>804.25</td>
</tr>
</tbody>
</table>
</div>
</div>
<div id="map">
<script>
async function fetchData(scale) {
let url = "http://localhost:8080/api/data";
if (scale === true) {
url += "?scale=true"
}
try {
const res = await fetch(url)
const weatherData = await res.json()
return weatherData
} catch {
console.error(error)
return null
}
}
</script>
<div id="rain-marker">
<script>
async function updateRainMarker() {
const weatherData = await fetchData(false);
if (!weatherData) {
return;
}
const lon = weatherData.map((location) => location.coord.lon);
const lat = weatherData.map((location) => location.coord.lat);
const text = weatherData.map(
(location) =>
`${location.name}: ${location.rain["1h"]} mm/hr (${location.weather[0].description})`
);
const data = [
{
type: "scattergeo",
mode: "markers+text",
lon: lon,
lat: lat,
marker: {
color: "rgb(17, 157, 255)",
size: 10,
},
text: text,
textposition: "bottom right",
},
];
const layout = {
map: { center: { lon: -110, lat: 50 }, zoom: 3.3 },
geo: {
center: { lon: -100, lat: 40 },
zoom: 3,
},
showlegend: false,
height: 500,
width: 750,
};
Plotly.newPlot("rain-marker", data, layout);
}
updateRainMarker();
</script>
</div>
<div id="rain-density">
<script>
async function updateRainDensity() {
const weatherData = await fetchData(true);
if (!weatherData) {
console.error("Failed to fetch weather data for rain-density.");
return;
}
const locations = weatherData.map((location) => location.coord.place);
console.log(weatherData)
console.log(locations)
const z = weatherData.map((location) => location.rain["1h"]);
var data = [
{
type: "choroplethmap",
name: "Rainfall Per Hour",
geojson:
"https://raw.githubusercontent.com/python-visualization/folium/master/examples/data/us-states.json",
// state-id, rainfall (scale 100)
locations: locations,
z: z,
zmin: 0,
zmax: 100,
colorscale: "Blues",
reversescale: true,
colorbar: {
y: 0,
yanchor: "bottom",
title: { text: "Rainfall Per Hour", side: "right" },
marker: { line: { width: 1, color: "white" } },
},
},
];
var layout = {
map: { style: "dark", center: { lon: -110, lat: 50 }, zoom: 3.3 },
width: 750,
height: 500,
margin: { t: 0, b: 0 },
};
Plotly.newPlot("rain-density", data, layout);
}
updateRainDensity();
</script>
</div>
</div>
</div>
</body>
</html>