Client project
CUNYServecivic-engagement platform
A platform that helps City University of New York students find volunteering, pre-health hospital placements and community events in one place.
- Client
- City University of New York
- Built
- August 2025, 19 commits over 11 days
- Stack
- Node.js, Express, MongoDB, Puppeteer
- Source
- simu18/CUNYServe
The problem
Opportunities spread across three websites
CUNY events, admissions information sessions and New York City volunteering opportunities each live on their own site. Students had to check all three, and nothing tracked who had signed up or finished orientation.
What I built
One calendar, kept fresh automatically
- Scrapers. Three Puppeteer scrapers run every night at 02:00. Each event is upserted by its source URL, so re-running a scrape updates events instead of duplicating them. Every run writes a log with new, updated and failed counts.
- Review before publishing. Scraped events land in an admin queue. An administrator reviews them and sees the scrape history before anything reaches students.
- Accounts. Google sign-in or email and password (bcrypt, JWT in cookies), a two-step sign-up, profiles, and email password reset through Nodemailer.
- Onboarding. A volunteer orientation flow that records which students have completed it.
- Student pages. Volunteer projects, a pre-health hospital volunteering track, an events calendar (FullCalendar), FAQ and code of conduct.
- Deployment. Express API and static pages on Vercel, with admin-only routes behind separate middleware.
What I would change
Lessons
Scrapers break when a source site changes its layout. The logs made failures visible, but the next step would be an alert when a scraper returns zero events several nights running, and tests that run each scraper against a saved copy of the page.