On this page
Documentation
A complete guide that covers local installation, deployment to Ubuntu 24.04 VPS using GitHub Actions, configuration of the production environment, queues, and how to theme your directory with Flux CSS variables.
Overview
Directory Pro is a digital business-card directory built for Uganda market (Only supports Momo/Airtel Payments) — its a publicly searchable directory for business cards, the card management system includes user avatar uploads. billing is powered by mobile-money via ioTec Pay. Directory pro also features a simple invoicing system with PDF receipts, and a full featured owners admin panel. The Tech Stack: PHP 8.5 / Laravel 13, Livewire 4, Flux UI 2, Tailwind 4, Vite, Pest.
Public directory
Paginated grid, avatar + bio, social links, per-card public page. Blocked users' cards are auto-hidden.
Billing & invoices
Free-slot gating, mobile-money checkout with webhook settlement, tokenized public invoice/receipt PDFs.
Admin panel
Dashboard, card moderation, user block/unblock, invoice oversight, site-wide Settings singleton.
Demo accounts
admin@dirpro.demo /
password & user@dirpro.demo /
password seeded via DemoSeeder.
Requirements
- — PHP 8.3+ (recommended 8.5)
- — Composer 2.x
- — Node.js 20+ (CI uses 24) & npm
- — SQLite / MySQL / Postgres
- — Git
- — Ubuntu 24.04 for VPS deploy
Manual setup (local)
Fastest way to get running locally. The repo ships a convenience script that does install, env, key, migrate and build in one go.
Clone & install
git clone <your-repo-url> directory-pro
cd directory-pro
# one-command setup (composer + env + key + migrate + build)
composer run setup
# alternative — step by step
composer install
cp .env.example .env
php artisan key:generate
touch database/database.sqlite # if using sqlite
php artisan migrate --force
npm install
npm run build
The composer run setup script is
defined in composer.json:scripts.setup and
copies .env.example only if
.env doesn't exist.
Environment essentials
Edit .env after cloning.
Minimum to change:
APP_NAME="Directory Pro"
APP_URL=http://localhost:8000
APP_ENV=local
APP_DEBUG=true
DB_CONNECTION=sqlite
# For MySQL/Postgres uncomment and fill:
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=directory_pro
# DB_USERNAME=root
# DB_PASSWORD=secret
QUEUE_CONNECTION=database
SESSION_DRIVER=database
CACHE_STORE=database
MAIL_MAILER=log
VITE_APP_NAME="${APP_NAME}"
Migrate & run
# dev servers (Laravel + Vite together)
composer run dev
# or separately
php artisan serve # http://localhost:8000
npm run dev # Vite HMR
# production build
npm run build
php artisan optimize
php artisan storage:link
# optional demo data
php artisan db:seed --class=Database\\Seeders\\DemoSeeder
Vite manifest not found,
run npm run build (or
npm run dev in
development). The app expects public/build/manifest.json.
Automated deploy — Ubuntu 24.04 VPS + GitHub Actions
The repo's .github/workflows/deploy.yml
builds assets on GitHub's runner and deploys to your VPS over SSH on every push to main. It uses an atomic
folder-swap strategy and preserves storage/.
Prepare the VPS (once)
- Provision Ubuntu 24.04, create a deploy user (e.g.
deploy) with sudo, and install PHP 8.5, Composer, Nginx/Apache, MySQL/SQLite. - Generate an SSH key pair for deploys:
ssh-keygen -t ed25519 -C "github-deploy". Add the public key to~/.ssh/authorized_keyson the VPS. - Create the web root and env source expected by the workflow:
sudo mkdir -p /var/www/envs sudo chown $USER:$USER /var/www/envs # put your production env here — never commit it nano /var/www/envs/.dirprodemo # or whatever ENV_SOURCE is - Configure Nginx/Apache vhost root to
/var/www/dirpro-demo.top.ug/public(or yourSITE_DIR). - Ensure the deploy user can run
git cloneto your repo. For private repos add the VPS's SSH key as a deploy key or use aGIT_REMOTEwith token.
Repository Variables (Settings → Secrets and variables → Actions → Variables)
Defined at the top of deploy.yml:env. Override them
here — each falls back to the default shown.
| Variable | Default | Purpose |
|---|---|---|
| SITE_DIR | dirpro-demo.top.ug | Live folder under WWW_DIR that
Nginx serves |
| SITE_NAME | dirpro-demo | Temp clone dir; also names the build artifact |
| GIT_REMOTE | github-directory-pro:top-ug/… | Clone URL (SSH alias or
git@github.com:…)
|
| ENV_SOURCE | envs/.dirprodemo | Path relative to WWW_DIR with
production .env |
| WWW_DIR | /var/www | Base directory on VPS |
| VITE_APP_NAME | — | Injected at build time for Vite |
Repository Secrets (same page → Secrets)
| Secret | Example | Notes |
|---|---|---|
| HOST | 203.0.113.10 | VPS IP or hostname |
| USERNAME | deploy | SSH user |
| PORT | 22 | SSH port (custom if you changed it) |
| SSHKEY | -----BEGIN OPENSSH… | Private key (paste full file incl. header/footer) |
What the workflow does (on push to main)
- Build on CI:
setup-php 8.5+setup-node 24→composer install→npm install && npm run build. Saves VPS CPU. - Copy assets:
public/buildis SCP'd to/tmp/<SITE_NAME>-buildon the VPS before cloning, so no Vite build needed on the server. - Fresh clone: Removes
$WWW_DIR/$SITE_NAME, runsgit clone $GIT_REMOTE $SITE_NAMEand copies$ENV_SOURCEto.env. - Install & migrate:
composer install --no-dev→ copy prebuiltpublic/build→php artisan migrate --force. - Atomic swap: Moves current
$SITE_DIRto$SITE_NAME_back, then promotes the new clone to$SITE_DIR; migratesstorage/from backup so uploads persist. - Post-install:
clear-compiled,optimize,storage:link, writesstorage/app/public/installedmarker, fixes permissionschgrp www-data / chmod ug+rwx.
Trigger: on.push.branches: [main].
Adjust if you deploy from another branch. Manual dispatch can be added with workflow_dispatch.
Configuration — .env & app settings
Infrastructure config lives in .env & config/*.php. Business
logic (pricing, currency, billing toggles) lives in the database via Admin →
Settings (the settings singleton).
Quick .env reference
| Key | Default / example | Description |
|---|---|---|
| APP_NAME | Directory Pro | Shown in mails, navbar, PDF header |
| APP_ENV | production | local vs
production
|
| APP_DEBUG | false | Never true in
production |
| APP_URL | https://example.com | Used by URL generation, mail & Vite |
| DB_CONNECTION | sqlite / mysql | Set DB_DATABASE
etc. for MySQL |
| QUEUE_CONNECTION | database | Keep database
unless you provision Redis |
| CACHE_STORE | database | Optional: redis
|
| SESSION_DRIVER | database | — |
| MAIL_MAILER | log / smtp | Set SMTP creds if you send invoices by mail |
| VITE_APP_NAME | "${APP_NAME}" | Exposed to Vite at build time |
After changing .env on the VPS run php artisan optimize:clear &&
php artisan optimize.
Admin → Settings (database-driven)
Visit /admin/settings as an
admin user. Changes apply instantly — no redeploy needed. Secrets (iotec_client_secret,
iotec_callback_secret)
are stored encrypted.
Directory
auto_publish_cards— publish immediately vs queue for reviewbilling_enabled+free_slots_per_user+price_per_cardcurrency— UGX / USD / ITX
Invoices & payments
invoices_billing_enabled+ free slots / price per docdefault_tax_rate+invoice/receipt_number_prefixiotec_*— wallet, client id/secret, callback secret
POST
/webhooks/iotec-pay validates X-Iotec-Signature
against iotec_callback_secret.
Correlation via externalId prefixes
card-{id} / invoice-{id}.
Queue workers & scheduler on the VPS
The app uses the database queue driver
by default. Jobs include mail, PDF generation and payment settlement. You also need the scheduler
for queued jobs that rely on cron.
Install the
queue worker (Supervisor) — queue-worker-setup.sh
Run from your project root
with sudo on Ubuntu 24.04. The script installs supervisor, rewrites
/etc/supervisor/supervisord.conf
(minfds 10000), and creates a per-project config /etc/supervisor/conf.d/laravel-queue-<project>.conf
with 2 workers as www-data.
# on the VPS, from the live folder (e.g. /var/www/dirpro-demo.top.ug)
sudo ./queue-worker-setup.sh
# what it creates (sanitized project name)
# /etc/supervisor/conf.d/laravel-queue-dirpro-demo-top-ug.conf
# command=php /var/www/dirpro-demo.top.ug/artisan queue:work --sleep=3 --tries=3 --max-time=3600
# numprocs=2, autostart=true, autorestart=true
# manage
sudo supervisorctl status
sudo supervisorctl restart laravel-queue-dirpro-demo-top-ug:*
sudo supervisorctl stop laravel-queue-dirpro-demo-top-ug:*
tail -f /var/log/supervisor/laravel-queue-dirpro-demo-top-ug.log
tail -f /var/log/supervisor/supervisord.log
basename $(pwd) lowercased
& sanitized. Alternatively use php artisan queue:work or queue:listen for development.
Enable the
scheduler — cron.sh
Laravel's scheduler runs every
minute. The helper script adds the cron entry for www-data (via sudo) or
current user.
# from project root
sudo ./cron.sh
# verifies artisan works, then installs:
# * * * * * cd /var/www/dirpro-demo.top.ug && php artisan schedule:run >> /dev/null 2>&1
# without sudo (current user crontab)
./cron.sh # or sudo not needed if no www-data user
# verify
sudo crontab -u www-data -l # when installed with sudo
crontab -l # otherwise
The script is idempotent — it removes any
previous artisan schedule:run
entries before adding the new one.
Theming & customization — CSS variables
Directory Pro uses Flux UI +
Tailwind 4 with CSS variables. No PHP needed to re-theme — edit the @theme block in resources/css/app.css
or generate a full theme visually.
Generate a theme at fluxui.dev/themes
- Open https://fluxui.dev/themes.
- Pick your palette, radius, fonts and dark-mode variant visually.
- Copy the generated CSS — it's a drop-in
@theme { … }snippet with all Flux variables. - Replace the
@theme+@layer themeblocks inresources/css/app.cssand rebuild.
# after editing resources/css/app.css
npm run build # production
npm run dev # HMR while tweaking
Current variables (starter palette)
File: resources/css/app.css:11
@theme {
--font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif;
/* map zinc → slate for a cooler neutral */
--color-zinc-50: var(--color-slate-50);
--color-zinc-100: var(--color-slate-100);
/* ... zinc-200 → slate-200 through zinc-950 → slate-950 */
--color-accent: var(--color-indigo-500);
--color-accent-content: var(--color-indigo-600);
--color-accent-foreground: var(--color-white);
}
@layer theme {
.dark {
--color-accent: var(--color-indigo-500);
--color-accent-content: var(--color-indigo-300);
--color-accent-foreground: var(--color-white);
}
}
Quick tweaks
- Change accent:
--color-accent→ e.g.--color-emerald-500 - Warmer neutral: map
--color-zinc-*to--color-stone-*orzinc-*itself - Font: change
--font-sans+ updatevite.config.js:fonts
Pro tip
Flux tokens like --color-accent cascade
to every <flux:button
variant="primary">, badge, and input ring — one variable recolors the whole
app.
Also see resources/views/components/app-logo.blade.php
for the mark — swap the SVG or set config('app.name') via
APP_NAME.
Troubleshooting
Vite manifest not found
npm run build locally,
or ensure public/build was SCP'd
during deploy. Check vite.config.js inputs.
403 / storage not linked
php artisan
storage:link and ensure storage/
bootstrap/cache are chgrp www-data + chmod
ug+rwx. Verify Nginx user is www-data.Queue not processing
QUEUE_CONNECTION=database,
run supervisorctl
status, tail the log. Ensure php artisan migrate
created jobs table.
Deploy fails on git clone
GIT_REMOTE var and that
the VPS SSH key is authorized on GitHub (deploy key or machine user). Test with sudo -u deploy git ls-remote
$GIT_REMOTE.