CCS Project Management System
Archived
Society · Web

CCS Project Management System

Back end — GitHub integration, webhook, socket server & data layer · Creative Computing Society · 2023

Project-management platform for the Creative Computing Society: GitHub-linked registration, a member directory, a project list you can apply to contribute to, a per-user dashboard of contributions and assigned tasks, a leaderboard, Chart.js panels of commit activity per project, and chat over Ratchet WebSockets. The per-project channels are a client-side filter rather than server isolation: the socket server broadcasts every frame to every client and the browser decides where it lands, so messages for a project you are not in still reach your machine and simply are not rendered.

Built with
PHPPHP
MySQLMySQL
ComposerComposer
RatchetRatchet
Chart.jsChart.js
JavaScriptJavaScript
CSSCSS
Project Details
RESOURCES

STATUS
Archived
ROLE

Back end — GitHub integration, webhook, socket server & data layer

ORGANISATION

Creative Computing Society

YEAR

2023

TYPE

Society · Web

TAGS
Project Management
WebSockets
GitHub API

The society ran a dozen projects across a dozen GitHub repos, and nobody could answer the basic question of who was actually shipping. Attendance sheets said one thing, commit logs said another. This is the system that made the commit log the answer, built in PHP by team Binary Brain for the Creative Computing Society.

Making the commit log the source of truth

You can build this by polling. Every few minutes, walk each repo's commits endpoint, diff against what you have, insert the new ones. It works and it is wrong for two reasons: the GitHub API rate limit is shared across every project you add, so the system gets slower precisely as the society gets busier, and the data is always minutes stale in a room where people are watching a leaderboard.

So the flow inverts. GitHub is told to push. When a project is created the system provisions the repository, adds the leader as a collaborator, and installs a push webhook pointing back at itself - three API calls in a chain, each gated on the previous one returning an id. From then on commits arrive as they happen and the ingest endpoint does one insert. Nothing polls.

The second half of that decision is that membership lives in GitHub too. Adding someone to a project on the site is a PUT to the repo's collaborators endpoint; removing them is a DELETE. There is no separate reconciliation job because there is nothing to reconcile - the site cannot grant access it did not also grant on GitHub.

Feature set

  • GitHub-linked registration with server-side validation. verification_credentials.php runs its own regex checks on name, email, phone and password strength, then returns a per-field JSON error object that the form reads back out of sessionStorage and repaints.
  • Project creation that provisions the repo. Submitting the new-project form POSTs to api.github.com/user/repos, creates the repository under the ccs-tiet-task org, then immediately PUTs the team leader onto the collaborators endpoint with permission: push, and finally POSTs a push-event webhook onto the new repo pointing at the site's ingest URL.
  • Contributor management that is real, not cosmetic. Adding or removing a member from a project hits the same collaborators endpoint with PUT or DELETE, so the site's member list and the repo's access list cannot drift apart.
  • A commit-ingesting webhook. github_webhook.php takes the push payload, skips the commit if its SHA is already recorded, resolves the author by GitHub login and the project by repo name, and writes a user_contributions row.
  • A GitHub-style activity heat map built from /stats/commit_activity, bucketed into five colour bands, with a per-day tooltip. It is fetched straight from the browser, so the request spends the visitor's rate limit rather than the org token's.
  • Chart.js panels per project for contributors, commit activity, additions against deletions, and language breakdown.
  • Task assignment for project leaders: assign, reassign, mark done, delete, with deadlines and separate pending/completed views.
  • A leaderboard that scores every member as completed tasks plus commit count. The personal comparison view relabels the requesting user's row as "You" before ranking.
  • General and per-project chat over WebSockets.
  • A deploy hook. github_pull.php verifies X-Hub-Signature with an HMAC of the raw body before running git pull on the server.

The chat server

Chat is a Ratchet (cboden/ratchet) IoServer listening on 8080, holding connections in an SplObjectStorage set. Each inbound frame is written to the chats table first and then broadcast to every attached client, so history and live delivery come from the same write path and a page reload replays the channel from MySQL rather than from memory.

The routing is the part that did not get finished. The server broadcasts every frame to every socket regardless of which project it belongs to; the browser decides where it lands by appending the message to a DOM container named after its project_id. Messages for a project you are not in still arrive at your machine and are simply not rendered. The chats table also declares id as a plain integer primary key with no auto-increment and the insert never supplies one, and the client sends its own created_at string that the insert never stores - so the history query orders by a column that is always null.

Schema on first run

There is no migration tool. database_connect.php creates the database and all seven tables (users, projects, project_users, keys, user_contributions, user_tasks, chats) with CREATE TABLE IF NOT EXISTS on every connection. Crude, but it meant a teammate could clone the repo, point XAMPP at it, and have a working instance with no setup step.

The shape of those tables says something about the priorities. project_users denormalises the member's GitHub handle, avatar and name onto the membership row rather than joining back to users, because every project page renders that list and the join was not worth it. user_contributions keeps the raw commit SHA and message alongside the resolved user and project ids, so a re-run of the webhook is idempotent on the SHA. And keys is a single-column table holding the org access token, read by a helper that every GitHub-calling script requires.

What I would not ship again

My work was the back end: the GitHub integration, the webhook, the socket server and the data layer. Reading it back, the data layer is where it shows its age. Every query is string-interpolated rather than parameterised, which is why the webhook has to strip quote characters out of commit messages before writing them - a sanitiser standing in for a prepared statement. Passwords go into a column called psw_hash unhashed and login compares them literally. Authorisation is client-side: the login page writes the user object into localStorage and every data endpoint takes an id from the query string with no session check behind it.

Two are worse than the rest. get_keys.php returns the org's GitHub token to anyone who requests it, and delete_all.php - a maintenance script that enumerates and deletes every repository in the organisation - gates itself on a password compared against that same token. The deploy hook, by contrast, does the right thing: HMAC over the raw body, compared before anything runs. The same codebase contains both the careful version of this idea and the careless one, which is a fair description of what a first backend looks like.

The leaderboard has a quieter bug worth keeping as a lesson: it sorts with strcmp on an integer score, so nine outranks ten. It was the leaderboard everyone looked at.

Project Details
RESOURCES

STATUS
Archived
ROLE

Back end — GitHub integration, webhook, socket server & data layer

ORGANISATION

Creative Computing Society

YEAR

2023

TYPE

Society · Web

TAGS
Project Management
WebSockets
GitHub API