Skip to content

Commit

Permalink
Merge pull request #6 from smdthiranjaya/dev
Browse files Browse the repository at this point in the history
Clean and add comments for the whole project.
  • Loading branch information
smdthiranjaya authored Apr 6, 2024
2 parents 5df4dc6 + f51e637 commit 38017c3
Show file tree
Hide file tree
Showing 10 changed files with 67 additions and 10 deletions.
23 changes: 16 additions & 7 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,24 +8,33 @@ const swaggerUi = require('swagger-ui-express');
const swaggerDocs = require('./src/swaggerConfig');
const subscriptionRoutes = require('./src/routes/subscriptionRoutes');

const app = express();
// Initialize Express app
const app = express();
// Enable CORS
app.use(cors());

// Parse JSON request bodies
app.use(express.json());
// Use weather routes
app.use(weatherRoutes);

app.get('/', (req, res) => {
// Root endpoint
app.get('/', (req, res) => {
res.send('Weather Data Fetching Service is running.');
});

app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
app.use('/api', subscriptionRoutes);
// Use swagger doc routes
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));
// Use subscription routes
app.use('/api', subscriptionRoutes);

setInterval(() => {
// Schedule email updates (6 hours)
setInterval(() => {
sendWeatherUpdates().catch(console.error);
}, 43200000);
}, 21600000);

setInterval(async () => {
// Schedule weather data updates (5 minutes)
setInterval(async () => {
try {
await updateWeatherDataManually();
} catch (error) {
Expand Down
1 change: 1 addition & 0 deletions src/config/index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require('dotenv').config();

// Export configuration settings
module.exports = {
PORT: process.env.PORT || 3000,
DATABASE_URL: process.env.DATABASE_URL
Expand Down
4 changes: 4 additions & 0 deletions src/controllers/subscriptionController.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
const pool = require('../db');

// Define an asynchronous function to add a subscription
async function addSubscription(req, res) {
const { email, city } = req.body;
try {
// Connect to the database
const client = await pool.connect();
const insertQuery = `
INSERT INTO subscriptions (email, city)
VALUES ($1, $2)
RETURNING *;
`;
const result = await client.query(insertQuery, [email, city]);
// Release database client back to pool
client.release();

res.status(200).json({
Expand All @@ -26,5 +29,6 @@ async function addSubscription(req, res) {
}
}

// Export the addSubscription function
module.exports = { addSubscription };

15 changes: 14 additions & 1 deletion src/controllers/weatherController.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const { storeWeatherData, fetchAllWeatherData } = require('../models/weatherModel');
const pool = require('../db');

// Function to manually update weather data
async function updateWeatherDataManually() {
console.log('Fetching and Storing weather data...');

// Define static data for cities and geo locations
const cities = [
{ id: 1, city: 'Colombo', lat: 6.932, lng: 79.848 },
{ id: 2, city: 'Kandy', lat: 7.296, lng: 80.636 },
Expand All @@ -23,6 +25,7 @@ async function updateWeatherDataManually() {
];


// Generate and map mock weather data for cities
const weatherIconMapping = {
'Sunny': 'https://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0001_sunny.png',
'Partly cloudy': 'https://cdn.worldweatheronline.com/images/wsymbols01_png_64/wsymbol_0002_sunny_intervals.png',
Expand Down Expand Up @@ -59,10 +62,14 @@ async function updateWeatherDataManually() {
};
});

// Connect to the database
const client = await pool.connect();
try {
// Start transaction
await client.query('BEGIN');
for (const data of manualWeatherData) {

// Insert or update weather data in the database for each city
const insertQuery = `
INSERT INTO weather_data (id, city, latitude, longitude, temperature, humidity, air_pressure, wind_speed, weather_descriptions, observation_time, weather_icons, is_day)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
Expand All @@ -83,19 +90,24 @@ async function updateWeatherDataManually() {
];
await client.query(insertQuery, values);
}
// Commit transaction if successful
await client.query('COMMIT');
} catch (e) {
console.error('Failed to update weather data:', e);
// Rollback transaction in case of an error
await client.query('ROLLBACK');
} finally {
// Release database client
client.release();
}
}


// Function to serve weather data via HTTP GET request
async function getWeather(req, res) {
try {
// Fetch weather data from the database and format the response
const weatherData = await fetchAllWeatherData();
// Send formatted weather data as response
const response = {
data: weatherData.map(row => ({
type: 'weather',
Expand All @@ -122,4 +134,5 @@ async function updateWeatherDataManually() {
}
}

// Export the functions for use in other modules
module.exports = { updateWeatherDataManually, getWeather };
4 changes: 4 additions & 0 deletions src/db/index.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
const { Pool } = require('pg');
const { DATABASE_URL } = require('../config');

// Create a new pool instance with connection settings
const pool = new Pool({
// Use the DATABASE_URL for connection string
connectionString: DATABASE_URL,
// Disable SSL to allow connections from localhost
ssl: {
rejectUnauthorized: false
}
});

// Export the pool for database operations elsewhere in the application
module.exports = pool;
5 changes: 5 additions & 0 deletions src/models/weatherModel.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
const pool = require('../db');

// Function to store multiple weather data entries into the database
async function storeWeatherData(weatherData) {
console.log('Storing weather data...');
const client = await pool.connect();
try {
await client.query('BEGIN');
// Prepare and execute SQL to insert or update weather data for each entry
for (const data of weatherData) {
const insertQuery = `
INSERT INTO weather_data (id, city, latitude, longitude, temperature, humidity, air_pressure, wind_speed, weather_descriptions, observation_time, weather_icons, is_day)
Expand Down Expand Up @@ -35,9 +37,11 @@ async function storeWeatherData(weatherData) {
}
}

// Function to fetch all weather data from the database
async function fetchAllWeatherData() {
const client = await pool.connect();
try {
// Execute a SELECT query to fetch all weather data
const { rows } = await client.query('SELECT * FROM weather_data');
return rows;
} catch (error) {
Expand All @@ -48,4 +52,5 @@ async function storeWeatherData(weatherData) {
}
}

// Export the functions for external use
module.exports = { storeWeatherData, fetchAllWeatherData };
3 changes: 3 additions & 0 deletions src/routes/subscriptionRoutes.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
const express = require('express');
const router = express.Router();
// Import the addSubscription function from the subscription controller
const { addSubscription } = require('../controllers/subscriptionController');

// Define a POST route for subscriptions
router.post('/subscribe', addSubscription);

// Export the router for use in other parts of the application
module.exports = router;
2 changes: 2 additions & 0 deletions src/routes/weatherRoutes.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ const express = require('express');
const { getWeather } = require('../controllers/weatherController');
const router = express.Router();

// Define a GET route for fetching weather data
router.get('/api/weather', getWeather);

// Swagger documentation for the /api/weather endpoint
/**
* @swagger
* /api/weather:
Expand Down
14 changes: 13 additions & 1 deletion src/services/emailService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,19 @@
const nodemailer = require('nodemailer');
const pool = require('../db');

// Asynchronously send weather updates to all subscribers
async function sendWeatherUpdates() {
const client = await pool.connect();
try {
// Retrieve a list of distinct cities from subscriptions
const { rows: subscriptions } = await client.query('SELECT DISTINCT city FROM subscriptions;');
// Iterate over each city to fetch and send weather updates
for (const { city } of subscriptions) {
// Fetch weather data for the current city
const weatherData = await fetchWeatherDataForCity(city);
// Retrieve all email addresses subscribed to the current city
const { rows: subscribers } = await client.query('SELECT email FROM subscriptions WHERE city = $1;', [city]);
// Send an email with weather data to each subscriber
for (const { email } of subscribers) {
await sendEmail(email, city, weatherData);
}
Expand All @@ -20,14 +26,17 @@ async function sendWeatherUpdates() {
}
}

// Asynchronously fetch weather data for a specific city
async function fetchWeatherDataForCity(city) {
const client = await pool.connect();
try {
// SQL query to select temperature stats for the specified city
const query = `
SELECT MAX(temperature) AS max_temp, MIN(temperature) AS min_temp, AVG(temperature) AS avg_temp
FROM weather_data
WHERE city = $1;
`;
// Execute the query and return the result
const result = await client.query(query, [city]);
return result.rows[0];
} catch (error) {
Expand All @@ -38,8 +47,9 @@ async function fetchWeatherDataForCity(city) {
}
}


// Asynchronously send an email to a recipient with weather data for a city
async function sendEmail(recipient, city, weatherData) {
// Configure nodemailer with email service and authentication details
let transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
Expand All @@ -48,6 +58,7 @@ async function sendEmail(recipient, city, weatherData) {
}
});

// Set up email options including recipient, subject, and body
let mailOptions = {
from: 'geo360.live@gmail.com',
to: recipient,
Expand All @@ -66,6 +77,7 @@ async function sendEmail(recipient, city, weatherData) {
Geo360 Team.`
};

// Send the email and log the result or error
transporter.sendMail(mailOptions, function(error, info){
if (error) {
console.log('Email send error:', error);
Expand Down
6 changes: 5 additions & 1 deletion src/swaggerConfig.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
const swaggerJsDoc = require('swagger-jsdoc');

// Define Swagger options for API documentation
const swaggerOptions = {
definition: {
openapi: '3.0.0',
// Provide basic information about the API (title, version, description)
info: {
title: 'Geo 360 Live Weather API',
version: '1.0.0',
description: 'A simple Express Weather API'
},
// Define the server(s) the API is available on
servers: [
{
url: 'https://www.geo360live.tech'
}
],
},
// Specify the path(s) to files containing Swagger annotations for routes
apis: ['./src/routes/*.js'],
};

module.exports = swaggerJsDoc(swaggerOptions);
module.exports = swaggerJsDoc(swaggerOptions);

0 comments on commit 38017c3

Please sign in to comment.