Its time for the inaugural rewrite of the webapp to show if its time to break fast lol
2022 version
server side on heroku
scrape using pupeteer, then persist in mysql db hosted on planetscale
2023 version
fully on nextjs hosted on vercel
scraping done with cheerio
persistence layer using redis
query on getServerSideProps will be reading from redis cache everytime
loading...
in this edition, i replaced pupeteer with cheerio js after realising i dont need to click or perform actions on the site, i just fetch the page, and find the id i need
the newest and shinest thing in this version is Vercel Cron jobs. So its pretty straightforward, a normal handler in the api folder, and it is triggered in accordance to a cron command that is defined in vercel.json
So in this sample, i created a cron folder and it has scrapetime.js that has the handler to do cheerio scraping on muis site, which will then update the redis (hosted on upstash which is integrated onto vercel platform (lets gooo vendor lock in)
on the frontend, whenver user accesses the site, it will do a redis get on maghrib and subuh timing
so yeap thats all folks, happy ramadan
here is the github repo: https://github.com/shaikzhafir/when-eat
{
"crons": [
{
"path": "/api/cron/scrapetime",
"schedule": "0 0 * * *"
}
]
}
// cron job api handler
export default async function handler(req, res) {
// scrape using cheerio
try {
const response = await axios.get('https://www.muis.gov.sg/')
const $ = cheerio.load(response.data);
const scrapedSubuh = $('#PrayerTimeControl1_Subuh').text();
const scrapedMaghrib = $('#PrayerTimeControl1_Maghrib').text();
const subuhTime = convertToTime(scrapedSubuh, false)
const maghribTime = convertToTime(scrapedMaghrib, true)
console.log(subuhTime); // Output: the text content of the div with id "PrayerTimeControl1_Subuh"
console.log(maghribTime); // Output: the text content of the div with id "PrayerTimeControl1_Maghrib"
let client = new Redis(process.env.REDIS_URL);
client.on("error", function (err) {
throw err;
});
await client.set('maghrib', maghribTime, Redis.print);
await client.set('subuh', subuhTime, Redis.print);
res.status(200).json({ subuh: subuhTime, maghrib: maghribTime })
} catch (error) {
console.log(error);
res.status(503).json({ error: error })
}
}
// getServerSideProps on client page
export async function getServerSideProps() {
try {
const Redis = require("ioredis");
require("dotenv").config();
let client = new Redis(process.env.REDIS_URL);
client.on("error", function (err) {
throw err;
});
const subuh = await client.get("subuh");
const maghrib = await client.get("maghrib");
return {
props: {
data: {
subuh: subuh,
maghrib: maghrib,
},
},
};
// in case of error we return a padded time lol
} catch (error) {
return {
props: {
data: {
subuh: "5:30",
maghrib: "19:30",
},
},
};
}
}