Add files via upload
This commit is contained in:
18
MojaAplikacja/Dockerfile
Normal file
18
MojaAplikacja/Dockerfile
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
FROM node:18-slim as builder
|
||||||
|
WORKDIR /app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN npm install
|
||||||
|
|
||||||
|
FROM node:18-slim
|
||||||
|
LABEL org.opencontainers.image.authors="Julia"
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
COPY --from=builder /app/node_modules ./node_modules
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 3000
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=30s --timeout=3s CMD curl -f http://localhost:3000/ || exit 1
|
||||||
|
|
||||||
|
CMD ["npm", "start"]
|
||||||
100
MojaAplikacja/app.js
Normal file
100
MojaAplikacja/app.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// Importujemy wymagane moduły
|
||||||
|
const express = require('express'); // framework do tworzenia serwera HTTP
|
||||||
|
const axios = require('axios'); // biblioteka do wykonywania zapytań HTTP
|
||||||
|
const path = require('path'); // moduł do obsługi ścieżek plików
|
||||||
|
|
||||||
|
// Tworzymy instancję aplikacji Express
|
||||||
|
const app = express();
|
||||||
|
const PORT = 3000; // Port, na którym nasłuchuje serwer
|
||||||
|
|
||||||
|
// Wyświetlamy informację o starcie aplikacji
|
||||||
|
const now = new Date();
|
||||||
|
console.log(`Aplikacja uruchomiona: ${now.toLocaleString()}, autor: Julia, port: ${PORT}`);
|
||||||
|
|
||||||
|
// Ustawiamy folder 'public' jako katalog ze statycznymi plikami (CSS, obrazy itd.)
|
||||||
|
app.use(express.static(path.join(__dirname, 'public')));
|
||||||
|
|
||||||
|
// Middleware do parsowania danych przesyłanych przez formularz (x-www-form-urlencoded)
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// Obsługa żądania GET na ścieżkę główną – wyświetlenie strony startowej (formularza)
|
||||||
|
app.get('/', (req, res) => {
|
||||||
|
res.sendFile(path.join(__dirname, 'views', 'index.html'));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Obsługa żądania POST na ścieżkę /pogoda – po wysłaniu formularza
|
||||||
|
app.post('/pogoda', async (req, res) => {
|
||||||
|
const country = req.body.country; // Pobieramy kraj z formularza
|
||||||
|
const city = req.body.city; // Pobieramy miasto z formularza
|
||||||
|
|
||||||
|
// Przykładowe dane – współrzędne geograficzne dla wybranych miast
|
||||||
|
const cities = {
|
||||||
|
"Polska": {
|
||||||
|
"Warszawa": { lat: 52.23, lon: 21.01 },
|
||||||
|
"Lublin": { lat: 51.25, lon: 22.57 },
|
||||||
|
"Gdańsk": { lat: 54.35, lon: 18.65 }
|
||||||
|
},
|
||||||
|
"Niemcy": {
|
||||||
|
"Berlin": { lat: 52.52, lon: 13.41 },
|
||||||
|
"Monachium": { lat: 48.14, lon: 11.58 },
|
||||||
|
"Hamburg": { lat: 53.55, lon: 9.99 }
|
||||||
|
},
|
||||||
|
"Francja": {
|
||||||
|
"Paryż": { lat: 48.85, lon: 2.35 },
|
||||||
|
"Marsylia": { lat: 43.30, lon: 5.37 },
|
||||||
|
"Lyon": { lat: 45.75, lon: 4.85 }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sprawdzenie, czy dane miasto i kraj są dostępne w naszej bazie
|
||||||
|
if (!cities[country] || !cities[country][city]) {
|
||||||
|
return res.send("Błąd: Nie znaleziono danych dla wybranego miasta i kraju.");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pobieramy współrzędne geograficzne miasta
|
||||||
|
const { lat, lon } = cities[country][city];
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Wysyłamy zapytanie do API open-meteo z aktualnymi danymi pogodowymi
|
||||||
|
const response = await axios.get(`https://api.open-meteo.com/v1/forecast`, {
|
||||||
|
params: {
|
||||||
|
latitude: lat,
|
||||||
|
longitude: lon,
|
||||||
|
current_weather: true // Pobieramy tylko aktualną pogodę
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Odczytujemy dane pogodowe z odpowiedzi
|
||||||
|
const weather = response.data.current_weather;
|
||||||
|
|
||||||
|
// Pobieramy bieżącą datę i czas serwera (w strefie czasu Warszawy)
|
||||||
|
const serverDate = new Date();
|
||||||
|
const localServerDate = new Date(serverDate.toLocaleString("en-US", { timeZone: "Europe/Warsaw" }));
|
||||||
|
|
||||||
|
// Formatowanie daty i czasu
|
||||||
|
const serverDateFormatted = localServerDate.toLocaleDateString('pl-PL');
|
||||||
|
const serverTimeFormatted = localServerDate.toLocaleTimeString('pl-PL', {
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false
|
||||||
|
});
|
||||||
|
|
||||||
|
// Wyświetlamy wyniki w przeglądarce
|
||||||
|
res.send(`
|
||||||
|
<h2>Pogoda w ${city}, ${country}</h2>
|
||||||
|
<p>Temperatura: ${weather.temperature}°C</p>
|
||||||
|
<p>Wiatr: ${weather.windspeed} km/h</p>
|
||||||
|
<p>Data i Godzina: ${serverDateFormatted} ${serverTimeFormatted}</p>
|
||||||
|
`);
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
// Obsługa błędów np. problemów z API
|
||||||
|
console.error(err);
|
||||||
|
res.send("Wystąpił błąd podczas pobierania pogody.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Uruchamiamy serwer i nasłuchujemy na wskazanym porcie
|
||||||
|
app.listen(PORT, '0.0.0.0', () => {
|
||||||
|
console.log(`Aplikacja działa na porcie ${PORT}`);
|
||||||
|
});
|
||||||
15
MojaAplikacja/package.json
Normal file
15
MojaAplikacja/package.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"name": "pogoda-app",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Aplikacja pogodowa w Express",
|
||||||
|
"main": "app.js",
|
||||||
|
"author": "Julia",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node app.js"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"axios": "^1.6.2",
|
||||||
|
"express": "^4.18.2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
70
MojaAplikacja/public/style.css
Normal file
70
MojaAplikacja/public/style.css
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
body {
|
||||||
|
font-family: Arial, sans-serif;
|
||||||
|
background-color: #f7f7f7;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 30px;
|
||||||
|
background-color: white;
|
||||||
|
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
|
||||||
|
border-radius: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
text-align: center;
|
||||||
|
color: #333;
|
||||||
|
font-size: 28px;
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #444;
|
||||||
|
font-size: 18px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
font-size: 18px;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
background-color: #fff;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: 100%;
|
||||||
|
padding: 18px;
|
||||||
|
font-size: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background-color: #28a745;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
font-weight: bold;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
button:hover {
|
||||||
|
background-color: #218838;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: #007bff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
color: #0056b3;
|
||||||
|
}
|
||||||
98
MojaAplikacja/views/index.html
Normal file
98
MojaAplikacja/views/index.html
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="pl">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8"> <!-- Ustawienie kodowania znaków na UTF-8 -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <!-- Responsywność na urządzeniach mobilnych -->
|
||||||
|
<title>Aplikacja Pogodowa</title> <!-- Tytuł strony w przeglądarce -->
|
||||||
|
<link rel="stylesheet" href="/style.css"> <!-- Dołączenie zewnętrznego pliku CSS -->
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container"> <!-- Główne opakowanie dla treści -->
|
||||||
|
<h1>Aplikacja Pogodowa</h1> <!-- Nagłówek strony -->
|
||||||
|
|
||||||
|
<!-- Formularz pogodowy wysyłany metodą POST na adres /pogoda -->
|
||||||
|
<form id="weather-form" method="POST" action="/pogoda">
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="country">Wybierz kraj:</label>
|
||||||
|
<!-- Lista rozwijana z krajami -->
|
||||||
|
<select name="country" id="country" required>
|
||||||
|
<option value="">Wybierz kraj...</option>
|
||||||
|
<option value="Polska">Polska</option>
|
||||||
|
<option value="Niemcy">Niemcy</option>
|
||||||
|
<option value="Francja">Francja</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="form-group">
|
||||||
|
<label for="city">Miasto:</label>
|
||||||
|
<!-- Lista rozwijana z miastami (uzupełniana dynamicznie przez JavaScript) -->
|
||||||
|
<select name="city" id="city" required>
|
||||||
|
<option value="">Wybierz miasto...</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<button type="submit">Sprawdź pogodę</button> <!-- Przycisk wysyłający formularz -->
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<!-- Sekcja, w której pojawi się wynik (odpowiedź z serwera) -->
|
||||||
|
<div id="weather-result" style="margin-top: 20px;"></div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Skrypt JS do dynamicznej obsługi formularza -->
|
||||||
|
<script>
|
||||||
|
// Po zmianie wybranego kraju, uzupełniamy listę miast
|
||||||
|
document.getElementById("country").addEventListener("change", function() {
|
||||||
|
const country = this.value; // Wartość wybranego kraju
|
||||||
|
const citySelect = document.getElementById("city"); // Element select z miastami
|
||||||
|
|
||||||
|
let cities = []; // Lista miast do uzupełnienia
|
||||||
|
|
||||||
|
// W zależności od kraju, ustawiamy odpowiednie miasta
|
||||||
|
if (country === "Polska") {
|
||||||
|
cities = ["Warszawa", "Lublin", "Gdańsk"];
|
||||||
|
} else if (country === "Niemcy") {
|
||||||
|
cities = ["Berlin", "Monachium", "Hamburg"];
|
||||||
|
} else if (country === "Francja") {
|
||||||
|
cities = ["Paryż", "Marsylia", "Lyon"];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Czyścimy poprzednie opcje i dodajemy nowe
|
||||||
|
citySelect.innerHTML = '<option value="">Wybierz miasto...</option>';
|
||||||
|
|
||||||
|
cities.forEach(function(city) {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = city;
|
||||||
|
option.textContent = city;
|
||||||
|
citySelect.appendChild(option);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Obsługa formularza – wysyłanie danych AJAXem (bez przeładowania strony)
|
||||||
|
document.getElementById("weather-form").addEventListener("submit", function(event) {
|
||||||
|
event.preventDefault(); // Zatrzymujemy domyślne wysyłanie formularza
|
||||||
|
|
||||||
|
const country = document.getElementById("country").value; // Wybrany kraj
|
||||||
|
const city = document.getElementById("city").value; // Wybrane miasto
|
||||||
|
|
||||||
|
// Wysyłamy dane do serwera metodą POST
|
||||||
|
fetch("/pogoda", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded",
|
||||||
|
},
|
||||||
|
body: new URLSearchParams({ country, city }) // Przesyłane dane formularza
|
||||||
|
})
|
||||||
|
.then(response => response.text()) // Odbieramy odpowiedź jako tekst HTML
|
||||||
|
.then(data => {
|
||||||
|
// Wyświetlamy wynik w <div id="weather-result">
|
||||||
|
document.getElementById("weather-result").innerHTML = data;
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
// Obsługa błędu – np. brak odpowiedzi z serwera
|
||||||
|
document.getElementById("weather-result").innerHTML = "Błąd podczas pobierania pogody.";
|
||||||
|
console.error("Błąd:", error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
45
MojaAplikacja/zadanie1.md
Normal file
45
MojaAplikacja/zadanie1.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
|
||||||
|
# Aplikacja Pogodowa w Node.js z użyciem Dockera
|
||||||
|
|
||||||
|
## 1. Opis aplikacji
|
||||||
|
|
||||||
|
Aplikacja pogodowa została stworzona w technologii **Node.js** z użyciem frameworka **Express**. Umożliwia użytkownikowi wybór kraju i miasta w celu wyświetlenia aktualnej pogody na podstawie danych pobieranych z API.
|
||||||
|
Aplikacja wykorzystuje metodę POST do przesyłania danych z formularza i dynamicznie wyświetla wynik w przeglądarce użytkownika.
|
||||||
|
Użytkownik może wybrać jeden z trzech krajów: Polska, Niemcy lub Francja, a następnie miasto dostępne w wybranym kraju.
|
||||||
|
|
||||||
|
## 2. Struktura projektu
|
||||||
|
|
||||||
|
├── public/
|
||||||
|
│ └── style.css # Style CSS dla aplikacji
|
||||||
|
├── views/
|
||||||
|
│ └── index.html # Szablon HTML z formularzem
|
||||||
|
├── app.js # Główny plik serwera Express
|
||||||
|
├── Dockerfile # Plik Dockerfile do budowania kontenera
|
||||||
|
├── package.json # Zależności npm i konfiguracja aplikacji
|
||||||
|
|
||||||
|
## 3. Uruchamianie aplikacji
|
||||||
|
|
||||||
|
1. Zbuduj obraz:
|
||||||
|
|
||||||
|
docker build -t moja-aplikacja .
|
||||||
|
|
||||||
|
2. Uruchom kontener:
|
||||||
|
|
||||||
|
docker run -d -p 3000:3000 --name moja-aplikacja moja-aplikacja
|
||||||
|
|
||||||
|
3. Sprawdzanie lagów:
|
||||||
|
|
||||||
|
docker logs moja-aplikacja
|
||||||
|
|
||||||
|
4. Rozmiar obrazu:
|
||||||
|
|
||||||
|
docker images
|
||||||
|
|
||||||
|
Aplikacja będzie dostępna pod adresem `http://localhost:3000`.
|
||||||
|
|
||||||
|
## 4. Linki
|
||||||
|
|
||||||
|
**GitHub (repozytorium z kodem źródłowym)**: https://github.com/Julka616/moja-aplikacja
|
||||||
|
|
||||||
|
**DockerHub (obraz aplikacji)**: [https://hub.docker.com/r/twoja-nazwa-uzytkownika/pogoda-app](https://hub.docker.com/r/twoja-nazwa-uzytkownika/pogoda-app)
|
||||||
|
|
||||||
Reference in New Issue
Block a user