Skip to content

MTEXdotDev/statichtmlsites

Repository files navigation

StaticHTMLSites

A Laravel 12 application for hosting static HTML mini-sites. Each page is accessible via subdomain ({slug}.statichtmlsites.mtex.dev) and path URL (statichtmlsites.mtex.dev/{slug}/...).


Tech Stack

Layer Technology
Backend Laravel 12, PHP 8.2+
Frontend Tailwind CSS 3, Alpine.js 3
Editor CodeMirror 6 (npm)
Build Vite 5 + laravel-vite-plugin
Auth Laravel Breeze (blade stack)
Database SQLite (default) or MySQL
Storage Local filesystem

Installation

# 1. Install PHP + JS dependencies
composer install
npm install

# 2. Environment
cp .env.example .env
php artisan key:generate

# 3. Migrate
php artisan migrate

# 4. Install Breeze auth scaffolding
php artisan breeze:install blade
php artisan migrate

# 5. Build assets
npm run build

Key .env variables:

Variable Value
APP_URL https://statichtmlsites.mtex.dev
APP_BASE_DOMAIN statichtmlsites.mtex.dev
MAX_UPLOAD_MB 50

Wildcard Subdomain (DNS + Nginx)

DNS:

*.statichtmlsites.mtex.dev  CNAME  statichtmlsites.mtex.dev

Nginx:

server {
    listen 443 ssl;
    server_name statichtmlsites.mtex.dev *.statichtmlsites.mtex.dev;
    ssl_certificate     /path/to/wildcard.crt;
    ssl_certificate_key /path/to/wildcard.key;
    root /var/www/statichtmlsites/public;
    index index.php;
    location / { try_files $uri $uri/ /index.php?$query_string; }
    location ~ \.php$ {
        fastcgi_pass unix:/var/run/php/php8.3-fpm.sock;
        fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
        include fastcgi_params;
    }
}

config/filesystems.php — add to the returned array:

'max_upload_mb' => env('MAX_UPLOAD_MB', 50),

Project Structure

app/
  Http/Controllers/
    DashboardController.php        Dashboard page
    FileManagerController.php      File CRUD JSON API
    PageController.php             Create / update / delete pages
    PageServeController.php        Serve static files + <base> injection
  Models/
    Page.php                       ULID model, storage helpers, slug uniqueness
    User.php                       Breeze user + pages() HasMany
  Policies/PagePolicy.php          Ownership checks
  Providers/AppServiceProvider.php Register FileManagerService singleton + policy
  Services/FileManagerService.php  All file ops: tree, read, save, upload, delete, rename

resources/
  css/app.css                      Tailwind + CodeMirror overrides
  js/
    app.js                         Global entry (Alpine on non-manager pages)
    file-manager.js                CodeMirror 6 + Alpine file-tree component
  views/
    layouts/app.blade.php          App shell with nav
    welcome.blade.php              Marketing landing
    pages/
      dashboard.blade.php          Page grid
      create.blade.php             New page form
      manager.blade.php            Full-screen file manager

database/migrations/
  ..._create_pages_table.php

routes/web.php                     All routes + subdomain group

Key Design Decisions

  • <base> injectionPageServeController detects subdomain vs. path access and injects <base href="..."> into every HTML file before serving, keeping relative asset paths correct under both URL shapes.
  • Path traversal guardFileManagerService::guard() and PageServeController both resolve real paths and assert they sit inside the page's storage root.
  • Storage isolationstorage/app/pages/{slug}/. Directories are auto-created on page creation, renamed on slug change, and deleted on page deletion via Eloquent model events.
  • File tree — Pure PHP recursive directory scan returned as JSON, rendered by an Alpine component with inline HTML strings.

License

MIT

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors