Voile Locker & Luggage Plugin
A visitor locker management plugin for Voile, the open-source GLAM library management system.
Provides per-node locker assignment during visitor check-in, staff management UI, session history, and auto-expiry.
Features
- Per-node locker enable/disable toggle
- Configurable locker count per node
- Auto-assign available locker during visitor check-in
- Manual locker assignment and release by staff
-
Locker statuses:
available,occupied,maintenance,reserved - Session history with timestamps and release tracking
-
Admin UI at
/manage/plugins/locker_luggage/
Requirements
| Requirement | Version |
|---|---|
| Elixir | ~> 1.18 |
| OTP | ~> 27 |
| Phoenix LiveView | ~> 1.1 |
| Voile | >= 0.1.2 |
| PostgreSQL | >= 14 |
Installation
Option A — From Hex (coming soon)
# mix.exs
{:voile_locker_luggage, "~> 0.1.0"}Option B — From GitHub (recommended for now)
You can pin to a specific release tag:
# mix.exs
{:voile_locker_luggage,
github: "curatorian/voile_locker_luggage",
tag: "v0.1.0",
sparse: "."
}Or track the latest commit on main (not recommended for production):
{:voile_locker_luggage,
github: "curatorian/voile_locker_luggage",
branch: "main",
sparse: "."
}Option C — From a local path (development / self-hosted)
If you have cloned this repository alongside your Voile installation:
# mix.exs — inside the host Voile app
{:voile_locker_luggage, path: "plugins/voile_locker_luggage"}
This is the recommended approach if you are running Voile from source in a
monorepo-style setup (all plugins live under plugins/ inside the Voile repo).
Setup Steps
After adding the dependency, follow these steps inside your Voile host app:
1. Fetch dependencies
mix deps.get2. Compile
mix compileThe plugin is a separate OTP application and will be compiled alongside Voile.
3. Install the plugin from the admin UI
Start Voile and navigate to:
/manage/plugins
Click "Install" next to Locker & Luggage. This runs the plugin's database
migrations and registers it in voile_plugins.
Alternatively, install via IEx:
Voile.PluginManager.install("Elixir.VoileLockerLuggage")4. Activate the plugin
After installing, click "Activate" in the admin UI, or via IEx:
Voile.PluginManager.activate("Elixir.VoileLockerLuggage")Activation registers the check-in hook so the plugin participates in visitor flows.
5. Configure per-node
Navigate to /manage/plugins/locker_luggage/nodes and enable the locker system for
each node (library branch / service desk) that has physical lockers.
6. Configure plugin settings
Navigate to /manage/plugins/locker_luggage/settings to configure:
| Setting | Default | Description |
|---|---|---|
| Allow Self-Release | true | Visitors can release their own locker at check-out |
| Auto-Expire After (hours) | 24 | Expire sessions automatically after N hours (0 = disabled) |
| Show Locker Number on Receipt | true | Print locker number on check-in receipt |
| Notify Staff on Expiry | false | Dashboard notification when a session expires |
Database Migrations
Migrations are applied automatically when you Install the plugin from the admin UI. They run against the same PostgreSQL database as the Voile host app.
Tables created by this plugin are prefixed with plugin_locker_luggage_:
plugin_locker_luggage_lockers— individual locker records per nodeplugin_locker_luggage_sessions— assignment history (active + closed)plugin_locker_luggage_node_configs— per-node enable/disable and locker count
To roll back all migrations (performed when you Uninstall the plugin):
Voile.PluginManager.uninstall("Elixir.VoileLockerLuggage")Warning: Uninstalling drops all plugin tables and their data permanently.
Production Installation
Using mix release
Elixir releases bake in all compiled code and dependencies at build time.
Because the plugin is a dependency in mix.exs, it is automatically included
in the release artifact.
Build steps:
# 1. Add the dep to mix.exs (see Installation above), then:
mix deps.get --only prod
MIX_ENV=prod mix compile
# 2. Build the release
MIX_ENV=prod mix release
# 3. Deploy the release artifact to the server
# (copy _build/prod/rel/voile/ to the target machine)
# 4. On the server, start the app
./bin/voile start
# 5. Install and activate the plugin (one-time, via remote IEx)
./bin/voile remote
iex> Voile.PluginManager.install("Elixir.VoileLockerLuggage")
iex> Voile.PluginManager.activate("Elixir.VoileLockerLuggage")
From the second deployment onward, the plugin is already registered in the
database — just redeploying the release is enough. If there are new migrations,
call on_update/2 or re-run install from the admin UI.
Using Podman / Docker (container-based)
The plugin must be included in the container image at build time — there is no dynamic plugin loading at container runtime. The image acts as the release artifact.
Example Containerfile / Dockerfile additions:
# If using Option C (local path dep):
# Make sure the plugin dir is copied before mix deps.get
COPY plugins/ plugins/
# Standard Phoenix build steps — no changes needed
RUN mix deps.get --only prod
RUN MIX_ENV=prod mix compile
RUN MIX_ENV=prod mix release
If using Option B (GitHub dep), no extra COPY step is needed — mix deps.get
fetches the plugin automatically during the image build.
Plugin activation in a containerised environment:
Since you cannot do an interactive IEx session easily in production containers, you can activate the plugin via a release eval command in your startup script or Docker entrypoint:
# In entrypoint.sh or deploy script — runs once on first deploy
./bin/voile eval "Voile.PluginManager.install(\"Elixir.VoileLockerLuggage\")"
./bin/voile eval "Voile.PluginManager.activate(\"Elixir.VoileLockerLuggage\")"Or add an idempotent task to your Voile deployment hooks so it is safe to run on every container restart:
# Safe to run every time — install/activate are no-ops if already done
./bin/voile eval "
case Voile.Plugins.get_plugin_by_plugin_id(\"locker_luggage\") do
nil -> Voile.PluginManager.install(\"Elixir.VoileLockerLuggage\")
_ -> :ok
end
"Key rule: Adding or removing a plugin always requires rebuilding the container image and redeploying. This is by design — plugins are trusted compiled Elixir code, not dynamic bytecode.
Updating the Plugin
1. Update the version in mix.exs
# Change tag to the new release
{:voile_locker_luggage, github: "your-org/voile_locker_luggage", tag: "v1.1.0"}2. Fetch and rebuild
mix deps.update voile_locker_luggage
mix deps.get
MIX_ENV=prod mix release # or rebuild the container image3. Run update migrations
From the admin UI at /manage/plugins, click "Update". This calls
on_update/2 which runs any new migrations.
Or via release eval:
./bin/voile eval "Voile.PluginManager.update(\"Elixir.VoileLockerLuggage\")"Version Checking
This plugin exposes its current version via VoileLockerLuggage.metadata/0:
VoileLockerLuggage.metadata().version # "1.0.0"Voile's plugin system is designed to support automatic version-checking against GitHub Releases. When implemented, the check works as follows:
-
Voile periodically fetches
https://api.github.com/repos/curatorian/voile_locker_luggage/releases/latest -
Compares the
tag_nameagainst the installed plugin'smetadata().version -
Shows an "Update available" badge in
/manage/pluginsif a newer version exists
This is an upcoming feature of the core Voile plugin manager. Until then, check the GitHub Releases page manually for new versions.
Uninstalling
-
Navigate to
/manage/plugins - Click "Deactivate" — removes all hooks from the running system
- Click "Uninstall" — rolls back all database migrations
Warning: Uninstalling permanently drops all locker and session data.
-
Remove the dependency from
mix.exs:
# Remove or comment out:
# {:voile_locker_luggage, ...}- Rebuild and redeploy.
Development
Clone this repository alongside Voile for local development:
git clone https://github.com/curatorian/voile_locker_luggage \
/path/to/voile/plugins/voile_locker_luggage
Ensure Voile's mix.exs references the plugin as a path dep (see Option C above).
Plugin code hot-reloads automatically in dev mode — changes to .ex and .heex
files under plugins/ are picked up by Phoenix's live reload without restarting
the server.
Running plugin migrations in dev
cd /path/to/voile
mix ecto.migrate # runs both core and plugin migrationsOr trigger via the admin UI after installing the plugin in dev.
Architecture
This plugin follows the Voile Plugin System contract:
-
Implements the
Voile.Pluginbehaviour (metadata/0,on_install/0,on_activate/0,on_deactivate/0,on_uninstall/0,on_update/2,hooks/0,routes/0,nav/0,settings_schema/0) -
Ships its own Ecto migrations under
priv/migrations/ -
Uses
Voile.Repofor all database operations -
Registers hooks via
Voile.Hooksto extend core behaviour -
Mounts LiveViews under
/manage/plugins/locker_luggage/
License
Apache 2.0 — see LICENSE.