├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── composer.json ├── config └── mojito.php ├── database └── migrations │ ├── add_custom_field_permission_tables.php │ ├── create_admin_table.php │ ├── create_menu_table.php │ └── create_permission_group_table.php └── src ├── AdminUserFactory.php ├── Console └── InstallCommand.php ├── Contacts └── UserContact.php ├── Database └── MojitoTableSeeder.php ├── Http ├── Controllers │ ├── AdminUserController.php │ ├── CaptchaController.php │ ├── ChangePasswordController.php │ ├── Controller.php │ ├── LoginController.php │ ├── MenuController.php │ ├── PermissionController.php │ ├── PermissionGroupController.php │ └── RoleController.php ├── Middleware │ └── Authenticate.php ├── MojitoResponse.php └── Requests │ ├── AdminUser │ └── CreateOrUpdateRequest.php │ ├── ChangePasswordRequest.php │ ├── Menu │ └── CreateOrUpdateRequest.php │ ├── Permission │ └── CreateOrUpdateRequest.php │ ├── PermissionGroup │ └── CreateOrUpdateRequest.php │ └── Role │ └── CreateOrUpdateRequest.php ├── Models ├── AdminUser.php ├── Menu.php ├── Permission.php ├── PermissionGroup.php └── User.php ├── Providers └── MojitoServiceProvider.php ├── Resources ├── AdminUser.php ├── AdminUserCollection.php ├── Menu.php ├── Permission.php ├── PermissionCollection.php ├── PermissionGroup.php ├── PermissionGroupCollection.php ├── Role.php └── RoleCollection.php ├── helpers.php └── routes.php /.gitattributes: -------------------------------------------------------------------------------- 1 | /tests export-ignore 2 | phpunit.xml export-ignore -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.idea 2 | /vendor 3 | composer.lock 4 | .phpunit.result.cache -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mojito 2 | 3 | Mojito 是基于 Laravel 开发的 [Mojito Admin](https://github.com/moell-peng/mojito-admin) 的服务端。 4 | 5 | 3.0 版本开始, 已将原来的项目分离为两个代码库,分别为以Vue3、Element Plus、Vite 开发的前端模板 [mojito -admin](https://github.com/moell-peng/mojito-admin) 和服务端 [mojito](https://github.com/moell-peng/mojito) 。如果是需要使用 vue2 版本,请访问 [2.0](https://github.com/moell-peng/mojito/tree/2.0) 分支。 6 | 7 | ## Mojito Admin 截图 8 | 9 | ![mojito.png](http://ww1.sinaimg.cn/large/7a679ca1gy1gtu09c4avej21590kstdb.jpg) 10 | 11 | ## 特性 12 | 13 | * 前后端分离,提供 [Mojito Admin](https://github.com/moell-peng/mojito-admin) 前端模板 14 | * 基于 laravel-permission 权限管理 15 | * 基于 sanctum 鉴权 16 | * 提供角色,权限,用户,菜单管理等功能的API 17 | * 多个后台支持统一管理权限,菜单和角色 18 | * 完善的PHPUnit测试 19 | 20 | ## 要求 21 | 22 | - Laravel >= 7.0.0 23 | - PHP >= 7.2.0 24 | 25 | ## 安装 26 | 27 | 首先安装laravel,并且确保你配置了正确的数据库连接。 28 | 29 | ``` 30 | composer require moell/mojito 31 | ``` 32 | 33 | 然后运行下面的命令来发布资源: 34 | 35 | ``` 36 | php artisan mojito:install 37 | ``` 38 | 39 | 命令执行成功会生成配置文件,数据迁移和构建SPA的文件。 40 | 41 | 修改 `app/Http/Kernel.php` : 42 | 43 | ``` 44 | class Kernel extends HttpKernel 45 | { 46 | protected $routeMiddleware = [ 47 | ... 48 | 'mojito.permission' => \Moell\Mojito\Http\Middleware\Authenticate::class, 49 | ]; 50 | 51 | protected $middlewareGroups = [ 52 | ... 53 | 'api' => [ 54 | ... 55 | \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, 56 | ], 57 | ]; 58 | } 59 | ``` 60 | 61 | 执行数据迁移,数据填充 62 | 63 | ``` 64 | php artisan migrate 65 | 66 | php artisan db:seed --class="Moell\Mojito\Database\MojitoTableSeeder" 67 | ``` 68 | 69 | 后台登录的账号 `admin` , 密码 `secret` 70 | 71 | ## 路由中间件 72 | 73 | * auth:sanctum 用于鉴权 74 | * mojito.permission 权限验证 75 | 76 | ## mojito.php 可选配置 77 | 78 | ```php 79 | return [ 80 | 'guards' => [ 81 | // laravel-permission 相对应的 guard 82 | 'admin' => [ 83 | 'model' => \Moell\Mojito\Models\AdminUser::class, //登录鉴权的模型 84 | 'login_fields' => [ // 登录验证的字段,支持多个 85 | 'username', 86 | ], 87 | 'conditions' => [ // 登录验证的额外条件 88 | ['status', '=', 1] 89 | ] 90 | ] 91 | ], 92 | 'route_prefix' => "api", //路由前缀 93 | 94 | 'middleware' => [ 95 | 'basic' => 'api', //基础中间件 96 | 97 | 'auth' => ['auth:sanctum'], //鉴权中间件 98 | 99 | 'permission' => ['auth:sanctum', 'mojito.permission'] //包含权限检测的中间件 100 | ] 101 | ]; 102 | ``` 103 | 104 | ## 依赖扩展包 105 | 106 | * spatie/laravel-permission 107 | * laravel/sanctum 108 | 109 | ## 常见错误 110 | 111 | * csrf token missing or incorrect , 请修改 sanctum.php 中的 `stateful` , 如 vite 使用的 `localhost:3000 `去除即可。更多详细请访问`laravel/sanctum`文档。 112 | 113 | 114 | ## 打赏 115 | 116 |

117 | 118 | 119 |

120 | 121 | ## License 122 | 123 | Apache License Version 2.0 see http://www.apache.org/licenses/LICENSE-2.0.html 124 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "moell/mojito", 3 | "description": "Mojito admin is a component of laravel, vue, element build backend system.", 4 | "keywords": ["laravel", "admin", "vue", "element", "laravel admin", "vue admin", "element admin"], 5 | "type": "library", 6 | "license": "MIT", 7 | "authors": [ 8 | { 9 | "name": "moell", 10 | "email": "moell91@foxmail.com" 11 | } 12 | ], 13 | "require": { 14 | "php": "^7.2|^8.0", 15 | "spatie/laravel-permission": "^5.3", 16 | "laravel/sanctum": "^2.4|^3.2", 17 | "gregwar/captcha": "^1.1" 18 | }, 19 | "require-dev": { 20 | "mockery/mockery": "^1.0", 21 | "orchestra/testbench": "^5.0|^6.0|^7.0|^8.0", 22 | "dms/phpunit-arraysubset-asserts": "^0.4.0", 23 | "doctrine/dbal": "^2.8", 24 | "laravel/legacy-factories": "^1.0.4" 25 | }, 26 | "autoload": { 27 | "psr-4": { 28 | "Moell\\Mojito\\": "src/" 29 | }, 30 | "files": [ 31 | "src/helpers.php" 32 | ] 33 | }, 34 | "autoload-dev": { 35 | "psr-4": { 36 | "Moell\\Mojito\\Tests\\": "tests/" 37 | } 38 | }, 39 | "extra": { 40 | "laravel": { 41 | "providers": [ 42 | "Moell\\Mojito\\Providers\\MojitoServiceProvider" 43 | ] 44 | } 45 | }, 46 | "minimum-stability":"dev", 47 | "prefer-stable": true 48 | } 49 | -------------------------------------------------------------------------------- /config/mojito.php: -------------------------------------------------------------------------------- 1 | [ 5 | 'admin' => [ 6 | 'model' => \Moell\Mojito\Models\AdminUser::class, 7 | 'login_fields' => [ 8 | 'username', 9 | ], 10 | 'conditions' => [ 11 | ['status', '=', 1] 12 | ] 13 | ] 14 | ], 15 | 16 | 'route_prefix' => "api", 17 | 18 | 'middleware' => [ 19 | 'basic' => 'api', 20 | 21 | 'auth' => ['auth:sanctum'], 22 | 23 | 'permission' => ['auth:sanctum', 'mojito.permission'] 24 | ], 25 | 26 | 'captcha_cache_ttl' => 2, 27 | ]; -------------------------------------------------------------------------------- /database/migrations/add_custom_field_permission_tables.php: -------------------------------------------------------------------------------- 1 | integer('pg_id')->default(0); 20 | $table->string('display_name', 50)->nullable(); 21 | $table->string('icon', 30)->nullable(); 22 | $table->smallInteger('sequence')->nullable(); 23 | $table->string('created_name', 50)->nullable(); 24 | $table->string('updated_name', 50)->nullable(); 25 | $table->string('description')->nullable(); 26 | }); 27 | 28 | Schema::table($tableNames['roles'], function (Blueprint $table) { 29 | $table->string('description')->nullable(); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | * 36 | * @return void 37 | */ 38 | public function down() 39 | { 40 | $tableNames = config('permission.table_names'); 41 | 42 | Schema::table($tableNames['permissions'], function (Blueprint $table) { 43 | $table->dropColumn('display_name'); 44 | $table->dropColumn('icon'); 45 | $table->dropColumn('sequence'); 46 | $table->dropColumn('created_name'); 47 | $table->dropColumn('updated_name'); 48 | $table->dropColumn('description'); 49 | }); 50 | 51 | Schema::table($tableNames['roles'], function (Blueprint $table) { 52 | $table->dropColumn('description'); 53 | }); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /database/migrations/create_admin_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->string('username')->unique(); 20 | $table->string('password'); 21 | $table->boolean("status"); 22 | $table->rememberToken(); 23 | $table->timestamps(); 24 | }); 25 | } 26 | 27 | /** 28 | * Reverse the migrations. 29 | * 30 | * @return void 31 | */ 32 | public function down() 33 | { 34 | Schema::dropIfExists('admin_users'); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /database/migrations/create_menu_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->integer('parent_id')->default(0); 19 | $table->string('icon', 50)->nullable(); 20 | $table->string('uri'); 21 | $table->tinyInteger('is_link')->default(0)->comment('0-no;1-yes'); 22 | $table->string('permission_name', 50)->nullable(); 23 | $table->string('name'); 24 | $table->string('guard_name', 30); 25 | $table->smallInteger('sequence')->default(0); 26 | $table->timestamps(); 27 | }); 28 | } 29 | 30 | /** 31 | * Reverse the migrations. 32 | * 33 | * @return void 34 | */ 35 | public function down() 36 | { 37 | Schema::dropIfExists('menus'); 38 | } 39 | } -------------------------------------------------------------------------------- /database/migrations/create_permission_group_table.php: -------------------------------------------------------------------------------- 1 | increments('id'); 18 | $table->string('name'); 19 | $table->timestamps(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('permission_groups'); 31 | } 32 | } -------------------------------------------------------------------------------- /src/AdminUserFactory.php: -------------------------------------------------------------------------------- 1 | 27 | * @return mixed 28 | */ 29 | public function handle() 30 | { 31 | $this->call('vendor:publish', ['--provider' => 'Spatie\Permission\PermissionServiceProvider']); 32 | $this->call('vendor:publish', ['--provider' => 'Moell\Mojito\Providers\MojitoServiceProvider']); 33 | $this->call('vendor:publish', ['--provider' => 'Laravel\Sanctum\SanctumServiceProvider']); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/Contacts/UserContact.php: -------------------------------------------------------------------------------- 1 | 'admin-user.index', 17 | 'display_name' => '列表', 18 | 'pg_id' => 1 19 | ], 20 | [ 21 | 'name' => 'admin-user.show', 22 | 'display_name' => '详细', 23 | 'pg_id' => 1 24 | ], 25 | [ 26 | 'name' => 'admin-user.store', 27 | 'display_name' => '添加', 28 | 'pg_id' => 1 29 | ], 30 | [ 31 | 'name' => 'admin-user.update', 32 | 'display_name' => '修改', 33 | 'pg_id' => 1 34 | ], 35 | [ 36 | 'name' => 'admin-user.destroy', 37 | 'display_name' => '删除', 38 | 'pg_id' => 1 39 | ], 40 | [ 41 | 'name' => 'admin-user.roles', 42 | 'display_name' => '用户角色列表', 43 | 'pg_id' => 1 44 | ], 45 | [ 46 | 'name' => 'admin-user.assign-roles', 47 | 'display_name' => '分配角色', 48 | 'pg_id' => 1 49 | ], 50 | [ 51 | 'name' => 'role.index', 52 | 'display_name' => '列表', 53 | 'pg_id' => 2 54 | ], 55 | [ 56 | 'name' => 'role.show', 57 | 'display_name' => '详细', 58 | 'pg_id' => 2 59 | ], 60 | [ 61 | 'name' => 'role.store', 62 | 'display_name' => '添加', 63 | 'pg_id' => 2 64 | ], 65 | [ 66 | 'name' => 'role.update', 67 | 'display_name' => '修改', 68 | 'pg_id' => 2 69 | ], 70 | [ 71 | 'name' => 'role.destroy', 72 | 'display_name' => '删除', 73 | 'pg_id' => 2 74 | ], 75 | [ 76 | 'name' => 'role.permissions', 77 | 'display_name' => '获取角色的权限', 78 | 'pg_id' => 2 79 | ], 80 | [ 81 | 'name' => 'role.assign-permissions', 82 | 'display_name' => '角色分配权限', 83 | 'pg_id' => 2 84 | ], 85 | [ 86 | 'name' => 'role.guard-name-roles', 87 | 'display_name' => '看守器对应的所有角色', 88 | 'pg_id' => 2 89 | ], 90 | [ 91 | 'name' => 'permission.index', 92 | 'display_name' => '列表', 93 | 'pg_id' => 3 94 | ], 95 | [ 96 | 'name' => 'permission.show', 97 | 'display_name' => '详细', 98 | 'pg_id' => 3 99 | ], 100 | [ 101 | 'name' => 'permission.store', 102 | 'display_name' => '添加', 103 | 'pg_id' => 3 104 | ], 105 | [ 106 | 'name' => 'permission.update', 107 | 'display_name' => '修改', 108 | 'pg_id' => 3 109 | ], 110 | [ 111 | 'name' => 'permission.destroy', 112 | 'display_name' => '删除', 113 | 'pg_id' => 3 114 | ], 115 | [ 116 | 'name' => 'menu.index', 117 | 'display_name' => '列表', 118 | 'pg_id' => 4 119 | ], 120 | [ 121 | 'name' => 'menu.show', 122 | 'display_name' => '详细', 123 | 'pg_id' => 4 124 | ], 125 | [ 126 | 'name' => 'menu.store', 127 | 'display_name' => '添加', 128 | 'pg_id' => 4 129 | ], 130 | [ 131 | 'name' => 'menu.update', 132 | 'display_name' => '修改', 133 | 'pg_id' => 4 134 | ], 135 | [ 136 | 'name' => 'menu.destroy', 137 | 'display_name' => '删除', 138 | 'pg_id' => 4 139 | ], 140 | [ 141 | 'name' => 'permission-group.index', 142 | 'display_name' => '列表', 143 | 'pg_id' => 5 144 | ], 145 | [ 146 | 'name' => 'permission-group.show', 147 | 'display_name' => '详细', 148 | 'pg_id' => 5 149 | ], 150 | [ 151 | 'name' => 'permission-group.store', 152 | 'display_name' => '添加', 153 | 'pg_id' => 5 154 | ], 155 | [ 156 | 'name' => 'permission-group.update', 157 | 'display_name' => '修改', 158 | 'pg_id' => 5 159 | ], 160 | [ 161 | 'name' => 'permission-group.destroy', 162 | 'display_name' => '删除', 163 | 'pg_id' => 5 164 | ], 165 | [ 166 | 'name' => 'permission-group.guard-name-for-permission', 167 | 'display_name' => '获取看守器权限', 168 | 'pg_id' => 5 169 | ], 170 | [ 171 | 'name' => 'permission-group.all', 172 | 'display_name' => '所有权限组', 173 | 'pg_id' => 5 174 | ] 175 | ]; 176 | 177 | /** 178 | * Run the database seeds. 179 | * 180 | * @author moell 181 | * @return void 182 | */ 183 | public function run() 184 | { 185 | app()['cache']->forget('spatie.permission.cache'); 186 | 187 | $this->createdAdminUser(); 188 | 189 | $this->createPermissionGroup(); 190 | 191 | $this->createRole(); 192 | 193 | $this->createPermission(); 194 | 195 | $this->createMenu(); 196 | 197 | $this->associateRolePermissions(); 198 | } 199 | 200 | /** 201 | * @author moell 202 | */ 203 | private function createdAdminUser() 204 | { 205 | AdminUserFactory::adminUser()->truncate(); 206 | 207 | AdminUserFactory::adminUser()->create([ 208 | 'name' => 'admin', 209 | 'username' => 'admin', 210 | 'status' => 1, 211 | 'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret 212 | ]); 213 | } 214 | 215 | /** 216 | * @author moell 217 | */ 218 | private function createPermission() 219 | { 220 | Permission::query()->delete(); 221 | 222 | foreach ($this->permissions as $permission) { 223 | $permission['guard_name'] = 'admin'; 224 | Permission::create($permission); 225 | } 226 | } 227 | 228 | /** 229 | * @author moell 230 | */ 231 | private function createPermissionGroup() 232 | { 233 | PermissionGroup::truncate(); 234 | PermissionGroup::insert([ 235 | [ 236 | 'id' => 1, 237 | 'name' => '管理员', 238 | ], [ 239 | 'id' => 2, 240 | 'name' => '角色' 241 | ], [ 242 | 'id' => 3, 243 | 'name' => '权限' 244 | ], [ 245 | 'id' => 4, 246 | 'name' => '菜单' 247 | ], [ 248 | 'id' => 5, 249 | 'name' => '权限组' 250 | ] 251 | ]); 252 | } 253 | 254 | /** 255 | * @author moell 256 | */ 257 | private function createRole() 258 | { 259 | Role::query()->delete(); 260 | Role::create([ 261 | 'name' => 'admin', 262 | 'guard_name' => 'admin' 263 | ]); 264 | } 265 | 266 | /** 267 | * @author moell 268 | */ 269 | private function createMenu() 270 | { 271 | Menu::truncate(); 272 | Menu::insert([ 273 | [ 274 | 'id' => 1, 275 | 'parent_id' => 0, 276 | 'uri' => '/dashboard', 277 | 'name' => 'Dashboard', 278 | 'icon' => 'Orange', 279 | 'guard_name'=> 'admin' 280 | ], 281 | [ 282 | 'id' => 2, 283 | 'parent_id' => 0, 284 | 'uri' => '/admin', 285 | 'name' => '系统管理', 286 | 'icon' => 'Setting', 287 | 'guard_name'=> 'admin' 288 | ], 289 | [ 290 | 'id' => 3, 291 | 'parent_id' => 2, 292 | 'uri' => '/admin-user', 293 | 'name' => '管理员', 294 | 'icon' => '', 295 | 'guard_name'=> 'admin' 296 | ], 297 | [ 298 | 'id' => 4, 299 | 'parent_id' => 2, 300 | 'uri' => '/role', 301 | 'name' => '角色', 302 | 'icon' => '', 303 | 'guard_name'=> 'admin' 304 | ], 305 | [ 306 | 'id' => 5, 307 | 'parent_id' => 2, 308 | 'uri' => '/permission', 309 | 'name' => '权限', 310 | 'icon' => '', 311 | 'guard_name'=> 'admin' 312 | ], 313 | [ 314 | 'id' => 6, 315 | 'parent_id' => 2, 316 | 'uri' => '/menu', 317 | 'name' => '菜单', 318 | 'icon' => '', 319 | 'guard_name'=> 'admin' 320 | ], 321 | 322 | ]); 323 | } 324 | 325 | /** 326 | * @author moell 327 | */ 328 | private function associateRolePermissions() 329 | { 330 | $role = Role::first(); 331 | 332 | AdminUserFactory::adminUser()->first()->assignRole($role->name); 333 | 334 | foreach ($this->permissions as $permission) { 335 | $role->givePermissionTo($permission['name']); 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /src/Http/Controllers/AdminUserController.php: -------------------------------------------------------------------------------- 1 | adminUserModel = AdminUserFactory::adminUser(); 25 | } 26 | 27 | /** 28 | * @author moell 29 | * @param Request $request 30 | * @return AdminUserCollection 31 | */ 32 | public function index(Request $request) 33 | { 34 | return new AdminUserCollection($this->adminUserModel->where(request_intersect(['name', 'username']))->paginate()); 35 | } 36 | 37 | /** 38 | * @author moell 39 | * @param $id 40 | * @return AdminUserResource 41 | */ 42 | public function show($id) 43 | { 44 | return new AdminUserResource($this->adminUserModel->findOrFail($id)); 45 | } 46 | 47 | /** 48 | * @author moell 49 | * @param CreateOrUpdateRequest $request 50 | * @return Response 51 | */ 52 | public function store(CreateOrUpdateRequest $request) 53 | { 54 | $data = request_intersect([ 55 | 'name', 'username', 'password' 56 | ]); 57 | $data['status'] = $request->status ? true : false; 58 | $data['password'] = bcrypt($data['password']); 59 | 60 | $this->adminUserModel->create($data); 61 | 62 | return $this->created(); 63 | } 64 | 65 | /** 66 | * @author moell 67 | * @param CreateOrUpdateRequest $request 68 | * @param $id 69 | * @return Response 70 | */ 71 | public function update(CreateOrUpdateRequest $request, $id) 72 | { 73 | $adminUser = $this->adminUserModel->findOrFail($id); 74 | 75 | $data = $request->only([ 76 | 'name', 'status' 77 | ]); 78 | 79 | if ($request->filled('password')) { 80 | $data['password'] = bcrypt($request->password); 81 | } 82 | 83 | $adminUser->fill($data); 84 | $adminUser->save(); 85 | 86 | return $this->noContent(); 87 | } 88 | 89 | /** 90 | * @author moell 91 | * @param $id 92 | * @return Response 93 | */ 94 | public function destroy($id) 95 | { 96 | $adminUser = $this->adminUserModel->findOrFail($id); 97 | 98 | $adminUser->delete(); 99 | 100 | return $this->noContent(); 101 | } 102 | 103 | /** 104 | * @author moell 105 | * @param $id 106 | * @param $provider 107 | * @return RoleCollection 108 | */ 109 | public function roles($id, $provider) 110 | { 111 | $user = $this->getGuardModel($provider)->findOrFail($id); 112 | 113 | return new RoleCollection($user->roles); 114 | } 115 | 116 | /** 117 | * @param $id 118 | * @param $guard 119 | * @param Request $request 120 | * @return Response 121 | *@author moell 122 | */ 123 | public function assignRoles($id, $guard, Request $request) 124 | { 125 | $user = $this->getGuardModel($guard)->findOrFail($id); 126 | 127 | $user->syncRoles($request->input('roles', [])); 128 | 129 | return $this->noContent(); 130 | } 131 | 132 | /** 133 | * @param $guard 134 | * @return Illuminate\Foundation\Auth\User 135 | */ 136 | private function getGuardModel($guard) 137 | { 138 | return app(config('mojito.guards.' . $guard . '.model')); 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /src/Http/Controllers/CaptchaController.php: -------------------------------------------------------------------------------- 1 | 16 | * @param CaptchaBuilder $captchaBuilder 17 | * @return \Illuminate\Http\JsonResponse 18 | */ 19 | public function generate(CaptchaBuilder $captchaBuilder) 20 | { 21 | $key = Str::uuid()->toString(); 22 | 23 | $captcha = $captchaBuilder->build(); 24 | $expiredAt = Carbon::now()->addMinutes(config("mojito.captcha_cache_ttl", 2)); 25 | 26 | Cache::put($key, ['code' => $captcha->getPhrase()], $expiredAt); 27 | 28 | return $this->success([ 29 | 'data' => [ 30 | 'key' => $key, 31 | 'expired_at' => $expiredAt->toDateTimeString(), 32 | 'image_content' => $captcha->inline() 33 | ] 34 | ]); 35 | } 36 | } -------------------------------------------------------------------------------- /src/Http/Controllers/ChangePasswordController.php: -------------------------------------------------------------------------------- 1 | old_password, $user->password)) { 22 | return $this->unprocesableEtity([ 23 | 'password' => 'Incorrect password' 24 | ]); 25 | } 26 | 27 | $user->password = bcrypt($request->password); 28 | $user->save(); 29 | 30 | return $this->noContent(); 31 | } 32 | } -------------------------------------------------------------------------------- /src/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | guard); 21 | 22 | if (! $config) { 23 | return $this->forbidden("Undefined guard"); 24 | } 25 | 26 | $cacheCaptcha = Cache::get($request->captcha_key); 27 | if (! $cacheCaptcha) { 28 | return $this->forbidden('Verification code has expired'); 29 | } 30 | 31 | if (strtolower($cacheCaptcha['code']) != strtolower($request->captcha)) { 32 | return $this->forbidden('Please enter correct verify code'); 33 | } 34 | 35 | Cache::forget($request->captcha_key); 36 | 37 | $conditions = data_get($config, 'conditions', []); 38 | 39 | $user = app($config['model'])->where(function ($query) use ($config) { 40 | foreach ($config['login_fields'] as $field) { 41 | $query->orWhere($field, request()->get('username')); 42 | } 43 | return $query; 44 | })->when($conditions, function ($query) use ($conditions) { 45 | return $query->where($conditions); 46 | })->first(); 47 | 48 | if (! $user || ! Hash::check($request->password, $user->password)) { 49 | throw ValidationException::withMessages([ 50 | 'username' => ['The provided credentials are incorrect.'], 51 | ]); 52 | } 53 | 54 | PersonalAccessToken::query()->where("tokenable_type", $config['model']) 55 | ->where("name", $request->guard) 56 | ->where("tokenable_id", $user->id) 57 | ->delete(); 58 | 59 | return response()->json([ 60 | 'data' => [ 61 | 'token' => $user->createToken($request->guard)->plainTextToken, 62 | ] 63 | ]); 64 | } 65 | 66 | /** 67 | * logout 68 | * @return \Illuminate\Http\Response 69 | */ 70 | public function logout() 71 | { 72 | Auth::user()->currentAccessToken()->delete(); 73 | 74 | return $this->noContent(); 75 | } 76 | } -------------------------------------------------------------------------------- /src/Http/Controllers/MenuController.php: -------------------------------------------------------------------------------- 1 | 16 | * @param Request $request 17 | * @return \Illuminate\Http\JsonResponse 18 | */ 19 | public function index(Request $request) 20 | { 21 | $menus = Menu::query() 22 | ->where('guard_name', $request->input('guard_name', 'admin')) 23 | ->orderBy('sequence', 'desc') 24 | ->get(); 25 | 26 | return response()->json(['data' => make_tree($menus->toArray())]); 27 | } 28 | /** 29 | * @author moell 30 | * @param CreateOrUpdateRequest $request 31 | * @return \Illuminate\Http\Response 32 | */ 33 | public function store(CreateOrUpdateRequest $request) 34 | { 35 | Menu::create($request->all()); 36 | 37 | return $this->created(); 38 | } 39 | 40 | /** 41 | * @author moell 42 | * @return \Illuminate\Http\JsonResponse 43 | */ 44 | public function my(Request $request) 45 | { 46 | $guardName = data_get(Auth::user()->currentAccessToken(), "name", "admin"); 47 | 48 | $userPermissions = Auth::user()->getAllPermissions()->pluck('name'); 49 | $menus = Menu::query() 50 | ->where('guard_name', $guardName) 51 | ->orderBy('sequence', 'desc') 52 | ->get() 53 | ->filter(function ($item) use ($userPermissions) { 54 | return !$item->permission_name || $userPermissions->contains($item->permission_name); 55 | }); 56 | 57 | return response()->json(['data' => make_tree($menus->toArray())]); 58 | } 59 | 60 | /** 61 | * @author moell 62 | * @param CreateOrUpdateRequest $request 63 | * @param $id 64 | * @return \Illuminate\Http\Response 65 | */ 66 | public function update(CreateOrUpdateRequest $request, $id) 67 | { 68 | $menu = Menu::query()->findOrFail($id); 69 | 70 | $menu->update($request->toArray()); 71 | 72 | return $this->noContent(); 73 | } 74 | 75 | /** 76 | * @author moell 77 | * @param $id 78 | * @return MenuResource 79 | */ 80 | public function show($id) 81 | { 82 | return new MenuResource(Menu::query()->findOrFail($id)); 83 | } 84 | 85 | /** 86 | * @author moell 87 | * @param $id 88 | * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response 89 | */ 90 | public function destroy($id) 91 | { 92 | $menu = Menu::query()->findOrFail($id); 93 | 94 | if (Menu::query()->where('parent_id', $menu->id)->count()) { 95 | return $this->unprocesableEtity([ 96 | 'parent_id' => 'Please delete the submenu first.' 97 | ]); 98 | } 99 | 100 | $menu->delete(); 101 | 102 | return $this->noContent(); 103 | } 104 | } -------------------------------------------------------------------------------- /src/Http/Controllers/PermissionController.php: -------------------------------------------------------------------------------- 1 | 18 | * @param Request $request 19 | * @return PermissionCollection 20 | */ 21 | public function index(Request $request) 22 | { 23 | $permissions =tap(Permission::latest(), function ($query) { 24 | $query->where(request_intersect([ 25 | 'name', 'guard_name', 'pg_id' 26 | ])); 27 | })->with('group')->paginate(); 28 | 29 | return new PermissionCollection($permissions); 30 | } 31 | 32 | /** 33 | * @author moell 34 | * @param $id 35 | * @return PermissionResource 36 | */ 37 | public function show($id) 38 | { 39 | return new PermissionResource(Permission::query()->findOrFail($id)); 40 | } 41 | 42 | /** 43 | * @author moell 44 | * @param CreateOrUpdateRequest $request 45 | * @return \Illuminate\Http\Response 46 | */ 47 | public function store(CreateOrUpdateRequest $request) 48 | { 49 | $attributes = $request->only([ 50 | 'pg_id', 'name', 'guard_name', 'display_name', 'icon', 'sequence', 'description' 51 | ]); 52 | $attributes['created_name'] = Auth::user()->name; 53 | 54 | Permission::create($attributes); 55 | 56 | return $this->created(); 57 | } 58 | 59 | /** 60 | * @author moell 61 | * @param CreateOrUpdateRequest $request 62 | * @param $id 63 | * @return \Illuminate\Http\Response 64 | */ 65 | public function update(CreateOrUpdateRequest $request, $id) 66 | { 67 | $permission = Permission::query()->findOrFail($id); 68 | 69 | $attributes = $request->only([ 70 | 'pg_id', 'name', 'guard_name', 'display_name', 'icon', 'sequence', 'description' 71 | ]); 72 | 73 | $attributes['updated_name'] = Auth::user()->name; 74 | 75 | $isset = Permission::query() 76 | ->where(['name' => $attributes['name'], 'guard_name' => $attributes['guard_name']]) 77 | ->where('id', '!=', $id) 78 | ->count(); 79 | 80 | if ($isset) { 81 | throw PermissionAlreadyExists::create($attributes['name'], $attributes['guard_name']); 82 | } 83 | 84 | $permission->update($attributes); 85 | 86 | return $this->noContent(); 87 | } 88 | 89 | /** 90 | * @author moell 91 | * @param $id 92 | * @return \Illuminate\Http\Response 93 | */ 94 | public function destroy($id) 95 | { 96 | permission::query()->findOrFail($id)->delete(); 97 | 98 | return $this->noContent(); 99 | } 100 | 101 | /** 102 | * @author moell 103 | * @return \Illuminate\Http\JsonResponse 104 | */ 105 | public function allUserPermission() 106 | { 107 | return response()->json(['data' => Auth::user()->getAllPermissions()->pluck('name')]); 108 | } 109 | } -------------------------------------------------------------------------------- /src/Http/Controllers/PermissionGroupController.php: -------------------------------------------------------------------------------- 1 | 17 | * @param Request $request 18 | * @return PermissionGroupCollection 19 | */ 20 | public function index(Request $request) 21 | { 22 | $permissionGroups = tap(PermissionGroup::latest(), function ($query) { 23 | $query->where(request_intersect(['name'])); 24 | })->paginate(); 25 | 26 | return new PermissionGroupCollection($permissionGroups); 27 | } 28 | 29 | /** 30 | * @author moell 31 | * @param Request $request 32 | * @return PermissionGroupCollection 33 | */ 34 | public function all(Request $request) 35 | { 36 | $permissionGroups = PermissionGroup::latest()->get(); 37 | 38 | return new PermissionGroupCollection($permissionGroups); 39 | } 40 | 41 | /** 42 | * @param $guardName 43 | * @return \Illuminate\Http\JsonResponse 44 | */ 45 | public function guardNameForPermissions($guardName) 46 | { 47 | $permissionGroups = PermissionGroup::query() 48 | ->with(['permission' => function ($query) use ($guardName) { 49 | $query->where('guard_name', $guardName); 50 | }]) 51 | ->get()->filter(function($item) { 52 | return count($item->permission) > 0; 53 | }); 54 | 55 | return response()->json([ 56 | 'data' => array_values($permissionGroups->toArray()) 57 | ]); 58 | } 59 | 60 | /** 61 | * @author moell 62 | * @param CreateOrUpdateRequest $request 63 | * @return \Illuminate\Http\Response 64 | */ 65 | public function store(CreateOrUpdateRequest $request) 66 | { 67 | PermissionGroup::create(request_intersect(['name'])); 68 | 69 | return $this->created(); 70 | } 71 | 72 | /** 73 | * @author moell 74 | * @param $id 75 | * @return PermissionGroupResource 76 | */ 77 | public function show($id) 78 | { 79 | return new PermissionGroupResource(PermissionGroup::findOrFail($id)); 80 | } 81 | 82 | /** 83 | * @author moell 84 | * @param CreateOrUpdateRequest $request 85 | * @param $id 86 | * @return \Illuminate\Http\Response 87 | */ 88 | public function update(CreateOrUpdateRequest $request, $id) 89 | { 90 | PermissionGroup::findOrFail($id)->update(request_intersect([ 91 | 'name' 92 | ])); 93 | 94 | return $this->noContent(); 95 | } 96 | 97 | /** 98 | * @param $id 99 | * @return \Illuminate\Http\JsonResponse|\Illuminate\Http\Response 100 | */ 101 | public function destroy($id) 102 | { 103 | $permissionGroup = PermissionGroup::findOrFail($id); 104 | 105 | if (Permission::query()->where('pg_id', $permissionGroup->id)->count()) { 106 | return $this->unprocesableEtity([ 107 | 'pg_id' => 'Please move or delete the vesting permission.' 108 | ]); 109 | } 110 | 111 | $permissionGroup->delete(); 112 | 113 | return $this->noContent(); 114 | } 115 | } -------------------------------------------------------------------------------- /src/Http/Controllers/RoleController.php: -------------------------------------------------------------------------------- 1 | 18 | * @param Request $request 19 | * @return RoleCollection 20 | */ 21 | public function index(Request $request) 22 | { 23 | return new RoleCollection(Role::query()->where(request_intersect(['name']))->paginate()); 24 | } 25 | 26 | /** 27 | * @author moell 28 | * @param $guardName 29 | * @return RoleCollection 30 | */ 31 | public function guardNameRoles($guardName) 32 | { 33 | return new RoleCollection(Role::query()->where('guard_name', $guardName)->get()); 34 | } 35 | 36 | public function show($id) 37 | { 38 | return new RoleResource(Role::query()->findOrFail($id)); 39 | } 40 | 41 | /** 42 | * @author moell 43 | * @param CreateOrUpdateRequest $request 44 | * @return \Illuminate\Http\Response 45 | */ 46 | public function store(CreateOrUpdateRequest $request) 47 | { 48 | Role::create(request_intersect([ 49 | 'name', 'guard_name', 'description' 50 | ])); 51 | 52 | return $this->created(); 53 | } 54 | 55 | /** 56 | * @author moell 57 | * @param CreateOrUpdateRequest $request 58 | * @param $id 59 | * @return \Illuminate\Http\Response 60 | */ 61 | public function update(CreateOrUpdateRequest $request, $id) 62 | { 63 | if (Role::where(request_intersect(['name', 'guard_name']))->where('id', '!=', $id)->count()) { 64 | throw RoleAlreadyExists::create($request->name, $request->guard_name); 65 | } 66 | 67 | $role = Role::query()->findOrFail($id); 68 | 69 | $role->update(request_intersect([ 70 | 'name', 'guard_name', 'description' 71 | ])); 72 | 73 | return $this->noContent(); 74 | } 75 | 76 | /** 77 | * @author moell 78 | * @param $id 79 | * @return \Illuminate\Http\Response 80 | */ 81 | public function destroy($id) 82 | { 83 | Role::destroy($id); 84 | 85 | return $this->noContent(); 86 | } 87 | 88 | /** 89 | * @author moell 90 | * @param $id 91 | * @return PermissionCollection 92 | */ 93 | public function permissions($id) 94 | { 95 | $role = Role::query()->findOrFail($id); 96 | 97 | return new PermissionCollection($role->permissions); 98 | } 99 | 100 | /** 101 | * Assign permission 102 | * 103 | * @author moell 104 | * @param $id 105 | * @param Request $request 106 | * @return \Illuminate\Http\Response 107 | */ 108 | public function assignPermissions($id, Request $request) 109 | { 110 | $role = Role::query()->findOrFail($id); 111 | 112 | $role->syncPermissions($request->input('permissions', [])); 113 | 114 | return $this->noContent(); 115 | } 116 | } -------------------------------------------------------------------------------- /src/Http/Middleware/Authenticate.php: -------------------------------------------------------------------------------- 1 | 13 | * @param $request 14 | * @param \Closure $next 15 | * @return mixed 16 | */ 17 | public function handle($request, \Closure $next) 18 | { 19 | $permission = Route::currentRouteName(); 20 | 21 | if (Auth::user()->hasPermissionTo($permission)) { 22 | return $next($request); 23 | } 24 | 25 | throw UnauthorizedException::forPermissions([$permission]); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Http/MojitoResponse.php: -------------------------------------------------------------------------------- 1 | 13 | * @param string $content 14 | * @return Response 15 | */ 16 | protected function created($content = '') 17 | { 18 | return new Response($content, Response::HTTP_CREATED); 19 | } 20 | 21 | /** 22 | * 202 23 | * 24 | * @author moell 25 | * @return Response 26 | */ 27 | protected function accepted() 28 | { 29 | return new Response('', Response::HTTP_ACCEPTED); 30 | } 31 | 32 | /** 33 | * 204 34 | * 35 | * @author moell 36 | * @return Response 37 | */ 38 | protected function noContent() 39 | { 40 | return new Response('', Response::HTTP_NO_CONTENT); 41 | } 42 | 43 | /** 44 | * 400 45 | * 46 | * @author moell 47 | * @param $message 48 | * @param array $headers 49 | * @param int $options 50 | * @return \Illuminate\Http\JsonResponse 51 | */ 52 | protected function badRequest($message, array $headers = [], $options = 0) 53 | { 54 | return response()->json([ 55 | 'message' => $message 56 | ], Response::HTTP_BAD_REQUEST, $headers, $options); 57 | } 58 | 59 | /** 60 | * 401 61 | * 62 | * @author moell 63 | * @param string $message 64 | * @param array $headers 65 | * @param int $options 66 | * @return \Illuminate\Http\JsonResponse 67 | */ 68 | protected function unauthorized($message = '', array $headers = [], $options = 0) 69 | { 70 | return response()->json([ 71 | 'message' => $message ? $message : 'Token Signature could not be verified.' 72 | ], Response::HTTP_UNAUTHORIZED, $headers, $options); 73 | } 74 | 75 | /** 76 | * 403 77 | * 78 | * @author moell 79 | * @param string $message 80 | * @param array $headers 81 | * @param int $options 82 | * @return \Illuminate\Http\JsonResponse 83 | */ 84 | protected function forbidden($message = '', array $headers = [], $options = 0) 85 | { 86 | return response()->json([ 87 | 'message' => $message ? $message : 'Insufficient permissions.' 88 | ], Response::HTTP_FORBIDDEN, $headers, $options); 89 | } 90 | 91 | /** 92 | * 422 93 | * 94 | * @author moell 95 | * @param array $errors 96 | * @param array $headers 97 | * @param string $message 98 | * @param int $options 99 | * @return \Illuminate\Http\JsonResponse 100 | */ 101 | protected function unprocesableEtity(array $errors = [], array $headers = [], $message = '', $options = 0) 102 | { 103 | return response()->json([ 104 | 'message' => $message ? $message : '422 Unprocessable Entity', 105 | 'errors' => $errors 106 | ], Response::HTTP_UNPROCESSABLE_ENTITY, $headers, $options); 107 | } 108 | 109 | /** 110 | * 200 111 | * 112 | * @author moell 113 | * @param array $data 114 | * @param array $headers 115 | * @param int $options 116 | * @return \Illuminate\Http\JsonResponse 117 | */ 118 | protected function success(array $data, array $headers = [], $options = 0) 119 | { 120 | return response()->json($data, Response::HTTP_OK, $headers, $options); 121 | } 122 | } -------------------------------------------------------------------------------- /src/Http/Requests/AdminUser/CreateOrUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 25 | * @return array 26 | */ 27 | public function rules() 28 | { 29 | $rules = [ 30 | 'name' => 'required|max:255' 31 | ]; 32 | 33 | switch ($this->method()) { 34 | case 'POST': 35 | $rules['password'] = 'required|min:6|max:32'; 36 | $rules['username'] = 'required|unique:' . AdminUserFactory::adminUser()->getTable(); 37 | break; 38 | case 'PATCH': 39 | $rules['password'] = 'nullable|min:8|max:32'; 40 | break; 41 | } 42 | 43 | return $rules; 44 | } 45 | } -------------------------------------------------------------------------------- /src/Http/Requests/ChangePasswordRequest.php: -------------------------------------------------------------------------------- 1 | 24 | * @return array 25 | */ 26 | public function rules() 27 | { 28 | return [ 29 | 'old_password' => 'required|min:6|max:32', 30 | 'password' => 'required|min:8|max:32|confirmed', 31 | 'password_confirmation' => 'required|min:8|max:32' 32 | ]; 33 | } 34 | } -------------------------------------------------------------------------------- /src/Http/Requests/Menu/CreateOrUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 24 | * @return array 25 | */ 26 | public function rules() 27 | { 28 | return [ 29 | 'parent_id' => 'required|numeric', 30 | 'name' => 'required', 31 | 'guard_name' => 'required', 32 | 'is_link' => 'in:0,1', 33 | 'uri' => 'required' 34 | ]; 35 | } 36 | } -------------------------------------------------------------------------------- /src/Http/Requests/Permission/CreateOrUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 25 | * @return array 26 | */ 27 | public function rules() 28 | { 29 | $rules = [ 30 | 'name' => 'required|max:255', 31 | 'guard_name' => 'required|max:255', 32 | 'display_name' => 'required:max:50', 33 | 'pg_id' => 'required|numeric', 34 | 'sequence' => 'numeric' 35 | ]; 36 | 37 | return $rules; 38 | } 39 | } -------------------------------------------------------------------------------- /src/Http/Requests/PermissionGroup/CreateOrUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 24 | * @return array 25 | */ 26 | public function rules() 27 | { 28 | $rules = [ 29 | 'name' => 'required|max:255' 30 | ]; 31 | 32 | return $rules; 33 | } 34 | } -------------------------------------------------------------------------------- /src/Http/Requests/Role/CreateOrUpdateRequest.php: -------------------------------------------------------------------------------- 1 | 24 | * @return array 25 | */ 26 | public function rules() 27 | { 28 | $rules = [ 29 | 'name' => 'required|max:255', 30 | 'guard_name' => 'required|max:255' 31 | ]; 32 | 33 | return $rules; 34 | } 35 | } -------------------------------------------------------------------------------- /src/Models/AdminUser.php: -------------------------------------------------------------------------------- 1 | belongsTo(PermissionGroup::class, 'pg_id'); 11 | } 12 | } -------------------------------------------------------------------------------- /src/Models/PermissionGroup.php: -------------------------------------------------------------------------------- 1 | hasMany('Moell\Mojito\Models\Permission', 'pg_id'); 15 | } 16 | } -------------------------------------------------------------------------------- /src/Models/User.php: -------------------------------------------------------------------------------- 1 | guard_name; 21 | } 22 | 23 | } -------------------------------------------------------------------------------- /src/Providers/MojitoServiceProvider.php: -------------------------------------------------------------------------------- 1 | app->runningInConsole()) { 20 | $this->registerMigrations(); 21 | 22 | $this->commands([ 23 | InstallCommand::class, 24 | ]); 25 | 26 | $this->publishes([ 27 | __DIR__.'/../../config/mojito.php' => config_path('mojito.php'), 28 | ], 'config'); 29 | } 30 | 31 | $this->registerRouter(); 32 | } 33 | 34 | /** 35 | * Register any application services. 36 | * 37 | * @return void 38 | */ 39 | public function register() 40 | { 41 | // 42 | } 43 | 44 | private function registerMigrations() 45 | { 46 | $migrationsPath = __DIR__ . '/../../database/migrations/'; 47 | 48 | $items = [ 49 | 'create_admin_table.php', 50 | 'add_custom_field_permission_tables.php', 51 | 'create_menu_table.php', 52 | 'create_permission_group_table.php' 53 | ]; 54 | 55 | $paths = []; 56 | foreach ($items as $key => $name) { 57 | $paths[$migrationsPath . $name] = database_path('migrations') . "/". $this->formatTimestamp($key+1) . '_' . $name; 58 | } 59 | 60 | $this->publishes($paths, 'migrations'); 61 | } 62 | 63 | /** 64 | * @param $addition 65 | * @return false|string 66 | */ 67 | private function formatTimestamp($addition) 68 | { 69 | return date('Y_m_d_His', time() + $addition); 70 | } 71 | 72 | /** 73 | * 注册路由 74 | * 75 | * @author moell 76 | */ 77 | private function registerRouter() 78 | { 79 | if (strpos($this->app->version(), 'Lumen') === false && !$this->app->routesAreCached()) { 80 | app('router')->middleware('api')->group(__DIR__.'/../routes.php'); 81 | } else { 82 | require __DIR__.'/../routes.php'; 83 | } 84 | } 85 | } -------------------------------------------------------------------------------- /src/Resources/AdminUser.php: -------------------------------------------------------------------------------- 1 | $this->id, 14 | 'name' => $this->name, 15 | 'username' => $this->username, 16 | 'status' => $this->status ? true : false, 17 | 'created_at' => (string)$this->created_at, 18 | 'updated_at' => (string)$this->updated_at 19 | ]; 20 | } 21 | } -------------------------------------------------------------------------------- /src/Resources/AdminUserCollection.php: -------------------------------------------------------------------------------- 1 | $this->collection 14 | ]; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Resources/Menu.php: -------------------------------------------------------------------------------- 1 | $this->id, 14 | 'name' => $this->name, 15 | 'guard_name' => $this->guard_name, 16 | 'icon' => $this->icon, 17 | 'uri' => $this->uri, 18 | 'is_link' => $this->is_link, 19 | 'created_at' => (string)$this->created_at, 20 | 'updated_at' => (string)$this->updated_at 21 | ]; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Resources/Permission.php: -------------------------------------------------------------------------------- 1 | $this->id, 14 | 'pg_id' => $this->pg_id, 15 | 'name' => $this->name, 16 | 'guard_name' => $this->guard_name, 17 | 'display_name' => $this->display_name, 18 | 'group' => new PermissionGroup($this->group), 19 | 'icon' => $this->icon, 20 | 'sequence' => $this->sequence, 21 | 'description' => $this->description, 22 | 'created_name' => $this->created_name, 23 | 'updated_name' => $this->updated_name, 24 | 'created_at' => (string)$this->created_at, 25 | 'updated_at' => (string)$this->created_at 26 | ]; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Resources/PermissionCollection.php: -------------------------------------------------------------------------------- 1 | $this->collection 14 | ]; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Resources/PermissionGroup.php: -------------------------------------------------------------------------------- 1 | $this->id, 14 | 'name' => $this->name, 15 | 'created_at' => (string)$this->created_at, 16 | 'updated_at' => (string)$this->updated_at 17 | ]; 18 | } 19 | } -------------------------------------------------------------------------------- /src/Resources/PermissionGroupCollection.php: -------------------------------------------------------------------------------- 1 | $this->collection 14 | ]; 15 | } 16 | } -------------------------------------------------------------------------------- /src/Resources/Role.php: -------------------------------------------------------------------------------- 1 | $this->id, 14 | 'name' => $this->name, 15 | 'guard_name' => $this->guard_name, 16 | 'description' => $this->description, 17 | 'created_at' => (string)$this->created_at, 18 | 'updated_at' => (string)$this->updated_at 19 | ]; 20 | } 21 | } -------------------------------------------------------------------------------- /src/Resources/RoleCollection.php: -------------------------------------------------------------------------------- 1 | $this->collection 14 | ]; 15 | } 16 | } -------------------------------------------------------------------------------- /src/helpers.php: -------------------------------------------------------------------------------- 1 | only(is_array($keys) ? $keys : func_get_args())); 12 | } 13 | } 14 | 15 | if (!function_exists('make_tree')) { 16 | /** 17 | * @param array $list 18 | * @param int $parentId 19 | * @return array 20 | */ 21 | function make_tree(array $list, $parentId = 0) { 22 | $tree = []; 23 | if (empty($list)) { 24 | return $tree; 25 | } 26 | 27 | $newList = []; 28 | foreach ($list as $k => $v) { 29 | $newList[$v['id']] = $v; 30 | } 31 | 32 | foreach ($newList as $value) { 33 | if ($parentId == $value['parent_id']) { 34 | $tree[] = &$newList[$value['id']]; 35 | } elseif (isset($newList[$value['parent_id']])) { 36 | $newList[$value['parent_id']]['children'][] = &$newList[$value['id']]; 37 | } 38 | } 39 | 40 | return $tree; 41 | } 42 | } -------------------------------------------------------------------------------- /src/routes.php: -------------------------------------------------------------------------------- 1 | namespace('\Moell\Mojito\Http\Controllers') 6 | ->prefix(config("mojito.route_prefix", "api")) 7 | ->middleware(config("mojito.middleware.basic", "api")) 8 | ->group(function ($router) { 9 | $router->post("auth/login", "LoginController@authenticate"); 10 | 11 | $router->get("/captcha", "CaptchaController@generate"); 12 | 13 | $router->middleware(config("mojito.middleware.auth", ['auth:sanctum']))->group(function ($router) { 14 | $router->post("auth/logout", "LoginController@logout")->name("auth.logout"); 15 | $router->get('permission-user-all', 'PermissionController@allUserPermission')->name("permission.all-user-permission"); 16 | $router->get('my-menu', 'MenuController@my')->name("menu.my"); 17 | $router->patch('user-change-password', 'ChangePasswordController@changePassword')->name("user.change-password"); 18 | }); 19 | 20 | 21 | $router->middleware(config("mojito.middleware.permission", ['auth:sanctum', 'mojito.permission'])) 22 | ->group(function ($router) { 23 | $router->apiResources([ 24 | 'role' => 'RoleController', 25 | 'permission' => 'PermissionController', 26 | 'admin-user' => 'AdminUserController', 27 | 'permission-group' => 'PermissionGroupController', 28 | 'menu' => 'MenuController', 29 | ]); 30 | 31 | $router->get('role/{id}/permissions', 'RoleController@permissions')->name('role.permissions'); 32 | $router->put('role/{id}/permissions', 'RoleController@assignPermissions')->name('role.assign-permissions'); 33 | $router->get('guard-name-roles/{guardName}', 'RoleController@guardNameRoles')->name('role.guard-name-roles'); 34 | $router->get('admin-user/{id}/roles/{guard}', 'AdminUserController@roles')->name('admin-user.roles'); 35 | $router->put('admin-user/{id}/roles/{guard}', 'AdminUserController@assignRoles')->name('admin-user.assign-roles'); 36 | $router->get('guard-name-for-permissions/{guardName}', 'PermissionGroupController@guardNameForPermissions') 37 | ->name('permission-group.guard-name-for-permission'); 38 | $router->get("permission-group-all", "PermissionGroupController@all")->name("permission-group.all"); 39 | }); 40 | }); 41 | --------------------------------------------------------------------------------