GhostClass is the ultimate academic survival tool for students who want to manage their attendance without the main character energy of a professor. Featuring a sleek dashboard with real-time analytics and visual performance charts, it helps you track your classes so you never accidentally ghost your degree. With a built-in "bunk calculator" to tell you exactly how many lectures you can skip before it becomes a canon event, and a dedicated tracker for suspicious absences, GhostClass ensures your attendance stays valid while you live your best life. Built as a better alternative to Ezygo, it presents your attendance data with a clean, intuitive interface. No more confusing numbers - just clear, actionable insights!
- The Bunk Calc 🧮: Know exactly how many classes you can miss before the threshold comes for your neck.
- Visual Receipts 📊: Performance charts and a detailed calendar history so you can see your attendance glow-up in real-time.
- Anti-Ghosting Tracker 👻: A personalized list to watch wrongly marked absences like a hawk until they get updated.
- Ezygo Integration 🔄 - Use your existing ezygo credentials - no new accounts needed
- Multi-Device Support 🔐 - Login from multiple devices simultaneously without losing sessions
- Real-time Updates ⚡ - Get instant updates on your attendance status and skip calculations
- Track Status Changes 📝 – Get notified when your attendance is updated
- Mobile Friendly 📱 - Access your attendance data on any device, anywhere
- API Documentation 📚 - Interactive OpenAPI documentation at
/api-docs
Core Framework
- Next.js 16.1.6 - React 19 with App Router and Turbopack
- TypeScript 5 - Strict mode for type safety
- Node.js - v20.19.0+ or v22.12.0+
Styling & UI
- Tailwind CSS 4 - Utility-first styling with custom design system
- Radix UI - Accessible, unstyled component primitives
- Shadcn UI - Beautiful pre-styled components
- Framer Motion - Smooth animations and transitions
- Lucide Icons - Modern, customizable icon library
Data & State Management
- TanStack Query (React Query) - Server state management
- React Hook Form + Zod - Form validation
- Recharts - Interactive data visualizations
API & Documentation
- OpenAPI 3.1 - API specification standard
- Scalar - Interactive API documentation viewer
- Redocly CLI - OpenAPI validation and linting
Backend & Database
- Supabase - PostgreSQL database with Row Level Security
- Supabase Auth - Secure authentication system
- Axios - HTTP client for API requests
Security & Monitoring
- AES-256-GCM Encryption - Secure token storage
- CSRF Protection - Custom token-based protection
- Upstash Redis - Rate limiting with
@upstash/ratelimit - Sentry - Error tracking and performance monitoring
- GA4 Measurement Protocol - Server-side analytics (CSP-compatible)
- Cloudflare Turnstile - Bot protection
DevOps & Deployment
- Docker - Containerized deployment with multi-stage builds
- GitHub Actions - CI/CD pipeline with reproducible builds
- Coolify - Self-hosted deployment platform
- Playwright - E2E testing
- Vitest - Unit and component testing
src/
├── app/ # Next.js app router pages and layouts
│ ├── (auth)/ # Authentication-related routes (login, signup)
│ ├── (protected)/ # Login-restricted routes (dashboard, profile, tracking)
│ │ ├── dashboard/ # Main dashboard with attendance overview
│ │ ├── profile/ # User profile and settings
│ │ ├── tracking/ # Manual attendance tracking interface
│ │ └── notifications/ # Notification center
│ ├── (public)/ # Public routes (home, contact, legal pages)
│ ├── accept-terms/ # Terms acceptance page (authenticated)
│ ├── actions/ # Server actions (contact, user operations)
│ ├── api/ # API routes
│ │ ├── auth/ # Authentication endpoints
│ │ ├── backend/ # Backend proxy endpoints
│ │ ├── cron/ # Scheduled jobs (sync, cleanup)
│ │ └── health/ # Health check endpoint
│ ├── config/ # App configuration files
│ ├── globals.css # Global styles and Tailwind directives
│ └── layout.tsx # Root layout with providers
├── components/ # Reusable React components
│ ├── attendance/ # Attendance-specific components
│ │ ├── course-card.tsx # Individual course display
│ │ ├── attendance-calendar.tsx # Calendar view
│ │ └── attendance-chart.tsx # Performance charts
│ ├── layout/ # Layout components (navbar, footer)
│ ├── ui/ # Shadcn UI components
│ └── user/ # User-related components
├── providers/ # React context providers
│ ├── attendance-settings.tsx # Attendance target settings
│ └── react-query.tsx # TanStack Query provider
├── hooks/ # Custom React hooks
│ ├── courses/ # Course data fetching hooks
│ ├── tracker/ # Tracking data hooks
│ └── users/ # User data hooks
├── lib/ # Core library code
│ ├── logic/ # Business logic
│ │ └── bunk.ts # Attendance calculation algorithm
│ ├── supabase/ # Supabase client configuration
│ ├── email.ts # Email service (Brevo/SendPulse)
│ ├── crypto.ts # AES-256-GCM encryption
│ ├── ratelimit.ts # Upstash Redis rate limiting
│ └── utils.ts # Utility functions
├── types/ # TypeScript type definitions
│ ├── attendance.d.ts # Attendance data types
│ ├── course.d.ts # Course types
│ └── user.d.ts # User types
└── assets/ # Static assets (images, icons)
supabase/
└── migrations/ # Database schema migrations
The core attendance calculation algorithm is implemented in bunk.ts and is used throughout the application, particularly in the course-card.tsx component.
function calculateAttendance(present, total, targetPercentage):
1. Input Validation & Normalization
- Ensure total > 0, present >= 0, present <= total
- Clamp targetPercentage between 1-100 (default: 75)
- Return zero result if invalid
2. Calculate Current Percentage
currentPercentage = (present / total) * 100
3. Check if Exactly at Target
if currentPercentage == targetPercentage:
return { isExact: true, canBunk: 0, requiredToAttend: 0 }
4. Below Target - Calculate Required Classes
if currentPercentage < targetPercentage:
if targetPercentage >= 100:
required = total - present
else:
required = ceil((target * total - 100 * present) / (100 - target))
return { requiredToAttend: required, canBunk: 0 }
5. Above Target - Calculate Bunkable Classes
if currentPercentage > targetPercentage:
bunkableExact = (100 * present - target * total) / target
bunkable = floor(bunkableExact)
// Edge case: Almost at target (0 < exact < 0.9 and floor = 0)
if bunkableExact in (0, 0.9) and bunkable == 0:
isExact = true
return { canBunk: bunkable, requiredToAttend: 0 }The course card combines official attendance data with manual tracking:
-
Official Data: Fetched from EzyGo API
realPresent: Official present countrealTotal: Official total classesrealAbsent: Official absent count
-
Manual Tracking Modifiers:
extraPresent/extraAbsent: Additional classes marked by user (adds to total)correctionPresent: Wrongly marked absences corrected to present (status swap only)
-
Final Calculation:
finalPresent = realPresent + correctionPresent + extraPresent finalTotal = realTotal + extras (extraPresent + extraAbsent) displayPercentage = (finalPresent / finalTotal) * 100
-
Dual Metrics Display:
safeMetrics: Based on official data only (fail-safe)extraMetrics: Includes manual tracking (what user sees)
Scenario 1: Need More Classes
Present: 45, Total: 60, Target: 75%
Current: 75.0% → At target
Result: isExact = true
Scenario 2: Can Bunk Classes
Present: 50, Total: 60, Target: 75%
Current: 83.33% → Above target
bunkableExact = (100*50 - 75*60) / 75 = 6.67
Result: canBunk = 6 classes
Scenario 3: Need to Attend
Present: 40, Total: 60, Target: 75%
Current: 66.67% → Below target
required = ceil((75*60 - 100*40) / (100-75)) = 6
Result: requiredToAttend = 6 classes
Formula derivation: To reach target% with x more classes attended:
(present + x) / (total + x) = target / 100
100(present + x) = target(total + x)
100*present + 100x = target*total + target*x
100x - target*x = target*total - 100*present
x(100 - target) = target*total - 100*present
x = (target*total - 100*present) / (100 - target)
- Node.js - v20.19.0+ or v22.12.0+ (specified in
package.jsonengines) - npm or yarn - Package manager
- Docker Desktop - Only for local Supabase development (optional)
- Supabase CLI - Install via
npm install supabase --save-dev - Git - Version control
-
Clone the Repository
git clone https://github.com/devakesu/GhostClass.git
-
Navigate to Project Directory
cd GhostClass -
Install Dependencies
npm install # or yarn install -
Go to Supabase.com and create a new project.
Login & Link:
npx supabase login npx supabase link --project-ref <your-new-project-id>
Create Database Tables: push the schema from this repo to your new remote database:
npx supabase db push
(This creates all tables, policies, and triggers defined in
supabase/migrations) -
Copy the example environment file
cp .env.example .envOpen
.envand populate the keys. -
Start Development Server
npm run dev # or yarn dev
The application will be available at:
- App:
http://localhost:3000 - API Docs:
http://localhost:3000/api-docs
🎉
The default minimum attendance target has been updated from 50% to 75% to align with common institutional requirements. This affects:
- New user onboarding: Default target percentage set to 75%
- Validation: Minimum target enforced at 75% (configurable via
NEXT_PUBLIC_ATTENDANCE_TARGET_MIN) - Existing users: Any target below the minimum (75%) is automatically adjusted upward to meet the threshold
To customize the minimum target, set the environment variable:
NEXT_PUBLIC_ATTENDANCE_TARGET_MIN=75 # Default: 75%Note: All existing users with targets below 75% will have their targets automatically adjusted to 75% on next login or settings sync. This ensures compliance with institutional attendance policies while preserving targets that already meet or exceed the minimum.
GhostClass is optimized for maximum performance:
Progressive Web App (PWA)
- Service worker with Serwist for offline functionality and caching
- Manifest file for installable web app experience
- Intelligent caching strategies:
- Static assets: StaleWhileRevalidate for CSS/JS/workers
- Images: CacheFirst with 30-day expiration (trusted sources only)
- API requests: NetworkFirst (no explicit timeout; serves cache if network request fails)
- Note: Only /api/public/* and /api/static/* API endpoints are cached; all other API endpoints, including /api/user-settings and /api/attendance, always use the network to ensure fresh user data
Testing PWA Features Locally By default, service workers are disabled in development to avoid caching issues. To test PWA functionality (offline mode, caching, install prompts) during development:
# Unix/Linux/macOS
ENABLE_SW_IN_DEV="true" npm run dev
# Windows Command Prompt
set ENABLE_SW_IN_DEV=true && npm run dev
# Windows PowerShell
$env:ENABLE_SW_IN_DEV="true"; npm run devThis enables the service worker in development mode without requiring a production build.
Code Splitting & Loading Strategy
- Next.js App Router automatic route-based code splitting for pages and layouts
- Lazy loaded Recharts components (XAxis, YAxis, Tooltip, ResponsiveContainer)
- Lazy loaded Framer Motion with
domAnimationfeatures only
Caching Strategy
- React Query with smart cache timing:
- Profile data: 5min stale time, 30min garbage collection
- General queries: 3min stale time, 10min garbage collection
- Refetch on window focus disabled
- Auto-refetch interval: 15 minutes
- Static assets: 1-year cache headers for fonts and
_next/static - Next.js Image optimization with AVIF/WebP formats
Bundle Optimization
- Tree-shaking for
lucide-react,date-fns,framer-motion - Console logging preserved in production (keeps log/error/warn)
- Font optimization with
display: swap(prevents FOIT) - Priority loading for critical images (logo, avatar)
- Blur placeholders for instant image feedback
Development Experience
- Turbopack enabled by default (Next.js 15+)
- Origin validation skipped in dev mode
- Fast Refresh with React 19
- No NProgress blur on login page
GhostClass uses Vitest for unit/component tests and Playwright for E2E tests.
src/
├── components/__tests__/
│ └── error-boundary.test.tsx # Error boundary component tests
├── hooks/
│ ├── __tests__/useUser.test.tsx # User hook tests
│ └── courses/__tests__/courses.test.tsx # Course hook tests
└── lib/
├── __tests__/
│ ├── utils.test.ts # Utility function tests
│ └── crypto.test.ts # Encryption/decryption tests
└── logic/__tests__/
└── bunk.test.ts # Attendance calculation tests
e2e/
├── homepage.spec.ts # Homepage E2E tests
└── smoke.spec.ts # Smoke tests for critical paths
# Unit & Component Tests (Vitest)
npm test # Run all tests once
npm run test:watch # Watch mode - reruns on file changes
npm run test:ui # Interactive UI for test debugging
npm run test:coverage # Generate coverage report
# E2E Tests (Playwright)
npm run test:e2e # Headless E2E tests
npm run test:e2e:ui # Interactive E2E with Playwright UI
# Run All Tests
npm run test:all # Unit + E2E tests
# Validate API Documentation
npm run docs:validate # Lint OpenAPI specCurrent test suite includes:
- ✅ Attendance Algorithm (
bunk.test.ts) - 100% coverage of calculation logic - ✅ Encryption/Decryption (
crypto.test.ts) - AES-256-GCM encryption tests - ✅ Utility Functions (
utils.test.ts) - Helper function validation - ✅ Error Boundaries (
error-boundary.test.tsx) - Error handling UI - ✅ Custom Hooks - User and course data fetching
- ✅ E2E Smoke Tests - Critical user flows
Coverage Goals:
- Current enforced threshold: 10% (configured in
vitest.config.ts) - All new features require accompanying tests
- Critical paths (auth, attendance calculation, data sync) have priority coverage
View detailed coverage report:
npm run test:coverage
# Open coverage/index.html in browserTests follow the Arrange-Act-Assert pattern:
import { describe, it, expect } from 'vitest';
import { calculateAttendance } from '@/lib/logic/bunk';
describe('calculateAttendance', () => {
it('should calculate required classes when below target', () => {
// Arrange
const present = 40;
const total = 60;
const target = 75;
// Act
const result = calculateAttendance(present, total, target);
// Assert
expect(result.requiredToAttend).toBe(6);
expect(result.canBunk).toBe(0);
});
});For component tests using React Testing Library:
import { render, screen, fireEvent } from '@testing-library/react';
it('should display attendance percentage', () => {
render(<CourseCard course={mockCourse} />);
expect(screen.getByText('75.0%')).toBeInTheDocument();
});GhostClass implements multiple layers of security:
- AES-256-GCM Encryption - All sensitive tokens and authentication credentials encrypted at rest
- Multi-Device Session Security - Canonical password pattern enables concurrent logins without session invalidation
- CSRF Protection - Custom token-based CSRF protection on critical endpoints
- Content Security Policy (CSP) - CSP Level 3 with nonce-based script execution and hash whitelisting; integrates with Cloudflare Zaraz
- Rate Limiting - Upstash Redis-based rate limiting to prevent abuse
- Row Level Security - Supabase RLS policies ensure users only access their data
- Secure Headers - HSTS, X-Frame-Options, X-Content-Type-Options, and Referrer-Policy
- Input Validation - Zod schemas validate all user input
- HttpOnly Cookies - Sensitive data stored in secure, httpOnly cookies
- Origin Validation - Strict origin checking in production (disabled in dev)
- Terms Acceptance Flow - Enforced legal agreement with version tracking
- Cloudflare Turnstile - Bot protection on public endpoints
For detailed security implementation, see SECURITY.md.
To report security vulnerabilities, please email: admin@ghostclass.devakesu.com
GhostClass uses a two-tier secret management strategy:
Tier 1: Build-time (Public)
NEXT_PUBLIC_*variables - Safe for client-side exposureSENTRY_AUTH_TOKEN- Secure BuildKit mount, not in image layers
Tier 2: Runtime (Private)
ENCRYPTION_KEY- AES-256-GCM encryption keyCRON_SECRET- Cron job authenticationSUPABASE_SERVICE_ROLE_KEY- Admin database accessUPSTASH_REDIS_REST_*- Rate limiting credentialsTURNSTILE_SECRET_KEY- Cloudflare Turnstile validation- Email provider credentials
See .example.env for complete list with descriptions.
Important: Never commit .env file to version control!
GhostClass is deployed using Docker with reproducible builds:
# Build with BuildKit
DOCKER_BUILDKIT=1 docker build -t ghostclass .
# Run container
docker run -p 3000:3000 --env-file .env ghostclass- GitHub Actions - Automated builds on push
- Reproducible Builds -
SOURCE_DATE_EPOCHfor deterministic builds - Multi-stage Build - Optimized image size (~500MB)
- Coolify Deployment - Self-hosted platform for production
- ✅ Set all required environment variables
- ✅ Configure Supabase RLS policies
- ✅ Set up Sentry project for error tracking
- ✅ Configure Cloudflare Turnstile
- ✅ Set up Redis instance for rate limiting
- ✅ Configure email service (Brevo or SendPulse)
- ✅ Enable HTTPS with valid SSL certificate
- ✅ Set up cron jobs for attendance sync
- ✅ Configure legal terms version and effective date
- ✅ Set encryption key for secure token storage
We welcome contributions! Here's how you can help:
- Fork the repository
- Create a feature branch
- Make your changes
- Submit a pull request
Credits: Bunkr
For any questions, feel free to reach out to me via email at fusion@devakesu.com
This project is licensed under the GNU General Public License v3.0 - see the LICENSE file for details.
Thank you for your interest in GhostClass! Bunk classes & enjoy, but don't forget to study!! 😝🤝