reuploading assignments to git
This commit is contained in:
parent
5380e26a33
commit
f7988f1f2d
64
assignment1/README.md
Normal file
64
assignment1/README.md
Normal file
@ -0,0 +1,64 @@
|
||||
# Docker Web Application Deployment
|
||||
|
||||
The application consists of two services:
|
||||
|
||||
1. **Web Service (Nginx):**
|
||||
- Uses the official Nginx image.
|
||||
- Listens on port 80 inside the container and is mapped to host port 8080.
|
||||
2. **Database Service (MySQL 5.7):**
|
||||
- Uses the official MySQL 5.7 image.
|
||||
- Configured with a root password and a default database.
|
||||
- Persists its data using the named volume `mysql-data`.
|
||||
|
||||
## Files Overview
|
||||
|
||||
- **prepare-app.sh:**
|
||||
- Creates the required Docker network (`myapp-net`) and persistent volume (`mysql-data`).
|
||||
- **docker-compose.yaml:**
|
||||
- Defines the two services along with their ports, environment variables, volumes, and restart policies.
|
||||
- **start-app.sh:**
|
||||
- Starts the services using Docker Compose in detached mode.
|
||||
- Displays the URL to access the web service.
|
||||
- **stop-app.sh:**
|
||||
- Stops the running containers without deleting the persistent volume.
|
||||
- **remove-app.sh:**
|
||||
- Removes all the created resources (containers, network, and volume) from the deployment.
|
||||
|
||||
## Deployment Instructions
|
||||
|
||||
1. **Preparation:**
|
||||
- Ensure Docker and Docker Compose are installed.
|
||||
- Make the scripts executable:
|
||||
```bash
|
||||
chmod +x prepare-app.sh start-app.sh stop-app.sh remove-app.sh
|
||||
```
|
||||
- Run the preparation script:
|
||||
```bash
|
||||
./prepare-app.sh
|
||||
```
|
||||
|
||||
2. **Starting the Application:**
|
||||
- Launch the services by running:
|
||||
```bash
|
||||
./start-app.sh
|
||||
```
|
||||
- Open your web browser and navigate to [http://localhost:8080](http://localhost:8080) to see the Nginx welcome page.
|
||||
|
||||
3. **Stopping the Application:**
|
||||
- Stop the services without losing data:
|
||||
```bash
|
||||
./stop-app.sh
|
||||
```
|
||||
|
||||
4. **Removing the Application:**
|
||||
- To completely remove all deployed resources, run:
|
||||
```bash
|
||||
./remove-app.sh
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- The application uses an external network (`myapp-net`) and a persistent volume (`mysql-data`) that are created in `prepare-app.sh`.
|
||||
- The Nginx container depends on the MySQL container to demonstrate inter-service communication within the `myapp-net` network.
|
||||
- Containers are configured to restart on failure.
|
||||
|
32
assignment1/docker-compose.yaml
Normal file
32
assignment1/docker-compose.yaml
Normal file
@ -0,0 +1,32 @@
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
web:
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- "8080:80"
|
||||
networks:
|
||||
- myapp-net
|
||||
restart: on-failure
|
||||
depends_on:
|
||||
- db
|
||||
|
||||
db:
|
||||
image: mysql:5.7
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: example
|
||||
MYSQL_DATABASE: appdb
|
||||
volumes:
|
||||
- mysql-data:/var/lib/mysql
|
||||
networks:
|
||||
- myapp-net
|
||||
restart: on-failure
|
||||
|
||||
networks:
|
||||
myapp-net:
|
||||
external: true
|
||||
|
||||
volumes:
|
||||
mysql-data:
|
||||
external: true
|
||||
|
20
assignment1/prepare-app.sh
Executable file
20
assignment1/prepare-app.sh
Executable file
@ -0,0 +1,20 @@
|
||||
#!/bin/bash
|
||||
# prepare-app.sh
|
||||
|
||||
if ! docker network ls | grep -w myapp-net >/dev/null; then
|
||||
docker network create myapp-net
|
||||
echo "Created Docker network: myapp-net"
|
||||
else
|
||||
echo "Docker network myapp-net already exists."
|
||||
fi
|
||||
|
||||
|
||||
if ! docker volume ls | grep -w mysql-data >/dev/null; then
|
||||
docker volume create mysql-data
|
||||
echo "Created Docker volume: mysql-data"
|
||||
else
|
||||
echo "Docker volume mysql-data already exists."
|
||||
fi
|
||||
|
||||
echo "Preparation complete."
|
||||
|
10
assignment1/remove-app.sh
Executable file
10
assignment1/remove-app.sh
Executable file
@ -0,0 +1,10 @@
|
||||
#!/bin/bash
|
||||
# remove-app.sh
|
||||
|
||||
docker-compose down
|
||||
|
||||
docker volume rm mysql-data
|
||||
docker network rm myapp-net
|
||||
|
||||
echo "All application resources removed."
|
||||
|
9
assignment1/start-app.sh
Executable file
9
assignment1/start-app.sh
Executable file
@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
# start-app.sh
|
||||
|
||||
docker-compose up -d
|
||||
|
||||
sleep 5
|
||||
|
||||
echo "Application started. Access the web service at http://localhost:8080"
|
||||
|
6
assignment1/stop-app.sh
Executable file
6
assignment1/stop-app.sh
Executable file
@ -0,0 +1,6 @@
|
||||
#!/bin/bash
|
||||
# stop-app.sh
|
||||
|
||||
docker-compose stop
|
||||
echo "Application services stopped. Persistent data remains intact."
|
||||
|
BIN
assignment2/.prepare-app.sh.swp
Normal file
BIN
assignment2/.prepare-app.sh.swp
Normal file
Binary file not shown.
2
assignment2/Dockerfile
Normal file
2
assignment2/Dockerfile
Normal file
@ -0,0 +1,2 @@
|
||||
FROM nginx:alpine
|
||||
COPY weather.html /usr/share/nginx/html/index.html
|
6
assignment2/Dockerfile.api
Normal file
6
assignment2/Dockerfile.api
Normal file
@ -0,0 +1,6 @@
|
||||
FROM node:18-alpine
|
||||
WORKDIR /app
|
||||
COPY server.js .
|
||||
RUN npm init -y && npm install express pg body-parser
|
||||
EXPOSE 3000
|
||||
CMD ["node", "server.js"]
|
136
assignment2/README.md
Normal file
136
assignment2/README.md
Normal file
@ -0,0 +1,136 @@
|
||||
# Weather Web App - Kubernetes Deployment
|
||||
|
||||
This project deploys a full-stack weather web application to Kubernetes. It includes:
|
||||
- A frontend website to search weather by city
|
||||
- A backend PostgreSQL database for logging searches
|
||||
- A Node.js API to connect the frontend to the database
|
||||
|
||||
---
|
||||
|
||||
## Application Description
|
||||
|
||||
- **Frontend (weather.html):** A responsive, modern web UI that shows current weather data using the OpenWeatherMap API.
|
||||
- **Backend (PostgreSQL):** Stores weather search history via a `weather_log` table.
|
||||
- **API Server (Node.js):** Receives weather data from the frontend and logs it to the PostgreSQL database.
|
||||
|
||||
---
|
||||
|
||||
## Containers Used
|
||||
|
||||
| Container | Image | Description |
|
||||
|------------------|--------------------|----------------------------------------------|
|
||||
| `weather-frontend` | Custom Nginx-based | Serves the `weather.html` UI |
|
||||
| `postgres` | `postgres:15` | Provides relational database backend |
|
||||
| `weather-api` | Custom Node.js | API that logs weather data to PostgreSQL |
|
||||
|
||||
---
|
||||
|
||||
## ☸️ Kubernetes Objects
|
||||
|
||||
| Object Type | File | Description |
|
||||
|------------------------|----------------------|----------------------------------------------------------------------|
|
||||
| `Namespace` | (inside script) | Isolates resources under `webapp-ns` |
|
||||
| `Deployment` | `deployment.yaml` | Manages frontend app |
|
||||
| `Deployment` | `deployment-api.yaml` | Manages the Node.js API |
|
||||
| `StatefulSet` | `statefulset.yaml` | Manages PostgreSQL instance with persistent volume |
|
||||
| `PersistentVolume` | `statefulset.yaml` | Host-mounted volume for PostgreSQL data |
|
||||
| `PersistentVolumeClaim`| `statefulset.yaml` | Requests storage for StatefulSet |
|
||||
| `Service` | `service.yaml` | Exposes frontend via NodePort |
|
||||
| `Service` | `deployment-api.yaml` | Exposes API via NodePort |
|
||||
| `ConfigMap` | Created by script | Stores `init-db.sql` used to create the `weather_log` table |
|
||||
|
||||
---
|
||||
|
||||
## Networking & Volumes
|
||||
|
||||
### Virtual Networks:
|
||||
- Kubernetes handles inter-service communication via internal DNS and `ClusterIP` services.
|
||||
- Frontend and backend are externally reachable using `NodePort` services.
|
||||
|
||||
### Volumes:
|
||||
- `PersistentVolume` and `PersistentVolumeClaim` ensure PostgreSQL data is retained.
|
||||
- `ConfigMap` mounts the SQL init script for PostgreSQL setup.
|
||||
|
||||
---
|
||||
|
||||
## ⚙️ Container Configurations
|
||||
|
||||
### Frontend
|
||||
- Dockerfile uses `nginx:alpine`
|
||||
- Serves `weather.html` (renamed as `index.html` inside container)
|
||||
- Exposed on port `80`
|
||||
|
||||
### Node.js API
|
||||
- Built with Node.js 18
|
||||
- Listens on port `3000`
|
||||
- Connects to PostgreSQL using service DNS
|
||||
- Accepts POST requests at `/log` with weather data
|
||||
|
||||
### PostgreSQL
|
||||
- `postgres:15` with `weatheruser`, `weatherpass`, and `weatherdb`
|
||||
- Init script creates a `weather_log` table with a sample row
|
||||
|
||||
---
|
||||
|
||||
## How to Use the Application
|
||||
|
||||
### 1. Build Docker Images
|
||||
|
||||
```bash
|
||||
./prepare-app.sh
|
||||
```
|
||||
|
||||
### 2. Start the App (create namespace and apply all configs)
|
||||
|
||||
```bash
|
||||
./start-app.sh
|
||||
```
|
||||
|
||||
### 3. View the App
|
||||
|
||||
```bash
|
||||
minikube service weather-service -n webapp-ns
|
||||
```
|
||||
|
||||
Or get the NodePort manually:
|
||||
|
||||
```bash
|
||||
kubectl get svc -n webapp-ns
|
||||
minikube ip
|
||||
```
|
||||
|
||||
Then open:
|
||||
```
|
||||
http://<minikube-ip>:<frontend-nodeport>
|
||||
```
|
||||
|
||||
### 4. Use the App
|
||||
- Enter a city
|
||||
- Weather will be shown
|
||||
- Data is logged in PostgreSQL automatically
|
||||
|
||||
### 5. Stop and Clean Up
|
||||
|
||||
```bash
|
||||
./stop-app.sh
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## API Key Setup
|
||||
|
||||
- Register at [OpenWeatherMap](https://openweathermap.org/api)
|
||||
- Get your free API key
|
||||
- Replace the placeholder in `weather.html`:
|
||||
|
||||
```js
|
||||
const apiKey = "YOUR_API_KEY";
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Conclusion
|
||||
|
||||
- Frontend calls OpenWeatherMap and logs data to your backend
|
||||
- PostgreSQL is prepped with a table to store queries
|
||||
- You can expand the API to serve saved data or stats
|
36
assignment2/deployment-api.yaml
Normal file
36
assignment2/deployment-api.yaml
Normal file
@ -0,0 +1,36 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: weather-api
|
||||
namespace: webapp-ns
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: weather-api
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: weather-api
|
||||
spec:
|
||||
containers:
|
||||
- name: weather-api
|
||||
image: weather-api:latest
|
||||
ports:
|
||||
- containerPort: 3000
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: weather-api-service
|
||||
namespace: webapp-ns
|
||||
spec:
|
||||
selector:
|
||||
app: weather-api
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 3000
|
||||
targetPort: 3000
|
||||
nodePort: 31000
|
||||
type: NodePort
|
||||
|
22
assignment2/deployment.yaml
Normal file
22
assignment2/deployment.yaml
Normal file
@ -0,0 +1,22 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: weather-frontend
|
||||
namespace: webapp-ns
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: weather-frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: weather-frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: weather-frontend
|
||||
image: weather-frontend:latest
|
||||
imagePullPolicy: IfNotPresent
|
||||
ports:
|
||||
- containerPort: 80
|
||||
|
11
assignment2/init-db.sql
Normal file
11
assignment2/init-db.sql
Normal file
@ -0,0 +1,11 @@
|
||||
CREATE TABLE IF NOT EXISTS weather_log (
|
||||
id SERIAL PRIMARY KEY,
|
||||
city VARCHAR(100),
|
||||
temperature DECIMAL(5,2),
|
||||
description TEXT,
|
||||
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
INSERT INTO weather_log (city, temperature, description)
|
||||
VALUES ('London', 15.5, 'partly cloudy');
|
||||
|
BIN
assignment2/minikube-linux-amd64
Normal file
BIN
assignment2/minikube-linux-amd64
Normal file
Binary file not shown.
11
assignment2/prepare-app.sh
Executable file
11
assignment2/prepare-app.sh
Executable file
@ -0,0 +1,11 @@
|
||||
#!/bin/bash
|
||||
|
||||
IMAGE_NAME=weather-frontend
|
||||
IMAGE_TAG=latest
|
||||
|
||||
echo "🔧 Building Docker image: $IMAGE_NAME:$IMAGE_TAG"
|
||||
docker build -t $IMAGE_NAME:$IMAGE_TAG .
|
||||
docker build -t weather-api:latest -f Dockerfile.api .
|
||||
|
||||
echo "✅ Build complete."
|
||||
|
35
assignment2/server.js
Normal file
35
assignment2/server.js
Normal file
@ -0,0 +1,35 @@
|
||||
const express = require('express');
|
||||
const bodyParser = require('body-parser');
|
||||
const { Pool } = require('pg');
|
||||
|
||||
const app = express();
|
||||
const port = 3000;
|
||||
|
||||
app.use(bodyParser.json());
|
||||
|
||||
const pool = new Pool({
|
||||
host: 'postgres-0.postgres.webapp-ns.svc.cluster.local',
|
||||
user: 'weatheruser',
|
||||
password: 'weatherpass',
|
||||
database: 'weatherdb',
|
||||
port: 5432
|
||||
});
|
||||
|
||||
app.post('/log', async (req, res) => {
|
||||
const { city, temperature, description } = req.body;
|
||||
try {
|
||||
await pool.query(
|
||||
'INSERT INTO weather_log (city, temperature, description) VALUES ($1, $2, $3)',
|
||||
[city, temperature, description]
|
||||
);
|
||||
res.status(200).send('Logged!');
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
res.status(500).send('Error saving data');
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(port, () => {
|
||||
console.log(`Weather API listening on port ${port}`);
|
||||
});
|
||||
|
14
assignment2/service.yaml
Normal file
14
assignment2/service.yaml
Normal file
@ -0,0 +1,14 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: weather-service
|
||||
namespace: webapp-ns
|
||||
spec:
|
||||
selector:
|
||||
app: weather-frontend
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 80
|
||||
targetPort: 80
|
||||
type: NodePort
|
||||
|
17
assignment2/start-app.sh
Executable file
17
assignment2/start-app.sh
Executable file
@ -0,0 +1,17 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🚀 Creating namespace 'webapp-ns'..."
|
||||
kubectl create namespace webapp-ns
|
||||
|
||||
echo "📦 Deploying frontend app..."
|
||||
kubectl apply -f deployment.yaml
|
||||
kubectl apply -f service.yaml
|
||||
|
||||
echo "🧾 Creating ConfigMap with init-db.sql..."
|
||||
kubectl create configmap db-init-script --from-file=init-db.sql=init-db.sql -n webapp-ns
|
||||
|
||||
echo "🗄️ Deploying database backend..."
|
||||
kubectl apply -f statefulset.yaml
|
||||
kubectl apply -f deployment-api.yaml
|
||||
|
||||
echo "✅ All resources created."
|
84
assignment2/statefulset.yaml
Normal file
84
assignment2/statefulset.yaml
Normal file
@ -0,0 +1,84 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolume
|
||||
metadata:
|
||||
name: postgres-pv
|
||||
namespace: webapp-ns
|
||||
spec:
|
||||
capacity:
|
||||
storage: 1Gi
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
hostPath:
|
||||
path: "/mnt/data/postgres"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: postgres-pvc
|
||||
namespace: webapp-ns
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: StatefulSet
|
||||
metadata:
|
||||
name: postgres
|
||||
namespace: webapp-ns
|
||||
spec:
|
||||
serviceName: "postgres"
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: postgres
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: postgres
|
||||
spec:
|
||||
initContainers:
|
||||
- name: init-db
|
||||
image: postgres:15
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
value: "weatheruser"
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: "weatherpass"
|
||||
- name: POSTGRES_DB
|
||||
value: "weatherdb"
|
||||
volumeMounts:
|
||||
- name: init-script
|
||||
mountPath: /docker-entrypoint-initdb.d
|
||||
- name: postgres-storage
|
||||
mountPath: /var/lib/postgresql/data
|
||||
containers:
|
||||
- name: postgres
|
||||
image: postgres:15
|
||||
ports:
|
||||
- containerPort: 5432
|
||||
env:
|
||||
- name: POSTGRES_USER
|
||||
value: "weatheruser"
|
||||
- name: POSTGRES_PASSWORD
|
||||
value: "weatherpass"
|
||||
- name: POSTGRES_DB
|
||||
value: "weatherdb"
|
||||
volumeMounts:
|
||||
- name: postgres-storage
|
||||
mountPath: /var/lib/postgresql/data
|
||||
volumes:
|
||||
- name: init-script
|
||||
configMap:
|
||||
name: db-init-script
|
||||
volumeClaimTemplates:
|
||||
- metadata:
|
||||
name: postgres-storage
|
||||
spec:
|
||||
accessModes: ["ReadWriteOnce"]
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
|
7
assignment2/stop-app.sh
Executable file
7
assignment2/stop-app.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo "🧼 Deleting all resources in namespace 'webapp-ns'..."
|
||||
kubectl delete namespace webapp-ns
|
||||
|
||||
echo "✅ Cleanup complete."
|
||||
|
122
assignment2/weather.html
Normal file
122
assignment2/weather.html
Normal file
@ -0,0 +1,122 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
|
||||
<title>Weather Info</title>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Inter', sans-serif;
|
||||
background: linear-gradient(to right, #e0f7fa, #80deea);
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.container {
|
||||
background: white;
|
||||
padding: 2rem;
|
||||
border-radius: 1rem;
|
||||
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
||||
width: 100%;
|
||||
max-width: 400px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
h1 {
|
||||
margin-bottom: 1.5rem;
|
||||
font-size: 1.75rem;
|
||||
font-weight: 600;
|
||||
color: #00796b;
|
||||
}
|
||||
|
||||
input {
|
||||
padding: 0.5rem;
|
||||
width: 65%;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 5px;
|
||||
margin-right: 0.5rem;
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 0.55rem 1rem;
|
||||
background-color: #00796b;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 5px;
|
||||
font-size: 1rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background-color: #00695c;
|
||||
}
|
||||
|
||||
#weather {
|
||||
margin-top: 2rem;
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
#weather p {
|
||||
margin: 0.25rem 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🌦️ Check the Weather</h1>
|
||||
<input type="text" id="city" placeholder="Enter city" />
|
||||
<button onclick="getWeather()">Get Weather</button>
|
||||
<div id="weather"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function getWeather() {
|
||||
const city = document.getElementById("city").value;
|
||||
const apiKey = "0ef7f5ac4207c6da232b7843eb1a663e";
|
||||
|
||||
try {
|
||||
const res = await fetch(
|
||||
`https://api.openweathermap.org/data/2.5/weather?q=${city}&units=metric&appid=${apiKey}`
|
||||
);
|
||||
const data = await res.json();
|
||||
|
||||
if (data.cod === 200) {
|
||||
document.getElementById("weather").innerHTML = `
|
||||
<h2>${data.name}, ${data.sys.country}</h2>
|
||||
<p><strong>${data.weather[0].description}</strong></p>
|
||||
<p>🌡️ <strong>${data.main.temp}°C</strong></p>
|
||||
`;
|
||||
|
||||
// Log to backend
|
||||
await fetch("http://192.168.49.2:31000/log", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
city: data.name,
|
||||
temperature: data.main.temp,
|
||||
description: data.weather[0].description
|
||||
})
|
||||
});
|
||||
|
||||
} else {
|
||||
document.getElementById("weather").innerHTML = `<p style="color:red;">City not found!</p>`;
|
||||
}
|
||||
} catch (err) {
|
||||
console.error("Fetch failed:", err);
|
||||
document.getElementById("weather").innerHTML = `<p style="color:red;">Error getting weather data.</p>`;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user