Architecture & Style
Read up on all the badly hacked nitty gritty that makes LRR tick here.

Installation Script

The install.pl script is essentially a sequence of commands executed to install the backend and frontend dependencies needed to run LRR, as well as basic environment checks.

Specific environment variables you can apply to change LRR behavior

Those variables were introduced for the Homebrew package, but they can be declared at anytime on any type of install; LRR will try to use them.
  • LRR_DATA_DIRECTORY - Data directory override. If this variable is set to a path, said path will house the content folder.
  • LRR_THUMB_DIRECTORY - Thumbnail directory override. If this variable is set to a path, said path will house the generated archive thumbnails.
  • LRR_TEMP_DIRECTORY - Temporary directory override. If this variable is set to a path, the temporary folder will be there instead of /public/temp.
  • LRR_LOG_DIRECTORY - Log directory override. Changes the location of the log folder.
  • LRR_FORCE_DEBUG - Debug Mode override. This will force Debug Mode to be enabled regardless of the user setting.
  • LRR_NETWORK - Network Interface. See the dedicated page in Advanced Operations.

Coding Style

While Perl's mantra is "There's more than one way to do it", I try to make LRR follow the PBP, aka Perl Best Practices. This is done by the use of the Perl::Critic module, which reports PBP violations. If installed, you can run the critic on the entire LRR source tree through the npm run critic shortcut command. Critic is automatically run on every commit made to LRR at the level 5 thanks to Github Actions.
I also run perltidy on the source tree every now and then for consistency. The rules used in perltidy passes are stored in the .perltidyrc file at the source root.
Some extras:
  • Code width limit is stupid for long strings and comments (ie the base64 pngs in plugin metadata), which is perltidy's default behavior.
  • The visual indentation when setting a bunch of variables at once a perltidy thing, but I actually really like it! I leave it in and try to repro it whenever it makes sense.
  • The codebase does have issues with variable naming -- perl packages usually go for snakecase buuut short variables are ok in flatcase (as per perlstyle )
  • ''s should only be used for escaping " easily and vice-versa but I don't really care about that one. 😐
A small practice I try to keep on my own for LRR's packages is to use methods (arrow notation, Class::Name->do_thing) to call subroutines that take no arguments, and functions (namespace notation, Class::Name::do_thing($param)) to call subs with arguments. It doesn't really matter much, but it looks cleaner to me! Also makes it easier if one day I take the OOP pill for this project, as methods always get the current object (or class name) as the first parameter of their call.
Packages in the Utils folder export most of their functions, as those are used by Plugins as well. I recommend trying to only use exported functions in your code, and consider the rest as internal API suspect to change/breakage.

Main App Architecture

1
root/
2
|- .devcontainer <- VSCode setup files for Codespaces
3
|- .github <- Github-specific files
4
| |- action-run-tests <- Run the LRR Test Suite
5
| |- ISSUE_TEMPLATE <- Template for bug reports
6
| |- workflows <- Github Actions workflows
7
| |- CD <- Continuous Delivery, Nightly builds
8
| |- CI <- Tests
9
| +- Release <- Build latest and upload .zip to release post on GH
10
| +- FUNDING.yml <- Github Sponsors file
11
|
12
|- content <- Default content folder
13
|
14
|- lib <- Core application code
15
| |- LANraragi.pm <- Entrypoint for the app, contains basic startup code and routing to Controllers
16
| |- Shinobu.pm <- Background Worker (see below)
17
| +- LANraragi
18
| |- Controller <- One Controller per page
19
| | |- Api <- API implementation
20
| | | +- ...
21
| +- *.pm <- Index, Config, Reader, etc.
22
| |- Model <- Application code that doesn't rely on Mojolicious
23
| |- Archive.pm <- Serve files from archives and OPDS catalog
24
| |- Backup.pm <- Encodes/Decodes Backup JSONs
25
| |- Category.pm <- Save/Read Category data
26
| |- Config.pm <- Communicates with the Redis DB to store/retrieve Configuration
27
| |- Plugins.pm <- Executes Plugins on archives
28
| |- Reader.pm <- Archive Extraction
29
| |- Search.pm <- Search Engine
30
| |- Stats.pm <- Tag Cloud and Statistics
31
| +- Upload.pm <- Handle incoming files (Download System)
32
| +- Plugin <- LRR Plugins are stored here
33
| |- Login
34
| |- Metadata
35
| +- Scripts
36
| +- Utils <- Generic Functions
37
| |- *.pm
38
| +- Minion.pm <- Minion jobs are implemented here
39
|
40
|- log <- Application Logs end up here
41
|
42
|- public <- Files available to Web Clients
43
| |- css <- Global CSS sheets
44
| |- lrr.css
45
| +- vendor <- Third-party CSS sheets obtained through NPM
46
| |- img <- Image resources
47
| |- js <- JavaScript functions
48
| |- *.js
49
| +- vendor <- Third-party JS obtained through NPM
50
| |- temp <- Archives are extracted in this folder to be served to clients. Also used for thumbnail creation.
51
| | |- server.pid <- PID of the currently running Prefork Manager process, if existing
52
| | |- shinobu.pid <- Last known PID of the Shinobu File Watcher (Serialized Proc::Simple object)
53
| | +- minion.pid <- Last known PID of the Minion Job Queue (Serialized Proc::Simple object)
54
| +- themes <- Contains CSS sheets for Themes.
55
|
56
|- script
57
| |- backup <- Standalone script for running database backups.
58
| |- launcher.pl <- Launcher, uses either Morbo or Prefork to run the bootstrap script
59
| +- lanraragi <- Bootstrap script, starts LANraragi.pm
60
|
61
|- tests <- Tests go here
62
|
63
|- templates <- Templates for Web pages
64
| +- *.html.tt2
65
|
66
|- tools <- Contains scripts for building and installing LRR.
67
| |- _screenshots <- Screenshots
68
| |- Documentation <- What you're reading right now
69
| |- build <- Build tools and scrpits
70
| |- windows <- Windows build script and submodule link to the Karen WPF Bootstrapper
71
| |- docker <- Dockerfile and configuration files for LRR Docker Container
72
| |- homebrew <- Script and configuration files for the LRR Homebrew cask
73
| |- vagrant <- Vagrantfile for LRR Vagrant Machine
74
| |- cpanfile <- Perl dependencies description
75
| |- install.pl <- LANraragi Installer
76
| +- lanraragi-systemd.service <- Example SystemD service
77
|
78
|- lrr.conf <- Mojolicious configuration file
79
|- .perltidy.rc <- PerlTidy config file to match the coding style
80
|- .eslintrc.json <- ESLint config file to match the coding style
81
+- package.json <- NPM file, contains front-end dependency listing and shortcuts
Copied!

Shinobu Architecture

The Shinobu File Watcher runs in parallel of the LRR Mojolicious Server and handles various tasks:
  • Scanning the content folder for new archives at start
  • Keeping track of new/deleted archives using inotify watches
  • Adding new archives to the database and executing Plugins on them if enabled
It's a second process spawned through the Proc::Simple Perl Module. Heavier tasks are handled by a Minion Job Queue, which is much more closely linked to Mojo and basically just werks™

About the Search Cache

When you perform a search in LRR, that search is saved to a cache in order to be served faster the next time it's queried. This cache is busted as soon as the archive index is modified in any way.(be it editing metadata or adding/removing archives)

Behaviour in Debug Mode

In Debug Mode:
  • The Mojolicious server auto-restarts on every file modification,
  • Logs are way more detailed,
  • You get access to Mojolicious logs in LRR's built-in Log View,
  • A Status dashboard becomes available at http://LRR_URL/debug.

Database Architecture

You can look inside the LRR Redis database at any moment through the redis-cli tool.
The base architecture is as follows:
1
-Redis Database
2
|- **************************************** <- 40-character long ID for every logged archive
3
| |- tags <- Saved tags
4
| |- name <- Name of the archive file, kept for filesystem checks
5
| |- title <- Title of the archive, as set by the User
6
| |- file <- Filesystem path to archive
7
| |- isnew <- Whether the archive has been opened in LRR once or not
8
| +- thumbhash <- SHA-1 hash of the first image of the archive
9
|
10
|- LRR_PLUGIN_xxxxxxx <- Settings for a plugin with namespace xxxxxxx
11
|
12
|- LRR_TOTALPAGESTAT <- Total pages read
13
|
14
|- LRR_FILEMAP <- Shinobu Filemap, maps IDs in the database to their location on the filesystem
15
|
16
|- SET_xxxxxxxxxx <- A Category.
17
|
18
|- LRR_CONFIG <- Configuration keys, usually set through the LRR Configuration page.
19
| |- htmltitle
20
| |- motd
21
| |- dirname <- Content directory
22
| |- thumbdir <- Thumbnail directory
23
| |- tempmaxsize <- Temp folder max size
24
| |- enableresize <- Whether automatic image resizing is enabled
25
| |- sizethreshold <- Auto-resizing threshold
26
| |- readerquality <- Auto-resizing quality
27
| |- enablecors <- Whether CORS headers are enabled
28
| |- tagruleson <- Whether tag rules are enabled
29
| |- tagrules <- Tag rules, saved as a big ol' string
30
| |- devmode <- Whether debug mode is enabled
31
| |- enablepass <- Enable/Disable Password Authentication.
32
| |- nofunmode <- Whether No-Fun Mode is enabled
33
| |- pagesize <- Amount of archives per Index page
34
| +- apikey <- Key for API requests
35
|
36
|- LRR_TAGRULES <- Computed Tag Rules, as a Redis list
37
|
38
+- LRR_SEARCHCACHE <- Search Cache
39
|- $columnfilter-$filter-$sortkey-$sortorder-$newonly <- Unique ID for a search. The search result is serialized and saved as the value for this ID.
40
+- --title-asc-0 <- Example ID for a search made on titles with no filters.
Copied!
The archive IDs computed by LRR are created by taking the first 512KBs of the file, and computing a SHA-1 hash from this data.
You can find the code used for the calculation in LANraragi::Utils::Database.
Last modified 1mo ago