├── assets ├── images │ ├── logo.png │ └── favicon │ │ ├── favicon.ico │ │ ├── apple-icon.png │ │ ├── favicon-16x16.png │ │ ├── favicon-32x32.png │ │ ├── favicon-96x96.png │ │ ├── ms-icon-70x70.png │ │ ├── apple-icon-57x57.png │ │ ├── apple-icon-60x60.png │ │ ├── apple-icon-72x72.png │ │ ├── apple-icon-76x76.png │ │ ├── ms-icon-144x144.png │ │ ├── ms-icon-150x150.png │ │ ├── ms-icon-310x310.png │ │ ├── android-icon-36x36.png │ │ ├── android-icon-48x48.png │ │ ├── android-icon-72x72.png │ │ ├── android-icon-96x96.png │ │ ├── apple-icon-114x114.png │ │ ├── apple-icon-120x120.png │ │ ├── apple-icon-144x144.png │ │ ├── apple-icon-152x152.png │ │ ├── apple-icon-180x180.png │ │ ├── android-icon-144x144.png │ │ ├── android-icon-192x192.png │ │ ├── apple-icon-precomposed.png │ │ ├── browserconfig.xml │ │ └── manifest.json └── plugins │ └── @mdi │ └── font │ ├── fonts │ ├── materialdesignicons-webfont.eot │ ├── materialdesignicons-webfont.ttf │ ├── materialdesignicons-webfont.woff │ └── materialdesignicons-webfont.woff2 │ ├── scss │ ├── materialdesignicons.scss │ ├── _icons.scss │ ├── _core.scss │ ├── _functions.scss │ ├── _path.scss │ ├── _animated.scss │ └── _extras.scss │ ├── .github │ └── ISSUE_TEMPLATE.md │ ├── README.md │ ├── package.json │ └── license.md ├── src ├── Resources │ ├── images │ │ ├── logo.png │ │ └── favicon │ │ │ ├── favicon.ico │ │ │ ├── apple-icon.png │ │ │ ├── favicon-16x16.png │ │ │ ├── favicon-32x32.png │ │ │ ├── favicon-96x96.png │ │ │ ├── ms-icon-70x70.png │ │ │ ├── apple-icon-57x57.png │ │ │ ├── apple-icon-60x60.png │ │ │ ├── apple-icon-72x72.png │ │ │ ├── apple-icon-76x76.png │ │ │ ├── ms-icon-144x144.png │ │ │ ├── ms-icon-150x150.png │ │ │ ├── ms-icon-310x310.png │ │ │ ├── android-icon-36x36.png │ │ │ ├── android-icon-48x48.png │ │ │ ├── android-icon-72x72.png │ │ │ ├── android-icon-96x96.png │ │ │ ├── apple-icon-114x114.png │ │ │ ├── apple-icon-120x120.png │ │ │ ├── apple-icon-144x144.png │ │ │ ├── apple-icon-152x152.png │ │ │ ├── apple-icon-180x180.png │ │ │ ├── android-icon-144x144.png │ │ │ ├── android-icon-192x192.png │ │ │ ├── apple-icon-precomposed.png │ │ │ ├── browserconfig.xml │ │ │ └── manifest.json │ ├── js │ │ ├── components │ │ │ ├── Pages │ │ │ │ ├── PageNotFound.vue │ │ │ │ ├── Issues │ │ │ │ │ └── children │ │ │ │ │ │ ├── ClientWidgets.vue │ │ │ │ │ │ ├── IssuesMeta.vue │ │ │ │ │ │ └── Exception.vue │ │ │ │ ├── Users │ │ │ │ │ ├── UserItem.vue │ │ │ │ │ ├── Users.vue │ │ │ │ │ └── children │ │ │ │ │ │ └── ProjectRightDetails.vue │ │ │ │ └── Projects │ │ │ │ │ ├── children │ │ │ │ │ ├── CreateProjectModal.vue │ │ │ │ │ └── ProjectMeta.vue │ │ │ │ │ ├── Projects.vue │ │ │ │ │ └── ProjectItem.vue │ │ │ ├── _base.vue │ │ │ └── BaseUI │ │ │ │ ├── CardModal.vue │ │ │ │ ├── Pagination.vue │ │ │ │ └── SearchForm.vue │ │ ├── store │ │ │ ├── Modules │ │ │ │ ├── _base-vuex.js │ │ │ │ ├── users.js │ │ │ │ ├── projects.js │ │ │ │ └── issues.js │ │ │ └── main.js │ │ ├── services │ │ │ ├── UserApi.js │ │ │ ├── ProjectApi.js │ │ │ └── IssueApi.js │ │ ├── app.js │ │ ├── bootstrap.js │ │ ├── assets │ │ │ └── scripts │ │ │ │ └── utilities.js │ │ ├── router.js │ │ └── mixins │ │ │ └── issue-client-widget-mixins.js │ ├── sass │ │ └── app.scss │ └── views │ │ └── main.blade.php ├── Routes │ ├── disabled.php │ ├── dsn.php │ └── web.php ├── Facades │ └── Bugphix.php ├── Models │ ├── Server.php │ ├── Client.php │ ├── User.php │ ├── Event.php │ ├── StackTrace.php │ ├── EventUser.php │ ├── EventClient.php │ ├── EventServer.php │ ├── Issue.php │ └── Project.php ├── Http │ ├── Controllers │ │ ├── Controller.php │ │ ├── BugphixController.php │ │ ├── _baseController.php │ │ ├── EventsController.php │ │ ├── UsersController.php │ │ ├── ProjectsController.php │ │ ├── DSNController.php │ │ └── IssuesController.php │ ├── Resources │ │ ├── HeaderResource.php │ │ ├── ServerResource.php │ │ ├── ClientResource.php │ │ ├── StackTraceResource.php │ │ ├── UserResource.php │ │ ├── EventResource.php │ │ ├── ProjectResource.php │ │ └── IssueResource.php │ └── Collections │ │ ├── UserCollection.php │ │ ├── ProjectCollection.php │ │ └── IssueCollection.php ├── Migrations │ ├── 2020_01_01_000004_create_bugphix_users_table.php │ ├── 2020_01_01_000003_create_bugphix_servers_table.php │ ├── 2020_01_01_000002_create_bugphix_clients_table.php │ ├── 2020_01_01_000001_create_bugphix_projects_table.php │ ├── 2020_01_01_000006_create_bugphix_events_table.php │ ├── 2020_01_01_000005_create_bugphix_issues_table.php │ ├── 2020_01_01_000008_create_bugphix_event_users_table.php │ ├── 2020_01_01_000010_create_bugphix_event_clients_table.php │ ├── 2020_01_01_000011_create_bugphix_event_servers_table.php │ └── 2020_01_01_000012_create_bugphix_stack_trace.php ├── Commands │ ├── BugphixAssetsSymlink.php │ ├── BugphixTestCommand.php │ └── InstallCommand.php ├── Publishable │ └── config │ │ └── bugphix.php ├── Bugphix.php ├── Traits │ ├── BugphixHelpers.php │ ├── BugphixProcess.php │ └── BugphixSetter.php └── BugphixServiceProvider.php ├── mix-manifest.json ├── .gitignore ├── .eslintrc.js ├── .editorconfig ├── .github └── ISSUE_TEMPLATE │ ├── feature_request.md │ └── bug_report.md ├── tests ├── TestCase.php └── Unit │ └── RoutingTest.php ├── LICENSE ├── composer.json ├── webpack.mix.js ├── phpunit.xml ├── package.json ├── .circleci └── config.yml ├── README.md └── CODE_OF_CONDUCT.md /assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/logo.png -------------------------------------------------------------------------------- /src/Resources/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/logo.png -------------------------------------------------------------------------------- /assets/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/favicon.ico -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon.png -------------------------------------------------------------------------------- /mix-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "/assets/js/app.js": "/assets/js/app.js", 3 | "/assets/css/app.css": "/assets/css/app.css" 4 | } 5 | -------------------------------------------------------------------------------- /assets/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /assets/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /assets/images/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /assets/images/favicon/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/ms-icon-70x70.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-57x57.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-60x60.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-72x72.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-76x76.png -------------------------------------------------------------------------------- /assets/images/favicon/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/ms-icon-144x144.png -------------------------------------------------------------------------------- /assets/images/favicon/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/ms-icon-150x150.png -------------------------------------------------------------------------------- /assets/images/favicon/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/ms-icon-310x310.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/favicon.ico -------------------------------------------------------------------------------- /assets/images/favicon/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/android-icon-36x36.png -------------------------------------------------------------------------------- /assets/images/favicon/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/android-icon-48x48.png -------------------------------------------------------------------------------- /assets/images/favicon/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/android-icon-72x72.png -------------------------------------------------------------------------------- /assets/images/favicon/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/android-icon-96x96.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-114x114.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-120x120.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-144x144.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-152x152.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-180x180.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon.png -------------------------------------------------------------------------------- /assets/images/favicon/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/android-icon-144x144.png -------------------------------------------------------------------------------- /assets/images/favicon/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/android-icon-192x192.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/favicon-16x16.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/favicon-32x32.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/favicon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/favicon-96x96.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/ms-icon-70x70.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/ms-icon-70x70.png -------------------------------------------------------------------------------- /assets/images/favicon/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/images/favicon/apple-icon-precomposed.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-57x57.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-57x57.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-60x60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-60x60.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-72x72.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-76x76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-76x76.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/ms-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/ms-icon-144x144.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/ms-icon-150x150.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/ms-icon-150x150.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/ms-icon-310x310.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/ms-icon-310x310.png -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/PageNotFound.vue: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /src/Resources/images/favicon/android-icon-36x36.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/android-icon-36x36.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/android-icon-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/android-icon-48x48.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/android-icon-72x72.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/android-icon-72x72.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/android-icon-96x96.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/android-icon-96x96.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-114x114.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-114x114.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-120x120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-120x120.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-144x144.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-152x152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-152x152.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-180x180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-180x180.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/android-icon-144x144.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/android-icon-144x144.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/android-icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/android-icon-192x192.png -------------------------------------------------------------------------------- /src/Resources/images/favicon/apple-icon-precomposed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/src/Resources/images/favicon/apple-icon-precomposed.png -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.eot -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.ttf -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.woff -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bugphix/bugphix-laravel/HEAD/assets/plugins/@mdi/font/fonts/materialdesignicons-webfont.woff2 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /public/hot 3 | /public/storage 4 | /storage/*.key 5 | /vendor 6 | .env 7 | .env.backup 8 | .phpunit.result.cache 9 | Homestead.json 10 | Homestead.yaml 11 | npm-debug.log 12 | yarn-error.log 13 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/scss/materialdesignicons.scss: -------------------------------------------------------------------------------- 1 | /* MaterialDesignIcons.com */ 2 | @import "variables"; 3 | @import "functions"; 4 | @import "path"; 5 | @import "core"; 6 | @import "icons"; 7 | @import "extras"; 8 | @import "animated"; -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | Disclaimer: 2 | Hi there, thanks for contributing! Before anything else, please ensure you didn't mean to create an issue on the main MaterialDesign repo instead. 3 | If this is intentional, just erase this message. Thanks! 4 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/scss/_icons.scss: -------------------------------------------------------------------------------- 1 | @each $key, $value in $mdi-icons { 2 | .#{$mdi-css-prefix}-#{$key}::before { 3 | content: char($value); 4 | } 5 | } 6 | 7 | .#{$mdi-css-prefix}-blank::before { 8 | content: "\F68C"; 9 | visibility: hidden; 10 | } -------------------------------------------------------------------------------- /src/Resources/js/store/Modules/_base-vuex.js: -------------------------------------------------------------------------------- 1 | const baseVuex = { 2 | namespaced: true, 3 | state: { 4 | // states, 5 | }, 6 | getters: { 7 | 8 | }, 9 | mutations: { 10 | 11 | }, 12 | actions: { 13 | 14 | } 15 | }; 16 | 17 | export default baseVuex; 18 | -------------------------------------------------------------------------------- /assets/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /src/Resources/images/favicon/browserconfig.xml: -------------------------------------------------------------------------------- 1 | 2 | #ffffff -------------------------------------------------------------------------------- /src/Routes/disabled.php: -------------------------------------------------------------------------------- 1 | config('bugphix.option.dsn_slug') 11 | ], function(){ 12 | 13 | $routePrefix = '\Bugphix\BugphixLaravel\Http\Controllers'; 14 | Route::any('/{projectID}/{token}', "{$routePrefix}\DSNController@store"); 15 | }); 16 | 17 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/scss/_core.scss: -------------------------------------------------------------------------------- 1 | .#{$mdi-css-prefix}:before, 2 | .#{$mdi-css-prefix}-set { 3 | display: inline-block; 4 | font: normal normal normal #{$mdi-font-size-base}/1 '#{$mdi-font-name}'; // shortening font declaration 5 | font-size: inherit; // can't have font-size inherit on line above, so need to override 6 | text-rendering: auto; // optimizelegibility throws things off #1094 7 | line-height: inherit; 8 | -webkit-font-smoothing: antialiased; 9 | -moz-osx-font-smoothing: grayscale; 10 | } -------------------------------------------------------------------------------- /src/Resources/js/app.js: -------------------------------------------------------------------------------- 1 | require('./bootstrap'); 2 | 3 | import Vue from 'vue' 4 | import axios from 'axios'; 5 | import Vuex from 'vuex' 6 | import router from './router' 7 | import store from './store/main' 8 | import Notifications from 'vue-notification' 9 | 10 | // extensions 11 | Vue.use(Vuex) 12 | Vue.use(Notifications) 13 | 14 | window.axios = axios; 15 | axios.defaults.baseURL = window.Bugphix.api; 16 | 17 | // components 18 | import App from './components/App' 19 | 20 | new Vue({ 21 | el: '#bugphix-app', 22 | components: {App}, 23 | store, 24 | router, 25 | }); 26 | -------------------------------------------------------------------------------- /src/Models/Client.php: -------------------------------------------------------------------------------- 1 | 'array', 25 | ]; 26 | } 27 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/scss/_functions.scss: -------------------------------------------------------------------------------- 1 | @function char($character-code) { 2 | @if function-exists("selector-append") { 3 | @return unquote("\"\\#{$character-code}\""); 4 | } 5 | 6 | @if "\\#{'x'}" == "\\x" { 7 | @return str-slice("\x", 1, 1) + $character-code; 8 | } 9 | @else { 10 | @return #{"\"\\"}#{$character-code + "\""}; 11 | } 12 | } 13 | 14 | @function mdi($name) { 15 | @if map-has-key($mdi-icons, $name) == false { 16 | @warn "Icon #{$name} not found."; 17 | @return ""; 18 | } 19 | @return char(map-get($mdi-icons, $name)); 20 | } -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/scss/_path.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: '#{$mdi-font-name}'; 3 | src: url('#{$mdi-font-path}/#{$mdi-filename}-webfont.eot?v=#{$mdi-version}'); 4 | src: url('#{$mdi-font-path}/#{$mdi-filename}-webfont.eot?#iefix&v=#{$mdi-version}') format('embedded-opentype'), 5 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.woff2?v=#{$mdi-version}') format('woff2'), 6 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.woff?v=#{$mdi-version}') format('woff'), 7 | url('#{$mdi-font-path}/#{$mdi-filename}-webfont.ttf?v=#{$mdi-version}') format('truetype'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | -------------------------------------------------------------------------------- /src/Models/User.php: -------------------------------------------------------------------------------- 1 | 'array', 20 | ]; 21 | 22 | public function scopeSearchUser($query, $keyword = '') 23 | { 24 | if (!$keyword) return $query; 25 | return $query->where('user_unique', 'LIKE', "%$keyword%") 26 | ->orWhere('user_meta', 'LIKE', "%$keyword%"); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /src/Models/Event.php: -------------------------------------------------------------------------------- 1 | where('event_issue_id', $issueId); 19 | } 20 | 21 | public function user() 22 | { 23 | return $this->hasOne('Bugphix\BugphixLaravel\Models\EventUser'); 24 | } 25 | 26 | public static function getTableName() 27 | { 28 | return with(new static)->getTable(); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Http/Resources/HeaderResource.php: -------------------------------------------------------------------------------- 1 | $this->id, 20 | // 'header_event_id' => $this->header_event_id, 21 | // 'header_info' => $this->header_info, 22 | // ]; 23 | 24 | return $this->header_info; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Resources/js/components/_base.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 29 | 30 | 32 | -------------------------------------------------------------------------------- /src/Models/StackTrace.php: -------------------------------------------------------------------------------- 1 | 'array', 24 | ]; 25 | 26 | public function scopeEventId($query, int $eventId) 27 | { 28 | return $query->where('stack_trace_event_id', $eventId); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: jericizon 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. 16 | 2. 17 | 18 | **Expected behavior** 19 | A clear and concise description of what you expected to happen. 20 | 21 | **Screenshots** 22 | If applicable, add screenshots to help explain your problem. 23 | 24 | **Client / Server details (please complete the following information):** 25 | - Server OS: [e.g. iOS] 26 | - Browser [e.g. chrome, safari] 27 | - Laravel version [e.g. 6] 28 | 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/scss/_animated.scss: -------------------------------------------------------------------------------- 1 | // From Font Awesome 2 | .#{$mdi-css-prefix}-spin:before { 3 | -webkit-animation: #{$mdi-css-prefix}-spin 2s infinite linear; 4 | animation: #{$mdi-css-prefix}-spin 2s infinite linear; 5 | } 6 | 7 | @-webkit-keyframes #{$mdi-css-prefix}-spin { 8 | 0% { 9 | -webkit-transform: rotate(0deg); 10 | transform: rotate(0deg); 11 | } 12 | 100% { 13 | -webkit-transform: rotate(359deg); 14 | transform: rotate(359deg); 15 | } 16 | } 17 | 18 | @keyframes #{$mdi-css-prefix}-spin { 19 | 0% { 20 | -webkit-transform: rotate(0deg); 21 | transform: rotate(0deg); 22 | } 23 | 100% { 24 | -webkit-transform: rotate(359deg); 25 | transform: rotate(359deg); 26 | } 27 | } -------------------------------------------------------------------------------- /src/Http/Resources/ServerResource.php: -------------------------------------------------------------------------------- 1 | $this->id, 20 | 'server_name' => $this->server_name, 21 | 'server_os' => $this->server_os, 22 | 'server_os_version' => $this->server_os_version, 23 | 'server_runtime' => $this->server_runtime, 24 | ]; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Issues/children/ClientWidgets.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 34 | 39 | -------------------------------------------------------------------------------- /tests/TestCase.php: -------------------------------------------------------------------------------- 1 | set('database.default', 'testdb'); 26 | // $app['config']->set('database.connections.testdb',[ 27 | // 'driver' => 'sqlite', 28 | // 'database' => ':memory:', 29 | // ]); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Http/Collections/UserCollection.php: -------------------------------------------------------------------------------- 1 | user_meta['name'] 20 | ?? $this->user_meta['email'] 21 | ?? $this->user_meta['username'] 22 | ?? $this->user_unique; 23 | return [ 24 | 'id' => $this->id, 25 | 'user_unique' => $this->user_unique, 26 | 'user_info' => $info, 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000004_create_bugphix_users_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('user_unique')->unique(); 19 | $table->longText('user_meta')->nullable(); 20 | }); 21 | } 22 | 23 | /** 24 | * Reverse the migrations. 25 | * 26 | * @return void 27 | */ 28 | public function down() 29 | { 30 | Schema::dropIfExists('bugphix_users'); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/Models/EventUser.php: -------------------------------------------------------------------------------- 1 | where('event_id', $eventId); 21 | } 22 | 23 | public function scopeUserId($query, int $userId) 24 | { 25 | return $query->where('user_id', $userId); 26 | } 27 | 28 | public function event() 29 | { 30 | return $this->belongsTo('Bugphix\BugphixLaravel\Models\Event'); 31 | } 32 | 33 | public function user() 34 | { 35 | return $this->belongsTo('Bugphix\BugphixLaravel\Models\User'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /assets/images/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /src/Resources/images/favicon/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "App", 3 | "icons": [ 4 | { 5 | "src": "\/android-icon-36x36.png", 6 | "sizes": "36x36", 7 | "type": "image\/png", 8 | "density": "0.75" 9 | }, 10 | { 11 | "src": "\/android-icon-48x48.png", 12 | "sizes": "48x48", 13 | "type": "image\/png", 14 | "density": "1.0" 15 | }, 16 | { 17 | "src": "\/android-icon-72x72.png", 18 | "sizes": "72x72", 19 | "type": "image\/png", 20 | "density": "1.5" 21 | }, 22 | { 23 | "src": "\/android-icon-96x96.png", 24 | "sizes": "96x96", 25 | "type": "image\/png", 26 | "density": "2.0" 27 | }, 28 | { 29 | "src": "\/android-icon-144x144.png", 30 | "sizes": "144x144", 31 | "type": "image\/png", 32 | "density": "3.0" 33 | }, 34 | { 35 | "src": "\/android-icon-192x192.png", 36 | "sizes": "192x192", 37 | "type": "image\/png", 38 | "density": "4.0" 39 | } 40 | ] 41 | } -------------------------------------------------------------------------------- /src/Models/EventClient.php: -------------------------------------------------------------------------------- 1 | where('event_id', $eventId); 21 | } 22 | 23 | public function scopeClientId($query, int $clientId) 24 | { 25 | return $query->where('client_id', $clientId); 26 | } 27 | 28 | public function event() 29 | { 30 | return $this->belongsTo('Bugphix\BugphixLaravel\Models\Event'); 31 | } 32 | 33 | public function client() 34 | { 35 | return $this->belongsTo('Bugphix\BugphixLaravel\Models\Client'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Models/EventServer.php: -------------------------------------------------------------------------------- 1 | where('event_id', $eventId); 21 | } 22 | 23 | public function scopeServerId($query, int $serverID) 24 | { 25 | return $query->where('server_id', $serverID); 26 | } 27 | 28 | public function event() 29 | { 30 | return $this->belongsTo('Bugphix\BugphixLaravel\Models\Event'); 31 | } 32 | 33 | public function server() 34 | { 35 | return $this->belongsTo('Bugphix\BugphixLaravel\Models\Server'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Http/Resources/ClientResource.php: -------------------------------------------------------------------------------- 1 | $this->id, 20 | 'client_method' => $this->client_method, 21 | 'client_url' => $this->client_url, 22 | 'client_browser' => $this->client_browser, 23 | 'client_browser_version' => $this->client_browser_version, 24 | 'client_os' => $this->client_os, 25 | 'client_ip' => $this->client_ip, 26 | 'client_header' => $this->client_header, 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000003_create_bugphix_servers_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('server_name'); 19 | $table->string('server_os'); 20 | $table->string('server_os_version'); 21 | $table->string('server_runtime')->nullable(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | * 28 | * @return void 29 | */ 30 | public function down() 31 | { 32 | Schema::dropIfExists('bugphix_servers'); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | window._ = require('lodash'); 2 | 3 | /** 4 | * We'll load the axios HTTP library which allows us to easily issue requests 5 | * to our Laravel back-end. This library automatically handles sending the 6 | * CSRF token as a header based on the value of the "XSRF" token cookie. 7 | */ 8 | 9 | window.axios = require('axios'); 10 | 11 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 12 | 13 | /** 14 | * Echo exposes an expressive API for subscribing to channels and listening 15 | * for events that are broadcast by Laravel. Echo and event broadcasting 16 | * allows your team to easily build robust real-time web applications. 17 | */ 18 | 19 | // import Echo from 'laravel-echo'; 20 | 21 | // window.Pusher = require('pusher-js'); 22 | 23 | // window.Echo = new Echo({ 24 | // broadcaster: 'pusher', 25 | // key: process.env.MIX_PUSHER_APP_KEY, 26 | // cluster: process.env.MIX_PUSHER_APP_CLUSTER, 27 | // forceTLS: true 28 | // }); 29 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/README.md: -------------------------------------------------------------------------------- 1 | > *Note:* Please use the main [MaterialDesign](https://github.com/Templarian/MaterialDesign/issues) repo to report issues. This repo is for distribution of the Webfont files only. 2 | 3 | # Webfont - Material Design Icons 4 | 5 | Webfont distribution for the [Material Design Icons](https://materialdesignicons.com). 6 | 7 | ``` 8 | npm install @mdi/font 9 | ``` 10 | 11 | > Package built with [@mdi/font-build](https://github.com/Templarian/MaterialDesign-Font-Build). 12 | 13 | ## Related Packages 14 | 15 | [NPM @MDI Organization](https://npmjs.com/org/mdi) 16 | 17 | - JavaScript/Typescript: [MaterialDesign-JS](https://github.com/Templarian/MaterialDesign-JS) 18 | - SVG: [MaterialDesign-SVG](https://github.com/Templarian/MaterialDesign-SVG) 19 | - Font-Build [MaterialDesign-Font-Build](https://github.com/Templarian/MaterialDesign-Font-Build) 20 | 21 | ## Learn More 22 | 23 | - [MaterialDesignIcons.com](https://materialdesignicons.com) 24 | - https://github.com/Templarian/MaterialDesign 25 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Issues/children/IssuesMeta.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/Commands/BugphixAssetsSymlink.php: -------------------------------------------------------------------------------- 1 | comment("Symlink already exists!"); 35 | return; 36 | } 37 | \App::make('files')->link(__DIR__ . '/../../assets/', public_path('bugphix-assets')); 38 | $this->comment("Symlink created!"); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Http/Resources/StackTraceResource.php: -------------------------------------------------------------------------------- 1 | $this->id, 20 | // 'stack_trace_event_id' => $this->stack_trace_event_id, 21 | 'stack_trace_error_file' => $this->stack_trace_error_file, 22 | 'stack_trace_error_line' => $this->stack_trace_error_line, 23 | 'stack_trace_start_line' => $this->stack_trace_start_line, 24 | 'stack_trace_end_line' => $this->stack_trace_end_line, 25 | 'stack_trace_data' => $this->stack_trace_data, 26 | 'stack_trace_full_log' => $this->stack_trace_full_log, 27 | ]; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Resources/js/services/ProjectApi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | import {serializeParams} from '@/assets/scripts/utilities'; 4 | 5 | export default { 6 | browse(params={}) { 7 | return axios({ 8 | method: 'get', 9 | url: `projects${serializeParams(params)}`, 10 | }) 11 | }, 12 | show(projectId) { 13 | return axios({ 14 | method: 'get', 15 | url: `projects/${projectId}`, 16 | }) 17 | }, 18 | create(data) { 19 | return axios({ 20 | method: 'post', 21 | url: `projects`, 22 | data, 23 | }) 24 | }, 25 | update(projectId, data) { 26 | return axios({ 27 | method: 'put', 28 | url: `projects/${projectId}`, 29 | data, 30 | }) 31 | }, 32 | getActiveProject(projectId=''){ 33 | const url = projectId ? `/${projectId}` : ''; 34 | return axios({ 35 | method: 'get', 36 | url: `get-active-project${url}`, 37 | }) 38 | }, 39 | getProjectListOptions(){ 40 | return axios({ 41 | method: 'get', 42 | url: `get-project-list-options`, 43 | }) 44 | } 45 | }; 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 bugphix 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /tests/Unit/RoutingTest.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 13 | } 14 | // public function is_home_redirected() 15 | // { 16 | // $response = $this->get("{$this->baseUrl}"); 17 | // $response->assertStatus(302); 18 | // } 19 | 20 | // /** @test */ 21 | // public function is_issues_page_okay() 22 | // { 23 | // $response = $this->get("{$this->baseUrl}/issues"); 24 | // $response->assertStatus(200); 25 | // } 26 | 27 | // /** @test */ 28 | // public function is_projects_page_okay() 29 | // { 30 | // $response = $this->get("{$this->baseUrl}/projects"); 31 | // $response->assertStatus(200); 32 | // } 33 | 34 | // /** @test */ 35 | // public function is_users_page_okay() 36 | // { 37 | // $response = $this->get("{$this->baseUrl}/users"); 38 | // $response->assertStatus(200); 39 | // } 40 | } 41 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000002_create_bugphix_clients_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('client_method')->default('GET'); 19 | $table->mediumText('client_url')->nullable(); 20 | $table->string('client_browser'); 21 | $table->string('client_browser_version')->nullable(); 22 | $table->string('client_os'); 23 | $table->ipAddress('client_ip')->nullable(); 24 | $table->longText('client_header')->nullable(); 25 | }); 26 | } 27 | 28 | /** 29 | * Reverse the migrations. 30 | * 31 | * @return void 32 | */ 33 | public function down() 34 | { 35 | Schema::dropIfExists('bugphix_clients'); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000001_create_bugphix_projects_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('project_id')->unique(); 19 | $table->string('project_name'); 20 | $table->string('project_description')->nullable(); 21 | $table->string('project_platform')->default('laravel'); 22 | $table->string('project_token'); 23 | $table->boolean('is_active')->default(1); 24 | $table->timestamps(); 25 | $table->softDeletes(); 26 | }); 27 | } 28 | 29 | /** 30 | * Reverse the migrations. 31 | * 32 | * @return void 33 | */ 34 | public function down() 35 | { 36 | Schema::dropIfExists('bugphix_projects'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000006_create_bugphix_events_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->unsignedBigInteger('event_issue_id'); 19 | $table->string('event_environment')->nullable(); 20 | $table->timestamps(); 21 | 22 | $table->foreign('event_issue_id') 23 | ->references('id') 24 | ->on('bugphix_issues') 25 | ->onDelete('cascade'); 26 | 27 | $table->index([ 28 | 'event_issue_id', 29 | ]); 30 | }); 31 | } 32 | 33 | /** 34 | * Reverse the migrations. 35 | * 36 | * @return void 37 | */ 38 | public function down() 39 | { 40 | Schema::dropIfExists('bugphix_events'); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Resources/js/services/IssueApi.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | 3 | import {serializeParams} from '@/assets/scripts/utilities'; 4 | 5 | export default { 6 | browse(params={}) { 7 | return axios({ 8 | method: 'get', 9 | url: `issues${serializeParams(params)}`, 10 | }) 11 | }, 12 | show(issueId, projectId) { 13 | return axios({ 14 | method: 'get', 15 | url: `issues/${issueId}?project_id=${projectId}`, 16 | }) 17 | }, 18 | update(issueId, data) { 19 | return axios({ 20 | method: 'put', 21 | url: `issues/${issueId}`, 22 | data, 23 | }) 24 | }, 25 | delete(issueId){ 26 | return axios({ 27 | method: 'delete', 28 | url: `issues/${issueId}` 29 | }) 30 | }, 31 | bulkUpdate(issueId, data) { 32 | return axios({ 33 | method: 'put', 34 | url: `bulk-update/issues/${issueId}`, 35 | data, 36 | }) 37 | }, 38 | bulkDelete(issueId, data) { 39 | return axios({ 40 | method: 'delete', 41 | url: `bulk-delete/issues/${issueId}` 42 | }) 43 | }, 44 | getEvent(eventId) { 45 | return axios({ 46 | method: 'get', 47 | url: `events/${eventId}`, 48 | }) 49 | }, 50 | }; 51 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000005_create_bugphix_issues_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->string('issue_project_id'); 19 | $table->mediumText('issue_error_exception'); 20 | $table->mediumText('issue_error_message'); 21 | $table->enum('issue_status', ['unresolved', 'resolved', 'ignored'])->default('unresolved'); 22 | $table->timestamps(); 23 | $table->softDeletes(); 24 | 25 | $table->foreign('issue_project_id') 26 | ->references('project_id') 27 | ->on('bugphix_projects') 28 | ->onDelete('cascade'); 29 | }); 30 | } 31 | 32 | /** 33 | * Reverse the migrations. 34 | * 35 | * @return void 36 | */ 37 | public function down() 38 | { 39 | Schema::dropIfExists('bugphix_issues'); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Http/Controllers/BugphixController.php: -------------------------------------------------------------------------------- 1 | first(); 30 | } else { 31 | $results = Project::first(); 32 | } 33 | 34 | if ($results) $success = true; 35 | 36 | return response()->json([ 37 | 'success' => $success, 38 | 'results' => $results, 39 | ]); 40 | } 41 | 42 | public function getProjectListOptions() 43 | { 44 | $success = false; 45 | $results = Project::withTrashed()->get(); 46 | if ($results) $success = true; 47 | 48 | return response()->json([ 49 | 'success' => $success, 50 | 'results' => $results, 51 | ]); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000008_create_bugphix_event_users_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->unsignedBigInteger('event_id')->unique(); 19 | $table->unsignedBigInteger('user_id')->nullable(); 20 | 21 | $table->foreign('event_id') 22 | ->references('id') 23 | ->on('bugphix_events') 24 | ->onDelete('cascade'); 25 | 26 | $table->foreign('user_id') 27 | ->references('id') 28 | ->on('bugphix_users') 29 | ->onDelete('cascade'); 30 | 31 | $table->index([ 32 | 'event_id', 33 | 'user_id', 34 | ]); 35 | }); 36 | } 37 | 38 | /** 39 | * Reverse the migrations. 40 | * 41 | * @return void 42 | */ 43 | public function down() 44 | { 45 | Schema::dropIfExists('bugphix_event_users'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000010_create_bugphix_event_clients_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->unsignedBigInteger('event_id')->unique(); 19 | $table->unsignedBigInteger('client_id')->nullable(); 20 | 21 | $table->foreign('event_id') 22 | ->references('id') 23 | ->on('bugphix_events') 24 | ->onDelete('cascade'); 25 | 26 | $table->foreign('client_id') 27 | ->references('id') 28 | ->on('bugphix_clients') 29 | ->onDelete('cascade'); 30 | 31 | $table->index([ 32 | 'event_id', 33 | 'client_id', 34 | ]); 35 | }); 36 | } 37 | 38 | /** 39 | * Reverse the migrations. 40 | * 41 | * @return void 42 | */ 43 | public function down() 44 | { 45 | Schema::dropIfExists('bugphix_event_clients'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000011_create_bugphix_event_servers_table.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->unsignedBigInteger('event_id')->unique(); 19 | $table->unsignedBigInteger('server_id')->nullable(); 20 | 21 | $table->foreign('event_id') 22 | ->references('id') 23 | ->on('bugphix_events') 24 | ->onDelete('cascade'); 25 | 26 | $table->foreign('server_id') 27 | ->references('id') 28 | ->on('bugphix_servers') 29 | ->onDelete('cascade'); 30 | 31 | $table->index([ 32 | 'event_id', 33 | 'server_id', 34 | ]); 35 | }); 36 | } 37 | 38 | /** 39 | * Reverse the migrations. 40 | * 41 | * @return void 42 | */ 43 | public function down() 44 | { 45 | Schema::dropIfExists('bugphix_event_servers'); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Migrations/2020_01_01_000012_create_bugphix_stack_trace.php: -------------------------------------------------------------------------------- 1 | bigIncrements('id'); 18 | $table->unsignedBigInteger('stack_trace_event_id'); 19 | $table->mediumText('stack_trace_error_file')->nullable(); 20 | $table->integer('stack_trace_error_line')->default(0); 21 | $table->integer('stack_trace_start_line')->default(1); 22 | $table->longText('stack_trace_full_log')->nullable(); 23 | $table->longText('stack_trace_data')->nullable(); 24 | 25 | $table->foreign('stack_trace_event_id') 26 | ->references('id') 27 | ->on('bugphix_events') 28 | ->onDelete('cascade'); 29 | 30 | $table->index([ 31 | 'stack_trace_event_id' 32 | ]); 33 | }); 34 | } 35 | 36 | /** 37 | * Reverse the migrations. 38 | * 39 | * @return void 40 | */ 41 | public function down() 42 | { 43 | Schema::dropIfExists('bugphix_stack_trace'); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bugphix/bugphix-laravel", 3 | "description": "Capture and monitor detailed error logs with nice dashboard and UI.", 4 | "keywords": [ 5 | "bugphix", 6 | "bugphix-laravel", 7 | "laravel-bugphix", 8 | "laravel-error", 9 | "error", 10 | "error-catcher", 11 | "error-handler", 12 | "error-manager", 13 | "error-monitoring", 14 | "error-watcher", 15 | "error-viewer", 16 | "error-logger" 17 | ], 18 | "type": "library", 19 | "license": "MIT", 20 | "authors": [ 21 | { 22 | "name": "Jeric Izon", 23 | "email": "im.jericizon@gmail.com" 24 | } 25 | ], 26 | "minimum-stability": "dev", 27 | "autoload": { 28 | "psr-4": { 29 | "Bugphix\\BugphixLaravel\\": "src/" 30 | } 31 | }, 32 | "autoload-dev": { 33 | "psr-4": { 34 | "Bugphix\\BugphixLaravel\\Tests\\": "tests/" 35 | } 36 | }, 37 | "extra": { 38 | "laravel": { 39 | "providers": [ 40 | "Bugphix\\BugphixLaravel\\BugphixServiceProvider" 41 | ] 42 | } 43 | }, 44 | "require": { 45 | "php": ">=7.0.0", 46 | "guzzlehttp/guzzle": "^6.3|^7.0" 47 | }, 48 | "require-dev": { 49 | "phpunit/phpunit": "~6.0", 50 | "laravel/laravel": "~6.0", 51 | "orchestra/testbench": "^4.0" 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /webpack.mix.js: -------------------------------------------------------------------------------- 1 | const mix = require('laravel-mix'); 2 | 3 | /* 4 | |-------------------------------------------------------------------------- 5 | | Mix Asset Management 6 | |-------------------------------------------------------------------------- 7 | | 8 | | Mix provides a clean, fluent API for defining some Webpack build steps 9 | | for your Laravel application. By default, we are compiling the Sass 10 | | file for the application as well as bundling up all the JS files. 11 | | 12 | */ 13 | 14 | const assetsPath = 'assets'; 15 | const resources = 'src/Resources'; 16 | 17 | mix.webpackConfig({ 18 | resolve: { 19 | extensions: ['.js', '.vue'], 20 | alias: { 21 | '@': __dirname + `/${resources}/js`, // for alias imports 22 | '@BaseUI': __dirname + `/${resources}/js/components/BaseUI`, 23 | '@Pages': __dirname + `/${resources}/js/components/Pages`, 24 | } 25 | }, 26 | module: { 27 | rules: [ 28 | { 29 | test: /\.pug$/, 30 | oneOf: [ 31 | { 32 | resourceQuery: /^\?vue/, 33 | use: ['pug-plain-loader'] 34 | }, 35 | { 36 | use: ['raw-loader', 'pug-plain-loader'] 37 | } 38 | ] 39 | } 40 | ] 41 | } 42 | }) 43 | .copy(`node_modules/@mdi`, `${assetsPath}/plugins/@mdi`) 44 | .js(`${resources}/js/app.js`, `${assetsPath}/js`) 45 | .sass(`${resources}/sass/app.scss`, `${assetsPath}/css`) 46 | .copy(`${resources}/images`, `${assetsPath}/images`); 47 | -------------------------------------------------------------------------------- /src/Resources/js/components/BaseUI/CardModal.vue: -------------------------------------------------------------------------------- 1 | 35 | 36 | 55 | -------------------------------------------------------------------------------- /src/Routes/web.php: -------------------------------------------------------------------------------- 1 | 'api'], function () use ($routePrefix) { 12 | 13 | Route::group(['prefix' => 'bulk-update'], function () use ($routePrefix) { 14 | Route::put('issues/{ids}', "{$routePrefix}\IssuesController@bulkUpdate"); 15 | }); 16 | 17 | Route::group(['prefix' => 'bulk-delete'], function () use ($routePrefix) { 18 | Route::delete('issues/{ids}', "{$routePrefix}\IssuesController@bulkDelete"); 19 | }); 20 | 21 | Route::resource('projects', "{$routePrefix}\ProjectsController")->except(['create', 'edit']); 22 | Route::resource('issues', "{$routePrefix}\IssuesController")->except(['create', 'store', 'edit']); 23 | Route::resource('events', "{$routePrefix}\EventsController")->only(['index', 'show']); 24 | Route::resource('users', "{$routePrefix}\UsersController")->only(['index', 'show']); 25 | 26 | Route::get('get-project-list-options', "{$routePrefix}\BugphixController@getProjectListOptions"); 27 | Route::get('get-active-project/{project_id?}', "{$routePrefix}\BugphixController@getActiveProject"); 28 | }); 29 | 30 | Route::get('/', "{$routePrefix}\BugphixController@home"); 31 | Route::get('/{any}', "{$routePrefix}\BugphixController@main")->where('any', '.*'); 32 | -------------------------------------------------------------------------------- /phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 15 | ./tests/Unit 16 | 17 | 18 | 19 | ./tests/Feature 20 | 21 | 22 | 23 | 24 | ./src 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/Resources/js/assets/scripts/utilities.js: -------------------------------------------------------------------------------- 1 | const isObjectEmpty = (obj) => { 2 | return Object.entries(obj).length === 0 && obj.constructor === Object 3 | } 4 | 5 | const serialize = (obj) => { 6 | return `?${Object.keys(obj).reduce((a, k) => { a.push(`${k}=${encodeURIComponent(obj[k])}`); return a; }, []).join('&')}`; 7 | } 8 | 9 | const renderEventHtml = (_time) => { 10 | 11 | if(typeof _time === 'string') return _time; 12 | 13 | const {date, formatted, ago} = _time; 14 | let eventTime = formatted || date || ''; 15 | if(!eventTime) return; 16 | return `${ago} ${eventTime}`; 17 | } 18 | 19 | function serializeParams(obj) { 20 | return `?${Object.keys(obj).reduce((a, k) => { a.push(`${k}=${encodeURIComponent(obj[k])}`); return a; }, []).join('&')}`; 21 | } 22 | 23 | function nFormat(num, digits=2) { 24 | var si = [ 25 | { value: 1, symbol: "" }, 26 | { value: 1E3, symbol: "k" }, 27 | { value: 1E6, symbol: "M" }, 28 | { value: 1E9, symbol: "G" }, 29 | { value: 1E12, symbol: "T" }, 30 | { value: 1E15, symbol: "P" }, 31 | { value: 1E18, symbol: "E" } 32 | ]; 33 | var rx = /\.0+$|(\.[0-9]*[1-9])0+$/; 34 | var i; 35 | for (i = si.length - 1; i > 0; i--) { 36 | if (num >= si[i].value) { 37 | break; 38 | } 39 | } 40 | return (num / si[i].value).toFixed(digits).replace(rx, "$1") + si[i].symbol; 41 | } 42 | 43 | export { 44 | isObjectEmpty, 45 | serialize, 46 | renderEventHtml, 47 | serializeParams, 48 | nFormat, 49 | }; 50 | -------------------------------------------------------------------------------- /src/Resources/js/router.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | Vue.use(VueRouter) 4 | 5 | // pages 6 | import Issues from '@/components/Pages/Issues/Issues.vue' 7 | import IssueItem from '@/components/Pages/Issues/IssueItem.vue' 8 | import Projects from '@/components/Pages/Projects/Projects.vue' 9 | import ProjectItem from '@/components/Pages/Projects/ProjectItem.vue' 10 | import Users from '@/components/Pages/Users/Users.vue' 11 | import UserItem from '@/components/Pages/Users/UserItem.vue' 12 | import PageNotFound from '@/components/Pages/PageNotFound.vue' 13 | 14 | const {urlPrefix} = window.Bugphix; 15 | 16 | export default new VueRouter({ 17 | mode: 'history', 18 | base: `${urlPrefix}/`, 19 | routes: [ 20 | { 21 | path: `/issues/:projectId?`, 22 | name: 'issues', 23 | component: Issues, 24 | }, 25 | { 26 | path: `/issues/:projectId/:id`, 27 | name: 'issue-item', 28 | component: IssueItem, 29 | }, 30 | { 31 | path: `/projects`, 32 | name: 'projects', 33 | component: Projects, 34 | }, 35 | { 36 | path: `/projects/detail/:projectId`, 37 | name: 'project-item', 38 | component: ProjectItem, 39 | }, 40 | { 41 | path: `/users`, 42 | name: 'users', 43 | component: Users, 44 | }, 45 | { 46 | path: `/users/detail/:userId`, 47 | name: 'user-item', 48 | component: UserItem, 49 | }, 50 | { 51 | path: "*", 52 | component: PageNotFound 53 | } 54 | ] 55 | }) 56 | -------------------------------------------------------------------------------- /src/Models/Issue.php: -------------------------------------------------------------------------------- 1 | first(); 29 | if ($project) { 30 | $projectId = $project->project_id; 31 | } 32 | } 33 | 34 | return $query->where('issue_project_id', $projectId); 35 | } 36 | 37 | public function scopeStatus($query, $status) 38 | { 39 | return $query->where('issue_status', $status); 40 | } 41 | 42 | public function scopeIsUnresolved($query) 43 | { 44 | return $query->where('issue_status', 'unresolved'); 45 | } 46 | 47 | public static function getTableName() 48 | { 49 | return with(new static)->getTable(); 50 | } 51 | 52 | public static function scopeSearchIssue($query, $keyword = '') 53 | { 54 | if (!$keyword) return $query; 55 | return $query->where('issue_error_exception', 'LIKE', "%$keyword%") 56 | ->orWhere('issue_error_message', 'LIKE', "%$keyword%"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "dev": "npm run development", 5 | "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js", 6 | "watch": "npm run development -- --watch", 7 | "watch-poll": "npm run watch -- --watch-poll", 8 | "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js", 9 | "prod": "npm run production", 10 | "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --no-progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js" 11 | }, 12 | "devDependencies": { 13 | "@vue/eslint-config-airbnb": "^4.0.0", 14 | "axios": "^0.21.1", 15 | "cross-env": "^5.1", 16 | "eslint": "^5.8.0", 17 | "eslint-plugin-vue": "^5.0.0", 18 | "laravel-mix": "^4.0.7", 19 | "lodash": "^4.17.21", 20 | "resolve-url-loader": "^2.3.1", 21 | "sass": "^1.15.2", 22 | "sass-loader": "^7.1.0", 23 | "vue-template-compiler": "^2.6.11" 24 | }, 25 | "dependencies": { 26 | "@mdi/font": "^4.9.95", 27 | "bulma": "^0.8.0", 28 | "js-cookie": "^2.2.1", 29 | "pug": "^3.0.1", 30 | "pug-plain-loader": "^1.0.0", 31 | "vue": "^2.6.11", 32 | "vue-chartjs": "^3.5.0", 33 | "vue-notification": "^1.3.20", 34 | "vue-router": "^3.1.5", 35 | "vuex": "^3.1.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Users/UserItem.vue: -------------------------------------------------------------------------------- 1 | 34 | 35 | 64 | 65 | 67 | -------------------------------------------------------------------------------- /src/Http/Resources/UserResource.php: -------------------------------------------------------------------------------- 1 | user_meta['name'] 20 | ?? $this->user_meta['email'] 21 | ?? $this->user_meta['username'] 22 | ?? $this->user_unique; 23 | return [ 24 | // 'id' => $this->id, 25 | 'user_unique' => $this->user_unique, 26 | 'user_meta' => $this->user_meta, 27 | 'user_initials' => $this->generate($info), 28 | 'user_info' => $info, 29 | ]; 30 | } 31 | 32 | /** 33 | * Generate initials from a name 34 | * 35 | * @param string $name 36 | * @return string 37 | */ 38 | protected function generate(string $name): string 39 | { 40 | $words = explode(' ', $name); 41 | if (count($words) >= 2) { 42 | return strtoupper(substr($words[0], 0, 1) . substr(end($words), 0, 1)); 43 | } 44 | return $this->makeInitialsFromSingleWord($name); 45 | } 46 | 47 | /** 48 | * Make initials from a word with no spaces 49 | * 50 | * @param string $name 51 | * @return string 52 | */ 53 | private function makeInitialsFromSingleWord(string $name): string 54 | { 55 | preg_match_all('#([A-Z]+)#', $name, $capitals); 56 | if (count($capitals[1]) >= 2) { 57 | return substr(implode('', $capitals[1]), 0, 2); 58 | } 59 | return strtoupper(substr($name, 0, 2)); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Http/Collections/ProjectCollection.php: -------------------------------------------------------------------------------- 1 | project_id)->count(); 22 | 23 | $deletedAt = null; 24 | if ($this->deleted_at) { 25 | $deletedAt = [ 26 | 'date' => $this->deleted_at->toDateTimeString(), 27 | 'formatted' => $this->deleted_at->format(config('bugphix.option.date_format')), 28 | 'ago' => $this->deleted_at->diffForHumans() 29 | ]; 30 | } 31 | 32 | return [ 33 | 'project_id' => $this->project_id, 34 | 'project_name' => $this->project_name, 35 | 'project_platform' => $this->project_platform, 36 | 'is_active' => $this->is_active, 37 | 'issues' => $issue, 38 | 'created_at' => [ 39 | 'date' => $this->created_at->toDateTimeString(), 40 | 'formatted' => $this->created_at->format(config('bugphix.option.date_format')), 41 | 'ago' => $this->created_at->diffForHumans() 42 | ], 43 | 'updated_at' => [ 44 | 'date' => $this->updated_at->toDateTimeString(), 45 | 'formatted' => $this->updated_at->format(config('bugphix.option.date_format')), 46 | 'ago' => $this->updated_at->diffForHumans() 47 | ], 48 | 'deleted_at' => $deletedAt, 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Http/Controllers/_baseController.php: -------------------------------------------------------------------------------- 1 | 2 | nav( 3 | v-if="pagesNumber.length > 1" 4 | class="pagination is-small" 5 | role="navigation" 6 | aria-label="pagination" 7 | ) 8 | ul.pagination-list 9 | li 10 | a( 11 | class="pagination-previous" 12 | :disabled="pagination.current_page <= 1" 13 | @click.prevent="changePage(pagination.current_page - 1)" 14 | ) 15 | i.mdi.mdi-chevron-left 16 | 17 | li( 18 | v-for="(page, index) in pagesNumber" 19 | :key="`pagination-${index}`" 20 | ) 21 | a.pagination-link( 22 | :class="{'is-current': page == pagination.current_page}" 23 | @click.prevent="changePage(page)" 24 | ) {{ page }} 25 | 26 | li 27 | a( 28 | class="pagination-next" 29 | :disabled="pagination.current_page >= pagination.last_page" 30 | @click.prevent="changePage(pagination.current_page + 1)" 31 | ) 32 | i.mdi.mdi-chevron-right 33 | 34 | 35 | 72 | -------------------------------------------------------------------------------- /src/Commands/BugphixTestCommand.php: -------------------------------------------------------------------------------- 1 | info('Creating bugphix event'); 37 | $app = app('bugphix'); 38 | $exception = $this->generateException('test bugphix', ['bug' => 'phix']); 39 | 40 | $userUnique = 'ID:'. rand(100,1000); 41 | $userMeta = array( 42 | 'ID' => $userUnique, 43 | 'email' => 'test-user@bugphix.com', 44 | 'name' => 'Bugphix user' 45 | ); 46 | 47 | $app->configUser($userUnique, $userMeta)->catchError($exception); 48 | 49 | $this->comment('Test error created'); 50 | 51 | } catch (Exception $e) { 52 | $this->error("Generating bugphix test event {$e->getMessage()}"); 53 | } 54 | 55 | error_reporting($errorReporting); 56 | } 57 | 58 | /** 59 | * Generate a test exception to send to Sentry. 60 | * 61 | * @param $command 62 | * @param $arg 63 | * 64 | * @return \Exception 65 | */ 66 | protected function generateException($command, $arg): ?Exception 67 | { 68 | try { 69 | throw new Exception('This is a test exception sent from the command [bugphix:test]'); 70 | } catch (Exception $ex) { 71 | return $ex; 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "_args": [ 3 | [ 4 | "@mdi/font@4.9.95", 5 | "D:\\xampp-7.3.10\\htdocs\\projects\\laravel-packages\\bugphix\\bugphix-laravel" 6 | ] 7 | ], 8 | "_from": "@mdi/font@4.9.95", 9 | "_id": "@mdi/font@4.9.95", 10 | "_inBundle": false, 11 | "_integrity": "sha512-m2sbAs+SMwRnWpkMriBxEulwuhmqRyh6X+hdOZlqSxYZUM2C2TaDnQ4gcilzdoAgru2XYnWViZ/xPuSDGgRXVw==", 12 | "_location": "/@mdi/font", 13 | "_phantomChildren": {}, 14 | "_requested": { 15 | "type": "version", 16 | "registry": true, 17 | "raw": "@mdi/font@4.9.95", 18 | "name": "@mdi/font", 19 | "escapedName": "@mdi%2ffont", 20 | "scope": "@mdi", 21 | "rawSpec": "4.9.95", 22 | "saveSpec": null, 23 | "fetchSpec": "4.9.95" 24 | }, 25 | "_requiredBy": [ 26 | "/" 27 | ], 28 | "_resolved": "https://registry.npmjs.org/@mdi/font/-/font-4.9.95.tgz", 29 | "_spec": "4.9.95", 30 | "_where": "D:\\xampp-7.3.10\\htdocs\\projects\\laravel-packages\\bugphix\\bugphix-laravel", 31 | "author": { 32 | "name": "Austin Andrews", 33 | "url": "http://twitter.com/templarian" 34 | }, 35 | "bugs": { 36 | "url": "https://github.com/Templarian/MaterialDesign/issues" 37 | }, 38 | "description": "Dist for Material Design Webfont. This includes the Stock and Community icons in a single webfont collection.", 39 | "homepage": "https://materialdesignicons.com", 40 | "keywords": [ 41 | "material", 42 | "design", 43 | "icons", 44 | "webfont" 45 | ], 46 | "licenses": [ 47 | { 48 | "type": "OFL-1.1", 49 | "url": "http://scripts.sil.org/OFL" 50 | }, 51 | { 52 | "type": "MIT", 53 | "url": "http://opensource.org/licenses/mit-license.html" 54 | } 55 | ], 56 | "name": "@mdi/font", 57 | "repository": { 58 | "type": "git", 59 | "url": "git+https://github.com/Templarian/MaterialDesign-Webfont.git" 60 | }, 61 | "scripts": { 62 | "test": "echo \"Error: no test specified\" && exit 1" 63 | }, 64 | "style": "css/materialdesignicons.css", 65 | "version": "4.9.95" 66 | } 67 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/scss/_extras.scss: -------------------------------------------------------------------------------- 1 | $mdi-sizes: 18 24 36 48; 2 | @each $mdi-size in $mdi-sizes { 3 | .#{$mdi-css-prefix}-#{$mdi-size}px { 4 | &.#{$mdi-css-prefix}-set, 5 | &.#{$mdi-css-prefix}:before { 6 | font-size: $mdi-size * 1px; 7 | } 8 | } 9 | } 10 | 11 | .#{$mdi-css-prefix}-dark { 12 | &:before { 13 | color: rgba(0, 0, 0, 0.54); 14 | } 15 | &.#{$mdi-css-prefix}-inactive:before { 16 | color: rgba(0, 0, 0, 0.26); 17 | } 18 | } 19 | .#{$mdi-css-prefix}-light { 20 | &:before { 21 | color: rgba(255, 255, 255, 1); 22 | } 23 | &.#{$mdi-css-prefix}-inactive:before { 24 | color: rgba(255, 255, 255, 0.3); 25 | } 26 | } 27 | 28 | $mdi-degrees: 45 90 135 180 225 270 315; 29 | @each $mdi-degree in $mdi-degrees { 30 | .#{$mdi-css-prefix}-rotate-#{$mdi-degree}{ 31 | &:before { 32 | -webkit-transform: rotate(#{$mdi-degree}deg); 33 | -ms-transform: rotate(#{$mdi-degree}deg); 34 | transform: rotate(#{$mdi-degree}deg); 35 | } 36 | /* 37 | // Not included in production 38 | &.#{$mdi-css-prefix}-flip-h:before { 39 | -webkit-transform: scaleX(-1) rotate(#{$mdi-degree}deg); 40 | transform: scaleX(-1) rotate(#{$mdi-degree}deg); 41 | filter: FlipH; 42 | -ms-filter: "FlipH"; 43 | } 44 | &.#{$mdi-css-prefix}-flip-v:before { 45 | -webkit-transform: scaleY(-1) rotate(#{$mdi-degree}deg); 46 | -ms-transform: rotate(#{$mdi-degree}deg); 47 | transform: scaleY(-1) rotate(#{$mdi-degree}deg); 48 | filter: FlipV; 49 | -ms-filter: "FlipV"; 50 | } 51 | */ 52 | } 53 | } 54 | .#{$mdi-css-prefix}-flip-h:before { 55 | -webkit-transform: scaleX(-1); 56 | transform: scaleX(-1); 57 | filter: FlipH; 58 | -ms-filter: "FlipH"; 59 | } 60 | .#{$mdi-css-prefix}-flip-v:before { 61 | -webkit-transform: scaleY(-1); 62 | transform: scaleY(-1); 63 | filter: FlipV; 64 | -ms-filter: "FlipV"; 65 | } -------------------------------------------------------------------------------- /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | # PHP CircleCI 2.0 configuration file 2 | # 3 | # Check https://circleci.com/docs/2.0/language-php/ for more details 4 | # 5 | version: 2 6 | jobs: 7 | build: 8 | docker: 9 | # Specify the version you desire here 10 | - image: circleci/php:7.4-cli 11 | 12 | # Specify service dependencies here if necessary 13 | # CircleCI maintains a library of pre-built images 14 | # documented at https://circleci.com/docs/2.0/circleci-images/ 15 | # Using the RAM variation mitigates I/O contention 16 | # for database intensive operations. 17 | # - image: circleci/mysql:5.7-ram 18 | # 19 | # - image: redis:2.8.19 20 | 21 | steps: 22 | - checkout 23 | 24 | - run: sudo apt update # PHP CircleCI 2.0 Configuration File# PHP CircleCI 2.0 Configuration File sudo apt install zlib1g-dev libsqlite3-dev 25 | - run: sudo docker-php-ext-install zip 26 | - run: sudo apt install curl 27 | - run: sudo apt install nodejs 28 | - run: nodejs --version 29 | - run: curl -L https://npmjs.org/install.sh | sudo sh 30 | - run: npm -v 31 | 32 | # Download and cache dependencies 33 | - restore_cache: 34 | keys: 35 | # "composer.lock" can be used if it is committed to the repo 36 | - v1.0.0-dependencies-{{ checksum "composer.json" }} 37 | # fallback to using the latest cache if no exact match is found 38 | - v1.0.0-dependencies- 39 | 40 | - run: composer install 41 | 42 | - save_cache: 43 | key: v1.0.0-dependencies-{{ checksum "composer.json" }} 44 | paths: 45 | - ./vendor 46 | - restore_cache: 47 | keys: 48 | - node-v1.0.0-{{ checksum "package.json" }} 49 | - node-v1.0.0- 50 | - run: npm install 51 | - save_cache: 52 | key: node-v1.0.0-{{ checksum "package.json" }} 53 | paths: 54 | - node_modules 55 | 56 | # prepare the database 57 | #- run: touch storage/testing.sqlite 58 | #- run: php artisan migrate --env=testing --database=sqlite_testing --force 59 | 60 | # run tests with phpunit 61 | - run: ./vendor/bin/phpunit 62 | -------------------------------------------------------------------------------- /src/Http/Resources/EventResource.php: -------------------------------------------------------------------------------- 1 | id)->first(); 26 | if ($user) $user = new UserResource($user->user); 27 | 28 | $client = EventClient::eventid($this->id)->first(); 29 | if ($client) $client = new ClientResource($client->client); 30 | 31 | $server = EventServer::eventid($this->id)->first(); 32 | if ($server) $server = new ServerResource($server->server); 33 | 34 | $stackTrace = StackTrace::eventid($this->id)->first(); 35 | if ($stackTrace) $stackTrace = new StackTraceResource($stackTrace); 36 | 37 | return [ 38 | 'id' => $this->id, 39 | 'event_issue_id' => $this->event_issue_id, 40 | 'event_environment' => $this->event_environment, 41 | 'user' => $user ?? '', 42 | 'client' => $client ?? '', 43 | 'server' => $server ?? '', 44 | 'stack_trace' => $stackTrace ?? '', 45 | 'updated_at' => [ 46 | 'date' => $this->updated_at->toDateTimeString(), 47 | 'formatted' => $this->updated_at->format(config('bugphix.option.date_format')), 48 | 'ago' => $this->updated_at->diffForHumans() 49 | ], 50 | 'created_at' => [ 51 | 'date' => $this->created_at->toDateTimeString(), 52 | 'formatted' => $this->created_at->format(config('bugphix.option.date_format')), 53 | 'ago' => $this->created_at->diffForHumans() 54 | ], 55 | ]; 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Resources/js/store/Modules/users.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import Vue from 'vue'; 3 | import UserApi from '@/services/UserApi'; 4 | 5 | const projects = { 6 | namespaced: true, 7 | state: { 8 | userList: [], 9 | userItem: '', 10 | totalUsers:0, 11 | }, 12 | getters: { 13 | getUserList: (state) => { 14 | if(typeof state.userList.length === 0) return []; 15 | return JSON.parse(JSON.stringify(state.userList)); 16 | }, 17 | }, 18 | mutations: { 19 | setUserList(state, item){ 20 | state.userList = item.results; 21 | }, 22 | setUserItem(state, item){ 23 | state.userItem = item; 24 | }, 25 | setTotalUsers(state, item){ 26 | state.totalUsers = item 27 | } 28 | }, 29 | actions: { 30 | async setUserList({rootGetters, commit, dispatch}, pageId){ 31 | 32 | commit('setIsPageLoading', true, {root:true}); 33 | 34 | const keyword= rootGetters.getFilterByPage('users').keyword || ''; 35 | const sort = rootGetters.getFilterByPage('users').sort || 'latest'; 36 | 37 | const params = { 38 | sort, 39 | keyword, 40 | page: pageId, 41 | } 42 | 43 | UserApi.browse(params) 44 | .then((response) => { 45 | if(response.data.success){ 46 | commit('setUserList', { 47 | results: response.data.results, 48 | pageId, 49 | }); 50 | commit('setTotalUsers', response.data.results.total || 0) 51 | } 52 | }) 53 | .catch((error) => { 54 | console.log(error); 55 | setTimeout(()=>{ 56 | dispatch('setUserList', pageId); 57 | }, 2000) 58 | }) 59 | .then(()=>{ 60 | commit('setIsPageLoading', false, {root:true}); 61 | }) 62 | 63 | }, 64 | async setUserItem({state, commit, dispatch}, userId = ''){ 65 | if(!userId) return; 66 | 67 | UserApi.show(userId) 68 | .then((response) => { 69 | if(response.data.success){ 70 | commit('setUserItem', response.data.results); 71 | } 72 | }) 73 | .catch((error) => { 74 | setTimeout(()=>{ 75 | dispatch('setUserItem', userId); 76 | }, 2000) 77 | }) 78 | }, 79 | } 80 | }; 81 | 82 | export default projects; 83 | -------------------------------------------------------------------------------- /src/Http/Collections/IssueCollection.php: -------------------------------------------------------------------------------- 1 | id); 25 | 26 | $eventUserArray = $event; 27 | $eventUser = EventUser::whereIn('event_id', $eventUserArray->pluck('id'))->count(); 28 | 29 | $deletedAt = null; 30 | if ($this->deleted_at) { 31 | $deletedAt = [ 32 | 'date' => $this->deleted_at->toDateTimeString(), 33 | 'formatted' => $this->deleted_at->format(config('bugphix.option.date_format')), 34 | 'ago' => $this->deleted_at->diffForHumans() 35 | ]; 36 | } 37 | 38 | return [ 39 | 'id' => $this->id, 40 | // 'issue_project_id' => $this->issue_project_id, 41 | 'issue_error_exception' => $this->issue_error_exception, 42 | 'issue_error_message' => [ 43 | 'full' => $this->issue_error_message, 44 | 'excerpt' => Str::limit($this->issue_error_message, 80), 45 | ], 46 | 'issue_status' => $this->issue_status, 47 | 'issue_counts' => $event->count() ?? 1, 48 | 'issue_users' => $eventUser ?? 0, 49 | 'created_at' => [ 50 | 'date' => $this->created_at->toDateTimeString(), 51 | 'formatted' => $this->created_at->format(config('bugphix.option.date_format')), 52 | 'ago' => $this->created_at->diffForHumans() 53 | ], 54 | 'updated_at' => [ 55 | 'date' => $this->updated_at->toDateTimeString(), 56 | 'formatted' => $this->updated_at->format(config('bugphix.option.date_format')), 57 | 'ago' => $this->updated_at->diffForHumans() 58 | ], 59 | 'deleted_at' => $deletedAt, 60 | ]; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Publishable/config/bugphix.php: -------------------------------------------------------------------------------- 1 | env('BUGPHIX_DSN', ''), 14 | 15 | /* 16 | |-------------------------------------------------------------------------- 17 | | Dashboard settings 18 | |-------------------------------------------------------------------------- 19 | */ 20 | 'dashboard' => [ 21 | 'url' => env('BUGPHIX_DASHBOARD_URL', 'bugphix'), // Update this if you need to change the bugphix prefix 22 | 'enable' => env('BUGPHIX_DASHBOARD_ENABLE', true), // If you need to hide the dashboard functions. 23 | // 'middleware' => ['auth'], // add middleware on your admin dashboard 24 | // 'logout_url' => env('APP_URL') . '/logout', // add logout link on your admin dashboard 25 | ], 26 | 27 | /* 28 | |-------------------------------------------------------------------------- 29 | | Assets settings 30 | |-------------------------------------------------------------------------- 31 | */ 32 | 'assets' => [ 33 | 'url' => env('APP_URL') . '/bugphix-assets' // modify this if you need to update the assets file path of bugphix 34 | ], 35 | 36 | /* 37 | |-------------------------------------------------------------------------- 38 | | Project settings 39 | |-------------------------------------------------------------------------- 40 | */ 41 | 'project' => [ 42 | 'length' => 5, // Min:5|Max:80, modify to change the length of unique id for each group 43 | 'prefix' => '', // add custom prefix in generating group id 44 | ], 45 | 46 | /* 47 | |-------------------------------------------------------------------------- 48 | | Miscellaneous 49 | |-------------------------------------------------------------------------- 50 | */ 51 | 'option' => [ 52 | 'date_format' => 'D, M d,y H:i:s A e', // You can change the date format in api return response 53 | 'dsn_slug' => '/bphix-dsn', // this will be slug url to get the requests coming from outside sources. 54 | ], 55 | ]; 56 | -------------------------------------------------------------------------------- /src/Commands/InstallCommand.php: -------------------------------------------------------------------------------- 1 | info('Installing buckle bug...'); 54 | 55 | // publish Bugphix config files 56 | $this->call('vendor:publish', ['--tag' => 'bugphix-config', '--force' => true]); 57 | 58 | // migrate database 59 | $this->info('Migrating database table'); 60 | $this->call('migrate', ['--force' => true]); 61 | 62 | // dump autoloaded files 63 | $this->info('Dumping the autoloaded files and reloading all new files'); 64 | $composer = $this->findComposer(); 65 | $process = new Process([$composer . ' dump-autoload']); 66 | $process->setTimeout(null); 67 | $process->setWorkingDirectory(base_path())->run(); 68 | 69 | $this->info('Creating default group'); 70 | $this->registerLocalProject(); 71 | 72 | $this->call('bugphix:assets-symlink'); 73 | $this->comment("Bugphix successfully installed!"); 74 | 75 | $this->comment(PHP_EOL . 'Try bugphix error run: php artisan bugphix:test'); 76 | } 77 | 78 | private function registerLocalProject() 79 | { 80 | 81 | if (Project::count()) return; // stop creating if there is a default group 82 | 83 | Project::createProject(['project_name' => env('APP_NAME', 'Bugphix')]); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Projects/children/CreateProjectModal.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 97 | -------------------------------------------------------------------------------- /src/Http/Resources/ProjectResource.php: -------------------------------------------------------------------------------- 1 | project_id)->count(); 25 | $event = Issue::where("{$issueTable}.issue_project_id", $this->project_id) 26 | ->join($eventTable, "{$eventTable}.event_issue_id", "{$issueTable}.id") 27 | ->select([ 28 | "{$eventTable}.id", 29 | "{$eventTable}.event_issue_id", 30 | "{$issueTable}.id", 31 | "{$issueTable}.issue_project_id" 32 | ]) 33 | ->count(); 34 | 35 | $deletedAt = null; 36 | if ($this->deleted_at) { 37 | $deletedAt = [ 38 | 'date' => $this->deleted_at->toDateTimeString(), 39 | 'formatted' => $this->deleted_at->format(config('bugphix.option.date_format')), 40 | 'ago' => $this->deleted_at->diffForHumans() 41 | ]; 42 | } 43 | 44 | return [ 45 | 'project_id' => $this->project_id, 46 | 'project_name' => $this->project_name, 47 | 'project_platform' => $this->project_platform, 48 | 'project_token' => $this->project_token, 49 | 'is_active' => $this->is_active, 50 | 'issues' => $issue, 51 | 'events' => $event, 52 | 'issue_table' => $issueTable ?? '', 53 | 'created_at' => [ 54 | 'date' => $this->created_at->toDateTimeString(), 55 | 'formatted' => $this->created_at->format(config('bugphix.option.date_format')), 56 | 'ago' => $this->created_at->diffForHumans() 57 | ], 58 | 'updated_at' => [ 59 | 'date' => $this->updated_at->toDateTimeString(), 60 | 'formatted' => $this->updated_at->format(config('bugphix.option.date_format')), 61 | 'ago' => $this->updated_at->diffForHumans() 62 | ], 63 | 'deleted_at' => $deletedAt, 64 | ]; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |

Bugphix logo

2 | 3 |

4 | Build Status 5 | MIT License 6 | GitHub last commit 7 | 8 | 9 | 10 |

11 | 12 |

Capture and monitor detailed error logs with nice dashboard and UI

13 | 14 |
15 |
16 | 17 |

Dashboard gif

18 | 19 | #### Requirements 20 | 21 | - [Check Laravel 6 requirements](https://laravel.com/docs/6.x#server-requirements) 22 | - [Check Laravel 7 requirements](https://laravel.com/docs/7.x#server-requirements) 23 | 24 | ## Installation 25 | $ composer require bugphix/bugphix-laravel 26 | 27 | ### Publish config files 28 | $ php artisan vendor:publish --tag=bugphix-config 29 | 30 | ### Run artisan installer 31 | $ php artisan bugphix:install 32 | 33 | ### Application usage 34 | edit: /app/Exceptions/Handler.php 35 | 36 | public function report(Exception $exception) 37 | { 38 | if (app()->bound('bugphix') && $this->shouldReport($exception)) { 39 | app('bugphix')->catchError($exception); 40 | } 41 | 42 | parent::report($exception); 43 | } 44 | 45 | ### Test Command 46 | $ php artisan bugphix:test 47 | 48 | ### View admin dashboard 49 | http://localhost:8080/bugphix/issues 50 | 51 | For full documentation: https://bugphix-docs.netlify.com 52 | 53 | ## License 54 | 55 | MIT 56 | 57 | Copyright (c) 2020, Jeric 58 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Users/Users.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/Http/Resources/IssueResource.php: -------------------------------------------------------------------------------- 1 | issue_project_id)->first(); 25 | $event = Event::issueId($this->id); 26 | 27 | $eventUserArray = $event; 28 | $eventUser = EventUser::whereIn('event_id', $eventUserArray->pluck('id'))->count(); 29 | 30 | $latestEvent = $event->latest()->first(); 31 | if ($latestEvent) { 32 | $latestEvent = new EventResource($latestEvent); 33 | } 34 | 35 | $deletedAt = null; 36 | if ($this->deleted_at) { 37 | $deletedAt = [ 38 | 'date' => $this->deleted_at->toDateTimeString(), 39 | 'formatted' => $this->deleted_at->format(config('bugphix.option.date_format')), 40 | 'ago' => $this->deleted_at->diffForHumans() 41 | ]; 42 | } 43 | 44 | return [ 45 | 'id' => $this->id, 46 | // 'issue_project_id' => $this->issue_project_id, 47 | 'issue_project' => $project, 48 | 'issue_error_exception' => $this->issue_error_exception, 49 | 'issue_error_message' => $this->issue_error_message, 50 | 'issue_status' => $this->issue_status, 51 | 'issue_counts' => $event->count() ?? 1, 52 | 'issue_users' => $eventUser ?? 0, 53 | 'latest_event' => $latestEvent, 54 | 'event_ids' => Event::issueId($this->id)->orderBy('id', 'desc')->pluck('id'), 55 | 'created_at' => [ 56 | 'date' => $this->created_at->toDateTimeString(), 57 | 'formatted' => $this->created_at->format(config('bugphix.option.date_format')), 58 | 'ago' => $this->created_at->diffForHumans() 59 | ], 60 | 'updated_at' => [ 61 | 'date' => $this->updated_at->toDateTimeString(), 62 | 'formatted' => $this->updated_at->format(config('bugphix.option.date_format')), 63 | 'ago' => $this->updated_at->diffForHumans() 64 | ], 65 | 'deleted_at' => $deletedAt, 66 | ]; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Bugphix.php: -------------------------------------------------------------------------------- 1 | asset('/js/app.js')); 31 | return stripos($fileHeaders[0], "200 OK") ? true : false; 32 | } catch (\Exception $e) { 33 | return false; 34 | } 35 | } 36 | 37 | public function catchError(Exception $e) 38 | { 39 | // $timeStart = microtime(true); 40 | $this->setProject(); 41 | 42 | if (!$this->bugphixProject) return; 43 | 44 | $this->bugphixException = $e; 45 | $this->setIssue(); 46 | $this->setEvent(); 47 | $this->setStackTrace(); 48 | $this->setServer(); 49 | $this->setClient(); 50 | 51 | if (!$this->hasActiveDSN()) { 52 | $this->bugphixStore(); 53 | } else { 54 | // pass to active dsn instead 55 | try { 56 | $dsn = config('bugphix.dsn'); 57 | $client = new GuzzleClient(); //GuzzleHttp\Client 58 | $res = $client->request('POST', $dsn, [ 59 | 'form_params' => [ 60 | 'bphix_exception' => explode(PHP_EOL, $this->bugphixException), 61 | 'bphix_issue' => $this->bugphixIssue, 62 | 'bphix_event' => $this->bugphixEvent, 63 | 'bphix_stack_trace' => $this->bugphixStackTrace, 64 | 'bphix_server' => $this->bugphixServer, 65 | 'bphix_client' => $this->bugphixClient, 66 | 'bphix_user' => $this->bugphixUser, 67 | ] 68 | ]); 69 | 70 | Log::info($res->getBody()); 71 | } catch (Exception $e) { 72 | dd($e); 73 | } 74 | } 75 | // Log::info('EXEC TIME: ' . number_format(microtime(true) - $timeStart, 2) . PHP_EOL . PHP_EOL); 76 | } 77 | 78 | public function configUser($userUnique = '', array $userMeta = []) 79 | { 80 | if (!$userUnique || !is_array($userMeta)) return $this; 81 | $this->setUser($userUnique, $userMeta); 82 | return $this; 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Http/Controllers/EventsController.php: -------------------------------------------------------------------------------- 1 | latest()->paginate(10); 22 | 23 | // return response()->json([ 24 | // 'success' => true, 25 | // 'results' => $results, 26 | // ]); 27 | } 28 | 29 | /** 30 | * Show the form for creating a new resource. 31 | * 32 | * @return \Illuminate\Http\Response 33 | */ 34 | public function create() 35 | { 36 | // 37 | } 38 | 39 | /** 40 | * Store a newly created resource in storage. 41 | * 42 | * @param \Illuminate\Http\Request $request 43 | * @return \Illuminate\Http\Response 44 | */ 45 | public function store(Request $request) 46 | { 47 | // 48 | } 49 | 50 | /** 51 | * Display the specified resource. 52 | * 53 | * @param int $id 54 | * @return \Illuminate\Http\Response 55 | */ 56 | public function show($id) 57 | { 58 | 59 | $success = false; 60 | $results = []; 61 | 62 | $item = Event::find($id); 63 | 64 | if ($item) { 65 | $success = true; 66 | $results = $item; 67 | } 68 | 69 | return response()->json([ 70 | 'success' => $success, 71 | 'results' => new EventResource($results), 72 | ]); 73 | } 74 | 75 | /** 76 | * Show the form for editing the specified resource. 77 | * 78 | * @param int $id 79 | * @return \Illuminate\Http\Response 80 | */ 81 | public function edit($id) 82 | { 83 | // 84 | } 85 | 86 | /** 87 | * Update the specified resource in storage. 88 | * 89 | * @param \Illuminate\Http\Request $request 90 | * @param int $id 91 | * @return \Illuminate\Http\Response 92 | */ 93 | public function update(Request $request, $id) 94 | { 95 | // 96 | } 97 | 98 | /** 99 | * Remove the specified resource from storage. 100 | * 101 | * @param int $id 102 | * @return \Illuminate\Http\Response 103 | */ 104 | public function destroy($id) 105 | { 106 | // 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/Models/Project.php: -------------------------------------------------------------------------------- 1 | 'boolean', 26 | ]; 27 | 28 | public function scopeCreateProject($query, array $options = []) 29 | { 30 | 31 | if (count($options) === 0) return; 32 | 33 | if (!isset($options['project_name'])) return; 34 | 35 | return $query->create([ 36 | 'project_id' => $this->generateprojectId(), 37 | 'project_name' => $options['project_name'], 38 | 'project_platform' => $options['project_platform'] ?? 'laravel', 39 | 'project_description' => $options['project_description'] ?? null, 40 | 'project_token' => $this->generateToken(), 41 | ]); 42 | } 43 | 44 | public function scopeSearchProject($query, $keyword = '') 45 | { 46 | if (!$keyword) return $query; 47 | return $query->where('project_id', 'LIKE', "%$keyword%") 48 | ->orWhere('project_name', 'LIKE', "%$keyword%"); 49 | } 50 | 51 | public function scopeIsActive($query) 52 | { 53 | return $query->where('is_active', 1); 54 | } 55 | 56 | public function scopeProjectId($query, string $projectId) 57 | { 58 | return $query->where('project_id', $projectId); 59 | } 60 | 61 | public function scopeProjectToken($query, $token) 62 | { 63 | return $query->where('project_token', $token); 64 | } 65 | 66 | private function generateToken() 67 | { 68 | while (1) { 69 | $token = strtoupper(Str::random(10)); 70 | if (self::where('project_token', $token)->first() == '') break; 71 | } 72 | return $token; 73 | } 74 | 75 | private function generateProjectId() 76 | { 77 | $projectIdLength = config('bugphix.project.length'); 78 | $projectPrefix = config('bugphix.project.prefix') ?? ''; 79 | 80 | if ($projectIdLength < 5) $projectIdLength = 5; 81 | if ($projectIdLength > 80) $projectIdLength = 80; 82 | 83 | while (1) { 84 | $projectId = $projectPrefix . strtoupper(Str::random($projectIdLength)); 85 | if (self::where('project_id', $projectId)->first() == '') break; 86 | } 87 | return $projectId; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /src/Traits/BugphixHelpers.php: -------------------------------------------------------------------------------- 1 | $this->bugphixException->getMessage(), 23 | 'exception' => get_class($this->bugphixException), 24 | 'file' => $this->bugphixException->getFile(), 25 | 'line' => $this->bugphixException->getLine() 26 | ]; 27 | } 28 | 29 | protected function generateStackTrace() 30 | { 31 | $e = (object) $this->getExceptionInArray(); 32 | 33 | if (!file_exists($e->file)) return []; 34 | 35 | $readFile = explode("\n", file_get_contents($e->file)); 36 | 37 | if (!is_array($readFile)) return []; 38 | if (count($readFile) === 0) return []; 39 | 40 | $buffLine = 15; 41 | $startLine = ($e->line - 1) - $buffLine; 42 | $endLine = ($buffLine * 2) + 1; 43 | 44 | // check file line limitations 45 | $startLine = $startLine <= 0 ? 0 : $startLine; 46 | 47 | return [ 48 | 'data' => array_slice($readFile, $startLine, $endLine), 49 | 'start_line' => $startLine + 1, // remove 0 index 50 | ]; 51 | } 52 | 53 | protected function getHeaders() 54 | { 55 | if (function_exists('getallheaders')) return getallheaders(); 56 | $headers = []; 57 | foreach ($_SERVER as $name => $value) { 58 | if (substr($name, 0, 5) == 'HTTP_') { 59 | $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value; 60 | } 61 | } 62 | return $headers; 63 | } 64 | 65 | protected function hasActiveDSN() 66 | { 67 | return config('bugphix.dsn') !== '' && filter_var(config('bugphix.dsn'), FILTER_VALIDATE_URL); 68 | } 69 | 70 | protected function logBugphixVars() 71 | { 72 | Log::info(PHP_EOL.PHP_EOL.'----------------- dsn -----------------'); 73 | Log::info($this->bugphixException); 74 | Log::debug($this->bugphixProject); 75 | Log::debug($this->bugphixIssue); 76 | Log::debug($this->bugphixEvent); 77 | Log::info(json_encode($this->bugphixStackTrace)); 78 | Log::debug($this->bugphixServer); 79 | Log::debug($this->bugphixClient); 80 | Log::debug($this->bugphixUser); 81 | Log::info('----------------- dsn END -----------------'.PHP_EOL.PHP_EOL); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Issues/children/Exception.vue: -------------------------------------------------------------------------------- 1 | 50 | 51 | 75 | 76 | 117 | -------------------------------------------------------------------------------- /src/Resources/js/components/BaseUI/SearchForm.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 80 | 81 | 111 | -------------------------------------------------------------------------------- /src/Resources/js/mixins/issue-client-widget-mixins.js: -------------------------------------------------------------------------------- 1 | const issueClientWidgetMixin = { 2 | data() { 3 | return { 4 | clientWidgetsData: [], 5 | } 6 | }, 7 | methods: { 8 | resetWidget(){ 9 | this.clientWidgetsData = []; 10 | }, 11 | pushWidget(icon, content){ 12 | this.clientWidgetsData.push({ 13 | icon: icon || 'mdi-help', 14 | content: content || '', 15 | }); 16 | }, 17 | addUserWidget(){ 18 | if(!this.userDetails) return; 19 | 20 | const {user_unique, user_info} = this.userDetails; 21 | 22 | let content = user_unique; 23 | if(user_unique !== user_info){ 24 | content += `
${user_info}`; 25 | } 26 | this.pushWidget('mdi-account', content); 27 | 28 | }, 29 | addClientWidget(){ 30 | if(!this.clientDetails) return; 31 | let icon; 32 | const {client_browser, client_browser_version, client_os} = this.clientDetails; 33 | 34 | if(client_os){ 35 | icon = 'mdi-help'; 36 | if(client_os.toLowerCase().indexOf('win') !== -1){ 37 | icon = 'mdi-windows'; 38 | } 39 | else if(client_os.toLowerCase().indexOf('mac') !== -1){ 40 | icon = 'mdi-apple'; 41 | } 42 | else if(client_os.toLowerCase().indexOf('linux') !== -1){ 43 | icon = 'mdi-linux'; 44 | } 45 | else if(client_os.toLowerCase().indexOf('ubuntu') !== -1){ 46 | icon = 'mdi-ubuntu'; 47 | } 48 | this.pushWidget(icon, client_os); 49 | } 50 | 51 | if(client_browser){ 52 | 53 | icon = 'mdi-help'; 54 | 55 | if(client_browser.toLowerCase().indexOf('chrome') !== -1){ 56 | icon = 'mdi-google-chrome'; 57 | } 58 | else if(client_browser.toLowerCase().indexOf('explorer') !== -1){ 59 | icon = 'mdi-internet-explorer'; 60 | } 61 | else if(client_browser.toLowerCase().indexOf('firefox') !== -1){ 62 | icon = 'mdi-firefox'; 63 | } 64 | else if(client_browser.toLowerCase().indexOf('safari') !== -1){ 65 | icon = 'mdi-apple-safari'; 66 | } 67 | else if(client_browser.toLowerCase().indexOf('opera') !== -1){ 68 | icon = 'mdi-opera'; 69 | } 70 | else if(client_browser.toLowerCase().indexOf('edge') !== -1){ 71 | icon = 'mdi-edge'; 72 | } 73 | const browserContent = `${client_browser}
Version: ${client_browser_version}`; 74 | this.pushWidget(icon, browserContent); 75 | } 76 | 77 | }, 78 | addServerWidgets(){ 79 | if(!this.serverDetails) return; 80 | const {server_runtime} = this.serverDetails; 81 | let icon = 'mdi-help'; 82 | 83 | if(server_runtime.indexOf('php')){ 84 | icon = 'mdi-language-php'; 85 | } 86 | this.pushWidget(icon, server_runtime); 87 | }, 88 | initWidgets(){ 89 | this.resetWidget(); 90 | this.addUserWidget(); 91 | this.addClientWidget(); 92 | this.addServerWidgets(); 93 | } 94 | } 95 | }; 96 | 97 | export default issueClientWidgetMixin; 98 | -------------------------------------------------------------------------------- /src/Http/Controllers/UsersController.php: -------------------------------------------------------------------------------- 1 | searchUser($_GET['keyword'] ?? ''); 31 | $users = $items->paginate(10); 32 | $results = $users; 33 | $results->data = UserCollection::collection($users); 34 | 35 | return response()->json([ 36 | 'success' => true, 37 | 'results' => $results 38 | ]); 39 | } 40 | 41 | /** 42 | * Show the form for creating a new resource. 43 | * 44 | * @return \Illuminate\Http\Response 45 | */ 46 | public function create() 47 | { 48 | // 49 | } 50 | 51 | /** 52 | * Store a newly created resource in storage. 53 | * 54 | * @param \Illuminate\Http\Request $request 55 | * @return \Illuminate\Http\Response 56 | */ 57 | public function store(Request $request) 58 | { 59 | // 60 | } 61 | 62 | /** 63 | * Display the specified resource. 64 | * 65 | * @param int $id 66 | * @return \Illuminate\Http\Response 67 | */ 68 | public function show($id) 69 | { 70 | 71 | $success = false; 72 | $results = []; 73 | 74 | $item = User::find($id); 75 | 76 | if ($item) { 77 | $success = true; 78 | $results = $item; 79 | } 80 | 81 | return response()->json([ 82 | 'success' => $success, 83 | 'results' => new UserResource($results), 84 | ]); 85 | } 86 | 87 | /** 88 | * Show the form for editing the specified resource. 89 | * 90 | * @param int $id 91 | * @return \Illuminate\Http\Response 92 | */ 93 | public function edit($id) 94 | { 95 | // 96 | } 97 | 98 | /** 99 | * Update the specified resource in storage. 100 | * 101 | * @param \Illuminate\Http\Request $request 102 | * @param int $id 103 | * @return \Illuminate\Http\Response 104 | */ 105 | public function update(Request $request, $id) 106 | { 107 | // 108 | } 109 | 110 | /** 111 | * Remove the specified resource from storage. 112 | * 113 | * @param int $id 114 | * @return \Illuminate\Http\Response 115 | */ 116 | public function destroy($id) 117 | { 118 | // 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Projects/children/ProjectMeta.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 114 | 115 | 125 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Users/children/ProjectRightDetails.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 114 | 115 | 132 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Projects/Projects.vue: -------------------------------------------------------------------------------- 1 | 47 | 48 | 119 | 120 | 138 | -------------------------------------------------------------------------------- /src/Resources/js/store/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue'; 2 | import Vuex from 'vuex'; 3 | import axios from 'axios'; 4 | import issues from './Modules/issues'; 5 | import projects from './Modules/projects'; 6 | import users from './Modules/users'; 7 | import Cookies from 'js-cookie' 8 | 9 | import ProjectApi from '@/services/ProjectApi'; 10 | 11 | Vue.use(Vuex); 12 | 13 | export default new Vuex.Store({ 14 | modules: { 15 | issues, 16 | projects, 17 | users, 18 | }, 19 | state: { 20 | isPageLoading: false, 21 | activeProject: {}, 22 | activePage: '', 23 | searchFilter: {}, 24 | projectListOptions: {}, 25 | }, 26 | getters: { 27 | getActiveProjectId: (state) => { 28 | if( typeof state.activeProject.project_id !== 'undefined' ) return state.activeProject.project_id; 29 | return ''; 30 | }, 31 | getActiveProjectName: (state) => { 32 | if( typeof state.activeProject.project_name !== 'undefined' ) return state.activeProject.project_name; 33 | return ''; 34 | }, 35 | getSearchKeyword: (state) => { 36 | if(typeof state.searchFilter[state.activePage] === 'undefined') return ''; 37 | return state.searchFilter[state.activePage].keyword || ''; 38 | }, 39 | getFilterSort: (state) => { 40 | if(typeof state.searchFilter[state.activePage] === 'undefined') return 'latest'; 41 | return state.searchFilter[state.activePage].sort || ''; 42 | }, 43 | getFilterByPage: (state) => (page) => { 44 | if(typeof state.searchFilter[page] === 'undefined') return {}; 45 | return state.searchFilter[page]; 46 | } 47 | }, 48 | mutations: { 49 | setIsPageLoading(state, item){ 50 | state.isPageLoading = item; 51 | }, 52 | setActiveProject(state, item){ 53 | state.activeProject = item; 54 | }, 55 | setActivePage(state, item){ 56 | state.activePage = item; 57 | }, 58 | setSearchFilter(state, item){ 59 | Vue.set(state.searchFilter, state.activePage, item); 60 | }, 61 | setProjectListOptions(state, item){ 62 | state.projectListOptions = item; 63 | } 64 | }, 65 | actions: { 66 | async bootApplication({ state, dispatch, commit, getters }, projectId){ 67 | if(Cookies.get('bugphix_default_project') && (Cookies.get('bugphix_default_project') !== 'undefined' || Cookies.get('bugphix_default_project') !== '')){ 68 | projectId = Cookies.get('bugphix_default_project'); 69 | } 70 | dispatch('setActiveProject', projectId); 71 | dispatch('setProjectListOptions'); 72 | }, 73 | async setActiveProject({ commit, dispatch}, projectId=''){ 74 | ProjectApi.getActiveProject(projectId) 75 | .then((response) => { 76 | if(response.data.success){ 77 | commit('setActiveProject', response.data.results); 78 | Cookies.set('bugphix_default_project', response.data.results.project_id); 79 | } 80 | else{ 81 | setTimeout(()=>{ 82 | Cookies.set('bugphix_default_project', ''); 83 | dispatch('setActiveProject', ''); 84 | }, 200) 85 | } 86 | }) 87 | .catch((error) => { 88 | // console.log(error); 89 | }); 90 | }, 91 | async setProjectListOptions({commit, dispatch}){ 92 | 93 | ProjectApi.getProjectListOptions() 94 | .then((response) => { 95 | if(response.data.success){ 96 | commit('setProjectListOptions', response.data.results); 97 | } 98 | }) 99 | .catch((error) => { 100 | // console.log(error); 101 | }); 102 | } 103 | } 104 | }); 105 | -------------------------------------------------------------------------------- /src/Resources/js/store/Modules/projects.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import Vue from 'vue'; 3 | import ProjectApi from '@/services/ProjectApi'; 4 | 5 | const projects = { 6 | namespaced: true, 7 | state: { 8 | projectList: [], 9 | projectItem: '', 10 | showCreateProjectModal: false, 11 | totalProjects:0, 12 | }, 13 | getters: { 14 | getProjectList: (state) => { 15 | if(typeof state.projectList.length === 0) return []; 16 | return JSON.parse(JSON.stringify(state.projectList)); 17 | }, 18 | }, 19 | mutations: { 20 | setProjectList(state, item){ 21 | state.currentIssuePage = item.pageId; 22 | state.projectList = item.results; 23 | }, 24 | setCurrentProjectPage(state, item){ 25 | if(item<=0) item = 1; 26 | state.currentProjectPage = item; 27 | }, 28 | setProjectItem(state, item){ 29 | state.projectItem = item; 30 | }, 31 | setShowCreateProjectModal(state, item){ 32 | state.showCreateProjectModal = item 33 | }, 34 | setTotalProjects(state, item){ 35 | state.totalProjects = item 36 | }, 37 | }, 38 | actions: { 39 | async setProjectList({rootGetters, commit, dispatch}, pageId){ 40 | 41 | commit('setIsPageLoading', true, {root:true}); 42 | 43 | const keyword= rootGetters.getFilterByPage('projects').keyword || ''; 44 | const sort = rootGetters.getFilterByPage('projects').sort || 'latest'; 45 | 46 | const params = { 47 | sort, 48 | keyword, 49 | page: pageId, 50 | } 51 | 52 | ProjectApi.browse(params) 53 | .then((response) => { 54 | if(response.data.success){ 55 | commit('setProjectList', { 56 | results: response.data.results, 57 | pageId, 58 | }); 59 | commit('setTotalProjects', response.data.results.total || 0) 60 | } 61 | }) 62 | .catch((error) => { 63 | console.log(error); 64 | setTimeout(()=>{ 65 | dispatch('setProjectList', pageId); 66 | }, 2000) 67 | }) 68 | .then(()=>{ 69 | commit('setIsPageLoading', false, {root:true}); 70 | }) 71 | 72 | }, 73 | async setProjectItem({state, commit, dispatch}, projectId = ''){ 74 | if(!projectId) return; 75 | 76 | ProjectApi.show(projectId) 77 | .then((response) => { 78 | if(response.data.success){ 79 | commit('setProjectItem', response.data.results); 80 | } 81 | }) 82 | .catch((error) => { 83 | setTimeout(()=>{ 84 | dispatch('setProjectItem', projectId); 85 | }, 2000) 86 | }) 87 | }, 88 | async createProject({state, commit, dispatch}, data){ 89 | ProjectApi.create(data) 90 | .then((response) => { 91 | if(response.data.success){ 92 | dispatch('setProjectList', 1); 93 | } 94 | }) 95 | .catch((error) => { 96 | setTimeout(()=>{ 97 | dispatch('createProject', projectId); 98 | }, 2000) 99 | }) 100 | }, 101 | async updateProject({state, commit, dispatch}, params){ 102 | 103 | const {projectId, data} = params; 104 | 105 | if(! projectId || ! data) return; 106 | 107 | ProjectApi.create(data) 108 | .then((response) => { 109 | if(response.data.success){ 110 | dispatch('setProjectList', 1); 111 | } 112 | }) 113 | .catch((error) => { 114 | setTimeout(()=>{ 115 | dispatch('createProject', projectId); 116 | }, 2000) 117 | }) 118 | }, 119 | } 120 | }; 121 | 122 | export default projects; 123 | -------------------------------------------------------------------------------- /src/BugphixServiceProvider.php: -------------------------------------------------------------------------------- 1 | alias('Bugphix', BugphixFacade::class); 26 | $this->app->singleton('bugphix', function () { 27 | return new Bugphix(); 28 | }); 29 | 30 | if ($this->app->runningInConsole()) { 31 | $this->bugphixPublishable(); 32 | $this->bugphixCommands(); 33 | } 34 | } 35 | 36 | /** 37 | * Bootstrap services. 38 | * 39 | * @return void 40 | */ 41 | public function boot() 42 | { 43 | $this->loadMigrationsFrom(__DIR__ . '/Migrations'); 44 | $this->loadViewsFrom(__DIR__ . '/Resources/views', 'bugphix'); 45 | $this->registerRoutes(); 46 | } 47 | 48 | protected function registerRoutes() 49 | { 50 | // DSN API route 51 | if (!in_array(config('bugphix.option.dsn_slug'), ['', '/'])) { 52 | $this->loadRoutesFrom(__DIR__ . "/Routes/dsn.php"); 53 | } 54 | 55 | // if has no valid dsn entered 56 | if (!filter_var(config('bugphix.dsn'), FILTER_VALIDATE_URL) && config('bugphix.dashboard.enable') === true) { 57 | Route::group($this->routeConfig(), function () { 58 | $route = $this->isRouteProtected() 59 | ? 'web.php' 60 | : 'disabled.php'; 61 | 62 | $this->loadRoutesFrom(__DIR__ . "/Routes/{$route}"); 63 | }); 64 | } 65 | } 66 | 67 | private function isRouteProtected() 68 | { 69 | $prefix = $this->routeConfig()['prefix'] ?? $this->defaultAdminSlug; 70 | $middleware = array_diff( $this->routeConfig()['middleware'] ?? [], ['web','api'] ); // excluding web and api as middleware 71 | $isLocal = app()->environment('local'); 72 | 73 | return $isLocal || count($middleware) || ($prefix !== $this->defaultAdminSlug); 74 | } 75 | 76 | /** 77 | * Publishable resources. 78 | */ 79 | private function bugphixPublishable() 80 | { 81 | $path = dirname(__DIR__) . '/src/Publishable'; 82 | 83 | $this->publishes([ 84 | "{$path}/config/bugphix.php" => config_path('bugphix.php'), 85 | ], 'bugphix-config'); 86 | } 87 | 88 | /** 89 | * Bugphix Commands 90 | */ 91 | 92 | private function bugphixCommands() 93 | { 94 | $this->commands([ 95 | Commands\InstallCommand::class, 96 | Commands\BugphixAssetsSymlink::class, 97 | Commands\BugphixTestCommand::class, 98 | ]); 99 | } 100 | 101 | private function routeConfig() 102 | { 103 | $prefix = !empty(config('bugphix.dashboard.url')) ? config('bugphix.dashboard.url') : $this->defaultAdminSlug; 104 | 105 | $configMiddleware = config('bugphix.dashboard.middleware') ?? []; 106 | 107 | if (!is_array($configMiddleware)) $configMiddleware = [$configMiddleware]; 108 | 109 | $middleware = array_merge(['web'], $configMiddleware); // always attach web middleware 110 | 111 | return [ 112 | 'as' => 'bugphix.', 113 | 'prefix' => $prefix, 114 | 'middleware' => $middleware, 115 | ]; 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/Http/Controllers/ProjectsController.php: -------------------------------------------------------------------------------- 1 | searchProject($_GET['keyword'] ?? ''); 28 | 29 | $projects = $items->paginate(10); 30 | $results = $projects; 31 | $results->data = ProjectCollection::collection($projects); 32 | 33 | return response()->json([ 34 | 'success' => true, 35 | 'results' => $results 36 | ]); 37 | } 38 | 39 | /** 40 | * Show the form for creating a new resource. 41 | * 42 | * @return \Illuminate\Http\Response 43 | */ 44 | public function create() 45 | { 46 | // 47 | } 48 | 49 | /** 50 | * Store a newly created resource in storage. 51 | * 52 | * @param \Illuminate\Http\Request $request 53 | * @return \Illuminate\Http\Response 54 | */ 55 | public function store(Request $request) 56 | { 57 | // 58 | 59 | $project = Project::createProject(['project_name' => $request->project_name]); 60 | 61 | return response()->json([ 62 | 'success' => true, 63 | 'results' => new ProjectResource($project) 64 | ]); 65 | } 66 | 67 | /** 68 | * Display the specified resource. 69 | * 70 | * @param int $id 71 | * @return \Illuminate\Http\Response 72 | */ 73 | public function show($id) 74 | { 75 | 76 | $success = false; 77 | $results = []; 78 | 79 | $item = Project::where('project_id', $id)->first(); 80 | 81 | if ($item) { 82 | $success = true; 83 | $results = $item; 84 | } 85 | 86 | return response()->json([ 87 | 'success' => $success, 88 | 'results' => new ProjectResource($results), 89 | ]); 90 | } 91 | 92 | /** 93 | * Show the form for editing the specified resource. 94 | * 95 | * @param int $id 96 | * @return \Illuminate\Http\Response 97 | */ 98 | public function edit($id) 99 | { 100 | // 101 | } 102 | 103 | /** 104 | * Update the specified resource in storage. 105 | * 106 | * @param \Illuminate\Http\Request $request 107 | * @param int $id 108 | * @return \Illuminate\Http\Response 109 | */ 110 | public function update(Request $request, $id) 111 | { 112 | $success = false; 113 | $message = 'Updating project failed'; 114 | 115 | $item = Project::where('project_id', $id)->first(); 116 | 117 | if ($item) { 118 | $item->project_name = $request->project_name; 119 | $item->is_active = $request->is_active; 120 | $item->save(); 121 | $success = true; 122 | $message = 'Project updated!'; 123 | } 124 | 125 | return response()->json([ 126 | 'success' => $success, 127 | 'message' => $message, 128 | ]); 129 | } 130 | 131 | /** 132 | * Remove the specified resource from storage. 133 | * 134 | * @param int $id 135 | * @return \Illuminate\Http\Response 136 | */ 137 | public function destroy($id) 138 | { 139 | // 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/Resources/sass/app.scss: -------------------------------------------------------------------------------- 1 | $primary: #2979ff; 2 | $secondary: #27f2eb; 3 | // $accent: #f2f7ff 4 | $link: $primary; 5 | 6 | @import "../../../node_modules/bulma/bulma.sass"; 7 | @import url('https://fonts.googleapis.com/css?family=Muli:400,600&display=swap'); 8 | 9 | .modal{ 10 | .modal-background { 11 | background-color: rgba(255, 255, 255, 0.5); 12 | } 13 | .modal-card { 14 | box-shadow: 7px 6px 20px rgba(0, 0, 0, 0.2); 15 | } 16 | } 17 | 18 | body{ 19 | font-family: 'Muli', sans-serif; 20 | font-size: 16px; 21 | line-height: 22px; 22 | font-weight: 400; 23 | } 24 | 25 | #main-menu{ 26 | li { 27 | a:hover, 28 | a.router-link-active{ 29 | background-color: $primary; 30 | color: #fff; 31 | } 32 | } 33 | } 34 | 35 | .loading-icon{ 36 | -webkit-animation: spinAround 500ms infinite linear; 37 | animation: spinAround 500ms infinite linear; 38 | border: 2px solid $primary; 39 | border-radius: 290486px; 40 | border-right-color: transparent; 41 | border-top-color: transparent; 42 | content: ""; 43 | display: block; 44 | height: 2em; 45 | position: relative; 46 | width: 2em; 47 | } 48 | 49 | .card-margin{ 50 | margin-top: 20px; 51 | margin-bottom: 30px; 52 | } 53 | 54 | .navbar-menu{ 55 | .router-link-active{ 56 | background-color: #fafafa; 57 | color: $primary; 58 | font-weight: 700; 59 | } 60 | } 61 | 62 | .switch-wrapper{ 63 | 64 | display: flex; 65 | align-items: center; 66 | 67 | /* The switch - the box around the slider */ 68 | .switch { 69 | position: relative; 70 | display: inline-block; 71 | width: 60px; 72 | height: 34px; 73 | margin-left: 10px; 74 | margin-right: 10px; 75 | 76 | /* Hide default HTML checkbox */ 77 | input { 78 | opacity: 0; 79 | width: 0; 80 | height: 0; 81 | 82 | &:checked { 83 | + .slider { 84 | 85 | &:before { 86 | -webkit-transform: translateX(26px); 87 | -ms-transform: translateX(26px); 88 | transform: translateX(26px); 89 | } 90 | 91 | background-color: $primary; 92 | } 93 | } 94 | &:focus { 95 | + .slider { 96 | box-shadow: 0 0 1px $primary; 97 | } 98 | } 99 | } 100 | } 101 | 102 | /* The slider */ 103 | .slider { 104 | position: absolute; 105 | cursor: pointer; 106 | top: 0; 107 | left: 0; 108 | right: 0; 109 | bottom: 0; 110 | background-color: #ccc; 111 | -webkit-transition: .4s; 112 | transition: .4s; 113 | 114 | &:before { 115 | position: absolute; 116 | content: ""; 117 | height: 26px; 118 | width: 26px; 119 | left: 4px; 120 | bottom: 4px; 121 | background-color: white; 122 | -webkit-transition: .4s; 123 | transition: .4s; 124 | } 125 | 126 | /* Rounded sliders */ 127 | &.round { 128 | border-radius: 34px; 129 | &:before { 130 | border-radius: 50%; 131 | } 132 | } 133 | 134 | } 135 | } 136 | 137 | // .loading-icon{ 138 | // -webkit-animation:spin 500ms linear infinite; 139 | // -moz-animation:spin 500ms linear infinite; 140 | // animation:spin 500ms linear infinite; 141 | // -webkit-animation: spin 500ms linear infinite; 142 | // animation: spin 500ms linear infinite; 143 | // display: flex; 144 | // align-items: center; 145 | // justify-content: center; 146 | // } 147 | 148 | // @-moz-keyframes spin { 100% { -moz-transform: rotate(360deg); } } 149 | // @-webkit-keyframes spin { 100% { -webkit-transform: rotate(360deg); } } 150 | // @keyframes spin { 100% { -webkit-transform: rotate(360deg); transform:rotate(360deg); } } 151 | -------------------------------------------------------------------------------- /src/Http/Controllers/DSNController.php: -------------------------------------------------------------------------------- 1 | false, 23 | 'message' => 'Unprocessable Entity', 24 | ); 25 | 26 | $project = Project::isActive()->projectId($projectId)->first(); 27 | 28 | if (!$project) { 29 | $response['message'] = 'Project not found'; 30 | } else { 31 | if ($project->project_token !== $token) { 32 | $code = 401; 33 | $response['message'] = "Invalid token"; 34 | } else { 35 | $code = 200; 36 | 37 | // 'exception' => $this->bugphixException, 38 | // 'issue' => $this->bugphixIssue, 39 | // 'stack_trace' => $this->bugphixStackTrace, 40 | // 'server' => $this->bugphixServer, 41 | // 'client' => $this->bugphixClient, 42 | // 'user' => $this->bugphixUser, 43 | 44 | // Log::info($req->exception); 45 | // Log::debug($req->issue); 46 | // Log::info(json_encode($req->stack_trace)); 47 | // Log::debug($req->server); 48 | // Log::debug($req->client); 49 | // Log::debug($req->user); 50 | 51 | // Log::info($req->exception); 52 | // Log::debug($req->all()); 53 | 54 | $req->validate([ 55 | 'bphix_exception' => 'required', 56 | 'bphix_issue' => 'required', 57 | 'bphix_event' => 'required', 58 | 'bphix_stack_trace' => 'required', 59 | ]); 60 | 61 | if ($req->all()) { 62 | if (is_array($req->exception)) { 63 | $e = implode(PHP_EOL, $req->bphix_exception); 64 | } else { 65 | $e = $req->bphix_exception; 66 | } 67 | 68 | $this->bugphixException = $e; 69 | $this->setProject($project->project_id); 70 | $this->setIssue($req->bphix_issue); 71 | $this->setEvent($req->bphix_event); 72 | $this->setStackTrace($req->bphix_stack_trace); 73 | 74 | if($req->has('bphix_server')){ 75 | $this->setServer($req->bphix_server); 76 | } 77 | if($req->has('bphix_client')){ 78 | $this->setClient($req->bphix_client); 79 | } 80 | if(isset($req->bphix_user['unique'])){ 81 | $this->setUser($req->bphix_user['unique'], $req->bphix_user['meta'] ?? []); 82 | } 83 | $this->bugphixStore(); 84 | 85 | // Log::info(PHP_EOL.PHP_EOL.'----------------- dsn -----------------'); 86 | // Log::info($this->bugphixException); 87 | // Log::debug($this->bugphixProject); 88 | // Log::debug($this->bugphixIssue); 89 | // Log::debug($this->bugphixEvent); 90 | // Log::info(json_encode($this->bugphixStackTrace)); 91 | // Log::debug($this->bugphixServer); 92 | // Log::debug($this->bugphixClient); 93 | // Log::debug($this->bugphixUser); 94 | 95 | // Log::info('----------------- dsn END -----------------'.PHP_EOL.PHP_EOL); 96 | 97 | 98 | $response['success'] = true; 99 | unset($response['message']); 100 | } 101 | } 102 | } 103 | 104 | return response()->json($response, $code); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/Resources/js/store/Modules/issues.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios'; 2 | import Vue from 'vue'; 3 | import IssueApi from '@/services/IssueApi'; 4 | 5 | const issues = { 6 | namespaced: true, 7 | state: { 8 | issueList: [], 9 | issueItem: '', 10 | currentEvent: '', 11 | totalIssues: 0, 12 | }, 13 | getters: { 14 | getIssueList: (state) => { 15 | if(typeof state.issueList.length === 0) return []; 16 | return JSON.parse(JSON.stringify(state.issueList)); 17 | }, 18 | }, 19 | mutations: { 20 | setIssueList(state, item){ 21 | state.currentIssuePage = item.pageId; 22 | state.issueList = item.results; 23 | }, 24 | setIssueItem(state, item){ 25 | state.issueItem = item; 26 | }, 27 | setCurrentEvent(state, item){ 28 | state.currentEvent = item; 29 | }, 30 | setTotalIssues(state, item){ 31 | state.totalIssues = item 32 | } 33 | }, 34 | actions: { 35 | async setIssueList({rootGetters, commit, dispatch}, pageId){ 36 | 37 | const projectId = rootGetters.getActiveProjectId; 38 | 39 | if(projectId === ''){ 40 | setTimeout(()=>{ 41 | dispatch('setIssueList', pageId); // call again when active projet is not yet ready 42 | }, 1000) 43 | return; 44 | } 45 | 46 | commit('setIsPageLoading', true, {root:true}); 47 | 48 | const keyword= rootGetters.getFilterByPage('issues').keyword || ''; 49 | const sort = rootGetters.getFilterByPage('issues').sort || 'latest'; 50 | const status = rootGetters.getFilterByPage('issues').status || 'unresolved'; 51 | 52 | const params = { 53 | sort, 54 | keyword, 55 | status, 56 | project_id: projectId, 57 | page: pageId, 58 | } 59 | 60 | IssueApi.browse(params) 61 | .then((response) => { 62 | if(response.data.success){ 63 | commit('setIssueList', { 64 | results: response.data.results, 65 | pageId, 66 | }); 67 | commit('setTotalIssues', response.data.results.total || 0) 68 | } 69 | }) 70 | .catch((error) => { 71 | console.log(error); 72 | setTimeout(()=>{ 73 | dispatch('setIssueList', pageId); 74 | }, 2000) 75 | }) 76 | .then(()=>{ 77 | commit('setIsPageLoading', false, {root:true}); 78 | }) 79 | }, 80 | async setIssueItem({state, commit, rootGetters, dispatch}, issueId = ''){ 81 | if(!issueId) return; 82 | 83 | const projectId = rootGetters.getActiveProjectId; 84 | 85 | if(projectId === ''){ 86 | setTimeout(()=>{ 87 | dispatch('setIssueItem', issueId); // call again when active projet is not yet ready 88 | }, 1000) 89 | return; 90 | } 91 | 92 | commit('setIsPageLoading', true, {root:true}) 93 | 94 | IssueApi.show(issueId, projectId) 95 | .then((response) => { 96 | if(response.data.success){ 97 | commit('setIssueItem', response.data.results); 98 | if(response.data.results.latest_event){ 99 | commit('setCurrentEvent', response.data.results.latest_event); 100 | } 101 | } 102 | }) 103 | .catch((error) => { 104 | setTimeout(()=>{ 105 | dispatch('setIssueItem', issueId); 106 | }, 2000) 107 | }) 108 | .then(()=>{ 109 | commit('setIsPageLoading', false, {root:true}) 110 | }) 111 | }, 112 | async setEventItem({state, commit, rootGetters, dispatch}, eventId = ''){ 113 | 114 | if(!eventId) return; 115 | 116 | commit('setIsPageLoading', true, {root:true}) 117 | 118 | IssueApi.getEvent(eventId) 119 | .then((response) => { 120 | if(response.data.success){ 121 | commit('setCurrentEvent', response.data.results); 122 | } 123 | }) 124 | .catch((error) => { 125 | setTimeout(()=>{ 126 | dispatch('setEventItem', eventId); 127 | }, 2000) 128 | }) 129 | .then(()=>{ 130 | commit('setIsPageLoading', false, {root:true}) 131 | }) 132 | }, 133 | } 134 | }; 135 | 136 | export default issues; 137 | -------------------------------------------------------------------------------- /assets/plugins/@mdi/font/license.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/), 2 | with Reserved Font Name Material Design Icons. 3 | 4 | Copyright (c) 2014, Google (http://www.google.com/design/) 5 | uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE 6 | 7 | This Font Software is licensed under the SIL Open Font License, Version 1.1. 8 | This license is copied below, and is also available with a FAQ at: 9 | http://scripts.sil.org/OFL 10 | 11 | 12 | ----------------------------------------------------------- 13 | SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 14 | ----------------------------------------------------------- 15 | 16 | PREAMBLE 17 | The goals of the Open Font License (OFL) are to stimulate worldwide 18 | development of collaborative font projects, to support the font creation 19 | efforts of academic and linguistic communities, and to provide a free and 20 | open framework in which fonts may be shared and improved in partnership 21 | with others. 22 | 23 | The OFL allows the licensed fonts to be used, studied, modified and 24 | redistributed freely as long as they are not sold by themselves. The 25 | fonts, including any derivative works, can be bundled, embedded, 26 | redistributed and/or sold with any software provided that any reserved 27 | names are not used by derivative works. The fonts and derivatives, 28 | however, cannot be released under any other type of license. The 29 | requirement for fonts to remain under this license does not apply 30 | to any document created using the fonts or their derivatives. 31 | 32 | DEFINITIONS 33 | "Font Software" refers to the set of files released by the Copyright 34 | Holder(s) under this license and clearly marked as such. This may 35 | include source files, build scripts and documentation. 36 | 37 | "Reserved Font Name" refers to any names specified as such after the 38 | copyright statement(s). 39 | 40 | "Original Version" refers to the collection of Font Software components as 41 | distributed by the Copyright Holder(s). 42 | 43 | "Modified Version" refers to any derivative made by adding to, deleting, 44 | or substituting -- in part or in whole -- any of the components of the 45 | Original Version, by changing formats or by porting the Font Software to a 46 | new environment. 47 | 48 | "Author" refers to any designer, engineer, programmer, technical 49 | writer or other person who contributed to the Font Software. 50 | 51 | PERMISSION & CONDITIONS 52 | Permission is hereby granted, free of charge, to any person obtaining 53 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 54 | redistribute, and sell modified and unmodified copies of the Font 55 | Software, subject to the following conditions: 56 | 57 | 1) Neither the Font Software nor any of its individual components, 58 | in Original or Modified Versions, may be sold by itself. 59 | 60 | 2) Original or Modified Versions of the Font Software may be bundled, 61 | redistributed and/or sold with any software, provided that each copy 62 | contains the above copyright notice and this license. These can be 63 | included either as stand-alone text files, human-readable headers or 64 | in the appropriate machine-readable metadata fields within text or 65 | binary files as long as those fields can be easily viewed by the user. 66 | 67 | 3) No Modified Version of the Font Software may use the Reserved Font 68 | Name(s) unless explicit written permission is granted by the corresponding 69 | Copyright Holder. This restriction only applies to the primary font name as 70 | presented to the users. 71 | 72 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 73 | Software shall not be used to promote, endorse or advertise any 74 | Modified Version, except to acknowledge the contribution(s) of the 75 | Copyright Holder(s) and the Author(s) or with their explicit written 76 | permission. 77 | 78 | 5) The Font Software, modified or unmodified, in part or in whole, 79 | must be distributed entirely under this license, and must not be 80 | distributed under any other license. The requirement for fonts to 81 | remain under this license does not apply to any document created 82 | using the Font Software. 83 | 84 | TERMINATION 85 | This license becomes null and void if any of the above conditions are 86 | not met. 87 | 88 | DISCLAIMER 89 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 90 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 91 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 92 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 93 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 94 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 95 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 96 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 97 | OTHER DEALINGS IN THE FONT SOFTWARE. -------------------------------------------------------------------------------- /src/Traits/BugphixProcess.php: -------------------------------------------------------------------------------- 1 | logBugphixVars(); 31 | 32 | // STORE ISSUE 33 | $issue = Issue::firstOrCreate([ 34 | 'issue_project_id' => $this->bugphixProject->project_id, 35 | 'issue_error_exception' => $this->bugphixIssue['error_exception'], 36 | 'issue_error_message' => $this->bugphixIssue['error_message'], 37 | ]); 38 | $issue->issue_status = 'unresolved'; 39 | $issue->updated_at = Carbon::now(); 40 | $issue->save(); 41 | 42 | // STORE EVENT 43 | $event = Event::create([ 44 | 'event_issue_id' => $issue->id, 45 | 'event_environment' => $this->bugphixEvent['environment'], 46 | ]); 47 | 48 | // STORE STACKTRACE 49 | if (isset($event->id) && count($this->bugphixStackTrace)) { 50 | StackTrace::firstOrCreate([ 51 | 'stack_trace_event_id' => $event->id, 52 | 'stack_trace_error_file' => $this->bugphixStackTrace['error_file'], 53 | 'stack_trace_error_line' => $this->bugphixStackTrace['error_line'], 54 | 'stack_trace_full_log' => $this->bugphixStackTrace['full_log'] ?? $this->bugphixException ?? '', 55 | 'stack_trace_data' => $this->bugphixStackTrace['data'], 56 | 'stack_trace_start_line' => $this->bugphixStackTrace['start_line'], 57 | ]); 58 | } 59 | 60 | // STORE SERVER 61 | if (count($this->bugphixServer)) { 62 | $server = Server::firstOrCreate([ 63 | 'server_name' => $this->bugphixServer['name'], 64 | 'server_os' => $this->bugphixServer['os'], 65 | 'server_os_version' => $this->bugphixServer['os_version'], 66 | 'server_runtime' => $this->bugphixServer['runtime'], 67 | ]); 68 | } 69 | 70 | if (count($this->bugphixClient)) { 71 | $client = Client::firstOrCreate([ 72 | 'client_method' => $this->bugphixClient['method'], 73 | 'client_url' => $this->bugphixClient['url'], 74 | 'client_browser' => $this->bugphixClient['browser'], 75 | 'client_browser_version' => $this->bugphixClient['browser_version'], 76 | 'client_os' => $this->bugphixClient['os'], 77 | 'client_ip' => $this->bugphixClient['ip'], 78 | 'client_header' => isset($this->bugphixClient['header']) && count($this->bugphixClient['header']) ? $this->bugphixClient['header'] : null, 79 | ]); 80 | } 81 | 82 | if (count($this->bugphixUser)) { 83 | 84 | $user = User::where('user_unique', $this->bugphixUser['unique'])->first(); 85 | 86 | if($user){ 87 | $user->user_meta = $this->bugphixUser['meta']; 88 | $user->save(); 89 | } 90 | else{ 91 | $user = User::create([ 92 | 'user_unique' => $this->bugphixUser['unique'], 93 | 'user_meta' => $this->bugphixUser['meta'], 94 | ]); 95 | } 96 | } 97 | 98 | /** 99 | * Create event relationship ids 100 | */ 101 | 102 | if ($event) { 103 | if (isset($client) && !empty($client)) { 104 | EventClient::firstOrCreate([ 105 | 'event_id' => $event->id, 106 | 'client_id' => $client->id, 107 | ]); 108 | } 109 | 110 | if (isset($server) && !empty($server)) { 111 | EventServer::firstOrCreate([ 112 | 'event_id' => $event->id, 113 | 'server_id' => $server->id, 114 | ]); 115 | } 116 | 117 | if (isset($user) && !empty($user)) { 118 | EventUser::firstOrCreate([ 119 | 'event_id' => $event->id, 120 | 'user_id' => $user->id, 121 | ]); 122 | } 123 | } 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/Resources/js/components/Pages/Projects/ProjectItem.vue: -------------------------------------------------------------------------------- 1 | 60 | 61 | 161 | 162 | 187 | -------------------------------------------------------------------------------- /src/Resources/views/main.blade.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Bugphix 5 | 6 | 7 | 8 | 9 | @if(!Bugphix::isAssetsExists()) 10 | 34 | @else 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | @endif 53 | 54 | 55 | @if(!Bugphix::isAssetsExists()) 56 |
57 |
58 |

We're sorry but Bugphix can't locate the assets folder, [{{Bugphix::asset('/')}}] make sure you have properly set it via config.

59 |

or you can run $ php artisan bugphix:assets-symlink manually to install the assets. 60 |

61 |
62 | @else 63 | 66 |
67 | 81 | 82 | @endif 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/Traits/BugphixSetter.php: -------------------------------------------------------------------------------- 1 | bugphixProject = ''; 31 | try { 32 | $this->bugphixProject = Project::projectId($projectId)->isActive()->first(); 33 | if (!$projectId && !$this->bugphixProject) { 34 | // get the default project 35 | $this->bugphixProject = Project::isActive()->first(); 36 | } 37 | } catch (Exception $e) { 38 | } 39 | } 40 | 41 | /** 42 | * Create issue 43 | */ 44 | 45 | protected function setIssue(array $issue=[]) 46 | { 47 | if(count($issue)){ 48 | $this->bugphixIssue = $issue; 49 | return; 50 | } 51 | 52 | try { 53 | $e = (object) $this->getExceptionInArray(); 54 | $this->bugphixIssue = array( 55 | 'project_id' => $this->bugphixProject->project_id, 56 | 'error_exception' => $e->exception ?? 'Unknown Error', 57 | 'error_message' => $e->message ?? 'Error', 58 | ); 59 | } catch (Exception $e) { 60 | } 61 | } 62 | 63 | /** 64 | * Create event 65 | */ 66 | 67 | protected function setEvent(array $event=[]) 68 | { 69 | if(count($event)){ 70 | $this->bugphixEvent = $event; 71 | return; 72 | } 73 | 74 | try { 75 | $this->bugphixEvent = array( 76 | 'environment' => app()->environment() ?? 'local', 77 | ); 78 | } catch (Exception $e) { 79 | } 80 | } 81 | 82 | /** 83 | * Generate custom stack trace 84 | */ 85 | 86 | protected function setStackTrace(array $stackTrace = []) 87 | { 88 | if(count($stackTrace)){ 89 | 90 | if(!isset($stackTrace['full_log']) && $this->bugphixException){ 91 | if(is_array($this->bugphixException)){ 92 | $stackTrace['full_log'] = implode(PHP_EOL, $this->bugphixException); 93 | } 94 | else{ 95 | $stackTrace['full_log'] = $this->bugphixException; 96 | } 97 | } 98 | 99 | $this->bugphixStackTrace = $stackTrace; 100 | return; 101 | } 102 | 103 | try { 104 | $getStackTrace = (object) $this->generateStackTrace(); 105 | $e = (object) $this->getExceptionInArray(); 106 | $this->bugphixStackTrace = array( 107 | 'error_file' => $e->file ?? 'Unknown file', 108 | 'error_line' => $e->line ?? 0, 109 | 'full_log' => $this->bugphixException, 110 | 'data' => $getStackTrace->data ?? [], 111 | 'start_line' => $getStackTrace->start_line ?? 1, 112 | ); 113 | } catch (Exception $e) { 114 | } 115 | } 116 | 117 | /** 118 | * Automatically generate details for current server 119 | */ 120 | 121 | protected function setServer(array $server=[]) 122 | { 123 | if(count($server)){ 124 | $this->bugphixServer = $server; 125 | return; 126 | } 127 | 128 | try { 129 | $this->bugphixServer = array( 130 | 'name' => php_uname('n'), // Host name 131 | 'os' => php_uname('s'), // Operating system 132 | 'os_version' => php_uname('v'), // version name 133 | 'runtime' => 'PHP v' . phpversion(), 134 | ); 135 | } catch (Exception $e) { 136 | } 137 | } 138 | 139 | /** 140 | * Automatically capture client's information 141 | */ 142 | 143 | protected function setClient(array $client=[]) 144 | { 145 | if(count($client)){ 146 | $this->bugphixClient = $client; 147 | return; 148 | } 149 | 150 | if (!$this->getBrowser() && !$this->getPlatform()) return; 151 | 152 | try { 153 | $this->bugphixClient = array( 154 | 'method' => $_SERVER['REQUEST_METHOD'] ?? 'Unknown Method', 155 | 'url' => $this->getEventUrl(), 156 | 'browser' => $this->getBrowser(), 157 | 'browser_version' => $this->getVersion(), 158 | 'os' => $this->getPlatform(), 159 | 'ip' => $this->getClientIp(), 160 | 'header' => $this->getHeaders() ?? [], 161 | ); 162 | } catch (Exception $e) { 163 | } 164 | } 165 | 166 | /** 167 | * Set user details 168 | */ 169 | 170 | protected function setUser($userUnique, array $userMeta) 171 | { 172 | 173 | try { 174 | $this->bugphixUser = array( 175 | 'unique' => $userUnique, 176 | 'meta' => $userMeta, 177 | ); 178 | } catch (Exception $e) { 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /src/Http/Controllers/IssuesController.php: -------------------------------------------------------------------------------- 1 | projectId($_GET['project_id'] ?? 'default'); 27 | $items->status(strtolower($_GET['status'] ?? 'unresolved')); 28 | 29 | $items->searchIssue($_GET['keyword'] ?? ''); 30 | 31 | $results = $items->paginate(10); 32 | $results->data = IssueCollection::collection($results); 33 | 34 | return response()->json([ 35 | 'success' => true, 36 | 'results' => $results 37 | ]); 38 | 39 | // return new IssueResource($results); 40 | } 41 | 42 | /** 43 | * Show the form for creating a new resource. 44 | * 45 | * @return \Illuminate\Http\Response 46 | */ 47 | public function create() 48 | { 49 | // 50 | } 51 | 52 | /** 53 | * Store a newly created resource in storage. 54 | * 55 | * @param \Illuminate\Http\Request $request 56 | * @return \Illuminate\Http\Response 57 | */ 58 | public function store(Request $request) 59 | { 60 | // 61 | } 62 | 63 | /** 64 | * Display the specified resource. 65 | * 66 | * @param int $id 67 | * @return \Illuminate\Http\Response 68 | */ 69 | public function show($id) 70 | { 71 | 72 | $success = false; 73 | $results = []; 74 | 75 | $item = Issue::projectId($_GET['project_id'] ?? 'default')->find($id); 76 | 77 | if ($item) { 78 | $success = true; 79 | $results = $item; 80 | } 81 | 82 | return response()->json([ 83 | 'success' => $success, 84 | 'results' => new IssueResource($results), 85 | ]); 86 | } 87 | 88 | /** 89 | * Show the form for editing the specified resource. 90 | * 91 | * @param int $id 92 | * @return \Illuminate\Http\Response 93 | */ 94 | public function edit($id) 95 | { 96 | // 97 | } 98 | 99 | /** 100 | * Update the specified resource in storage. 101 | * 102 | * @param \Illuminate\Http\Request $request 103 | * @param int $id 104 | * @return \Illuminate\Http\Response 105 | */ 106 | public function update(Request $req, $id) 107 | { 108 | $success = false; 109 | $message = 'Updating issue failed'; 110 | 111 | $item = Issue::find($id); 112 | 113 | $post = $req->all(); 114 | 115 | if ($item && count($post)) { 116 | 117 | unset($post['id']); 118 | unset($post['issue_project_id']); 119 | 120 | $item->update($post); 121 | $success = true; 122 | $message = 'Issue updated!'; 123 | } 124 | 125 | return response()->json([ 126 | 'success' => $success, 127 | 'message' => $message, 128 | ]); 129 | } 130 | 131 | /** 132 | * Remove the specified resource from storage. 133 | * 134 | * @param int $id 135 | * @return \Illuminate\Http\Response 136 | */ 137 | public function destroy($id) 138 | { 139 | $success = false; 140 | $message = 'Cannot delete issue'; 141 | 142 | $delete = Issue::where('id', $id)->delete(); 143 | if ($delete) { 144 | $success = true; 145 | $message = 'Issue deleted!'; 146 | } 147 | return response()->json([ 148 | 'success' => $success, 149 | 'message' => $message, 150 | ]); 151 | } 152 | 153 | public function bulkUpdate(Request $req, $id) 154 | { 155 | $ids = explode(',', $id); 156 | $success = false; 157 | $message = 'Updating issue failed'; 158 | 159 | if (count($ids)) { 160 | $item = Issue::whereIn('id', $ids); 161 | $post = $req->all(); 162 | if ($item && count($post)) { 163 | unset($post['id']); 164 | unset($post['issue_project_id']); 165 | 166 | $item->update($post); 167 | $success = true; 168 | $message = count($ids) .' Issues updated!'; 169 | } 170 | } 171 | 172 | return response()->json([ 173 | 'success' => $success, 174 | 'message' => $message, 175 | ]); 176 | } 177 | 178 | public function bulkDelete($id) 179 | { 180 | $ids = explode(',', $id); 181 | $success = false; 182 | $message = 'Deleting issue failed'; 183 | 184 | if (count($ids)) { 185 | $delete = Issue::whereIn('id', $ids)->delete(); 186 | if ($delete) { 187 | $success = true; 188 | $message = count($ids) .' Issues deleted!'; 189 | } 190 | } 191 | 192 | return response()->json([ 193 | 'success' => $success, 194 | 'message' => $message, 195 | ]); 196 | } 197 | } 198 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Citizen Code of Conduct 2 | 3 | ## 1. Purpose 4 | 5 | A primary goal of Bugphix Laravel is to be inclusive to the largest number of contributors, with the most varied and diverse backgrounds possible. As such, we are committed to providing a friendly, safe and welcoming environment for all, regardless of gender, sexual orientation, ability, ethnicity, socioeconomic status, and religion (or lack thereof). 6 | 7 | This code of conduct outlines our expectations for all those who participate in our community, as well as the consequences for unacceptable behavior. 8 | 9 | We invite all those who participate in Bugphix Laravel to help us create safe and positive experiences for everyone. 10 | 11 | ## 2. Open [Source/Culture/Tech] Citizenship 12 | 13 | A supplemental goal of this Code of Conduct is to increase open [source/culture/tech] citizenship by encouraging participants to recognize and strengthen the relationships between our actions and their effects on our community. 14 | 15 | Communities mirror the societies in which they exist and positive action is essential to counteract the many forms of inequality and abuses of power that exist in society. 16 | 17 | If you see someone who is making an extra effort to ensure our community is welcoming, friendly, and encourages all participants to contribute to the fullest extent, we want to know. 18 | 19 | ## 3. Expected Behavior 20 | 21 | The following behaviors are expected and requested of all community members: 22 | 23 | * Participate in an authentic and active way. In doing so, you contribute to the health and longevity of this community. 24 | * Exercise consideration and respect in your speech and actions. 25 | * Attempt collaboration before conflict. 26 | * Refrain from demeaning, discriminatory, or harassing behavior and speech. 27 | * Be mindful of your surroundings and of your fellow participants. Alert community leaders if you notice a dangerous situation, someone in distress, or violations of this Code of Conduct, even if they seem inconsequential. 28 | * Remember that community event venues may be shared with members of the public; please be respectful to all patrons of these locations. 29 | 30 | ## 4. Unacceptable Behavior 31 | 32 | The following behaviors are considered harassment and are unacceptable within our community: 33 | 34 | * Violence, threats of violence or violent language directed against another person. 35 | * Sexist, racist, homophobic, transphobic, ableist or otherwise discriminatory jokes and language. 36 | * Posting or displaying sexually explicit or violent material. 37 | * Posting or threatening to post other people's personally identifying information ("doxing"). 38 | * Personal insults, particularly those related to gender, sexual orientation, race, religion, or disability. 39 | * Inappropriate photography or recording. 40 | * Inappropriate physical contact. You should have someone's consent before touching them. 41 | * Unwelcome sexual attention. This includes, sexualized comments or jokes; inappropriate touching, groping, and unwelcomed sexual advances. 42 | * Deliberate intimidation, stalking or following (online or in person). 43 | * Advocating for, or encouraging, any of the above behavior. 44 | * Sustained disruption of community events, including talks and presentations. 45 | 46 | ## 5. Weapons Policy 47 | 48 | No weapons will be allowed at Bugphix Laravel events, community spaces, or in other spaces covered by the scope of this Code of Conduct. Weapons include but are not limited to guns, explosives (including fireworks), and large knives such as those used for hunting or display, as well as any other item used for the purpose of causing injury or harm to others. Anyone seen in possession of one of these items will be asked to leave immediately, and will only be allowed to return without the weapon. Community members are further expected to comply with all state and local laws on this matter. 49 | 50 | ## 6. Consequences of Unacceptable Behavior 51 | 52 | Unacceptable behavior from any community member, including sponsors and those with decision-making authority, will not be tolerated. 53 | 54 | Anyone asked to stop unacceptable behavior is expected to comply immediately. 55 | 56 | If a community member engages in unacceptable behavior, the community organizers may take any action they deem appropriate, up to and including a temporary ban or permanent expulsion from the community without warning (and without refund in the case of a paid event). 57 | 58 | ## 7. Reporting Guidelines 59 | 60 | If you are subject to or witness unacceptable behavior, or have any other concerns, please notify a community organizer as soon as possible. im.jericizon@gmail.com. 61 | 62 | 63 | 64 | Additionally, community organizers are available to help community members engage with local law enforcement or to otherwise help those experiencing unacceptable behavior feel safe. In the context of in-person events, organizers will also provide escorts as desired by the person experiencing distress. 65 | 66 | ## 8. Addressing Grievances 67 | 68 | If you feel you have been falsely or unfairly accused of violating this Code of Conduct, you should notify bugphix with a concise description of your grievance. Your grievance will be handled in accordance with our existing governing policies. 69 | 70 | 71 | 72 | ## 9. Scope 73 | 74 | We expect all community participants (contributors, paid or otherwise; sponsors; and other guests) to abide by this Code of Conduct in all community venues--online and in-person--as well as in all one-on-one communications pertaining to community business. 75 | 76 | This code of conduct and its related procedures also applies to unacceptable behavior occurring outside the scope of community activities when such behavior has the potential to adversely affect the safety and well-being of community members. 77 | 78 | ## 10. Contact info 79 | 80 | im.jericizon@gmail.com 81 | 82 | ## 11. License and attribution 83 | 84 | The Citizen Code of Conduct is distributed by [Stumptown Syndicate](http://stumptownsyndicate.org) under a [Creative Commons Attribution-ShareAlike license](http://creativecommons.org/licenses/by-sa/3.0/). 85 | 86 | Portions of text derived from the [Django Code of Conduct](https://www.djangoproject.com/conduct/) and the [Geek Feminism Anti-Harassment Policy](http://geekfeminism.wikia.com/wiki/Conference_anti-harassment/Policy). 87 | 88 | _Revision 2.3. Posted 6 March 2017._ 89 | 90 | _Revision 2.2. Posted 4 February 2016._ 91 | 92 | _Revision 2.1. Posted 23 June 2014._ 93 | 94 | _Revision 2.0, adopted by the [Stumptown Syndicate](http://stumptownsyndicate.org) board on 10 January 2013. Posted 17 March 2013._ 95 | --------------------------------------------------------------------------------