diff --git a/.gitignore b/.gitignore index 808ffe5..f510506 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,3 @@ node_modules/ -demo/ +demo-app/ .DS_Store \ No newline at end of file diff --git a/README.md b/README.md index cbc0efe..5ccf3f9 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Add [PHP](https://www.php.net/), [Composer](https://getcomposer.org/), [MySQL](https://mariadb.org/), [phpMyAdmin](https://www.phpmyadmin.net/) and [PHP CRUD API](https://github.com/mevdschee/php-crud-api) to your local development environment. +You might be interested in a simple [Synchronization between local IndexedDB and MySQL Database](https://github.com/scriptPilot/dexie-mysql-sync). + ## Installation 1. Install [Docker](https://www.docker.com/) and [Node.js](https://nodejs.org/) @@ -22,6 +24,8 @@ Add [PHP](https://www.php.net/), [Composer](https://getcomposer.org/), [MySQL](h - Run `npm run backend` to start the backend - Open the PHP server at http://localhost:8000 + - API endpoint at http://localhost:8000/api.php + - example: http://localhost:8000/api.php/records/tasks - Open phpMyAdmin at http://localhost:8080 - Login with username `root` and password `root` - Use the PHP CRUD API in frontend with `/api.php` @@ -50,8 +54,9 @@ Report bugs in the [issues list](https://github.com/scriptPilot/add-php-backend/ ## Maintainer -1. Apply changes -2. Run `npm run backend` to build the `demo` folder -3. Commit changes with an issue (closure) reference -4. Run `npm version patch | minor | major` and push changes -5. Let the workflow manage the release to GitHub and NPM +1. Apply changes to the code +2. Run `npm run demo` to build and start the `demo-app` +3. Apply changes to the `README.md` file and screenshots +4. Commit changes with an issue (closure) reference +5. Run `npm version patch | minor | major` and push changes +6. Let the workflow manage the release to GitHub and NPM diff --git a/package-lock.json b/package-lock.json index 99c69c0..a7573ea 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "add-php-backend", - "version": "1.7.0", + "version": "1.10.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "add-php-backend", - "version": "1.7.0", + "version": "1.10.0", "license": "MIT", "dependencies": { "fs-extra": "^11.2.0", diff --git a/package.json b/package.json index 6c8dd46..651f1e5 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "add-php-backend", - "version": "1.7.0", + "version": "1.10.0", "description": "", "main": "src/index.js", "bin": "src/index.js", "type": "module", "scripts": { "test": "", - "backend": "rm -rf demo && node . && cd demo && npm run backend", + "demo": "rm -rf demo-app && npm create vite demo-app -- --template react && node . && cd demo-app && npm install && npm run dev", "preversion": "npm install && npm test" }, "repository": { diff --git a/src/index.js b/src/index.js index ece1702..6130c59 100755 --- a/src/index.js +++ b/src/index.js @@ -10,7 +10,7 @@ import fs from 'fs-extra' const scriptFolder = path.dirname(url.fileURLToPath(import.meta.url)) const processFolder = process.cwd() const isDevMode = processFolder === path.resolve(scriptFolder, '..') -const appFolder = isDevMode ? path.resolve(scriptFolder, '../demo') : processFolder +const appFolder = isDevMode ? path.resolve(scriptFolder, '../demo-app') : processFolder const templateFolder = path.resolve(scriptFolder, 'templates') // Define files @@ -52,7 +52,7 @@ const nextPackageFileJson = { ...packageFileJson, scripts: { ...packageFileJson.scripts, - dev: 'npm run backend' + (packageFileJson.scripts?.dev ? ' && ' + packageFileJson.scripts.dev : ''), + dev: (packageFileJson.scripts?.dev?.startsWith('npm run backend') ? packageFileJson.scripts.dev : 'npm run backend' + (packageFileJson.scripts?.dev ? ' && ' + packageFileJson.scripts.dev : '')), backend: '(docker stop $(docker ps -a -q) || true) && (docker rm -f $(docker ps -a -q) || true) && (docker volume rm $(docker volume ls -q) || true) && docker compose up -d --build', } } @@ -68,7 +68,6 @@ const serverStr = ` },` const proxyStr = ` proxy: { - // eslint-disable-next-line no-useless-escape '^(.+)\\\\.php': 'http://localhost:8000/', },` if (!viteConfigFileContent.match(regexStartOfServer)) { diff --git a/src/templates/public/api.php b/src/templates/public/api.php index d340de4..85f792e 100644 --- a/src/templates/public/api.php +++ b/src/templates/public/api.php @@ -1,65 +1,58 @@ MYSQL_DATABASE === 'development', - // Database Credentials + // Credentials 'address' => MYSQL_HOST, 'database' => MYSQL_DATABASE, 'username' => MYSQL_USERNAME, 'password' => MYSQL_PASSWORD, - // Database Authentication - 'middlewares' => 'dbAuth,authorization', + // Middlewares + 'middlewares' => 'dbAuth,authorization,multiTenancy', + + // Database authentication 'dbAuth.mode' => 'optional', 'dbAuth.registerUser' => '1', - 'dbAuth.returnedColumns' => 'id,username', - 'authorization.tableHandler' => function ($operation, $tableName) { - // Disallow user table for delete operations - if ($operation === 'delete' && $tableName === 'users') { - return false; - } - // No other table limitation + 'dbAuth.passwordLength' => '3', + + // Database Authorization + 'authorization.tableHandler' => function ($operation, $tableName) { + + // No access to the users table + if ($tableName === 'users') return false; + + // Access to all other tables return true; + }, - 'authorization.columnHandler' => function ($operation, $tableName, $columnName) { - // Hide user/password column - if (($operation === 'read' || $operation === 'list') && $tableName === 'users' && $columnName === 'password') { - return false; - } - // No other column limitation - return true; + + // Multi Tenancy + 'multiTenancy.handler' => function ($operation, $tableName) { + + // For all tables, limit access to the current user + return ['userId' => $_SESSION['user']['id'] ?? 0]; + }, - 'authorization.recordHandler' => function ($operation, $tableName) { - // Limit user records to same user - if ($tableName === 'users' && $operation !== 'create') { - return 'filter=id,eq,' . USERID; - } - // No other record limitation - return true; - } - + ]); // Initialization diff --git a/src/templates/schema.sql b/src/templates/schema.sql index 5ffb15f..2f86c63 100644 --- a/src/templates/schema.sql +++ b/src/templates/schema.sql @@ -1,11 +1,12 @@ CREATE TABLE IF NOT EXISTS `users` ( - `id` integer(4) NOT NULL PRIMARY KEY AUTO_INCREMENT, - `username` varchar(255) NOT NULL, - `password` varchar(255) NOT NULL + `id` INTEGER(8) NOT NULL PRIMARY KEY AUTO_INCREMENT, + `username` VARCHAR(255) NOT NULL, + `password` VARCHAR(255) NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; CREATE TABLE IF NOT EXISTS `tasks` ( - `id` integer(4) NOT NULL PRIMARY KEY AUTO_INCREMENT, - `title` varchar(255) NOT NULL, - `done` integer(1) NOT NULL DEFAULT 0 + `id` INTEGER(8) NOT NULL PRIMARY KEY AUTO_INCREMENT, + `userId` INTEGER(8) NOT NULL DEFAULT 0, + `title` VARCHAR(255) NOT NULL DEFAULT "", + `done` TINYINT(1) NOT NULL DEFAULT 0 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci; \ No newline at end of file diff --git a/src/templates/testdata.sql b/src/templates/testdata.sql index 7732132..4d13f7f 100644 --- a/src/templates/testdata.sql +++ b/src/templates/testdata.sql @@ -1,5 +1,2 @@ -INSERT IGNORE INTO `users` (`id`, `username`, `password`) -VALUES (1, "root", "cm9vdA=="); - INSERT IGNORE INTO `tasks` (`id`, `title`, `done`) VALUES (1, "First Task", 1), (2, "Second Task", 0), (3, "Third Task", 1); \ No newline at end of file