RecordRanks is a sports organization and ranking system. It's a web application that provides tools for organizing competitions for different kinds of competitive sports, selecting events (fully customizable), writing rules, managing competitor information, entering live results, and automated global rankings and records for each event. It also has support for user roles for streamlined moderation to ensure the integrity of the results, it supports results submitted with video evidence, and it supports World, continental and national records, including for team events.
RecordRanks can be deployed on any Linux server and runs as a web application, with self-hosted Supabase providing the database, logs, storage, cron, and a rich suite of system administration tools. It also automates the creation of daily backups of DB data.
Deni Mintsaev is the main developer of RecordRanks, and all contributions go directly towards the development of the project. You can support the project on Ko-fi:
Below are some screenshots from one of the RecordRanks instances: Cubing Contests (a lot of this is mock data). This was the first instance of RecordRanks.
To deploy an instance of RecordRanks, you have to first set up a Linux server and obtain a custom domain name. You'll also need a Dockerhub account to host your custom Docker images. Then, you can follow this guide to publish your custom RecordRanks image and deploy it on your server.
You will have to set up a local .env file for releasing your Docker image and another one on your server, which will contain all of your secrets. Note that you MUST NOT use your local .env file or the .env.example file in production, because using the default values will leave your instance completely exposed. To set up a local .env file, follow these steps:
- Create
.envfile:cp .env.example .env. - Set
PROD_HOSTNAMEto your custom domain name without the protocol (e.g.mysportsproject.com). - Set
SUPABASE_HOSTNAMEto your Supabase hostname without the protocol (e.g.supabase.mysportsproject.com). - Set
NEXT_PUBLIC_PROJECT_NAMEto your project name (e.g.My Sports Project). - Set
PROJECT_IDto an alphanumeric ID for your project, in lowercase (e.g.mysportsproject). - Set
NEXT_PUBLIC_AUTH_PROVIDERSto the authentication methods you would like to use (comma-separated). - Set your Dockerhub username in
DOCKER_IMAGE_NAME(e.g.dockerhubuser/$PROJECT_ID-nextjs).
To set up a production .env file, follow these steps:
- Create
.envfile:cp .env.example .env. - Generate secure secret keys for Supabase:
./bin/supabase-generate-keys.sh. - Comment out all variables marked with
for local developmentand uncomment variables marked withfor production. - Set
RR_DB_USERNAMEto a custom username for the DB user. - Set
RR_DB_PASSWORDto a secure password. - Set
BETTER_AUTH_SECRETto a secure secret. - Set
PROD_HOSTNAMEto your custom domain name without the protocol (e.g.mysportsproject.com). - Set
PROJECT_IDto an alphanumeric ID for your project, in lowercase (e.g.mysportsproject). - Optionally, set the
METADATA_...values for SEO and theANALYTICS_...values for analytics. - Set
EMAIL_HOST,EMAIL_PORT,EMAIL_USERNAMEandEMAIL_PASSWORDto your transactional email credentials. - Set your Dockerhub username in
DOCKER_IMAGE_NAME(e.g.dockerhubuser/$PROJECT_ID-nextjs).
* Note: for WCA OAuth you will have to set these values when you set it up in your WCA OAuth settings:
- Name: (same as
NEXT_PUBLIC_PROJECT_NAME) - Redirect URI:
https://<PROD_HOSTNAME>/api/auth/oauth2/callback/wca - Scopes:
public openid email profile
To generate an icon, place an icon.svg file in the client directory (this file is git-ignored). The ICO file will be generated automatically when the Docker image is built and included in the image.
Caddy uses the down-for-maintenance-page.html file at the root of the repo as the fallback for when the Next JS application is down. This file is git-ignored, as it's supposed to be unique to each RecordRanks instance. Copy the example file on your deployed server and edit it to fit your instance. You can use this command:
cp down-for-maintenance-page.html.example down-for-maintenance-page.htmlThere is an example robots.txt at client/app/robots.txt.example. You can copy that file to client/app/robots.txt and edit it to define a list of paths you would like to prevent web crawlers from indexing. Learn more about this here.
Once you have a Dockerhub account, you can publish your Docker image using the script (see the Scripts section).
Before you deploy the instance, you will have to set up your DNS records:
- Set up
A,AAAAandNSrecords to point from your domain name to your server. - Set up
A,AAAAandNSrecords to point fromsupabase.yourdomainname.comto your server. - Set up records to enable email sending using your domain name (follow the instructions from your transactional email provider*).
- If you would like to use a custom email address using your domain name, set up the records for that (follow the instructions from your email service provider*).
* Note: a transactional email provider is not the same as an email service provider; the former enables you to send automated emails from your domain name (e.g. no-reply@yourdomainname.com), while the latter enables you to create an email inbox, often with the ability to set up a custom domain name (e.g. inquiries@yourdomainname.com).
The docker-compose.rr.yml file includes a Caddy reverse proxy, which handles proxying for both Next JS and Supabase.
If you're using a firewall on your server, make sure the following ports are not being blocked: 80, 443, 443/udp, <the port from EMAIL_PORT>.
To deploy your RecordRanks instance, you will have to install the following dependencies on your Linux server: git, docker, node, pnpm and rsync (for backups). It is also recommended that you set up a better logging driver for Docker. Here's an example /etc/docker/daemon.json file you could use for your server (don't forget to restart Docker and any running containers after setting it up):
{
"log-driver": "local",
"log-opts": {
"mode": "non-blocking"
}
}There are two Docker Compose files used for deploying an instance: one for starting Supabase and one for starting RecordRanks. Run the following command to start Supabase:
docker compose -f docker-compose.supabase.yml up -dThe Scripts section shows how to start RecordRanks.
RecordRanks instances run alongside self-hosted Supabase, which provides the database, blob storage, a sysadmin dashboard (Supabase Studio), and more. The credentials for accessing Supabase Studio are in the .env file. Note that the Auth, Edge Functions and PostgREST modules are excluded in the self-hosted Supabase configuration in this project.
RecordRanks is designed to be customizable, allowing certain features to be enabled or disabled. The values in the settings table can be edited directly to customize some of the functionality of the instance. Only edit the value column (keep in mind the values cannot be null). The description column includes descriptions for each setting.
There is a simple blog feature, but it currently has no UI for creating posts within RecordRanks itself. For now, blog posts can be published directly using the posts table. A post has the following schema:
postId: a unique text ID for the post; this is used in the URL for the post (e.g.our-first-announcement)title: the title of the post, shown at the top of the pagecontent: the content of the post (Markdown supported)date: the date of the post (this doesn't have to be the same as the creation date;createdAtis a separate auto-generated column)createdBy: the user ID of the author; it's expected that there is a person tied to the user (get this value from theuserstable)
Supabase Logs contains logs for both the Supabase services and the RecordRanks application. There is a snippet in SQL Editor that can be copied over to Supabase Logs to view RecordRanks logs. The snippet also contains the instructions for this.
Note that RecordRanks simply uses the existing Edge Functions sink for internal logs, but this project excludes the Edge Functions module in the self-hosted Supabase configuration.
Blob storade is used for hosting public image files (although you can also use it for other files). Follow these instructions to set up a storage bucket for your public assets:
- Create a public bucket with the name
public_bucket. - Create a policy using the template "Give access to a nested folder called admin/assets only to a specific user" and set it up like this:
- Policy name: Give access to assets folder
- Allowed operation: (select all)
- Target roles: authenticated
- Policy definition:
bucket_id = 'public_bucket' AND (storage.foldername(name))[1] = 'assets' AND (select auth.uid()::text) = '<LEAVE PRE-FILLED USER ID>'
- Create an
assetsfolder at the root of the bucket.
You can then place any assets you want to be publicly accessible via the URL in that folder. If you would like to have link image previews for certain pages, you can create an assets/screenshots folder and place the screenshots there. Search for screenshots/ in the codebase to see which pages have link image previews.
To enable automatic public exports that run at regular intervals, you have to set up a cron job with Supabase:
- Open Supabase Studio and go to Integrations -> Vault -> Secrets.
- Add secret
service_role_keywith the value being the same asSERVICE_ROLE_KEYin your production.envfile. - Add secret
base_urlwith the valuehttps://<PROD_HOSTNAME>(use your production hostname value). - Go to SQL Editor and run the query "Schedule public export cron job".
- Go to the
settingstable in Table Editor and set thepublic-exports-to-keepvalue to a number above 0.
NOTE: while this cron job will be visible in Integrations -> Cron, it cannot be edited directly, due to the complex value of the authorization header; only activated and deactivated. To change the cron job, delete it and create it again following step 4.
To test this locally with test-prod.sh, use http://rr-nextjs:<NEXTJS_PORT> as the base_url value in Supabase Vault, temporarily add shared network to the supabase-db container in docker-compose.supabase.yml, change the value of SUPABASE_PUBLIC_URL to http://supabase-kong:<KONG_HTTP_PORT> in the .env file and restart the supabase-db container. You can also test it with the normal local dev environment using this command:
# Make sure to replace <NEXTJS_PORT> with your Next JS container port (3000 by default)
curl -X POST -H "Authorization: Bearer <SERVICE_ROLE_KEY>" http://localhost:<NEXTJS_PORT>/api/export/create-public-exportFor debugging you can look at the history of cron job runs in Integrations -> Cron and at the contents of the net schema in Table Editor.
The export files can be imported with Supabase, but keep in mind that they don't include the data for some internal columns, including organization_id. The import process for each table is as follows:
- Go to "SQL Editor" and run the "Public exports pre-import helper" snippet (THIS DELETES DATA).
- Go to "Table Editor" and select schema
record_ranks. - Click "Insert" -> "Import data from CSV" -> select the CSV file -> "Import data".
- Go to "SQL Editor" and run the "Public exports post-import helper" snippet.
Note: due to limitations with the CSV format, empty string values are represented as __EMPTY_STRING__. You can (and should) safely change those values to "" (empty string), if you find any.
There are several custom scripts located in the bin directory. These should be executed from the root of the project with ./bin/<script>.
| Script | Description |
|---|---|
start-prod.sh |
Start RecordRanks in production. If it's already running, add -r to restart it instead. |
test-prod.sh |
Start project locally for testing, similar to the production environment. To clean up running project, add -c. |
apply-db-migrations.sh |
Apply DB migrations using Drizzle Kit. Also handles disabling "server-only" while Drizzle Kit is running. |
supabase-reset.sh |
Reset Supabase (remove containers and delete DB data and storage). |
supabase-generate-keys.sh |
Generate Supabase secret keys. This is REQUIRED for production! |
release-new-version.sh |
Release new version of RecordRanks (pushes to Codeberg). |
release-new-image.sh |
Create Docker image for the Next JS app and publish it. |
create-full-backup.sh |
Create encrypted backup of the Supabase database and storage. |
There is also a convert-svg-to-ico.sh script in the client directory to convert an SVG file with the icon to an ICO file. The first argument is the path to the SVG file; the second argument is the path to the output ICO file (defaults to ./app/favicon.ico). This script runs automatically on Docker image build.
This project uses Next JS as a full-stack web application and self-hosted Supabase for various backend utilities. These instructions assume you're using Linux (or WSL). To set up the development environment, install Node, PNPM and Docker, and then follow these steps:
- Create a
.envfile:cp .env.example .env(skip this step if you already have a.envfile; DO NOT use the example.envin production!) - Start Supabase:
docker compose -f docker-compose.supabase.yml up -d - Apply DB migrations:
./bin/apply-db-migrations.sh(skip this step if there are no new migrations since last time) cd client- Start Next JS:
pnpm dev
Note that Next JS accesses the variables in .env through the client/.env symlink, which means that it won't be able to detect changes made to the source file. If you change any values in .env, simply restart pnpm dev.
This repo uses Biome for formatting and linting. If you intend to contribute code to this repo, please install the Biome extension for your IDE and set it up as your default formatter.
Go to localhost:3000 to see the website. Go to localhost:8000 to open Supabase Studio. The default username is supabase and the password is rr (you can see this in the .env file). The default ports can be overridden in the .env file.
Global constants are located in constants.ts. Keep in mind that some features are only enabled for the Cubing Contests instance (via the IS_CUBING_CONTESTS_INSTANCE constant).
Please note that some Supabase features, like analytics and cron only work in a production environment. You can test the production environment using the test-prod.sh script (see the Supabase section for more information).
To stop Supabase, use this command: docker compose -f docker-compose.supabase.yml down.
If your DB is empty, the backend will fill the events table with the data from eventsStub.ts. It will also seed some test data from client/helpers/test-data. This includes users admin, mod and user, all with the password rr.
To access the DB container with admin privileges directly, use this command (make sure to use the values from .env):
docker exec -it supabase-db psql postgresql://supabase_admin:${POSTGRES_PASSWORD}@localhost:${POSTGRES_PORT}/${POSTGRES_DB}To test email sending, use Smtp4Dev locally:
docker compose -f docker-compose.smtp4dev.yml up -dMake sure your email environment variables are set to the following values:
EMAIL_HOST="localhost"
EMAIL_PORT=25
EMAIL_USERNAME=""
EMAIL_PASSWORD=""To get the list of events, use the endpoint below:
/api/[slug]/events
slug (optional) = URL slug for the space (this parameter can be omitted to use the default space)
To get the rankings, use the endpoint below:
/api/[slug]/results/rankings/[eventId]/[type]/[category]?show=[show]®ion=[region]&topN=[topN]
slug (optional) = URL slug for the space (this parameter can be omitted to use the default space)
eventId = ID of the event
type = "single" for top single rankings; "average" for top average rankings; "all-avg-formats" for top average rankings, including Mo3 and Ao5 formats
category = record category; accepts values: "competitions" | "meetups" | "online" | "all"
show (optional) = "persons" for top persons rankings (default); "results" for top results rankings
region (optional) = region (shows World rankings if omitted); accepts values: 2 letter country ISO code | "XF" | "XA" | "XE" | "XO" | "XN" | "XS" (continent codes)
topN (optional) = how many top results to return; number between 1 and 100,000; defaults to 100
WIP
To see the current healthcheck status, use the endpoint below:
/api/healthcheck





