You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
A full-stack event aggregation platform that scrapes events from public websites, allows admin curation, and serves approved events to end users through a marketplace.
sequenceDiagram
participant Cron as β° Cron Scheduler
participant Scraper as π·οΈ Playwright Scraper
participant Eventbrite as π Eventbrite
participant DB as ποΈ PostgreSQL
participant API as βοΈ Backend API
participant Admin as π€ Admin Dashboard
participant User as π₯ End Users
Cron->>Scraper: Trigger hourly job
Scraper->>Eventbrite: Fetch event listings
Eventbrite-->>Scraper: HTML pages
Scraper->>Scraper: Parse & normalize data
Scraper->>DB: Upsert events (status: new/updated)
Admin->>API: Login via Google OAuth
API-->>Admin: JWT token
Admin->>API: GET /admin/events
API-->>Admin: Unapproved events list
Admin->>API: POST /admin/event/:id
API->>DB: Update (isApproved: true)
User->>API: GET /events
API->>DB: Query approved events
DB-->>API: Event list
API-->>User: Approved events JSON
User->>API: POST /events/:id/lead
API->>DB: Save lead email
Loading
Flow Summary
Scraping: The scraper periodically visits public event websites (Eventbrite) and extracts event information using Playwright
Storage: Scraped data is normalized and stored in PostgreSQL using Prisma ORM
Lifecycle: Events are tagged based on their status (new, updated, inactive, imported)
API: The backend API exposes endpoints to fetch event data and manage admin actions
Frontend: The marketplace consumes these APIs to render events for end users
Admin Auth: Admin users authenticate via Google OAuth to access protected features
ποΈ Database Schema
Models
Admin
Field
Type
Description
id
UUID
Primary key
name
String
Admin display name
email
String
Unique email address
avatar
String?
Profile picture URL
provider
Enum
Auth provider (google, email)
providerId
String?
OAuth provider ID
Event
Field
Type
Description
id
UUID
Primary key
title
String
Event title
description
Text
Full description
summary
String
Short summary (max 500 chars)
venueName
String
Location name
venueAddress
String
Full address
city
String
City (default: Sydney)
category
String
Event category
dateTimeStart
DateTime
Event start time
dateTimeEnd
DateTime
Event end time
imageUrl
String?
Event image
sourceWebsite
String
Source (e.g., "Eventbrite")
originalUrl
String
Original event URL
status
Enum
new, updated, inactive, imported
isApproved
Boolean
Admin approval status
hash
String
Content hash for change detection
EventLead
Field
Type
Description
id
UUID
Primary key
email
String
User's email
eventId
UUID
Related event
consent
Boolean
Marketing consent
originalEventUrl
String
Redirect URL
π£οΈ API Routes
Public Endpoints
Method
Route
Description
GET
/
Health check
GET
/events
Get all approved, active events
POST
/events/:id/lead
Capture lead email before redirect
Authentication
Method
Route
Description
POST
/auth/google
Login with Google OAuth
GET
/auth/verify
Verify JWT token
Admin (Protected - Requires JWT)
Method
Route
Description
GET
/admin/events
Get unapproved events
GET
/admin/imported/events
Get imported events
POST
/admin/event/:id
Approve/import an event
π·οΈ Scraper Details
The scraper uses Playwright with headless Chromium to scrape events from Eventbrite.
Features
Hourly execution via node-cron (0 * * * *)
Deduplication using content hashing
Upsert logic: Creates new events or updates existing ones
Inactive marking: Events no longer found are marked inactive
Multiple extraction strategies: JSON-LD, data attributes, DOM parsing
Event Lifecycle States
stateDiagram-v2
[*] --> new: Scraped for first time
new --> imported: Admin approves
new --> updated: Content changed
updated --> imported: Admin approves
new --> inactive: No longer found
updated --> inactive: No longer found
imported --> inactive: No longer found
Loading
π Getting Started
Prerequisites
Node.js 18+
pnpm 8+
PostgreSQL database (or NeonDB account)
Installation
# Clone the repository
git clone <repo-url>cd eventscale
# Install dependencies
pnpm install
# Set up environment variables
cp apps/backend/.env.example apps/backend/.env
cp apps/scraper/.env.example apps/scraper/.env
sequenceDiagram
participant User as Admin User
participant Frontend as Frontend
participant Google as Google OAuth
participant Backend as Backend API
participant DB as Database
User->>Frontend: Click "Login with Google"
Frontend->>Google: Redirect to Google login
Google-->>Frontend: Credential token
Frontend->>Backend: POST /auth/google {credential}
Backend->>Google: Verify token
Google-->>Backend: User info
Backend->>DB: Find or create admin
Backend-->>Frontend: JWT token + admin info
Frontend->>Frontend: Store token in localStorage