BowlUp Tracker — Admin HelpAdmin Help Center
Documentatie voor beheerders van het BowlUp-platform. Uitsluitend toegankelijk voor admin-accounts. Documentation for BowlUp platform administrators. Only accessible to admin accounts.
Version 1.10 — May 2026 🔧 Admin onlyHet admin-portaal (admin.html) is uitsluitend toegankelijk voor beheerders van het BowlUp-platform. Trainers en leerlingen hebben geen toegang tot dit portaal.
- Navigeer naar
/admin.html. - Log in met je admin e-mailadres en wachtwoord.
- Bij het eerste gebruik: gebruik Setup om het eerste admin-account aan te maken.
Het portaal is opgedeeld in vijf tabbladen: 📊 Overzicht, 👥 Gebruikers, 📋 Audit Log, 💾 Storage en 🏷️ Promo Codes. Elke tab is hieronder afzonderlijk gedocumenteerd.
The admin portal (admin.html) is only accessible to BowlUp platform administrators. Trainers and students have no access to this portal.
- Navigate to
/admin.html. - Log in with your admin email address and password.
- First use: use Setup to create the first admin account.
The portal is divided into five tabs: 📊 Overview, 👥 Users, 📋 Audit Log, 💾 Storage and 🏷️ Promo Codes. Each tab is documented separately below.
De Overzicht-tab toont bij het openen van het admin-portaal een actuele samenvatting van het hele platform in drie panelen.
The Overview tab shows an up-to-date platform summary in three panels when you open the admin portal.
De Gebruikers-tab toont een tabel van alle accounts op het platform (trainers, leerlingen en admins). Gebruik de zoekbalk en filters bovenaan om snel het gewenste account te vinden.
Elke rij toont een status-badge die de huidige toestand van het account weergeeft:
- Actief — account functioneert normaal
- Geblokkeerd — account is geblokkeerd (met eventuele einddatum)
- Trial t/m [datum] — trainer heeft tijdelijk Pro-toegang
Klik op de tier-dropdown in de rij van een trainer en selecteer Starter, Pro of Elite. De wijziging wordt direct opgeslagen. De nieuwe tier is van kracht bij de volgende inlog van de trainer (het JWT wordt dan opnieuw aangemaakt).
- Klik op het 🔒 icoontje in de actiekolom van de trainer.
- Vul een reden in (verplicht).
- Stel optioneel een einddatum in. Laat leeg voor een permanente blokkering.
- Klik op Blokkeren.
Klik op het 🔓 icoontje. De blokkering wordt direct opgeheven zonder verdere bevestiging.
- Klik op het ⏱ icoontje in de actiekolom van de trainer.
- Selecteer een einddatum voor de trial via de datepicker.
- Klik op Opslaan.
Gedurende de trial krijgt de trainer automatisch Pro-functionaliteit, ongeacht het opgeslagen abonnement. De effectieve tier wordt berekend bij elke inlog. Laat de datum leeg en sla op om de trial te verwijderen.
Klik op het prullenbakicoontje om alle facturen van een trainer te verwijderen. Dit is een onomkeerbare actie — gebruik alleen op verzoek van de trainer of bij technische noodzaak.
The Users tab shows a table of all accounts on the platform (trainers, students and admins). Use the search bar and filters at the top to quickly find the desired account.
Each row shows a status badge indicating the current state of the account:
- Active — account is functioning normally
- Blocked — account is blocked (with optional end date)
- Trial until [date] — trainer has temporary Pro access
Click the tier dropdown in the trainer's row and select Starter, Pro or Elite. The change is saved immediately. The new tier takes effect on the trainer's next login (the JWT is then regenerated).
- Click the 🔒 icon in the action column of the trainer.
- Enter a reason (required).
- Optionally set an end date. Leave blank for a permanent block.
- Click Block.
Click the 🔓 icon. The block is lifted immediately without further confirmation.
- Click the ⏱ icon in the action column of the trainer.
- Select a trial end date using the date picker.
- Click Save.
During the trial the trainer automatically gets Pro features, regardless of the stored subscription. The effective tier is calculated on every login. Leave the date blank and save to remove the trial.
Click the trash icon to remove all invoices for a trainer. This is an irreversible action — use only at the trainer's request or when technically necessary.
De Audit Log-tab toont een chronologisch, pagineerd overzicht van alle login-pogingen en admin-acties op het platform. Elke pagina toont 50 regels.
Gebruik de filterbalken bovenaan de tabel om het log te verfijnen:
- Actie-type — dropdown met alle bekende actie-codes
- Rol — filter op trainer, student of admin
- E-mail — vrij tekstveld, zoekt op e-mailadres
- Datum van — toont alleen regels vanaf deze datum
| Actie | Beschrijving | Kleur |
|---|---|---|
login_ok | Succesvolle inlog van trainer, leerling of admin | Groen |
login_failed | Inlogpoging met verkeerd wachtwoord of onbekend e-mail | Rood |
login_blocked | Inlogpoging terwijl account geblokkeerd is | Rood |
password_reset | Wachtwoordherstel via reset-token | Blauw |
admin_tier_change | Admin heeft tier van een gebruiker gewijzigd | Oranje |
admin_block | Admin heeft een account geblokkeerd | Oranje |
admin_unblock | Admin heeft blokkering opgeheven | Oranje |
admin_trial_set | Admin heeft trial-einddatum ingesteld of verwijderd | Oranje |
admin_promo_create | Admin heeft een promo code aangemaakt | Oranje |
admin_promo_delete | Admin heeft een promo code verwijderd | Oranje |
- Datum/tijd — UTC tijdstempel
- E-mail — e-mailadres van de betrokken gebruiker
- Rol — trainer / student / admin
- Actie — actie-code (zie tabel hierboven)
- IP-adres — client-IP via X-Forwarded-For (Railway proxy)
- Details — JSON met extra context (bijv. reden bij blokkering, nieuwe tier)
app.set('trust proxy', 1). Achter de Railway reverse proxy wordt het echte client-IP opgehaald uit de X-Forwarded-For header.
The Audit Log tab shows a chronological, paginated overview of all login attempts and admin actions on the platform. Each page shows 50 rows.
Use the filter bars at the top of the table to narrow down the log:
- Action type — dropdown with all known action codes
- Role — filter by trainer, student or admin
- Email — free text field, searches by email address
- Date from — shows only rows from this date onward
| Action | Description | Colour |
|---|---|---|
login_ok | Successful login by trainer, student or admin | Green |
login_failed | Login attempt with wrong password or unknown email | Red |
login_blocked | Login attempt while account is blocked | Red |
password_reset | Password recovery via reset token | Blue |
admin_tier_change | Admin changed a user's tier | Orange |
admin_block | Admin blocked an account | Orange |
admin_unblock | Admin lifted a block | Orange |
admin_trial_set | Admin set or removed a trial end date | Orange |
admin_promo_create | Admin created a promo code | Orange |
admin_promo_delete | Admin deleted a promo code | Orange |
- Date/time — UTC timestamp
- Email — email address of the user involved
- Role — trainer / student / admin
- Action — action code (see table above)
- IP address — client IP via X-Forwarded-For (Railway proxy)
- Details — JSON with extra context (e.g. reason for block, new tier)
app.set('trust proxy', 1). Behind the Railway reverse proxy, the real client IP is extracted from the X-Forwarded-For header.
De Storage-tab toont per trainer hoeveel ruimte logo's en profielfoto's innemen in de database. De tabel is gesorteerd op totaalgrootte (aflopend), zodat de zwaarste gebruikers bovenaan staan.
Bovenaan de pagina staat een samenvattingsbalk met:
- Totale mediaopslag van het hele platform
- Totaal aantal trainers
- Totaal aantal leerlingen
- Trainer — naam en e-mailadres
- Logo (KB) — opslaggrootte van het trainer-logo
- Foto's (KB) — totale opslaggrootte van alle leerlingprofielfoto's
- Totaal (KB) — logo + foto's gecombineerd
- Gebruik-balk — visuele indicatie relatief aan de grootste gebruiker
- Leerlingen / Lessen / Scores — tellingen voor context
- Normaal — minder dan 1 MB totaal
- Oranje — totaal meer dan 1 MB
- Rood — totaal meer dan 5 MB
logo_data) en profielfoto's (profile_photo). Deze worden als base64-tekst in de database opgeslagen. De grootte wordt gemeten via LENGTH() op die kolommen.
The Storage tab shows per trainer how much space logos and profile photos take up in the database. The table is sorted by total size (descending) so the heaviest users appear at the top.
At the top of the page is a summary bar showing:
- Total media storage across the entire platform
- Total number of trainers
- Total number of students
- Trainer — name and email address
- Logo (KB) — storage size of the trainer logo
- Photos (KB) — total storage size of all student profile photos
- Total (KB) — logo + photos combined
- Usage bar — visual indicator relative to the largest user
- Students / Lessons / Scores — counts for context
- Normal — less than 1 MB total
- Orange — total more than 1 MB
- Red — total more than 5 MB
logo_data) and profile photos (profile_photo). These are stored as base64 text in the database. Size is measured via LENGTH() on those columns.
De Promo Codes-tab biedt volledig CRUD-beheer van promotie- en kortingscodes. Codes kunnen worden gekoppeld aan toekomstige checkout-flows.
| Veld | Beschrijving | Verplicht |
|---|---|---|
| Code | Unieke kortingscode. Wordt automatisch omgezet naar hoofdletters. | Ja |
| Omschrijving | Vrije tekst ter verduidelijking van het doel van de code. | Nee |
| Type | percentage — procentuele korting (bijv. 20%) fixed — vast bedrag in euro's (bijv. €5,00) | Ja |
| Waarde | Numeriek kortingsbedrag of percentage. | Ja |
| Geldig van | Optionele startdatum. Code is niet geldig vóór deze datum. | Nee |
| Geldig t/m | Optionele einddatum. Code vervalt na deze datum. | Nee |
| Max. gebruik | Optioneel maximum aantal keer dat de code gebruikt mag worden. Leeg = onbeperkt. | Nee |
| Actief | Aan/uit schakelaar. Inactieve codes worden niet geaccepteerd, ook niet binnen de geldigheidsperiode. | — |
- Actief — geldig, actief en binnen het gebruikslimiet
- Inactief — de actief-schakelaar staat uit
- Verlopen — de einddatum is verstreken
- Nog niet geldig — de startdatum ligt in de toekomst
- Max bereikt — het maximale aantal gebruik is bereikt
- Klik op + Nieuwe code.
- Vul de velden in (code en type + waarde zijn verplicht).
- Klik op Opslaan.
Klik op het potloodicoontje in de rij van de code. Pas de gewenste velden aan en sla op. De gebruik-teller kan niet handmatig worden aangepast.
Klik op het prullenbakicoontje. De code wordt direct verwijderd. Deze actie is onomkeerbaar.
Het endpoint GET /api/promo-codes/validate/:code controleert de geldigheid van een code zonder authenticatie. Bedoeld voor toekomstige checkout-integratie. Retourneert geldigheid, type en waarde.
The Promo Codes tab provides full CRUD management of promo and discount codes. Codes can be linked to future checkout flows.
| Field | Description | Required |
|---|---|---|
| Code | Unique discount code. Automatically converted to uppercase. | Yes |
| Description | Free text to clarify the purpose of the code. | No |
| Type | percentage — percentage discount (e.g. 20%) fixed — fixed amount in euros (e.g. €5.00) | Yes |
| Value | Numeric discount amount or percentage. | Yes |
| Valid from | Optional start date. Code is not valid before this date. | No |
| Valid until | Optional end date. Code expires after this date. | No |
| Max uses | Optional maximum number of times the code can be used. Blank = unlimited. | No |
| Active | On/off toggle. Inactive codes are not accepted, even within the validity period. | — |
- Active — valid, active and within usage limit
- Inactive — the active toggle is off
- Expired — the end date has passed
- Not yet valid — the start date is in the future
- Max reached — the maximum usage count has been reached
- Click + New code.
- Fill in the fields (code and type + value are required).
- Click Save.
Click the pencil icon in the code's row. Adjust the desired fields and save. The uses counter cannot be adjusted manually.
Click the trash icon. The code is immediately deleted. This action is irreversible.
The endpoint GET /api/promo-codes/validate/:code checks the validity of a code without authentication. Intended for future checkout integration. Returns validity, type and value.
Ga naar het tabblad 👥 Gebruikers, zoek de trainer en klik op het 🔒 icoontje. Vul een reden in en stel optioneel een einddatum in. Geblokkeerde trainers ontvangen HTTP 403 bij een inlogpoging.
Ja, als je bij het blokkeren een einddatum hebt ingesteld. Op het moment dat die datum is verstreken en de trainer probeert in te loggen, wordt de blokkering automatisch opgeheven.
Klik op het ⏱ icoontje bij de trainer in de Gebruikers-tab. Selecteer een einddatum. Gedurende de trial krijgt de trainer automatisch Pro-functionaliteit, ongeacht het opgeslagen abonnement. Laat de datum leeg en sla op om de trial te verwijderen.
Een tier-wijziging is permanent en wordt direct in de database opgeslagen. Een trial is tijdelijk: na de einddatum valt de trainer terug op zijn opgeslagen tier. Met een trial geef je tijdelijke Pro-toegang zonder de vaste tier te veranderen.
Controleer of app.set('trust proxy', 1) is geconfigureerd in server.js. Achter de Railway reverse proxy is dit vereist om het echte client-IP te lezen uit de X-Forwarded-For header. Zonder deze instelling registreert Express het interne proxy-IP.
Ja, tenzij je een Max. gebruik hebt ingesteld. Als het maximum is bereikt, krijgt de code de badge Max bereikt en wordt hij niet meer geaccepteerd door het validatie-endpoint.
Gebruik de Audit Log-tab en filter op het e-mailadres van de admin. Alle admin_* acties worden geregistreerd met het e-mailadres van de uitvoerende admin en een details-veld met context (bijv. welke gebruiker is geblokkeerd en de opgegeven reden).
Go to the 👥 Users tab, find the trainer and click the 🔒 icon. Enter a reason and optionally set an end date. Blocked trainers receive HTTP 403 on login attempts.
Yes, if you set an end date when blocking. Once that date has passed and the trainer attempts to log in, the block is automatically lifted.
Click the ⏱ icon next to the trainer in the Users tab. Select an end date. During the trial the trainer automatically gets Pro features, regardless of the stored subscription. Leave the date blank and save to remove the trial.
A tier change is permanent and stored directly in the database. A trial is temporary: after the end date the trainer reverts to their stored tier. Use a trial to give temporary Pro access without changing the permanent tier.
Check that app.set('trust proxy', 1) is configured in server.js. Behind the Railway reverse proxy this is required to read the real client IP from the X-Forwarded-For header. Without this setting Express logs the internal proxy IP.
Yes, unless you set a Max uses value. Once the maximum is reached, the code receives the Max reached badge and is no longer accepted by the validation endpoint.
Use the Audit Log tab and filter by the admin's email address. All admin_* actions are logged with the executing admin's email address and a details field with context (e.g. which user was blocked and the reason given).