mayu
⭐ Mayu
Moe-Counter Compatible Website Hit Counter
Mayu is a drop-in replacement for Moe-Counter. It serves the same counter routes and reads Moe-Counter's existing SQLite database.
Mayu commits every hit to the database as it arrives, so a crashed or killed process loses no counts. With every theme loaded, an idle Mayu container uses 23 MiB of memory. An idle container from Moe-Counter's official image uses 90 MiB. Mayu's Docker image is 23 MB compressed, and Moe-Counter's is 84 MB.
Mayu is written in Crystal and stores counters in SQLite or PostgreSQL.
Usage
The theme query parameter of a get request selects the theme, for example mayu.due.moe/get/@demo?theme=urushi.
This repository tracks four original themes: garukura, lain, urushi, and yuruyuri. ./scripts/sync-themes.sh adds the rest of the Moe-Counter theme collection, including asoul, gelbooru, moebooru, and rule34. The Docker image runs the script at build time; run it yourself after cloning to get the full set locally.
The script fetches the upstream revision pinned inside it and copies only files that are missing locally. It never replaces or deletes local files. To copy from a local upstream checkout instead, pass its path as the first argument.
Mayu pads the count with leading zeroes to 6 digits. The padding query parameter of a get request changes this, up to a maximum of 12.
Local
$ git clone git@github.com:Fuwn/mayu.git
$ cd mayu
$ shards install
$ shards build
$ ./bin/mayu
Building needs Crystal and the SQLite development headers. With Nix, nix develop provides them.
Docker
docker run --volume 'mayu:/mayu/data/' -p '80:3000' --rm fuwn/mayu:latest
The named volume, mayu, keeps the SQLite database across container restarts. PostgreSQL deployments do not need it, because the data lives in the database server.
Mayu listens on port 3000 unless PORT is set, and binds every interface over both IPv4 and IPv6.
Database
By default, Mayu stores counters in SQLite at data/count.db, relative to the working directory. In the Docker image, that is /mayu/data/count.db.
To use PostgreSQL instead, set MAYU_DATABASE_URL to a postgres:// or postgresql:// URL, for example postgres://mayu:secret@localhost:5432/mayu. Add ?sslmode=require if your provider requires TLS. Mayu encrypts the connection but does not verify the server's certificate, so it refuses to start with sslmode=verify-ca or sslmode=verify-full. Mayu creates its table on start-up, so an empty database is enough.
Mayu uses Moe-Counter's table layout. To keep existing counts, copy Moe-Counter's database file to data/count.db before starting Mayu.
On start-up, Mayu adds created_at and updated_at columns if the table lacks them. The record route returns these timestamps; counters that existed before the columns were added report an empty created_at.
Configuration
Mayu reads its configuration from environment variables. All of them are optional.
| Variable | Description |
|---|---|
PORT |
Port the server listens on. Defaults to 3000. |
MAYU_DATABASE_URL |
A postgres:// or postgresql:// URL. When unset, Mayu uses the SQLite file at data/count.db. |
MAYU_THEMES |
Comma-separated list of themes to load, for example asoul,garukura. When unset, Mayu loads every theme. A shorter list uses less memory and lowers the largest possible response. On memory-tight hosts, leaving out lain, urushi, and yuruyuri saves the most, because they hold about 70% of the theme data. |
MAYU_PRUNE_MIN_COUNT |
Counters with fewer than this many hits are eligible for pruning. |
MAYU_PRUNE_AFTER_DAYS |
Counters not incremented within this many days are eligible for pruning. |
MAYU_PRUNE_EVERY_HOURS |
How often, in hours, the prune sweep runs. |
Pruning runs only when all three MAYU_PRUNE_* variables are positive integers. Each sweep deletes a counter only if it has fewer than MAYU_PRUNE_MIN_COUNT hits and no hits within MAYU_PRUNE_AFTER_DAYS days. A counter that meets just one of those conditions stays.
Routes
/serves the homepage, which builds embed snippets for a counter./get/@nameincrements counternameand returns it as animage/svg+xmlimage. It accepts thethemeandpaddingquery parameters. Without a validtheme, Mayu usesasoulif it is loaded, otherwise the first loaded theme in alphabetical order whose name does not end in-h./record/@nameincrements counternameand returns itsname,num,created_at, andupdated_atas JSON./heart-beatreturnsalive.
Counter names must be 1 to 64 characters long. The demo counter always shows 1234567890 and is never stored. Any other path redirects to this repository.
Checks
crystal tool format --check src spec checks formatting, and crystal spec runs the SQLite and request tests.
MAYU_TEST_DATABASE_URL=postgres://postgres@localhost:5432/mayu_test crystal spec also runs the PostgreSQL tests, which are pending without that variable. They work in a temporary schema inside a transaction, then roll the transaction back so the database keeps its previous state.
python3 test/http_smoke.py http://localhost:3000 checks a running instance, including concurrent increments. It creates a counter, so point it at a disposable database. CI runs it against the built container.
Resource Attributions
- A-SOUL_Official
- Moebooru
- Rule 34 (NSFW)
- Gelbooru (NSFW)
- Urushi
- Lain Iwakura
- GaruKura
Licence
This project is licensed under the GNU General Public License v3.0.
mayu
- 25
- 2
- 0
- 0
- 3
- about 1 hour ago
- May 14, 2024
GNU General Public License v3.0
Sun, 08 Nov 2026 01:39:46 GMT