From 2351daca070bc801509e9c13094500eddca87985 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 1 Nov 2026 22:20:50 +0000 Subject: [PATCH] README: bring the general sections up to date with the new features The per-feature sections were written with each feature, but the overview parts still described an earlier version. Updated: the title (PHP 8.2+), the table of contents (Tables and CRUD generator were missing), "What's included" (CRUD generator, DataTables, login security and audit log, accounts and e-mail, menu and page builder, installer, scaling), the Authentication section (no longer "no password reset / OAuth"; the seeded account is super_admin), the project structure (new libraries, modules, config, src folders), the console commands (mailtest, migrate lock, prune of tokens), Docker (Redis profile), Roles (module permissions, pages.html), the Turnstile forms list and the troubleshooting entries (login blocking, TRUSTED_PROXIES, exports, account links, pages and menus). Co-Authored-By: Claude Sonnet 5.5 Claude-Session: https://claude.ai/code/session_01EYbe92Bns1mKdy23b7txT8 --- README.md | 101 +++++++++++++++++++++++++++++++++++------------------- 1 file changed, 65 insertions(+), 36 deletions(-) diff --git a/README.md b/README.md index f737552..0eebef7 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CodeIgniter 3 Starter — PHP 8.4+, HMVC, DevelBar +# CodeIgniter 3 Starter — PHP 8.2+, HMVC, DevelBar ![CI](https://github.com/godzam/codeigniter3-new/actions/workflows/ci.yml/badge.svg) ![PHP](https://img.shields.io/badge/PHP-8.2%E2%80%938.5-777bb4) @@ -19,6 +19,8 @@ If you've ever done a fresh CodeIgniter 3 install on PHP 8.2+ and immediately se - [Authentication](#authentication) - [Accounts & e-mail (verification, password reset, Google)](#accounts--e-mail-verification-password-reset-google) - [Roles & permissions](#roles--permissions) +- [Tables (DataTables, server-side)](#tables-datatables-server-side) +- [CRUD generator](#crud-generator) - [Menu builder & page builder](#menu-builder--page-builder) - [Login security & audit log](#login-security--audit-log) - [Exports](#exports) @@ -43,18 +45,25 @@ If you've ever done a fresh CodeIgniter 3 install on PHP 8.2+ and immediately se | **HMVC (Modular Extensions)** | Instead of one flat `application/controllers` + `application/models` + `application/views`, group each feature into its own self-contained module: `application/modules/blog/{controllers,models,views}`. Modules can even call each other. A working example ships in `application/modules/example/`. | | **Bootstrap 5 template & theming** | Public, auth, and admin layouts (Bootstrap 5.3, Bootstrap Icons, SweetAlert2 — all vendored, no CDN). An admin **Settings** page controls the primary/secondary colors, light/dark/auto mode, corner roundness, font, sidebar and navbar style, app name and logo — stored in the database, with a live preview. | | **Roles & permissions (RBAC)** | Create, edit and delete roles from the admin UI, and tick which permissions each one holds. Permissions are declared per module (`module.action`) and checked with `can()` / `require_permission()`. `super_admin` is the built-in top role: every permission, can't be edited, deleted or created again. | +| **CRUD generator** | Admin → Generator (super admin) turns a form into a working module: a table, a server-side list, add/edit in a modal, delete, per-module permissions and a menu entry. Choose which of add / edit / delete the module has, per-field validation, uploads with compression, and optional CSV / Excel / PDF export. | +| **Server-side DataTables** | Search, sort and paging happen in SQL, mobile first, counting capped for big tables. One helper for every list. | +| **Login security & audit log** | 5 wrong passwords block the IP address and the account for 1 hour, 3 blocks in a row for 1 day; admins (or the console) lift blocks. Every add / edit / delete, sign-in and block is written to a read-only audit log with the content, secrets hidden. | +| **Accounts & e-mail** | Optional e-mail verification, "Forgot password?" and Sign in with Google (OpenID Connect with PKCE), all off until switched on in Settings. One-time links are random and stored hashed. | +| **Menu builder & page builder** | Build the admin sidebar (links, dropdowns, sections, an icon picker) and public pages from blocks at `/page/{address}`, without editing code. Text is sanitised; raw HTML needs its own permission. | +| **One-click installer** | `/install` creates the database, runs the migrations, saves the settings and the first super admin. | +| **Built to grow** | Shared sessions and cache (database or Redis), real client IP behind a load balancer, `/health/ready`, a migration lock, log retention (`console prune`), stderr logging, and limits that keep big exports and lists cheap. See [Big data & several servers](#big-data--several-servers). | | **Cloudflare Turnstile** | Optional CAPTCHA on the login and register forms, switched on and configured from Settings → Security (site key, secret key, per-form switches, widget theme and size). Fails closed if Cloudflare can't be reached. | -| **Authentication** | Email/password register, login, and logout (`application/modules/auth`), password hashing, session-backed `is_logged_in()`/`require_login()`/`require_role()` helpers, and per-IP login rate limiting. Not a full framework — a real, working starting point for your own auth. | +| **Authentication** | Email/password register, login, and logout (`application/modules/auth`), password hashing, session-backed `is_logged_in()`/`require_login()`/`require_role()` helpers, and login blocking per IP address and per account. Not a full framework — a real, working starting point for your own auth. | | **Database migrations & seeders** | CI3's built-in `Migration` library, enabled and wired to a CLI runner, plus a small seeder pattern CI3 doesn't ship with (`application/seeds/`). | | **DevelBar** | A toolbar docked to the bottom of the page (development only) showing benchmarks, database queries, session data, loaded config, and a per-view memory breakdown. | | **`.env` support** | Optional — every setting that reads from it has a hardcoded fallback, so nothing breaks if `.env` doesn't exist. | -| **Security defaults** | CSRF protection on, a conservative security-headers hook (CSP, X-Frame-Options, etc.), rate limiting available for any endpoint that needs it. | +| **Security defaults** | CSRF protection on, a conservative security-headers hook (CSP, X-Frame-Options, etc.), rate limiting available for any endpoint that needs it (shared across servers with `CACHE_DRIVER=redis`). | | **Static analysis & style** | PHPStan (level 3, scoped to code this project owns) and PHP-CS-Fixer (PSR-12), both wired as `composer` scripts and in CI. | | **Tests** | PHPUnit for the application layer — real unit tests plus HTTP-level feature tests that boot the app as a subprocess — separate from CodeIgniter's own framework test suite in `tests/`. | | **Custom error handling** | CI4/Laravel-style: a detailed exception page in development (stack trace with code snippets, request data, editor links), clean per-status-code error pages in production instead of a blank screen, JSON errors for API requests, and `abort(404)` / `abort_if()` / `abort_unless()` helpers. | | **Structured logging** | An opt-in Monolog-backed `Logger` library alongside CI3's native `log_message()`, for when you want multiple handlers or structured context. | -| **`/health` endpoint** | A JSON status endpoint for load balancers/uptime monitors. | -| **Docker** | `Dockerfile` + `docker-compose.yml` (app + MySQL + phpMyAdmin) for a one-command local environment. | +| **`/health` endpoints** | `/health` (alive) and `/health/ready` (real query, shared cache, writable folders) as JSON for load balancers and uptime monitors. | +| **Docker** | `Dockerfile` + `docker-compose.yml` (app + MySQL + phpMyAdmin, and Redis with `--profile ha`) for a one-command local environment. | | **CI** | GitHub Actions: boots the app and hits real routes on PHP 8.2–8.4, plus separate PHPStan/CS-Fixer/PHPUnit jobs. Dependabot keeps dependencies current. | None of the vendored pieces are exotic — HMVC and DevelBar are the same libraries ([wiredesignz HMVC](https://github.com/5112n4/wiredesignz-codeigniter-modular-extensions) + [DevelBar](https://github.com/JCSama/CodeIgniter-develbar)) that have powered plenty of CodeIgniter 3 apps for years — just vendored in, patched for current PHP, and verified working together, with modern tooling and a real (if minimal) auth system layered on top. @@ -118,46 +127,54 @@ application/ config/ database.php.example — copy to database.php and fill in your credentials production/ — example of CI3's environment-specific config overrides + app_settings.php — the settings schema (fields on the Settings page) + permissions.php — core permissions (modules add their own) + menu.php — admin sidebar items (the menu builder adds to them) + routes.php — short URLs for the modules + email.php, redis.php — mail and Redis settings, read from .env core/ - MY_Loader.php — extends MX_Loader (HMVC) + per-view memory tracking for DevelBar - MY_Router.php — extends MX_Router (HMVC) + MY_Loader.php, MY_Router.php — HMVC + per-view memory tracking for DevelBar + MY_Exceptions.php — the error pages + MY_Log.php — log_message() to stderr with LOG_CHANNEL=stderr controllers/ - Console.php — CLI-only: migrate, seed - Health.php — GET /health - config/ - app_settings.php — the settings schema (fields on the Settings page) - menu.php — admin sidebar items + Console.php — CLI-only: migrate, seed, unblock, prune, mailtest + Health.php — GET /health and /health/ready helpers/ - auth_helper.php — is_logged_in(), current_user(), can(), require_permission(), require_login(), require_role() - theme_helper.php — theme CSS variables, asset_url(), flash(), menu_items() + auth_helper.php — is_logged_in(), current_user(), can(), require_permission(), google_login_button() ... + theme_helper.php — theme CSS variables, asset_url(), flash(), menu_items(), menu_href() + datatable_helper.php — datatable_table() views/layouts/ — public / auth / admin layouts libraries/ - Settings.php — DB-backed settings with defaults - Template.php — $this->template->render('view', $data, 'admin') - Turnstile.php — Cloudflare Turnstile widget + server-side check - Rbac.php — current role, permission checks, role/user data access - Ratelimiter.php — fixed-window rate limiting (file cache, no Redis needed) - Logger.php — opt-in Monolog logging - Seeder.php — seeder runner (application/seeds/*_seeder.php) + Settings, Template, Turnstile — settings, layouts, CAPTCHA + Rbac — current role, permission checks, role/user data access + Datatable — server-side DataTables (paging, search, sort, export chunks) + Crud_store, Crud_rules — the CRUD generator's storage and validation rules + Loginguard, Ratelimiter — login blocking, fixed-window rate limiting + Audit — the audit log (created / updated / deleted / event) + Mailer, Accountlinks, Usertokens — e-mail, the verification and reset links, their tokens + Sharedcache — cache shared between servers (file / redis / memcached) + Installer, Seeder, Logger — one-click install, seeders, opt-in Monolog hooks/ SecurityHeaders.php — CSP, X-Frame-Options, etc. migrations/ — CI3 migrations (enabled) seeds/ — seeder classes modules/ example/ — working reference HMVC module, safe to delete - auth/ — register/login/logout - dashboard/ — landing page after login (admin layout) - settings/ — admin Settings page (/admin/settings) - users/ — user list + role assignment (/admin/users) - roles/ — role CRUD + permission matrix (/admin/roles) - src/ — Composer-autoloaded App\ namespace for plain PHP classes + auth/ — register / login / logout, e-mail links (Account), Google (Google_auth) + dashboard/, settings/, users/, roles/ — landing page, Settings, user list, roles + permission matrix + generator/, crud/ — the CRUD generator (super admin) and the pages it makes (/admin/c/{key}) + security/, audit/ — blocked IPs/users, audit log viewer + menus/, pages/ — menu builder, page builder + install/ — the /install wizard + src/ — Composer-autoloaded App\ namespace: plain, unit-tested PHP (Auth, Audit, Crud, + DataTables, Export, Install, Menus, Page, Security, Settings, Theme ...) third_party/ MX/ — HMVC (Modular Extensions) library DevelBar/ — dev toolbar system/ core/*.php, database/DB_driver.php, libraries/Driver.php — patched with #[AllowDynamicProperties] for PHP 8.2+ -assets/ — css/, js/, and vendor/ (Bootstrap, Bootstrap Icons, SweetAlert2) +assets/ — css/, js/ and vendor/ (Bootstrap, Bootstrap Icons + icons.json, SweetAlert2, DataTables) tests-app/ — PHPUnit for application code (not CI3's own tests/) phpstan.neon.dist, .php-cs-fixer.dist.php — static analysis / style config Dockerfile, docker-compose.yml — containerized dev environment @@ -299,7 +316,8 @@ Bootstrap, Bootstrap Icons, and SweetAlert2 are vendored in `assets/vendor/` (no - `GET`/`POST /register`, `GET`/`POST /login`, `GET /logout` (short URLs — see `application/config/routes.php`) - Passwords hashed with `password_hash()`/`password_verify()` - Wrong passwords are counted per IP address and per account and lead to a temporary block (see [Login security & audit log](#login-security--audit-log)); if the security tables are missing it falls back to 5 attempts per 60 seconds per IP (`application/libraries/Ratelimiter.php`) -- A single `role` column on `users` (seeded default: `user`; the seeded admin gets `admin`) for basic access control +- A single `role` column on `users` (new sign-ups get the default role; the seeded and installer-created account is `super_admin`); what a role may do is set on the Roles page +- Optional e-mail verification, password reset and Google sign-in: see [Accounts & e-mail](#accounts--e-mail-verification-password-reset-google) Use it from any controller via `application/helpers/auth_helper.php` (autoloaded — no `$this->load->helper()` needed): @@ -311,7 +329,7 @@ current_user(); // array or null: id, name, email, role has_role('admin'); // bool ``` -The `users` table comes from `application/migrations/20260813120000_create_users_table.php` — run `php index.php console migrate` to create it. This is intentionally minimal (no password reset, no email verification, no OAuth) — extend `application/modules/auth/controllers/Auth.php` for anything beyond that. +The `users` table comes from `application/migrations/20260813120000_create_users_table.php` — run `php index.php console migrate` to create it. Login and registration live in `Auth.php`; the e-mail-link flows are in `Account.php` and Google in `Google_auth.php` (same module). Extend those for anything beyond that. ## Accounts & e-mail (verification, password reset, Google) @@ -343,6 +361,7 @@ Each user has **one role**; each role holds a set of **permissions**. Both are m - **`super_admin` is the top role.** It is built in, always holds every permission (including ones added later), and **cannot be created again, edited or deleted**. Anyone below it is up to you. There must always be at least one super admin, and only a super admin can grant, take away, or change the `super_admin` role. - **One role is the default**, given to new sign-ups (set it with the switch on the role form). It can't be deleted, and a role that still has users can't be deleted either: move them first. - **Admins can't escalate.** Someone with `roles.edit` can only tick or untick permissions they hold themselves (the others show greyed out, and the server ignores them), and nobody but a super admin can change their own role. +- **Module permissions.** Every generated module registers its own (`.view`, `.create`, `.edit`, `.delete`, and `.export` when it offers exports) the moment it exists, and only the ones the module actually has are listed. A few permissions (`pages.html`, raw HTML in pages) are deliberately left out of the `admin` role, even on a fresh install, because they are as powerful as a developer's access. - **Changes apply immediately.** The role is read from the database on every request, so editing a role or reassigning a user takes effect on their next click, with no re-login. ### Using permissions in code @@ -600,7 +619,7 @@ If the installer cannot write `application/config/database.php` (folder not writ A privacy-friendly CAPTCHA for the login and register forms. It is **off by default**; nothing changes until you turn it on. 1. In the Cloudflare dashboard, create a Turnstile site and copy its **site key** and **secret key**. -2. Log in as an admin → **Settings → Security**, switch Turnstile on, paste both keys, and choose which forms use it (login, register), plus the widget theme and size. Saving with Turnstile on but a key missing is rejected. +2. Log in as an admin → **Settings → Security**, switch Turnstile on, paste both keys, and choose which forms use it (login, register, forgot password), plus the widget theme and size. Saving with Turnstile on but a key missing is rejected. The keys can also come from the environment (`TURNSTILE_SITE_KEY`, `TURNSTILE_SECRET_KEY` in `.env`) as defaults; a value saved in Settings wins. The secret is never printed back into the page. @@ -661,11 +680,12 @@ To go back to CI3's stock error handling, remove the `require_once APPPATH.'core CodeIgniter 3 has no built-in artisan-like CLI. `application/controllers/Console.php` is the idiomatic CI3 way to get one — a normal controller invoked from the command line instead of over HTTP, refusing to run any other way: ```bash -php index.php console migrate # run pending migrations +php index.php console migrate # run pending migrations (takes a database lock, so several servers can deploy at once) php index.php console seed # run every application/seeds/*_seeder.php php index.php console seed users # run one seeder by name php index.php console unblock # lift a login block (see Login security) -php index.php console prune # delete old audit rows / idle lock rows (run it nightly from cron) +php index.php console prune # delete old audit rows / idle locks / old e-mail tokens (run it nightly from cron) +php index.php console mailtest you@example.com # send a test e-mail with the .env mail settings ``` Add a migration with CI3's normal `CI_Migration` API in `application/migrations/`. Add a seeder by creating `application/seeds/_seeder.php` with a class extending `CI_Seeder` and implementing `run()` — see `application/seeds/users_seeder.php`. @@ -698,7 +718,7 @@ CodeIgniter 3's dynamic "super object" pattern means PHPStan needs a bit of help docker compose up --build ``` -Starts the app on http://localhost:8080, MySQL on `3306`, and phpMyAdmin on http://localhost:8081 (user `root`, password `secret` — this is a local dev default, change it for anything beyond your own machine). The container copies `database.php.example` to `database.php` automatically and reads connection details from `docker-compose.yml`'s `environment:` block via the same `getenv()` fallback pattern `.env` uses locally — no extra config needed to get a working database inside the container. +Starts the app on http://localhost:8080, MySQL on `3306`, and phpMyAdmin on http://localhost:8081 (add `--profile ha` to also start Redis, then set `SESS_DRIVER=redis`, `CACHE_DRIVER=redis` and `REDIS_HOST=redis`) (user `root`, password `secret` — this is a local dev default, change it for anything beyond your own machine). The container copies `database.php.example` to `database.php` automatically and reads connection details from `docker-compose.yml`'s `environment:` block via the same `getenv()` fallback pattern `.env` uses locally — no extra config needed to get a working database inside the container. Run migrations/seeders inside the running container: ```bash @@ -745,8 +765,17 @@ Make sure `application/config/database.php` exists (step 2 of [Quick start](#qui **DevelBar isn't showing up.** Check `ENVIRONMENT` — it only shows in `development` by default. -**Login says "Too many attempts."** -That's the rate limiter (5 attempts/60s per IP) — wait it out, or clear `application/cache/` locally. +**Login says "Too many failed login attempts."** +That's [login blocking](#login-security--audit-log): 5 wrong passwords block the IP address and the account for an hour. An admin lifts it at Admin → Login security, or run `php index.php console unblock `. Behind a load balancer or CDN, set `TRUSTED_PROXIES` in `.env`, otherwise every visitor shares the balancer's address and one person's mistakes block everybody. + +**The Export menu has no Excel option / "not installed".** +Excel needs PHP's `zip` extension and PDF needs `dompdf` (`composer install`). The generator shows the format greyed out when it is missing. + +**Verification, reset or Google sign-in does nothing.** +Run `php index.php console migrate` first, then check the mail settings with `php index.php console mailtest you@example.com`. For Google, the redirect URI registered in the Google Cloud console must be exactly `https://your-site/google/callback`. + +**A page I made is a 404 / the menu builder shows no tables.** +The menu and page tables come from migrations: `php index.php console migrate`. Drafts are only visible to people who may edit pages. **I want clean URLs without `index.php` in them.** Already set up — a root `.htaccess` handles the rewrite, and `application/config/config.php` already has `index_page` set accordingly. If your server doesn't have `mod_rewrite`, set `$config['index_page'] = 'index.php'` there instead (see the comment in `.htaccess`).