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

  1. Entry pointartisan boots Laravel. The HTTP request enters app/Http/Kernel.php, which registers the middleware stack (app/Http/Middleware/*).
  2. Routing – routes are defined in routes/web.php (not listed but standard). Requests to / hit HomeController@show, which loads data via the Link model (app/Models/Link.php) and returns resources/views/home.blade.php.
  3. Admin flow – Authenticated admin routes invoke AdminController methods that call LinkTypeController, UserController, etc., each persisting changes through Eloquent models (app/Models/*).
  4. Asset pipeline – The Blade layout includes assets/js/app.js and the compiled CSS under assets/css/. app/js/app.js imports 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.
  5. Database interaction – All persistence goes through Laravel’s Eloquent ORM; the only database files are in database/ (migrations, seeds). No raw SQL appears.
  6. Key hotspotsapp/Http/Livewire/UserTable.php and the installer view have the deepest nesting (8 levels) and therefore the highest cognitive load. The JavaScript button editor (assets/button-editor/js/jquery.js and jquery-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.env is 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 filesassets/button-editor/js/jquery.js and jquery-1.7.js exceed 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_TOKENrelease.yml lacks explicit permissions; add contents: read.
  • Medium – No dependency vulnerability scan – Add dependency-review-action or osv-scanner to the CI.
  • Medium – No pre‑commit secret gate – Install a pre‑commit hook that scans for secrets.
  • Low – Missing job timeouts – Add timeout-minutes to 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.