More Projects
59 in total
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.
Project Details
Back end — GitHub integration, webhook, socket server & data layer
Creative Computing Society
2023
Society · Web
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.phpruns 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 ofsessionStorageand repaints. - Project creation that provisions the repo. Submitting the new-project form POSTs to
api.github.com/user/repos, creates the repository under theccs-tiet-taskorg, then immediately PUTs the team leader onto the collaborators endpoint withpermission: push, and finally POSTs apush-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.phptakes 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 auser_contributionsrow. - 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.phpverifiesX-Hub-Signaturewith an HMAC of the raw body before runninggit pullon 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
Back end — GitHub integration, webhook, socket server & data layer
Creative Computing Society
2023
Society · Web