Skip to content

tee4tao/StreetWatch

Repository files navigation

πŸ›£οΈ StreetWatch

StreetWatch is a community-driven web application that empowers residents to report and track local infrastructure issues β€” potholes, broken streetlights, illegal dumping, water leaks, and more β€” and visualize them on an interactive map in real time.


πŸ“‹ Table of Contents


Overview

StreetWatch bridges the gap between residents and local authorities by making it dead simple to report neighborhood issues with photos and precise locations. Reports are pinned on a live Leaflet map, filtered to a 15-mile radius around the user using the Haversine formula, and updated in real time via Pusher WebSockets β€” so everyone in the community sees new reports the moment they're submitted.


Features

  • πŸ“ Interactive Map β€” Leaflet-powered map with custom markers per issue category
  • πŸ“Έ Photo Uploads β€” Drag-and-drop image upload backed by AWS S3 with pre-signed URLs
  • πŸ”΄ Live Updates β€” Real-time issue feed powered by Pusher WebSockets; no refresh needed
  • πŸ“ Proximity Filtering β€” Haversine formula filters the map to a 15-mile radius around the user
  • πŸ” Authentication β€” Secure sign-up / login flows via Better Auth
  • πŸ›‘οΈ Bot Protection & Rate Limiting β€” ArcJet shields all sensitive API endpoints
  • πŸ—ΊοΈ Geocoding β€” Forward and reverse geocoding powered by LocationIQ for address search and coordinate-to-address resolution
  • πŸ“Š Status Tracking β€” Each issue carries a status: Pending, In Progress, or Resolved
  • 🌐 Community Impact Dashboard β€” Stats and charts showing reported vs. resolved issues
  • πŸ“± Responsive Design β€” Mobile-first layout with a dedicated mobile map/list toggle

Tech Stack

Layer Technology
Framework Next.js 16 (App Router)
Language TypeScript
Styling Tailwind CSS v4, tw-animate-css
UI Components Radix UI, shadcn/ui, Lucide React
Animation Motion (Framer Motion v12)
Map React Leaflet + Leaflet.js
Charts Recharts
Auth Better Auth
Database PostgreSQL via Prisma ORM + Prisma Accelerate
File Storage AWS S3 (via @aws-sdk)
Real-time Pusher + pusher-js
Form Handling React Hook Form + Zod
Geocoding LocationIQ (forward & reverse)
Bot Protection ArcJet
Notifications Sonner

Project Structure

StreetWatch/
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ (public)/
β”‚   β”‚   β”œβ”€β”€ (auth)/
β”‚   β”‚   β”‚   β”œβ”€β”€ login/
β”‚   β”‚   β”‚   └── sign-up/
β”‚   β”‚   └── layout.tsx
β”‚   └── (root)/
β”‚       β”œβ”€β”€ _components/
β”‚       β”‚   β”œβ”€β”€ landing/           # Landing page sections
β”‚       β”‚   β”‚   β”œβ”€β”€ AreaIssues.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ CommunityImpact.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ CTA.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ Hero.tsx
β”‚       β”‚   β”‚   β”œβ”€β”€ MakingDifference.tsx
β”‚       β”‚   β”‚   └── YourAreaIssues.tsx
β”‚       β”‚   β”œβ”€β”€ CallMap.tsx
β”‚       β”‚   β”œβ”€β”€ Countup.tsx
β”‚       β”‚   β”œβ”€β”€ Footer.tsx
β”‚       β”‚   β”œβ”€β”€ IssueMap.tsx        # Main interactive map
β”‚       β”‚   β”œβ”€β”€ IssueMarker.tsx
β”‚       β”‚   β”œβ”€β”€ Map.tsx
β”‚       β”‚   β”œβ”€β”€ Navbar.tsx
β”‚       β”‚   └── UserDropdown.tsx
β”‚       β”œβ”€β”€ report-issue/           # Issue submission flow
β”‚       └── reported-issues/        # Browse & filter issues
β”‚           β”œβ”€β”€ _components/
β”‚           β”‚   β”œβ”€β”€ header.tsx
β”‚           β”‚   β”œβ”€β”€ issue-detail.tsx
β”‚           β”‚   β”œβ”€β”€ issue-list.tsx
β”‚           β”‚   β”œβ”€β”€ LocationError.tsx
β”‚           β”‚   β”œβ”€β”€ LocationSearchBar.tsx
β”‚           β”‚   β”œβ”€β”€ map-view.tsx
β”‚           β”‚   β”œβ”€β”€ MobileViewToggle.tsx
β”‚           β”‚   └── ReportedIssuesContainer.tsx
β”‚           β”œβ”€β”€ action.ts
β”‚           └── page.tsx
β”œβ”€β”€ api/
β”‚   β”œβ”€β”€ auth/
β”‚   β”œβ”€β”€ geocoding/
β”‚   β”œβ”€β”€ issues/
β”‚   └── s3/
β”œβ”€β”€ components/
β”‚   β”œβ”€β”€ file-uploader/
β”‚   β”œβ”€β”€ ui/
β”‚   β”œβ”€β”€ AsyncBoundary.tsx
β”‚   └── ErrorBoundary.tsx
β”œβ”€β”€ contexts/
β”‚   └── LocationContext.tsx
β”œβ”€β”€ hooks/
β”‚   β”œβ”€β”€ use-construct-url.ts
β”‚   β”œβ”€β”€ use-facebook.ts
β”‚   β”œβ”€β”€ use-github.ts
β”‚   β”œβ”€β”€ use-google.ts
β”‚   β”œβ”€β”€ use-media-query.ts
β”‚   └── use-signout.ts
β”œβ”€β”€ lib/
β”‚   β”œβ”€β”€ animation.ts
β”‚   β”œβ”€β”€ arcjet.ts
β”‚   β”œβ”€β”€ auth-client.ts
β”‚   β”œβ”€β”€ auth.ts
β”‚   β”œβ”€β”€ geo-utils.ts           # Haversine formula & distance helpers
β”‚   β”œβ”€β”€ prisma.ts
β”‚   β”œβ”€β”€ pusher.ts
β”‚   β”œβ”€β”€ S3Client.ts
β”‚   β”œβ”€β”€ types.ts
β”‚   β”œβ”€β”€ utils.ts
β”‚   └── zodSchema.ts
β”œβ”€β”€ prisma/
β”‚   β”œβ”€β”€ migrations/
β”‚   └── schema.prisma
β”œβ”€β”€ providers/
β”‚   └── PusherProvider.tsx
└── public/

Getting Started

Prerequisites

  • Node.js >= 18.x
  • npm or pnpm
  • PostgreSQL database (or a Prisma Accelerate connection string)
  • AWS S3 bucket with appropriate IAM permissions
  • Pusher app credentials
  • ArcJet API key

Installation

git clone https://github.com/your-username/streetwatch.git
cd streetwatch
npm install

Environment Variables

Create a .env file in the root of the project:

# Database
DATABASE_URL="postgresql://USER:PASSWORD@HOST:PORT/DATABASE"

# Better Auth
BETTER_AUTH_SECRET="your-auth-secret"
BETTER_AUTH_URL="http://localhost:3000"

# AWS S3
AWS_REGION="your-region"
AWS_ACCESS_KEY_ID="your-access-key"
AWS_SECRET_ACCESS_KEY="your-secret-key"
AWS_ENDPOINT_URL_S3="your-s3-endpoin-url"
AWS_ENDPOINT_URL_IAM="your-IAM-endpoin-url"
NEXT_PUBLIC_S3_BUCKET_NAME_IMAGES=="your-bucket-name"

# Pusher
PUSHER_APP_ID="your-app-id"
PUSHER_SECRET="your-pusher-secret"
NEXT_PUBLIC_PUSHER_KEY="your-pusher-key"
NEXT_PUBLIC_PUSHER_CLUSTER="your-cluster"

# LocationIQ
LOCATIONIQ_API_KEY="your-locationiq-api-key"

# ArcJet
ARCJET_KEY="your-arcjet-key"

# OAuth (optional)
GITHUB_CLIENT_ID="..."
GITHUB_CLIENT_SECRET="..."
GOOGLE_CLIENT_ID="..."
GOOGLE_CLIENT_SECRET="..."
FACEBOOK_CLIENT_ID="..."
FACEBOOK_CLIENT_SECRET="..."

Database Setup

# Generate Prisma client
npx prisma generate

# Run migrations
npx prisma migrate deploy

# (Optional) Seed the database
npx prisma db seed

Running the App

# Development
npm run dev

# Production build
npm run build
npm start

Open http://localhost:3000 in your browser.


Architecture & Key Decisions

Proximity Filtering with Haversine Formula

Rather than loading every issue in the database onto the map, StreetWatch fetches only issues within a 15-mile radius of the user's current location. The Haversine formula (implemented in lib/geo-utils.ts) calculates the great-circle distance between two coordinates, keeping the map focused and performant regardless of how many total issues exist in the system.

Real-time Updates with Pusher

When a user submits a new issue, the server publishes an event to a Pusher channel. All connected clients subscribed via PusherProvider.tsx receive the update instantly and append the new marker to the map without a page refresh. This keeps the community map live and accurate.

Image Uploads via AWS S3

The file uploader generates a pre-signed S3 URL server-side (via the /api/s3 route), then uploads directly from the browser to S3. This keeps large binary payloads out of the Next.js server and reduces latency significantly.

Bot Protection & Rate Limiting via ArcJet

All write endpoints (/api/issues, /api/s3, auth routes) are protected by ArcJet. This adds bot detection, rate limiting, and shield rules without any infrastructure overhead, protecting the platform from spam submissions and abuse.

Authentication with Better Auth

Better Auth handles credential-based sign-up/login as well as OAuth flows (GitHub, Google, Facebook). Session management is server-side, and the auth client is exposed via lib/auth-client.ts for use in client components.


API Routes

Method Route Description
GET /api/issues Fetch issues (filtered by lat/lng radius)
POST /api/issues Submit a new issue report
PATCH /api/issues/[id] Update issue status
POST /api/s3 Generate a pre-signed S3 upload URL
GET /api/geocoding Forward (coordinates β†’ address) & Reverse geocode (address β†’ coordinates) using LocationIQ
GET/POST /api/auth/[...all] Better Auth handler

Security

  • ArcJet β€” Bot detection and rate limiting on all sensitive routes
  • Pre-signed S3 URLs β€” Uploads go directly to S3; secrets never exposed to the client
  • Zod validation β€” All form inputs and API payloads are validated against strict schemas (lib/zodSchema.ts)
  • Better Auth β€” CSRF protection and secure session management built in
  • Environment variables β€” All secrets are kept out of source control via .env

Deployment

StreetWatch is optimized for deployment on Vercel, but works on any platform that supports Next.js.

Vercel (recommended):

  1. Push your repository to GitHub
  2. Import the project in the Vercel dashboard
  3. Add all environment variables from the .env template above
  4. Deploy β€” Vercel handles builds, edge functions, and CDN automatically

Other platforms: Ensure NODE_ENV=production, run npm run build, then npm start. A PostgreSQL instance must be accessible from the host.


Contributing

Contributions are welcome! Please follow these steps:

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/your-feature
  3. Commit your changes: git commit -m "feat: add your feature"
  4. Push to the branch: git push origin feature/your-feature
  5. Open a Pull Request

Please make sure your code passes npm run lint before submitting.


Built with ❀️ for communities everywhere.

About

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors