Skip to content

Matrixk1ng/FirstApply

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

30 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

FirstApply 🚀

A job tracking mobile app that monitors job boards for new internships and sends push notifications to your Android phone when new postings are found.

Features

  • 📱 Mobile App - React Native (Expo) app with clean Twitter/X-style job feed
  • 🔔 Push Notifications - Instant alerts when new jobs are posted
  • 🕷️ Web Scraping - Automatic scraping of We Work Remotely job board
  • ☁️ Cloud Hosted - Backend deployed on Render (free tier)
  • 💾 Supabase DB - PostgreSQL database for job and token storage

Tech Stack

Component Technology
Frontend React Native (Expo Router + Expo Notifications)
Backend Python FastAPI
Database Supabase (PostgreSQL)
Scraping BeautifulSoup4 + Requests
Hosting Render (free tier)
Automation cron-job.org (free tier)

Project Structure

firstapply/
├── mobile/                 # React Native Expo app
│   ├── app/
│   │   ├── _layout.js     # Navigation + notification setup
│   │   └── index.js       # Job feed screen
│   ├── assets/            # App icons and images
│   ├── package.json
│   └── app.json
├── server/                 # Python FastAPI backend
│   ├── main.py            # API endpoints
│   ├── database.py        # Supabase operations
│   ├── scraper.py         # Web scraper + notifications
│   ├── requirements.txt
│   └── render.yaml        # Render deployment config
└── README.md

🛠️ Setup Instructions

Prerequisites

  • Python 3.9+
  • Node.js 18+
  • Android Studio (for emulator) or physical Android device
  • Supabase account (free)
  • Render account (free)

1. Supabase Setup

  1. Create Project

    • Go to supabase.com and sign up/login
    • Click "New Project"
    • Fill in project details and wait for setup (~2 minutes)
  2. Create Database Tables

    Go to SQL Editor (left sidebar) and run these commands:

    -- Jobs table: stores scraped job listings
    CREATE TABLE jobs (
      id BIGSERIAL PRIMARY KEY,
      title TEXT NOT NULL,
      company TEXT NOT NULL,
      url TEXT UNIQUE NOT NULL,
      posted_date TEXT,
      created_at TIMESTAMPTZ DEFAULT NOW()
    );
    
    -- Add indexes for better query performance
    CREATE INDEX idx_jobs_url ON jobs(url);
    CREATE INDEX idx_jobs_created_at ON jobs(created_at DESC);
    
    -- Users table: stores Expo Push Tokens
    CREATE TABLE users (
      id BIGSERIAL PRIMARY KEY,
      expo_push_token TEXT UNIQUE NOT NULL,
      created_at TIMESTAMPTZ DEFAULT NOW()
    );
  3. Get API Credentials

    • Go to Settings → API (in left sidebar)
    • Copy Project URL (e.g., https://xxxx.supabase.co)
    • Copy anon public key (under "Project API keys")

2. Backend Setup

# Navigate to server directory
cd server

# Create virtual environment
python -m venv venv

# Activate virtual environment
# Windows:
venv\Scripts\activate
# macOS/Linux:
source venv/bin/activate

# Install dependencies
pip install -r requirements.txt

# Create .env file with your Supabase credentials
# Copy the template or create manually:

Create server/.env:

SUPABASE_URL=https://your-project-id.supabase.co
SUPABASE_KEY=your-anon-public-key
# Start the server
uvicorn main:app --reload --port 8000

The API will be available at http://localhost:8000

Test the API:

# Health check
curl http://localhost:8000/

# Get jobs (will be empty initially)
curl http://localhost:8000/jobs

# Trigger scraper
curl http://localhost:8000/trigger-scrape

3. Mobile App Setup

# Navigate to mobile directory
cd mobile

# Install dependencies
npm install

# Create .env file for API URL

Create mobile/.env:

# For Android Emulator:
EXPO_PUBLIC_API_URL=http://10.0.2.2:8000

# For physical device (replace with your computer's IP):
# EXPO_PUBLIC_API_URL=http://192.168.1.100:8000

# For production:
# EXPO_PUBLIC_API_URL=https://firstapply-api.onrender.com

Finding your local IP (for physical device):

  • Windows: Run ipconfig, look for IPv4 Address
  • macOS/Linux: Run ifconfig or ip addr
# Start Expo development server
npx expo start

# Press 'a' to open on Android emulator
# Or scan QR code with Expo Go app on physical device

4. Add Notification Icon

Create a 96x96 pixel white icon on transparent background:

File: mobile/assets/notification-icon.png

Requirements:

  • Size: 96x96 pixels
  • White foreground (#FFFFFF)
  • Transparent background
  • PNG format

Quick option: Use any free icon generator or create a simple shape in any image editor.


🤖 Automation Setup (cron-job.org)

Since Render's free tier doesn't include cron jobs, we use cron-job.org for free scheduling.

Why This Approach?

Render's free tier has two challenges:

  1. No cron jobs - Cron scheduling costs $$ on Render
  2. Server sleeps - Free tier spins down after 15 min of inactivity

Solution: External cron pinging our /trigger-scrape endpoint:

  • ✅ Scrapes for new jobs
  • ✅ Keeps server awake
  • ✅ Completely free

Setup Steps

  1. Sign up at cron-job.org (free tier: 5 jobs)

  2. Create a new cron job:

    • URL: https://your-app.onrender.com/trigger-scrape
    • Schedule: Every 15 minutes
    • Method: GET
  3. Recommended schedule: */15 * * * * (every 15 minutes)

    • Balances freshness vs rate limits
    • Keeps server from sleeping

🚀 Deployment

Deploy Backend to Render

  1. Push code to GitHub

  2. Connect to Render:

    • Go to render.com and sign up
    • Click "New +" → "Web Service"
    • Connect your GitHub repo
    • Render will auto-detect render.yaml
  3. Set Environment Variables:

    • In Render dashboard → Environment
    • Add SUPABASE_URL and SUPABASE_KEY
  4. Deploy!

    • Render will build and deploy automatically
    • Note your URL: https://your-app.onrender.com

Update Mobile App for Production

  1. Update mobile/.env:

    EXPO_PUBLIC_API_URL=https://your-app.onrender.com
  2. Build APK:

    # Install EAS CLI
    npm install -g eas-cli
    
    # Login to Expo
    eas login
    
    # Build APK
    eas build -p android --profile preview

🧪 Quick Test Flow

1. Test Backend Locally

# Start server
cd server
uvicorn main:app --reload --port 8000

# Test health
curl http://localhost:8000/

# Test scraping
curl http://localhost:8000/trigger-scrape

# Check jobs
curl http://localhost:8000/jobs

2. Test Mobile App

# Start app
cd mobile
npx expo start

# Open on Android emulator (press 'a')

3. Test Push Notifications

After the app registers its token:

# Send test notification to all devices
curl -X POST http://localhost:8000/test-notification

# Or test with specific token
curl -X POST http://localhost:8000/test-notification \
  -H "Content-Type: application/json" \
  -d '{"token": "ExponentPushToken[xxx]"}'

4. Verify Full Flow

  1. ✅ App loads and shows job list
  2. ✅ Pull-to-refresh works
  3. ✅ Tapping job opens URL in browser
  4. ✅ Test notification appears on device
  5. ✅ Tapping notification opens job URL

📡 API Reference

Endpoint Method Description
/ GET Health check
/jobs GET Get all jobs (newest first)
/register-token POST Register Expo Push Token
/trigger-scrape GET Run scraper manually
/test-notification POST Send test notification

Example Requests

# Register token
curl -X POST http://localhost:8000/register-token \
  -H "Content-Type: application/json" \
  -d '{"token": "ExponentPushToken[abc123]"}'

# Get jobs
curl http://localhost:8000/jobs

# Trigger scrape
curl http://localhost:8000/trigger-scrape

🐛 Troubleshooting

"Network request failed" on Android Emulator

  • Make sure API URL is http://10.0.2.2:8000 (not localhost)
  • Check that backend server is running
  • Verify no firewall blocking port 8000

Push notifications not working

  • Must test on physical device (emulators have limitations)
  • Check notification permissions in device settings
  • Verify token is registered: check Supabase users table
  • Test with /test-notification endpoint

Scraper returning 0 jobs

  • Site structure may have changed
  • Check for rate limiting (429 errors)
  • Verify internet connection
  • Check server logs for errors

Render server sleeping

  • Set up cron-job.org to ping every 15 minutes
  • First request after sleep takes ~30 seconds (cold start)

📝 Notes

Android Emulator Networking

The Android emulator uses special IP addresses:

  • 10.0.2.2 → Host machine's localhost
  • 10.0.2.3 → Host machine's loopback interface

That's why we use http://10.0.2.2:8000 instead of http://localhost:8000

Expo Push Token Format

Tokens look like: ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]

These are unique per device and can expire when:

  • App is uninstalled
  • User revokes notification permissions
  • Token rotation (rare)

Invalid tokens are automatically cleaned up when Expo returns DeviceNotRegistered


📜 License

MIT License - feel free to use and modify!


🙏 Acknowledgments

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published