Author: Jashanpreet Singh Gill
Institution: Humber College
Course: Capstone
Date: November 13, 2025
- Introduction
- Problem Statement
- Project Objectives
- System Architecture
- Technology Stack
- System Features
- Database Design
- Implementation Details
- Security Features
- User Interface Design
- Testing
- Deployment Guide
- Future Improvements
- What I Learned
- References
So I've been working with the BrickMMO project for a while now, and there's a real problem - we have multiple repos and contributors, but no good way to track who's working on what and for how long. People were using spreadsheets, sticky notes, or just trying to remember what they worked on. It's messy and we lose track of things.
I thought there has to be a better way, so for my capstone project, I decided to build a timesheet system that integrates with GitHub. Since we're already using GitHub for everything, why not use it for authentication too?
This is a web application that lets contributors log their hours, tracks what repos they worked on, and gives admins a way to see what everyone's been doing. Users can log in with their GitHub account (no separate passwords needed), log hours for different repos, and see their own history. Admins can import repos from GitHub and manage the system.
This took me about 2 months to build, working on it part-time around classes and other commitments. I had to learn a lot about OAuth, GitHub's API, and web security along the way.
-
Everyone was tracking time differently - Some people used Google Sheets, others used notes apps, some didn't track at all. Data was getting lost.
-
No centralized view - If I wanted to see what everyone worked on across all our repos, I'd have to ask around or dig through different spreadsheets.
-
Manual work - Making reports meant manually collecting data from everyone, which took forever and had errors.
-
Hard to see progress - As a project lead, there was no easy way to see who was contributing what and when.
-
Too many accounts - We didn't want to make people create yet another account when they already have GitHub.
I built a single web app where:
- Users log in with GitHub (no new accounts!)
- Everyone logs hours in the same place
- The system pulls repo info from GitHub automatically
- Admins can see reports and manage repos
- It's all in one place and everyone uses the same system
-
GitHub OAuth Login
- Let users sign in with their GitHub account
- Automatically create/update their profile
- Keep sessions secure
-
Time Logging
- Easy form to log hours worked
- Select which repo you worked on
- Pick a date and enter hours
- Optional description of what you did
-
User Dashboard
- See your total hours logged
- View recent entries
- Basic stats (how many repos you've worked on, etc.)
-
Admin Panel
- Import repos from GitHub org
- Enable/disable which repos show up
- See system-wide stats
-
Security
- Protect against SQL injection (used prepared statements everywhere)
- CSRF protection on forms
- Input validation and sanitization
- Make it responsive (works on mobile)
- Good error handling
- Easy to add features later
Here's how it all works together:
User's Browser (HTML/CSS/JavaScript)
|
| HTTPS requests
|
Web Server (Apache with PHP)
|
----|----|----
| | |
GitHub MySQL Session
OAuth DB Storage
API
-
Frontend (what users see)
- HTML pages
- CSS for styling
- JavaScript for some interactivity
- Flatpickr for the date picker (found this library, it's great)
-
Backend (PHP)
- Handles authentication
- Processes form submissions
- Talks to the database
- Makes API calls to GitHub
-
Database (MySQL)
- Stores user info
- Stores repo info
- Stores time entries
-
GitHub API
- For OAuth authentication
- For fetching repo data
I tried to keep things separated - config in one place, database stuff separate from business logic. Not perfect MVC but it works.
Backend:
- PHP 8.0+ (it's what I know best, and XAMPP made local dev easy)
- MySQL 8.0 (database)
- PDO (for database stuff - safer than mysqli)
- cURL (for API calls to GitHub)
Frontend:
- HTML5, CSS3, JavaScript
- Flatpickr (for the date picker - really nice library)
- Google Fonts (Inter font - looks clean)
Services:
- GitHub OAuth 2.0 (for login)
- GitHub REST API (for getting repo info)
Tools:
- XAMPP (for local development)
- phpMyAdmin (for database stuff)
I kept it simple - no fancy frameworks or build tools. Just vanilla PHP, HTML, CSS, and some JavaScript.
When you click "Login", it:
- Generates a random state token (for security)
- Sends you to GitHub to authorize
- GitHub sends you back with a code
- The app exchanges the code for an access token
- Fetches your GitHub profile info
- Creates/updates your account in the database
- Logs you in
The tricky part was understanding OAuth flow - took me a while to get the state parameter right and handle all the edge cases. But it works now!
You can:
- Pick a repo from a dropdown (only shows active repos)
- Select a date (calendar popup)
- Enter hours (between 0.25 and 16 hours per day)
- Add a description (optional)
The form validates everything on the server side. I had some client-side validation too but you can't trust that - always validate server-side.
Shows:
- Your total hours logged
- How many entries you've made
- How many different repos you've worked on
- Your most recent 10 entries
Pretty basic but useful. I wanted to add charts but ran out of time.
You can see all your entries, filter by repo, search, paginate through results. Basic CRUD stuff.
Only accessible to admins (me and Adam for now). Admins can:
- Import all repos from the GitHub org (one click)
- Toggle which repos are active/visible
- See system stats
The import feature was fun to build - it fetches all repos from GitHub, checks if they exist in the DB, updates or inserts as needed.
On the homepage, anyone can browse active repos, search them, see contributor stats. No login required for this.
I have 3 main tables:
Stores GitHub user info - their GitHub ID, username, name, email, avatar URL, and whether they're an admin.
Stores repository info - name, description, language, whether it's active, etc. This gets populated from the GitHub import.
The actual time entries - links to a user and an application, has a date, duration, and optional description.
I used foreign keys with cascade delete - if a user or repo gets deleted, their hours get deleted too. Seems safer that way.
I added some indexes on commonly queried fields (user_id + date, application_id + date) to speed things up.
The complete schema is in database/schema.sql. Here's what the tables look like:
-- Users table
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
github_id INT UNIQUE NOT NULL,
login VARCHAR(255) NOT NULL,
name VARCHAR(255),
email VARCHAR(255),
avatar_url TEXT,
html_url TEXT,
is_admin BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Applications (repositories) table
CREATE TABLE applications (
id INT AUTO_INCREMENT PRIMARY KEY,
github_id INT UNIQUE NOT NULL,
name VARCHAR(255) NOT NULL,
full_name VARCHAR(255) NOT NULL,
description TEXT,
html_url TEXT,
language VARCHAR(100),
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);
-- Hours (time entries) table
CREATE TABLE hours (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
application_id INT NOT NULL,
work_date DATE NOT NULL,
duration DECIMAL(4,2) NOT NULL CHECK (duration >= 0.25 AND duration <= 16.00),
description TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (application_id) REFERENCES applications(id) ON DELETE CASCADE,
INDEX idx_user_date (user_id, work_date),
INDEX idx_application_date (application_id, work_date)
);The OAuth flow was the hardest part. I had to:
- Generate a secure random state token
- Store it in the session
- Redirect to GitHub
- When GitHub redirects back, check the state matches
- Exchange the code for a token
- Use the token to get user info
- Save everything to the database
Took me a few tries to get it right. The state parameter is crucial for preventing CSRF attacks - I learned that the hard way when testing.
Here's the basic flow:
// Generate state token
$state = bin2hex(random_bytes(16));
$_SESSION['oauth_state'] = $state;
// Redirect to GitHub
$github_url = 'https://github.com/login/oauth/authorize?' . http_build_query([
'client_id' => GITHUB_CLIENT_ID,
'redirect_uri' => GITHUB_REDIRECT_URI,
'scope' => 'user:email',
'state' => $state
]);
header("Location: $github_url");Then when GitHub sends the user back, I check the state matches and exchange the code for a token.
I validate:
- CSRF token (must match session)
- Repository ID (must exist and be active)
- Date (must be valid, can't be in the future)
- Duration (between 0.25 and 16 hours)
- Description is optional but gets sanitized
All validation happens server-side. Never trust the client.
This was cool to build. It:
- Makes a request to GitHub API:
/orgs/BrickMMO/repos - Gets back a JSON array of all repos
- Loops through each one
- Checks if it exists in DB (by GitHub ID)
- Updates if exists, inserts if new
- Sets language, visibility, etc.
I ran into rate limiting issues at first - GitHub only lets you make 60 requests per hour without a token. I added a GitHub Personal Access Token to the .env file to increase that.
I used PDO prepared statements everywhere. No string concatenation in SQL - learned that lesson in security class. All user input goes through prepared statements.
Example:
$stmt = $db->prepare("SELECT * FROM users WHERE id = ?");
$stmt->execute([$user_id]);
$user = $stmt->fetch();I used JOINs to get user names with entries, repo names with entries, etc. Had to look up the syntax a few times but got the hang of it.
Every form has a CSRF token. I generate a random token, store it in the session, and check it matches on submit. Prevents cross-site request forgery attacks.
function generateCSRFToken() {
if (!isset($_SESSION[CSRF_TOKEN_NAME])) {
$_SESSION[CSRF_TOKEN_NAME] = bin2hex(random_bytes(32));
}
return $_SESSION[CSRF_TOKEN_NAME];
}Used PDO prepared statements everywhere. Never build SQL with string concatenation. All user input gets bound as parameters.
I sanitize all user input with htmlspecialchars() before displaying. This prevents XSS attacks.
function sanitizeInput($input) {
return htmlspecialchars(trim($input), ENT_QUOTES, 'UTF-8');
}Sessions timeout after 1 hour of inactivity. On login, I regenerate the session ID. On logout, I destroy the session completely.
Only specific users (checked by name/login) can access admin features. Not the most scalable solution but works for now. In production, I'd probably have a proper roles table.
I tried to make it clean and simple. Used the BrickMMO orange color (#DD5A3A) as the primary color since that's the brand color.
- Responsive: Works on mobile, tablet, desktop. Used CSS media queries.
- Clean forms: Simple inputs, clear labels, helpful error messages
- Navigation: Simple menu bar, shows where you are
- Feedback: Success/error messages when you submit forms
I'm not a designer, so I kept it simple. Used Inter font from Google Fonts - looks modern and clean.
The date picker (Flatpickr) is really nice - users can click a calendar icon and pick dates visually.
- Primary: #DD5A3A (BrickMMO Orange)
- Background: #FDF6F3 (Light Beige)
- Text: #2C3E50 (Dark Gray)
- Accent: #E8D5CF (Light Orange)
Nothing fancy, just clean and readable.
I tested manually - no automated tests . I tested:
- Tested authentication: Login, logout, session timeout
- Tested time logging: Valid entries, invalid entries, edge cases
- Tested admin features: Import repos, toggle repos
- Tested security: Tried SQL injection attempts (they failed!), tested CSRF tokens
- Tested on different devices: Desktop, tablet
- Had some bugs with session handling at first (fixed now)
- Date validation needed tweaking (rejecting valid dates initially)
- Pagination had an off-by-one error (classic mistake)
- Admin check wasn't working consistently (fixed by checking both name and login)
Most bugs were small logic errors. Nothing major, but lots of little fixes.
I didn't do formal performance testing, but:
- Pages load pretty quickly (under 2 seconds usually)
- Database queries are fast (under 100ms for most queries)
- GitHub API calls take a bit longer (300-500ms)
Could probably optimize more, but it works fine for now.
I used XAMPP:
- Install XAMPP
- Put project in
htdocsfolder - Create database in phpMyAdmin
- Run
database/schema.sqlto create tables - Copy
.env.exampleto.envand fill in your values - Start Apache and MySQL
- Go to
http://localhost/http5225/Testingphp/
I'm deploying this to InfinityFree. Steps:
- Upload all files via FTP
- Create database in InfinityFree control panel
- Update
.envfile with InfinityFree database credentials:- DB_HOST (usually something like
sqlXXX.infinityfree.com) - DB_NAME (usually
username_dbname) - DB_USER (usually
username_dbuser) - DB_PASS (the password InfinityFree gives you)
- DB_HOST (usually something like
- Set BASE_URL to your domain
- Set DEVELOPMENT=false
- Make sure
.envfile isn't publicly accessible
The .env file approach makes it easy to switch between local and production without changing code. Much better than hardcoding values.
You need to:
- Go to GitHub Settings → Developer settings → OAuth Apps
- Create a new OAuth App
- Set the callback URL to your production URL +
/auth/callback.php - Copy the Client ID and Secret to your
.envfile
For local dev, use http://localhost/http5225/Testingphp/auth/callback.php. For production, use your actual domain.
To avoid rate limiting on GitHub API calls:
- Go to GitHub Settings → Developer settings → Personal access tokens
- Generate a new token with
repoandread:orgscopes - Add it to your
.envfile asGITHUB_TOKEN=your_token_here
Without this, you'll hit rate limits pretty quickly when importing repos.
If I had more time, I'd add:
-
Better analytics: Charts showing hours over time, breakdown by repo, etc. Maybe use Chart.js or similar.
-
Export functionality: Export your data to CSV or PDF. Would be useful for reports.
-
Better admin features: User management, bulk operations, better stats.
-
Email notifications: Maybe reminders to log hours, or weekly summaries.
-
Mobile app: Native app would be nice, but that's a whole other project.
-
Unit tests: Learn PHPUnit and write proper tests. Would catch bugs earlier.
-
Better error handling: More graceful error messages, better error pages.
-
Caching: Cache GitHub API responses to avoid rate limits. Maybe Redis.
Some of this is in the "nice to have" category. The core functionality works for what we need right now.
This project taught me a lot:
-
OAuth is complex - But really powerful once you understand it. The state parameter is crucial.
-
Security matters - Prepared statements aren't optional. CSRF protection is important. Input sanitization everywhere.
-
API rate limits are real - Hit GitHub's rate limits a few times. Adding a token helped.
-
Database design matters - Spent time thinking about relationships, indexes, constraints. Worth it.
-
Environment variables are great - Using
.envmakes deployment so much easier. No more hardcoding secrets. -
Testing is important - Wish I'd written tests, but manual testing found a lot of bugs.
-
Documentation is hard - Writing this README took almost as long as some features.
-
Don't overthink it - Started with something simple, added features as needed. Better than trying to build everything at once.
Biggest challenge was understanding OAuth flow and GitHub's API. But once I got it, things clicked.
Biggest success was getting the whole thing working end-to-end. Seeing someone else use it and log hours was pretty cool.
I used these resources a lot:
-
PHP Manual - https://www.php.net/manual/ (especially the PDO section)
-
GitHub API Docs - https://docs.github.com/en/rest (for API endpoints)
-
GitHub OAuth Docs - https://docs.github.com/en/apps/oauth-apps (for OAuth flow)
-
Stack Overflow - For specific problems and syntax questions
-
OWASP Top 10 - For security best practices
-
Flatpickr Docs - https://flatpickr.js.org/ (for the date picker)
-
MySQL Documentation - For SQL syntax and constraints
Stack Overflow helped a lot with specific issues. PHP.net manual for PDO syntax. GitHub docs for API endpoints and OAuth flow.
This is part of the BrickMMO project. All rights reserved.
Developer: Jashanpreet Singh Gill
Institution: Humber College
GitHub: [jashanfzk]
Last Updated: November 2025
Status: Capstone Project Submission
Thanks for reading! If you have questions or suggestions, feel free to reach out.