The Problem
Teams that need a self‑hosted “link‑in‑bio” page must cobble together static pages, third‑party widgets, or pay SaaS services. Maintaining a consistent look, handling user accounts, and adding new links quickly becomes a manual, error‑prone process.
What This Does
LinkStack is a Laravel‑based web app that delivers a multi‑link profile page with a built‑in admin UI. Core PHP logic lives under app/ – e.g. app/Http/Controllers/HomeController.php renders the public page, while app/Http/Controllers/AdminController.php and app/Http/Controllers/InstallerController.php manage site configuration and user onboarding.
Static assets (styles, scripts, icons) are stored in assets/. The main JavaScript bundle is assets/js/app.js; it pulls in the button editor (assets/button-editor/js/*.js) and FullCalendar plugins (assets/vendor/fullcalendar/*/package.json). Blade templates in resources/views/ drive the HTML, with resources/views/layouts/installing.blade.php showing the installer UI.
How It Is Wired
- Entry point –
artisanboots Laravel. The HTTP request entersapp/Http/Kernel.php, which registers the middleware stack (app/Http/Middleware/*). - Routing – routes are defined in
routes/web.php(not listed but standard). Requests to/hitHomeController@show, which loads data via theLinkmodel (app/Models/Link.php) and returnsresources/views/home.blade.php. - Admin flow – Authenticated admin routes invoke
AdminControllermethods that callLinkTypeController,UserController, etc., each persisting changes through Eloquent models (app/Models/*). - Asset pipeline – The Blade layout includes
assets/js/app.jsand the compiled CSS underassets/css/.app/js/app.jsimports a single module (resources/js/app) and then loads the FullCalendar core (assets/vendor/fullcalendar/core/main.js). The import graph shows 289 internal modules with only one edge, so the JavaScript side is shallowly coupled. - Database interaction – All persistence goes through Laravel’s Eloquent ORM; the only database files are in
database/(migrations, seeds). No raw SQL appears. - Key hotspots –
app/Http/Livewire/UserTable.phpand the installer view have the deepest nesting (8 levels) and therefore the highest cognitive load. The JavaScript button editor (assets/button-editor/js/jquery.jsandjquery-1.7.js) are oversized (≈6.5 k lines each) and contain duplicated blocks, making any change risky.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/LinkStack.git
cd LinkStack
# Install PHP dependencies (composer.json is present in the upstream but omitted here)
composer install
# Install front‑end packages that have their own package.json
cd assets/vendor/fullcalendar/bootstrap && npm ci && cd - >/dev/null
# repeat for core, daygrid, google-calendar, interaction, list, luxon if needed
# Copy environment template and generate an app key
cp .env.example .env
php artisan key:generate
# Run migrations
php artisan migrate --seed
# Start the development server
php artisan serve
The application expects a MySQL (or compatible) database; connection details are read from .env (DB_HOST, DB_DATABASE, DB_USERNAME, DB_PASSWORD). No Dockerfile is provided, so containerisation must be added manually.
Real‑World Use
A marketing team deploys the app on a cheap VPS, points the domain links.mybrand.com to it, and creates individual user accounts for each brand ambassador. Each ambassador logs in, adds their social URLs via the admin UI, and the public page (https://links.mybrand.com/username) automatically pulls the data from the links table and renders it with the selected theme from themes/.
Code Health & Issues
- High – .env tracked –
.envis version‑controlled despite being listed in.gitignore. Remove it (git rm --cached .env) and rotate all credentials. - High – GitHub Action not pinned – Workflow uses
shivammathur/setup-php@v2; replace with a SHA to avoid supply‑chain drift. - High – No test suite – 547 source files have zero automated tests; add at least one PHPUnit test per controller and a CI step.
- High – Duplicate code – 944 identical 6‑line blocks spread across 93 files (e.g.,
app/Http/Controllers/AdminController.php,assets/button-editor/js/jquery.js). Extract to shared helpers. - High – Oversized JS files –
assets/button-editor/js/jquery.jsandjquery-1.7.jsexceed 6 k lines; split by responsibility. - Medium – Empty catch blocks – Silent
catch {}in the same oversized JS files; log or rethrow errors. - Medium – Least‑privilege GITHUB_TOKEN –
release.ymllacks explicit permissions; addcontents: read. - Medium – No dependency vulnerability scan – Add
dependency-review-actionorosv-scannerto the CI. - Medium – No pre‑commit secret gate – Install a pre‑commit hook that scans for secrets.
- Low – Missing job timeouts – Add
timeout-minutesto each workflow job.
Additional observations: the repo ships a full set of front‑end assets but lacks a central package.json; asset builds are therefore manual. No Dockerfile means container builds must be custom. The licence file is present, satisfying open‑source compliance.
The Bottom Line
LinkStack provides a functional self‑hosted link‑tree replacement with a Laravel back‑end and a rich set of front‑end assets. However, the codebase suffers from duplicated, oversized JavaScript, deep nesting in key PHP files, and serious hygiene issues (tracked .env, missing tests, unpinned CI actions). It is usable for small teams that can allocate time to refactor and add proper testing, but larger organizations should consider addressing the health findings before relying on it in production.