├── 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 |
2 | .container(id="page-404")
3 | h1.title 404 page not found
4 |
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 |
2 | #base-container(
3 | class="container is-fluid"
4 | )
5 |
6 |
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 |
2 | .box.is-shadowless
3 | article.media
4 | .media-left
5 | span.icon.is-large
6 | i.mdi.mdi-36px(
7 | :class="icon"
8 | )
9 | .media-content
10 | .content
11 | p(
12 | v-html="content"
13 | )
14 |
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 |
2 | div.table-container(
3 | v-if="hasItems"
4 | )
5 | h3.subtitle.is-5(v-if="title") {{title}}
6 |
7 | table.table.is-fullwidth.is-striped.is-bordered
8 | tbody
9 | tr(
10 | v-for="(value, index) in items"
11 | :key="index"
12 | )
13 | td {{index}}:
14 | td(
15 | v-html="value"
16 | )
17 |
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 |
2 | .modal(
3 | v-if="show"
4 | :class="show ? 'is-active':''"
5 | )
6 | .modal-background
7 | .modal-card
8 | header.modal-card-head(
9 | v-if="title"
10 | )
11 | p.modal-card-title {{ title }}
12 | button.delete(
13 | v-if="closeButton"
14 | aria-label="close"
15 | @click="$emit('close')"
16 | )
17 |
18 | .modal-card-body(ref="modalContent")
19 | slot(:modalContent="this.$refs.modalContent")
20 |
21 | .modal-card-foot(
22 | v-if="confirmButton || cancelButton"
23 | )
24 | .button.is-primary(
25 | v-if="confirmButton"
26 | @click="$emit('confirm')"
27 | ) {{confirmButtonText}}
28 |
29 | .button(
30 | v-if="cancelButton"
31 | @click="$emit('cancel')"
32 | ) {{cancelButtonText}}
33 |
34 |
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 |
2 | .container(id="user-page-item")
3 | template(v-if="!hasItems") Loading...
4 |
5 | template(v-else)
6 | h2.title.is-4 User information
7 |
8 | .card.card-margin
9 | .card-content
10 | .table-container
11 | table.table.is-bordered.is-striped.is-fullwidth
12 | tbody
13 | tr
14 | td Unique
15 | td {{userItem.user_unique}}
16 | tr
17 | td Info
18 | td {{userItem.user_info}}
19 |
20 | .card.card-margin
21 | .card-content
22 | h3.subtitle.is-5 User Meta
23 | .table-container
24 | table.table.is-bordered.is-striped.is-fullwidth
25 | tbody
26 | tr(
27 | v-for="(value, index) in userItem.user_meta"
28 | :key="`user-details-${index}`"
29 | )
30 | td {{index}}
31 | td {{value}}
32 |
33 |
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 |
2 | BPhixCardModal(
3 | :show="showCreateProjectModal"
4 | title="Create new project"
5 | :closeButton="false"
6 | @confirm="createproject"
7 | @cancel="cancel"
8 | )
9 |
10 | .field
11 | label.label Project name
12 | .control
13 | input.input(
14 | type="text"
15 | placeholder="Enter project name"
16 | v-model="projectName"
17 | :disabled="formSending"
18 | )
19 |
20 | //- .field
21 | //- label.label Project platform
22 | //- .control
23 | //- .select
24 | //- select
25 | //- option Laravel
26 | //- option VueJS
27 |
28 |
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 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Capture and monitor detailed error logs with nice dashboard and UI
13 |
14 |
15 |
16 |
17 |
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 |
2 | .container(id="users-page")
3 | .columns
4 | .column
5 | h2.subtitle Users ({{totalUsers}})
6 |
7 | .column
8 | BPhixSearchForm(
9 | :sort="getFilterSort"
10 | :keyword="getSearchKeyword"
11 | @confirm="confirmSearch"
12 | )
13 |
14 | .table-container
15 | table.table.is-bordered.is-striped.is-hoverable.is-fullwidth
16 | thead
17 | tr
18 | th Unique
19 | th User info
20 |
21 | tbody
22 | template(v-if="!hasUsers && !pageLoadComplete")
23 | tr
24 | td(colspan="2")
25 | p No user
26 |
27 | template(v-else)
28 | tr(
29 | v-for="(item, key) in getUserList.data"
30 | :key="`users-${key}`"
31 | )
32 | td
33 | router-link(:to="{name: 'user-item',params: { userId: item.id}}") {{item.user_unique}}
34 | td {{ item.user_info }}
35 |
36 | BPhixPagination(
37 | :pagination="getUserList"
38 | @paginate="paginate(getUserList.current_page)"
39 | )
40 |
41 |
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 |
2 | .section-spacing.issues-exception-details
3 | h3.subtitle.is-5 Exception:
4 |
5 | p.issue-file-link(
6 | :title="fileError"
7 | ) {{fileError}}
8 |
9 | br
10 |
11 | .buttons.has-addons
12 | button.button.is-small(
13 | :class="isFullLog ? '' : 'is-primary is-selected'"
14 | @click="isFullLog = false"
15 | ) Excerpt
16 |
17 | button.button.is-small(
18 | :class="!isFullLog ? '' : 'is-primary is-selected'"
19 | @click="isFullLog = true"
20 | ) Full log
21 |
22 | template(
23 | v-if="!isFullLog"
24 | )
25 | .box
26 | article.media
27 | .media-left
28 | span.icon.is-large
29 | i.mdi.mdi-36px.mdi-information-outline
30 |
31 | .media-content
32 | .content
33 | p {{errorMessage}}
34 |
35 | ol.stacktrace-prettify(
36 | :start="startLine"
37 | )
38 | li(
39 | v-for="(line, index) in stackTraceArray"
40 | :class="(startLine + index) === errorLine ? 'is-target-line' : ''"
41 | :key="index"
42 | )
43 | .code-line {{line}}
44 |
45 |
46 |
47 | template(v-else)
48 | p.exception-full-log {{fullLog}}
49 |
50 |
51 |
75 |
76 |
117 |
--------------------------------------------------------------------------------
/src/Resources/js/components/BaseUI/SearchForm.vue:
--------------------------------------------------------------------------------
1 |
2 | #search-box
3 | .search-filter.buttons
4 |
5 | slot(name="searchBefore")
6 |
7 | .dropdown.is-right(
8 | :class="{'is-active' : openSortFilter}"
9 | @click="openSortFilter = !openSortFilter"
10 | )
11 | .dropdown-trigger
12 | button.button(
13 | aria-haspopup="true"
14 | aria-controls="dropdown-menu"
15 | )
16 | span Sort by: {{sortFilter}}
17 | span.icon.is-small
18 | i.mdi.mdi-chevron-down
19 |
20 | .dropdown-menu
21 | .dropdown-content
22 | a.dropdown-item(
23 | v-for="(sort, key) in sortFilterList"
24 | :key="`filter-sort-${key}`"
25 | :class="sort === sortFilter ? 'is-active' : ''"
26 | @click.prevent="updateSortFilter(sort)"
27 | ) Sort by: {{sort}}
28 |
29 | .field.has-addons.search-box
30 | .control(
31 | :class="{'is-loading' : searchIsRunning}"
32 | )
33 | input.input.search-field(
34 | type="search"
35 | v-model="searchKeyword"
36 | placeholder="Search"
37 | @change="returnEmit"
38 | )
39 | .control
40 | button.btn-submit.button.is-primary(
41 | @click="returnEmit"
42 | )
43 | i.mdi.mdi-magnify
44 |
45 |
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 |
2 | div.table-container(
3 | v-if="hasItems"
4 | )
5 | h3.subtitle.is-5 Project details
6 |
7 | table.table.is-fullwidth.is-striped
8 | tbody
9 | tr
10 | td Current status
11 | td {{projectStatus}}
12 | tr
13 | td DSN
14 | td.project-dsn
15 | input(
16 | type="hidden"
17 | id="copy-clipboard-dsn"
18 | :value="projectDSN"
19 | )
20 | span.link(
21 | :title="projectDSN"
22 | ) {{projectDSN}}
23 | button.button.is-small.is-primary(
24 | @click="copyDsn"
25 | ) Copy
26 | tr
27 | td Issues
28 | td {{projectIssues}}
29 | tr
30 | td Events
31 | td {{projectEvents}}
32 | tr
33 | td Updated
34 | td(v-html="projectUpdated")
35 | tr
36 | td Created
37 | td(v-html="projectCreated")
38 |
39 |
40 |
114 |
115 |
125 |
--------------------------------------------------------------------------------
/src/Resources/js/components/Pages/Users/children/ProjectRightDetails.vue:
--------------------------------------------------------------------------------
1 |
2 | div.table-container(
3 | v-if="hasItems"
4 | )
5 | h3.subtitle.is-5 Project details
6 |
7 | table.table.is-fullwidth.is-striped
8 | tbody
9 | tr
10 | td Current status
11 | td {{projectStatus}}
12 | tr
13 | td DSN
14 | td.project-dsn
15 | input(
16 | type="hidden"
17 | id="copy-clipboard-dsn"
18 | :value="projectDSN"
19 | )
20 | span.link(
21 | :title="projectDSN"
22 | ) {{projectDSN}}
23 | button.button.is-small.is-primary(
24 | @click="copyDsn"
25 | ) Copy
26 | tr
27 | td Issues
28 | td {{projectIssues}}
29 | tr
30 | td Events
31 | td {{projectEvents}}
32 | tr
33 | td Updated
34 | td(v-html="projectUpdated")
35 | tr
36 | td Created
37 | td(v-html="projectCreated")
38 |
39 |
40 |
114 |
115 |
132 |
--------------------------------------------------------------------------------
/src/Resources/js/components/Pages/Projects/Projects.vue:
--------------------------------------------------------------------------------
1 |
2 | .container(id="projects-page")
3 | .columns
4 | .column
5 | h2.subtitle Projects ({{totalProjects}})
6 | button.button.is-small.is-primary.btn-create-project(
7 | @click="showModal"
8 | ) Create
9 |
10 | .column
11 | BPhixSearchForm(
12 | :sort="getFilterSort"
13 | :keyword="getSearchKeyword"
14 | @confirm="confirmSearch"
15 | )
16 |
17 | .table-container
18 | table.table.is-bordered.is-striped.is-hoverable.is-fullwidth
19 | thead
20 | tr
21 | th Projects
22 | th.column-narrow(align="center") Status
23 | th.column-narrow(align="center") Issues
24 |
25 | tbody
26 | tr(
27 | v-for="(item, key) in getProjectList.data"
28 | :key="`projects-${key}`"
29 | :class="{'project-deactivated': !item.is_active}"
30 | )
31 | td
32 | router-link(
33 | class="error-exception"
34 | :to="{name: 'project-item',params: { projectId: item.project_id }}"
35 | ) {{item.project_name}}
36 | td(align="center") {{item.is_active ? 'Active' : 'Deactivated'}}
37 | td(align="center") {{formatIssue(item.issues)}}
38 |
39 | BPhixPagination(
40 | :pagination="getProjectList"
41 | @paginate="paginate(getProjectList.current_page)"
42 | )
43 |
44 | CreateProjectModal
45 |
46 |
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 |
2 | .container(id="projects-page-item")
3 | template(v-if="!hasItems") Loading...
4 |
5 | template(v-else)
6 | .project-toggle-active
7 | .switch-wrapper
8 | span Activate project
9 | label.switch
10 | input(
11 | type="checkbox"
12 | :checked="isActive"
13 | v-model="formData.is_active"
14 | )
15 | span.slider.round
16 |
17 | h2.title.is-4 Project information
18 |
19 | .card.card-margin
20 | .card-content
21 | .field
22 | label.label Project name
23 | .control
24 | input.input(
25 | type="text"
26 | v-model="formData.project_name"
27 | )
28 |
29 | //- .field Platform
30 | //- .control
31 | //- input.input(
32 | //- type="text"
33 | //- v-model="formData.project_platform"
34 | //- )
35 |
36 | .field
37 | label.label API Token
38 | .control
39 | input.input(
40 | type="text"
41 | v-model="formData.project_token"
42 | )
43 |
44 | .buttons.save-button
45 | button.button.is-primary(
46 | @click="saveProject"
47 | )
48 | span.icon
49 | i.mdi.mdi-floppy
50 | span Save
51 |
52 | .card.card-margin
53 | .card-content
54 | ProjectMeta(
55 | v-if="projectId"
56 | :projectItem="projectItem"
57 | )
58 |
59 |
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 |
64 | We're sorry but Bugphix doesn't work properly without JavaScript enabled. Please enable it to continue.
65 |
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 |
--------------------------------------------------------------------------------