commit c29badf6b73c6423742f25d93d46544b4261133d Author: Snider Date: Mon Jan 26 23:24:57 2026 +0000 Initial commit diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..8e11abd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,24 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +indent_size = 4 +indent_style = space +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false + +[*.{yml,yaml}] +indent_size = 2 + +[*.{js,jsx,ts,tsx,vue}] +indent_size = 2 + +[*.json] +indent_size = 2 + +[docker-compose.yml] +indent_size = 2 diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..01b4da4 --- /dev/null +++ b/.env.example @@ -0,0 +1,76 @@ +APP_NAME="Core PHP App" +APP_ENV=local +APP_KEY= +APP_DEBUG=true +APP_TIMEZONE=UTC +APP_URL=http://localhost + +APP_LOCALE=en_GB +APP_FALLBACK_LOCALE=en_GB +APP_FAKER_LOCALE=en_GB + +APP_MAINTENANCE_DRIVER=file + +BCRYPT_ROUNDS=12 + +LOG_CHANNEL=stack +LOG_STACK=single +LOG_DEPRECATIONS_CHANNEL=null +LOG_LEVEL=debug + +DB_CONNECTION=sqlite +# DB_HOST=127.0.0.1 +# DB_PORT=3306 +# DB_DATABASE=core +# DB_USERNAME=root +# DB_PASSWORD= + +SESSION_DRIVER=database +SESSION_LIFETIME=120 +SESSION_ENCRYPT=false +SESSION_PATH=/ +SESSION_DOMAIN=null + +BROADCAST_CONNECTION=log +FILESYSTEM_DISK=local +QUEUE_CONNECTION=database + +CACHE_STORE=database +CACHE_PREFIX= + +MEMCACHED_HOST=127.0.0.1 + +REDIS_CLIENT=phpredis +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_MAILER=log +MAIL_HOST=127.0.0.1 +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null +MAIL_FROM_ADDRESS="hello@example.com" +MAIL_FROM_NAME="${APP_NAME}" + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= +AWS_USE_PATH_STYLE_ENDPOINT=false + +VITE_APP_NAME="${APP_NAME}" + +# Core PHP Framework +CORE_CACHE_DISCOVERY=true + +# CDN Configuration (optional) +CDN_ENABLED=false +CDN_DRIVER=bunny +BUNNYCDN_API_KEY= +BUNNYCDN_STORAGE_ZONE= +BUNNYCDN_PULL_ZONE= + +# Flux Pro (optional) +FLUX_LICENSE_KEY= diff --git a/.gemini/settings.json b/.gemini/settings.json new file mode 100644 index 0000000..4e613e9 --- /dev/null +++ b/.gemini/settings.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://schema.gemini.google.dev/settings.json", + "codeAssist": { + "enabled": true, + "contextFiles": [ + "GEMINI.md", + "CLAUDE.md", + "composer.json", + "config/core.php" + ] + } +} diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..fcb21d3 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,11 @@ +* text=auto eol=lf + +*.blade.php diff=html +*.css diff=css +*.html diff=html +*.md diff=markdown +*.php diff=php + +/.github export-ignore +CHANGELOG.md export-ignore +.styleci.yml export-ignore diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..fdd4114 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,4 @@ +# GitHub Sponsors configuration +# Support Trees for the Future through our fundraiser + +custom: ["https://donate.trees.org/-/NPMMSVUP?member=SWZTDDWH"] diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md new file mode 100644 index 0000000..69bfe03 --- /dev/null +++ b/.github/copilot-instructions.md @@ -0,0 +1,83 @@ +# GitHub Copilot Instructions + +## Project Overview + +This is a Laravel application using the Core PHP Framework - a modular monolith architecture with event-driven module registration. + +## Architecture + +### Module System +- Modules live in `app/Mod/{ModuleName}/` +- Each module has a `Boot.php` class with event listeners +- Events: `WebRoutesRegistering`, `ApiRoutesRegistering`, `AdminPanelBooting` + +### Example Boot.php +```php + 'onWebRoutes', + ApiRoutesRegistering::class => 'onApiRoutes', + ]; + + public function onWebRoutes(WebRoutesRegistering $event): void + { + $event->routes(fn() => require __DIR__.'/Routes/web.php'); + $event->views('blog', __DIR__.'/Views'); + } +} +``` + +## Coding Standards + +### Language +- Use UK English (colour, organisation, centre, behaviour) +- No American spellings (color, organization, center, behavior) + +### PHP Style +- PSR-12 with Laravel conventions +- Strict types: `declare(strict_types=1);` +- Type hints on all parameters and return types +- Final classes by default unless inheritance is intended + +### Naming +- Models: singular PascalCase (`Post`, `Comment`) +- Tables: plural snake_case (`posts`, `comments`) +- Controllers: `{Model}Controller` +- Livewire: `{Feature}Page`, `{Feature}Modal` + +### Testing +- Use Pest, not PHPUnit directly +- Feature tests for HTTP/Livewire +- Unit tests for services/utilities + +## UI Framework + +- **Livewire 3** for reactive components +- **Flux Pro** for UI components (not vanilla Alpine) +- **Tailwind CSS** for styling +- **Font Awesome Pro** for icons (not Heroicons) + +## Key Packages + +| Package | Purpose | +|---------|---------| +| `host-uk/core` | Core framework, events, modules | +| `host-uk/core-admin` | Admin panel, modals | +| `host-uk/core-api` | REST API, rate limiting | +| `host-uk/core-mcp` | AI agent tools (MCP) | + +## Don't + +- Don't use Heroicons (use Font Awesome Pro) +- Don't use vanilla Alpine components (use Flux Pro) +- Don't create controllers for Livewire pages +- Don't use American English spellings +- Don't add unnecessary abstractions diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..6867d41 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,29 @@ +version: 2 + +updates: + - package-ecosystem: composer + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + labels: + - dependencies + - php + + - package-ecosystem: npm + directory: / + schedule: + interval: weekly + open-pull-requests-limit: 5 + labels: + - dependencies + - javascript + + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly + open-pull-requests-limit: 3 + labels: + - dependencies + - ci diff --git a/.github/package-workflows/README.md b/.github/package-workflows/README.md new file mode 100644 index 0000000..999966f --- /dev/null +++ b/.github/package-workflows/README.md @@ -0,0 +1,62 @@ +# Package Workflows + +These workflow templates are for **library packages** (host-uk/core, host-uk/core-api, etc.), not application projects. + +## README Badges + +Add these badges to your package README (replace `{package}` with your package name): + +```markdown +[![CI](https://github.com/host-uk/{package}/actions/workflows/ci.yml/badge.svg)](https://github.com/host-uk/{package}/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/host-uk/{package}/graph/badge.svg)](https://codecov.io/gh/host-uk/{package}) +[![Latest Version](https://img.shields.io/packagist/v/host-uk/{package})](https://packagist.org/packages/host-uk/{package}) +[![PHP Version](https://img.shields.io/packagist/php-v/host-uk/{package})](https://packagist.org/packages/host-uk/{package}) +[![License](https://img.shields.io/badge/License-EUPL--1.2-blue.svg)](LICENSE) +``` + +## Usage + +Copy the relevant workflows to your library's `.github/workflows/` directory: + +```bash +# In your library repo +mkdir -p .github/workflows +cp path/to/core-template/.github/package-workflows/ci.yml .github/workflows/ +cp path/to/core-template/.github/package-workflows/release.yml .github/workflows/ +``` + +## Workflows + +### ci.yml +- Runs on push/PR to main +- Tests against PHP 8.2, 8.3, 8.4 +- Tests against Laravel 11 and 12 +- Runs Pint linting +- Runs Pest tests + +### release.yml +- Triggers on version tags (v*) +- Generates changelog using git-cliff +- Creates GitHub release + +## Requirements + +For these workflows to work, your package needs: + +1. **cliff.toml** - Copy from core-template root +2. **Pest configured** - `composer require pestphp/pest --dev` +3. **Pint configured** - `composer require laravel/pint --dev` +4. **CODECOV_TOKEN** - Add to repo secrets for coverage uploads +5. **FUNDING.yml** - Copy `.github/FUNDING.yml` for sponsor button + +## Recommended composer.json scripts + +```json +{ + "scripts": { + "lint": "pint", + "test": "pest", + "test:coverage": "pest --coverage" + } +} +``` diff --git a/.github/package-workflows/ci.yml b/.github/package-workflows/ci.yml new file mode 100644 index 0000000..7c5f722 --- /dev/null +++ b/.github/package-workflows/ci.yml @@ -0,0 +1,55 @@ +# CI workflow for library packages (host-uk/core-*, etc.) +# Copy this to .github/workflows/ci.yml in library repos + +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + tests: + runs-on: ubuntu-latest + + strategy: + fail-fast: true + matrix: + php: [8.2, 8.3, 8.4] + laravel: [11.*, 12.*] + exclude: + - php: 8.2 + laravel: 12.* + + name: PHP ${{ matrix.php }} / Laravel ${{ matrix.laravel }} + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite + coverage: pcov + + - name: Install dependencies + run: | + composer require "laravel/framework:${{ matrix.laravel }}" --no-interaction --no-update + composer update --prefer-dist --no-interaction --no-progress + + - name: Run Pint + run: vendor/bin/pint --test + + - name: Run tests + run: vendor/bin/pest --ci --coverage --coverage-clover coverage.xml + + - name: Upload coverage to Codecov + if: matrix.php == '8.3' && matrix.laravel == '12.*' + uses: codecov/codecov-action@v4 + with: + files: coverage.xml + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} diff --git a/.github/package-workflows/release.yml b/.github/package-workflows/release.yml new file mode 100644 index 0000000..035294e --- /dev/null +++ b/.github/package-workflows/release.yml @@ -0,0 +1,40 @@ +# Release workflow for library packages +# Copy this to .github/workflows/release.yml in library repos + +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + name: Create Release + + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Generate changelog + id: changelog + uses: orhun/git-cliff-action@v3 + with: + config: cliff.toml + args: --latest --strip header + env: + OUTPUT: CHANGELOG.md + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + body_path: CHANGELOG.md + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b37b20f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,68 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + tests: + if: github.event.repository.visibility == 'public' + runs-on: ubuntu-latest + + strategy: + fail-fast: true + matrix: + php: [8.2, 8.3, 8.4] + + name: PHP ${{ matrix.php }} + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php }} + extensions: dom, curl, libxml, mbstring, zip, pcntl, pdo, sqlite, pdo_sqlite + coverage: pcov + + - name: Install dependencies + run: composer install --prefer-dist --no-interaction --no-progress + + - name: Run Pint + run: vendor/bin/pint --test + + - name: Run tests + run: vendor/bin/pest --ci --coverage --coverage-clover coverage.xml + + - name: Upload coverage to Codecov + if: matrix.php == '8.3' + uses: codecov/codecov-action@v5 + with: + files: coverage.xml + fail_ci_if_error: false + token: ${{ secrets.CODECOV_TOKEN }} + + assets: + if: github.event.repository.visibility == 'public' + runs-on: ubuntu-latest + name: Assets + + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Build assets + run: npm run build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..da1ba48 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,38 @@ +name: Release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + release: + if: github.event.repository.visibility == 'public' + runs-on: ubuntu-latest + name: Create Release + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + + - name: Generate changelog + id: changelog + uses: orhun/git-cliff-action@v4 + with: + config: cliff.toml + args: --latest --strip header + env: + OUTPUT: CHANGELOG.md + + - name: Create release + uses: softprops/action-gh-release@v2 + with: + body_path: CHANGELOG.md + generate_release_notes: true + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..899ea82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +.DS_Store +/.phpunit.cache +/node_modules +/public/build +/public/hot +/public/storage +/storage/*.key +/storage/pail +/vendor +.env +.env.backup +.env.production +.phpactor.json +.phpunit.result.cache +Homestead.json +Homestead.yaml +auth.json +npm-debug.log +yarn-error.log +/.fleet +/.idea +/.nova +/.vscode +/.zed diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..79b4594 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,124 @@ +# AI Agent Instructions + +> For Jules, Devin, and other autonomous coding agents. + +## Quick Start + +1. This is a Laravel 12 + Livewire 3 application +2. Modules go in `app/Mod/{Name}/Boot.php` +3. Use UK English (colour, not color) +4. Run `vendor/bin/pint --dirty` before committing +5. Run `vendor/bin/pest` to test + +## Architecture + +**Modular monolith** - Features are self-contained modules that register via events. + +### Creating a Module + +```bash +php artisan make:mod {Name} --all +``` + +Or manually create `app/Mod/{Name}/Boot.php`: + +```php + 'onWebRoutes', + ]; + + public function onWebRoutes(WebRoutesRegistering $event): void + { + $event->routes(fn() => require __DIR__.'/Routes/web.php'); + $event->views('{name}', __DIR__.'/Views'); + } +} +``` + +## Task Checklist + +When implementing features: + +- [ ] Create module in `app/Mod/{Name}/` +- [ ] Add `Boot.php` with event listeners +- [ ] Create routes in `Routes/web.php` or `Routes/api.php` +- [ ] Create Livewire components in `Livewire/` +- [ ] Create Blade views in `Views/` +- [ ] Add migrations in `Migrations/` +- [ ] Write tests in `Tests/` +- [ ] Run `vendor/bin/pint --dirty` +- [ ] Run `vendor/bin/pest` + +## File Locations + +| What | Where | +|------|-------| +| Models | `app/Mod/{Name}/Models/` | +| Livewire | `app/Mod/{Name}/Livewire/` | +| Views | `app/Mod/{Name}/Views/` | +| Routes | `app/Mod/{Name}/Routes/` | +| Migrations | `app/Mod/{Name}/Migrations/` | +| Tests | `app/Mod/{Name}/Tests/` | +| Services | `app/Mod/{Name}/Services/` | + +## Critical Rules + +1. **UK English** - colour, organisation, centre (never American spellings) +2. **Strict types** - `declare(strict_types=1);` in every PHP file +3. **Type hints** - All parameters and return types +4. **Flux Pro** - Use Flux components, not vanilla Alpine +5. **Font Awesome** - Use FA icons, not Heroicons +6. **Pest** - Write tests using Pest syntax, not PHPUnit + +## Example Livewire Component + +```php + Post::latest()->paginate(10), + ]); + } +} +``` + +## Testing Example + +```php +count(3)->create(); + + $this->get('/blog') + ->assertOk() + ->assertSee($posts->first()->title); +}); +``` diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..138e17b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,66 @@ +# Core PHP Framework Project + +## Architecture + +Modular monolith using Core PHP Framework. Modules live in `app/Mod/{Name}/Boot.php`. + +**Event-driven registration:** +```php +class Boot +{ + public static array $listens = [ + WebRoutesRegistering::class => 'onWebRoutes', + ApiRoutesRegistering::class => 'onApiRoutes', + AdminPanelBooting::class => 'onAdminPanel', + ]; +} +``` + +## Commands + +```bash +composer run dev # Dev server (if configured) +php artisan serve # Laravel dev server +npm run dev # Vite +./vendor/bin/pint --dirty # Format changed files +php artisan test # All tests +php artisan make:mod Blog # Create module +``` + +## Module Structure + +``` +app/Mod/Blog/ +├── Boot.php # Event listeners +├── Models/ # Eloquent models +├── Routes/ +│ ├── web.php # Web routes +│ └── api.php # API routes +├── Views/ # Blade templates +├── Livewire/ # Livewire components +├── Migrations/ # Database migrations +└── Tests/ # Module tests +``` + +## Packages + +| Package | Purpose | +|---------|---------| +| `host-uk/core` | Core framework, events, module discovery | +| `host-uk/core-admin` | Admin panel, Livewire modals | +| `host-uk/core-api` | REST API, scopes, rate limiting, webhooks | +| `host-uk/core-mcp` | Model Context Protocol for AI agents | + +## Conventions + +- UK English (colour, organisation, centre) +- PSR-12 coding style (Laravel Pint) +- Pest for testing +- Livewire + Flux Pro for UI + +## License + +- `Core\` namespace and vendor packages: EUPL-1.2 (copyleft) +- `app/Mod/*`, `app/Website/*`: Your choice (no copyleft) + +See LICENSE for full details. diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 0000000..103179c --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,122 @@ +# Core PHP Framework - AI Assistant Context + +> For Gemini Code Assist, Jules, and other Google AI tools. + +## Project Type + +Laravel 12 application using Core PHP Framework - a modular monolith with event-driven architecture. + +## Directory Structure + +``` +app/ +├── Mod/ # Feature modules (your code) +│ └── {Name}/ +│ ├── Boot.php # Event listeners +│ ├── Models/ +│ ├── Routes/ +│ ├── Views/ +│ └── Livewire/ +├── Core/ # Local framework overrides (EUPL-1.2) +└── Providers/ + +config/core.php # Framework configuration +``` + +## Module Pattern + +Every module has a `Boot.php` with static `$listens` array: + +```php + 'onWebRoutes', + ApiRoutesRegistering::class => 'onApiRoutes', + AdminPanelBooting::class => 'onAdminPanel', + ]; + + public function onWebRoutes(WebRoutesRegistering $event): void + { + $event->routes(fn() => require __DIR__.'/Routes/web.php'); + $event->views('shop', __DIR__.'/Views'); + } + + public function onApiRoutes(ApiRoutesRegistering $event): void + { + $event->routes(fn() => require __DIR__.'/Routes/api.php'); + } + + public function onAdminPanel(AdminPanelBooting $event): void + { + $event->navigation('Shop', 'shop.admin.index', 'shopping-cart'); + } +} +``` + +## Code Style Requirements + +### Language: UK English +- colour (not color) +- organisation (not organization) +- centre (not center) +- behaviour (not behavior) +- licence (noun), license (verb) + +### PHP Standards +- `declare(strict_types=1);` in all files +- Full type hints (parameters + return types) +- PSR-12 formatting (use Laravel Pint) +- Pest for testing + +### Naming Conventions +| Type | Convention | Example | +|------|------------|---------| +| Model | Singular PascalCase | `Product` | +| Table | Plural snake_case | `products` | +| Controller | `{Model}Controller` | `ProductController` | +| Livewire Page | `{Feature}Page` | `ProductListPage` | +| Livewire Modal | `{Feature}Modal` | `EditProductModal` | + +## UI Stack + +- **Livewire 3** - Server-side reactivity +- **Flux Pro** - UI component library (NOT vanilla Alpine) +- **Tailwind CSS** - Utility-first styling +- **Font Awesome Pro** - Icons (NOT Heroicons) + +## Common Commands + +```bash +php artisan make:mod Blog --all # Create module with all features +php artisan serve # Development server +vendor/bin/pint --dirty # Format changed files +vendor/bin/pest # Run tests +``` + +## Packages + +| Package | Namespace | Purpose | +|---------|-----------|---------| +| host-uk/core | `Core\` | Framework core | +| host-uk/core-admin | `Core\Admin\` | Admin panel | +| host-uk/core-api | `Core\Api\` | REST API | +| host-uk/core-mcp | `Core\Mcp\` | AI agent tools | + +## Avoid + +- American English spellings +- Heroicons (use Font Awesome) +- Vanilla Alpine components (use Flux) +- Over-engineering / premature abstraction +- PHPUnit syntax (use Pest) diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5838b02 --- /dev/null +++ b/LICENSE @@ -0,0 +1,108 @@ +Core PHP Framework Template +=========================== + +Copyright (c) 2026 Host UK / Snider + +This project uses a dual-licensing approach: + + +1. CORE FRAMEWORK CODE (EUPL-1.2) +--------------------------------- + +The Core PHP Framework packages (host-uk/core, host-uk/core-admin, +host-uk/core-api, host-uk/core-mcp) and any code within the `Core\` +namespace are licensed under the European Union Public Licence v1.2 +(EUPL-1.2). + +The full EUPL-1.2 license text is available at: +https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12 + +And in the Core PHP Framework repository: +https://github.com/host-uk/core-php/blob/main/LICENSE + + +2. APPLICATION CODE EXCEPTION +----------------------------- + +As the copyright holder, we grant the following additional permission: + +Code you create within the following directories is EXEMPT from the +EUPL-1.2 copyleft requirements and may be licensed under any terms +you choose (including proprietary licenses): + + app/Mod/ - Your feature modules + app/Website/ - Your website/domain modules + app/Service/ - Your service classes + database/ - Your migrations, seeders, factories + resources/ - Your views, assets, translations + routes/ - Your route definitions + tests/ - Your test files + config/ - Your configuration files (except config/core.php) + +This exception applies ONLY to original code you create in these +directories. It does NOT apply to: + + - Modified copies of Core framework code + - Code that extends or overrides the `Core\` namespace + - Derivative works of the Core framework itself + + +3. CORE NAMESPACE MODIFICATIONS +------------------------------- + +If you create local overrides or extensions within the `Core\` namespace +(e.g., in `app/Core/`), those modifications ARE subject to EUPL-1.2 and +its copyleft requirements. + +We encourage you to submit such modifications as pull requests to: +https://github.com/host-uk/core-php + +This requirement constitutes the consideration for the license grant +herein. The Core PHP Framework is not provided without cost; rather, +the license fee is paid in kind through the contribution of improvements +to the community. By using this software, you acknowledge and agree that +modifications to the Core\ namespace shall be made available to the +community under the same terms, ensuring that all users benefit from +collective improvements. This reciprocal obligation is a fundamental +condition of the license grant and not merely a request. + + +4. SUMMARY +---------- + +┌─────────────────────────────────────────────────────────────────────┐ +│ Location │ License │ Copyleft Required? │ +├─────────────────────────────────────────────────────────────────────┤ +│ vendor/host-uk/* │ EUPL-1.2 │ Yes │ +│ app/Core/* │ EUPL-1.2 │ Yes (submit as PR) │ +│ Core\ namespace │ EUPL-1.2 │ Yes │ +├─────────────────────────────────────────────────────────────────────┤ +│ app/Mod/* │ Your choice │ No │ +│ app/Website/* │ Your choice │ No │ +│ app/Service/* │ Your choice │ No │ +│ database/* │ Your choice │ No │ +│ resources/* │ Your choice │ No │ +│ routes/* │ Your choice │ No │ +│ tests/* │ Your choice │ No │ +└─────────────────────────────────────────────────────────────────────┘ + + +5. WARRANTY DISCLAIMER +---------------------- + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. + + +6. QUESTIONS +------------ + +If you have questions about licensing, contact: hello@host.uk.com + +For the full EUPL-1.2 text, see: +https://github.com/host-uk/core-php/blob/main/LICENSE diff --git a/README.md b/README.md new file mode 100644 index 0000000..5db97d9 --- /dev/null +++ b/README.md @@ -0,0 +1,138 @@ +# Core PHP Framework Project + +[![CI](https://github.com/host-uk/core-template/actions/workflows/ci.yml/badge.svg)](https://github.com/host-uk/core-template/actions/workflows/ci.yml) +[![codecov](https://codecov.io/gh/host-uk/core-template/graph/badge.svg)](https://codecov.io/gh/host-uk/core-template) +[![PHP Version](https://img.shields.io/packagist/php-v/host-uk/core-template)](https://packagist.org/packages/host-uk/core-template) +[![Laravel](https://img.shields.io/badge/Laravel-12.x-FF2D20?logo=laravel)](https://laravel.com) +[![License](https://img.shields.io/badge/License-EUPL--1.2-blue.svg)](LICENSE) + +A modular monolith Laravel application built with Core PHP Framework. + +## Features + +- **Core Framework** - Event-driven module system with lazy loading +- **Admin Panel** - Livewire-powered admin interface with Flux UI +- **REST API** - Scoped API keys, rate limiting, webhooks, OpenAPI docs +- **MCP Tools** - Model Context Protocol for AI agent integration + +## Requirements + +- PHP 8.2+ +- Composer 2.x +- SQLite (default) or MySQL/PostgreSQL +- Node.js 18+ (for frontend assets) + +## Installation + +```bash +# Clone or create from template +git clone https://github.com/host-uk/core-template.git my-project +cd my-project + +# Install dependencies +composer install +npm install + +# Configure environment +cp .env.example .env +php artisan key:generate + +# Set up database +touch database/database.sqlite +php artisan migrate + +# Start development server +php artisan serve +``` + +Visit: http://localhost:8000 + +## Project Structure + +``` +app/ +├── Console/ # Artisan commands +├── Http/ # Controllers & Middleware +├── Models/ # Eloquent models +├── Mod/ # Your custom modules +└── Providers/ # Service providers + +config/ +└── core.php # Core framework configuration + +routes/ +├── web.php # Public web routes +├── api.php # REST API routes +└── console.php # Artisan commands +``` + +## Creating Modules + +```bash +# Create a new module with all features +php artisan make:mod Blog --all + +# Create module with specific features +php artisan make:mod Shop --web --api --admin +``` + +Modules follow the event-driven pattern: + +```php + 'onWebRoutes', + ApiRoutesRegistering::class => 'onApiRoutes', + AdminPanelBooting::class => 'onAdminPanel', + ]; + + public function onWebRoutes(WebRoutesRegistering $event): void + { + $event->routes(fn() => require __DIR__.'/Routes/web.php'); + $event->views('blog', __DIR__.'/Views'); + } +} +``` + +## Core Packages + +| Package | Description | +|---------|-------------| +| `host-uk/core` | Core framework components | +| `host-uk/core-admin` | Admin panel & Livewire modals | +| `host-uk/core-api` | REST API with scopes & webhooks | +| `host-uk/core-mcp` | Model Context Protocol tools | + +## Flux Pro (Optional) + +This template uses the free Flux UI components. If you have a Flux Pro license: + +```bash +# Configure authentication +composer config http-basic.composer.fluxui.dev your-email your-license-key + +# Add the repository +composer config repositories.flux-pro composer https://composer.fluxui.dev + +# Install Flux Pro +composer require livewire/flux-pro +``` + +## Documentation + +- [Core PHP Framework](https://github.com/host-uk/core-php) +- [Getting Started Guide](https://host-uk.github.io/core-php/guide/) +- [Architecture](https://host-uk.github.io/core-php/architecture/) + +## License + +EUPL-1.2 (European Union Public Licence) diff --git a/app/Http/Controllers/.gitkeep b/app/Http/Controllers/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/Mod/.gitkeep b/app/Mod/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/Models/.gitkeep b/app/Models/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php new file mode 100644 index 0000000..452e6b6 --- /dev/null +++ b/app/Providers/AppServiceProvider.php @@ -0,0 +1,24 @@ +handleCommand(new ArgvInput); + +exit($status); diff --git a/bootstrap/app.php b/bootstrap/app.php new file mode 100644 index 0000000..4687853 --- /dev/null +++ b/bootstrap/app.php @@ -0,0 +1,26 @@ +withProviders([ + // Core PHP Framework + \Core\LifecycleEventProvider::class, + \Core\Website\Boot::class, + \Core\Front\Boot::class, + \Core\Mod\Boot::class, + ]) + ->withRouting( + web: __DIR__.'/../routes/web.php', + api: __DIR__.'/../routes/api.php', + commands: __DIR__.'/../routes/console.php', + health: '/up', + ) + ->withMiddleware(function (Middleware $middleware) { + \Core\Front\Boot::middleware($middleware); + }) + ->withExceptions(function (Exceptions $exceptions) { + // + })->create(); diff --git a/bootstrap/cache/.gitignore b/bootstrap/cache/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/bootstrap/cache/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/bootstrap/providers.php b/bootstrap/providers.php new file mode 100644 index 0000000..38b258d --- /dev/null +++ b/bootstrap/providers.php @@ -0,0 +1,5 @@ + +""" +trim = true + +[git] +conventional_commits = true +filter_unconventional = true +split_commits = false +commit_parsers = [ + { message = "^feat", group = "Features" }, + { message = "^fix", group = "Bug Fixes" }, + { message = "^doc", group = "Documentation" }, + { message = "^perf", group = "Performance" }, + { message = "^refactor", group = "Refactor" }, + { message = "^style", group = "Styling" }, + { message = "^test", group = "Testing" }, + { message = "^chore\\(release\\)", skip = true }, + { message = "^chore\\(deps.*\\)", skip = true }, + { message = "^chore|^ci", group = "Miscellaneous" }, + { body = ".*security", group = "Security" }, +] +protect_breaking_commits = false +filter_commits = false +topo_order = false +sort_commits = "oldest" diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..5cdb126 --- /dev/null +++ b/composer.json @@ -0,0 +1,78 @@ +{ + "name": "host-uk/core-template", + "type": "project", + "description": "Core PHP Framework - Project Template", + "keywords": ["laravel", "core-php", "modular", "framework", "template"], + "license": "EUPL-1.2", + "require": { + "php": "^8.2", + "laravel/framework": "^12.0", + "laravel/tinker": "^2.10", + "livewire/flux": "^2.0", + "livewire/livewire": "^3.0", + "host-uk/core": "dev-main", + "host-uk/core-admin": "dev-main", + "host-uk/core-api": "dev-main", + "host-uk/core-mcp": "dev-main" + }, + "require-dev": { + "fakerphp/faker": "^1.23", + "laravel/pail": "^1.2", + "laravel/pint": "^1.18", + "laravel/sail": "^1.41", + "mockery/mockery": "^1.6", + "nunomaduro/collision": "^8.6", + "pestphp/pest": "^3.0", + "pestphp/pest-plugin-laravel": "^3.0" + }, + "autoload": { + "psr-4": { + "App\\": "app/", + "Database\\Factories\\": "database/factories/", + "Database\\Seeders\\": "database/seeders/" + } + }, + "autoload-dev": { + "psr-4": { + "Tests\\": "tests/" + } + }, + "repositories": [ + { + "type": "vcs", + "url": "https://github.com/host-uk/core-php.git" + } + ], + "scripts": { + "post-autoload-dump": [ + "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", + "@php artisan package:discover --ansi" + ], + "post-update-cmd": [ + "@php artisan vendor:publish --tag=laravel-assets --ansi --force" + ], + "post-root-package-install": [ + "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" + ], + "post-create-project-cmd": [ + "@php artisan key:generate --ansi", + "@php -r \"file_exists('database/database.sqlite') || touch('database/database.sqlite');\"", + "@php artisan migrate --graceful --ansi" + ] + }, + "extra": { + "laravel": { + "dont-discover": [] + } + }, + "config": { + "optimize-autoloader": true, + "preferred-install": "dist", + "sort-packages": true, + "allow-plugins": { + "php-http/discovery": true + } + }, + "minimum-stability": "stable", + "prefer-stable": true +} diff --git a/config/core.php b/config/core.php new file mode 100644 index 0000000..06502fa --- /dev/null +++ b/config/core.php @@ -0,0 +1,24 @@ + [ + app_path('Core'), + app_path('Mod'), + app_path('Website'), + ], + + 'services' => [ + 'cache_discovery' => env('CORE_CACHE_DISCOVERY', true), + ], + + 'cdn' => [ + 'enabled' => env('CDN_ENABLED', false), + 'driver' => env('CDN_DRIVER', 'bunny'), + ], +]; diff --git a/database/factories/.gitkeep b/database/factories/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database/migrations/.gitkeep b/database/migrations/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/database/seeders/DatabaseSeeder.php b/database/seeders/DatabaseSeeder.php new file mode 100644 index 0000000..df6818f --- /dev/null +++ b/database/seeders/DatabaseSeeder.php @@ -0,0 +1,16 @@ + + + + + tests/Unit + + + tests/Feature + + + + + app + + + + + + + + + + + + + + + + diff --git a/postcss.config.js b/postcss.config.js new file mode 100644 index 0000000..49c0612 --- /dev/null +++ b/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..3aec5e2 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,21 @@ + + + Options -MultiViews -Indexes + + + RewriteEngine On + + # Handle Authorization Header + RewriteCond %{HTTP:Authorization} . + RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] + + # Redirect Trailing Slashes If Not A Folder... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_URI} (.+)/$ + RewriteRule ^ %1 [L,R=301] + + # Send Requests To Front Controller... + RewriteCond %{REQUEST_FILENAME} !-d + RewriteCond %{REQUEST_FILENAME} !-f + RewriteRule ^ index.php [L] + diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..947d989 --- /dev/null +++ b/public/index.php @@ -0,0 +1,17 @@ +handleRequest(Request::capture()); diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 0000000..eb05362 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,2 @@ +User-agent: * +Disallow: diff --git a/resources/css/app.css b/resources/css/app.css new file mode 100644 index 0000000..b5c61c9 --- /dev/null +++ b/resources/css/app.css @@ -0,0 +1,3 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; diff --git a/resources/js/app.js b/resources/js/app.js new file mode 100644 index 0000000..e59d6a0 --- /dev/null +++ b/resources/js/app.js @@ -0,0 +1 @@ +import './bootstrap'; diff --git a/resources/js/bootstrap.js b/resources/js/bootstrap.js new file mode 100644 index 0000000..953d266 --- /dev/null +++ b/resources/js/bootstrap.js @@ -0,0 +1,3 @@ +import axios from 'axios'; +window.axios = axios; +window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; diff --git a/resources/views/welcome.blade.php b/resources/views/welcome.blade.php new file mode 100644 index 0000000..88808ac --- /dev/null +++ b/resources/views/welcome.blade.php @@ -0,0 +1,65 @@ + + + + + + Core PHP Framework + + + +
+

Core PHP Framework

+

Laravel {{ Illuminate\Foundation\Application::VERSION }} | PHP {{ PHP_VERSION }}

+ +
+ + diff --git a/routes/api.php b/routes/api.php new file mode 100644 index 0000000..15fbf70 --- /dev/null +++ b/routes/api.php @@ -0,0 +1,5 @@ +