
Introduction
This is the starting point of the documentation for the implementation of the Kuala Lumpur Hash House Harriettes (KLH3) website. This website has been completely rebuilt from December 2024 to June 2025 and replaces the legacy solution based on the Yii2 Framework (the respective code is available here)
How to work with this documentation? Best start by reading through this introduction page, which provides quick access to the very basics - Objectives, Project Structure, Business Context and Terminology used and, finally a few Personal Remarks.
If you look for a high level view on how everything works together, go to Architecture. For implementation details jump to Frontend and Backend sections - both offer an Introduction explaining the key building blocks and concepts of the respective functionality. If you are interested to install and configure all of this freshly on an own server, have a look at the Server Setup section, where you will find detailed guidance for all components.
Objective
The main goals of the rebuilding project were to:
- Move the website to a more modern look and feel (the legacy was deployed in 2016 based on Bootstrap 1)
- Move away from a well-renowned framework with a small user community to a widely used technology platform
- Modernize the technologies used to a state-of-the-art versions
- Make the website easier to maintain and more easy to learn for potential future maintainers
- Rebuild existing functionality for smooth user experience
- Add additional functionality where required/requested
Whereas recognizable progress was made for all objectives, the complexity (main driver for Objective 4.) was certainly increased 😱, but everything should be easy to maintain 👍.
Project Structure
The main project resides in a parent GitHub Repository which contains three Git submodules:
- Parent Repo klhhh: Serves as parent repository for the entire website and holds the documentation for all building blocks
- Submodule server-setup: Hosts all code related to the ansible based installation and maintenance processes for the website. No release management applied.
- Submodule backend: Includes all code for the Strapi 5 implementation. Release Management independent of frontend.
- Submodule frontend: Provides the complete Frontend code in Vue.js/Vuetify. Release Management independent of backend.
There are two additional repositories which have meanwhile been archived:
- migration: This hosts Python code used for data migration from the old to the new website, i.e., a number of Extract-Transform-Load (ETL) routines for the main data tables. The code is not very well-structured and documented. More meant for throw away 😅
- klharriettes: A code copy of the previous Yii2 Framework based website.
Business Context
In order to understand the data objects and the implemented functionality of the website, it is necessary to introduce some context here. Relevant terms used throughout the documentation are highlighted in bold.
Hashing is a sport invented in the 1930s in Malaysia, where a hare sets a run on a weekly basis by marking a trail (usually by putting paper or flour on the ground) in a more or less rough and natural environment. This trail is broken a number of times at marked places (checks) and the pack, runners following the paper trail after it has been set, has to find the continuation.
On the scheduled day (in our case every Wednesday) of the run, members of a hash club arrive at the runsite (a larger parking space where the run/paper trail starts) and are registered (signed-out) by a member of the club's committee. After the members return from the run they are signed-in, so that it can be easily monitored if members are still out on the trail.
Besides members, who pay subscription fee, also guests can attend a hash run. Guests running the first time with KLH3 need to be added to the database once by noting down their name and contact number. They are also registered for the run but have to pay a guest fee either in cash or by DuitNow. After the run guests are also signed-in.
The - previously paper-based - document to support this process of registering members and guests and signing them in is the often colloquially referred to as sign-in sheet. Major parts of the Website's functionality (the rest is mostly static information) were newly built to create a digital twin of the paper-based sign-in sheet in the digital world.
After the run, a so-called circle (gathering) is held where the run is discussed and attendees can be charged for mis-behavior. After the run attendees can also buy drinks or members can pay their subscription fee either in cash or by DuitNow.
INFO
DuitNow is an immediate bank transfer method widely used in Malaysia. A QR code needs to be scanned to initiate the payment
For each member the attendance at each run is recorded in a so-called runrecord, so that runs per member can be counted. With the introduction of the new website the same procedure has been implemented for guests.
Members are differentiated in Beer Members (who are entitled to get free beer after the run) and Non-Beer Members (who only get soft drinks)
After every run the expenses have to be recorded (e.g., for the supplier of the drinks) and a financial report has to be created by the responsible person (OnCash, see below) stating the income and the expenses and resulting cash balance.
The process parts relevant for the Sign-In App (see below) are depicted as a sequence diagram in Figure 1.
The clubs operational business is managed by a number of office bearers (only IT relevant parts of the functionS described here):
- OnSec: Manage members, i.e., create new members, resign leaving members, maintain the data of the members. Can maintain guest data and records
- OnCash: Manage the financial matters of the club. Collect subscription fees, create a chart of accounts for income and expenses of the club
- Trail Mistress: accountable for aligning them so-called hareline a list of future runs with a hare and a runsite assigned
- Committee on Duty (CoD) committee member accountable for attendees registration, adding guests and onsite sales process of a specific run.
Terminology Confusion
The wording around the (digital) registration/sign-in process is quite confusing and on top of it depending on whom you ask.
Often, when hashers talk about the sign-in sheet the registration of an attendee for a run (marking his attendance and the fact that the member is at the runsite) is referred to as "signing somebody in" (which the name sign-in sheet implies), that is - at least in the convention of KLH3 wrong.
As outlined above the sign-in is the process of marking, that an attendee finished the run and arrived safely at the runsite again. The process of marking that an attendee arrived at the runsite and will be joining the run is technically speaking a sign-out (a term hardly used by the way). To add to the confusion other Hash Chapters use the reverse definition/terminology.
Terminology definition
To avoid misunderstandings at least throughout this documentation it is aspired to use the following terminology:
- the web page used for all runsite processes is called the Sign-In App (SIA)
- members and guests registered for the run are collectively called attendees
- attendees arriving at the runsite are registered
- new/unknown guests are added to the database/KLH3 community
- next run is used for the run on the next Wednesday in the future
- attendees returning after the run to the runsite are signed-in
But....🔥
As this terminology was defined only at the time of writing this documentation, it is not used in technical/code file names, table names, function names, variable names etc.
For these most often the following "translations" can be used:
- createGuest -> addGuest
- guestSignIn -> guestRegistration
- memberSignIn -> memberRegistration
- signInSheet -> Sign-In App
---
title: Process overview
config:
theme: base
themeVariables:
noteBkgColor: '#AD1457'
primaryTextColor: '#AD1457'
secondaryTextColor: '#FFFFFF'
noteTextColor: '#FFFFFF'
primaryColor: '#FCE4EC'
lineColor: '#AD1457'
background: '#FFFFFF'
---
sequenceDiagram
actor M as Member
actor G as Guest
actor COD as Committee on Duty
actor OC as OnCash
alt Member
M->>COD: Arrive at runsite
activate COD
COD-->>M: Register (sign out) for run
deactivate COD
else Guest
G->>COD: Arrive and provide name/contact
activate COD
opt First Time/archived Guest
COD-->>G: Add Guest to database
end
COD-->>G: Register (sign out) for run
G->>COD: Pay Guest Fee
COD-->>G: Record payment and type
deactivate COD
end
Note over M,G: Members and Guests run the trail
alt Member
M->>COD: Return from run
activate COD
COD-->M: Sign in
deactivate COD
else Guest
G->>COD: Return from run
activate COD
COD-->>M: Sign in
deactivate COD
end
opt If applicable
opt Members only
M->>COD: Pay subscription
activate COD
COD-->>G: record payment and type
deactivate COD
end
alt Member
M->>COD: Buy drinks (Cash/DuitNow)
activate COD
else Guest
G->>COD: Buy drinks (Cash/DuitNow)
end
COD->>G: Record payment and type
deactivate COD
end
OC->>OC: Records expenses
OC->>OC: Review Financial ReportFigure 1: Sequence diagram of Runsite Processes as implemented in the Sign-In App
A personal remark
This documentation was written for two reasons: to offload the many things which were major learning experiences - often enlightening moments after long hours of trying to make things work - during the development of the website. This is mostly for myself of course, knowing that when looking at this code even in the near future many of these enlightening moments will have vanished from my memories.
On the other hand it is supposed to give a helping hand to someone who will - ever - take over this website, mostly inspired by the fact that the initial website had little to none documentation, and digesting what and how it was implemented was a rather challenging task.
In consequence, a focus is on concepts and structures more than on the code itself (which hopefully is self-explanatory enough) and the documentation is often quite verbose. There is no - as ChatGPT, an inevitable helper throughout the journey would phrase it - TL;DR (Too Long; Didn't Read) way through it. I tried however to structure and write things in a way that make the parts stand for themselves, and hope by that I have opened a way into the complexity of this, which is walkable for an educated layman (without any programming skills and some basic knowledge about modern web platforms the path will be rather cumbersome I am afraid).
At this time I am the only human - greetings again to ChatGPT 😉 - architect, developer, maintainer, reviewer and documenter. Moreover, I was never educated as a developer and learned everything by doing. There are certainly quirks in this code, and typos in the documentation.
Anyway, I hope you enjoy this documentation as much as I enjoyed creating it. May it help you to walk through the jungle of my implementation. I know: there are many things which could have been done differently (sometimes even better 😇) - bare with me!
- Thomas Roch, June 2025 -
