├── backend ├── dump.html ├── public │ ├── favicon.ico │ ├── robots.txt │ ├── .htaccess │ └── index.php ├── resources │ ├── css │ │ └── app.css │ ├── js │ │ ├── app.js │ │ └── bootstrap.js │ └── views │ │ └── test.blade.php ├── database │ ├── .gitignore │ ├── seeders │ │ └── DatabaseSeeder.php │ ├── migrations │ │ ├── 2014_10_12_100000_create_password_reset_tokens_table.php │ │ ├── 2014_10_12_000000_create_users_table.php │ │ ├── 2019_08_19_000000_create_failed_jobs_table.php │ │ └── 2019_12_14_000001_create_personal_access_tokens_table.php │ └── factories │ │ └── UserFactory.php ├── bootstrap │ ├── cache │ │ └── .gitignore │ └── app.php ├── storage │ └── framework │ │ ├── sessions │ │ └── .gitignore │ │ ├── testing │ │ └── .gitignore │ │ ├── views │ │ └── .gitignore │ │ ├── cache │ │ ├── data │ │ │ └── .gitignore │ │ └── .gitignore │ │ └── .gitignore ├── docker │ ├── docker-compose.yml │ ├── supervisord.conf │ ├── nginx.conf │ └── Dockerfile ├── tests │ ├── TestCase.php │ ├── Unit │ │ └── ExampleTest.php │ ├── Feature │ │ └── ExampleTest.php │ └── CreatesApplication.php ├── .gitattributes ├── package.json ├── vite.config.js ├── .gitignore ├── .editorconfig ├── app │ ├── Http │ │ ├── Middleware │ │ │ ├── EncryptCookies.php │ │ │ ├── VerifyCsrfToken.php │ │ │ ├── PreventRequestsDuringMaintenance.php │ │ │ ├── TrimStrings.php │ │ │ ├── TrustHosts.php │ │ │ ├── Authenticate.php │ │ │ ├── ValidateSignature.php │ │ │ ├── TrustProxies.php │ │ │ └── RedirectIfAuthenticated.php │ │ ├── Controllers │ │ │ ├── Controller.php │ │ │ └── DumpsController.php │ │ └── Kernel.php │ ├── Providers │ │ ├── BroadcastServiceProvider.php │ │ ├── AppServiceProvider.php │ │ ├── AuthServiceProvider.php │ │ ├── EventServiceProvider.php │ │ └── RouteServiceProvider.php │ ├── Console │ │ ├── Commands │ │ │ ├── HealthcheckCommand.php │ │ │ └── ServerDumpCommand.php │ │ └── Kernel.php │ ├── Exceptions │ │ └── Handler.php │ ├── Services │ │ └── Healthcheck.php │ ├── Descriptors │ │ └── HtmlDumpDescriptor.php │ ├── Models │ │ └── User.php │ ├── Outputs │ │ └── RedisOutput.php │ └── Dumpers │ │ └── HtmlCustomDumper.php ├── routes │ ├── channels.php │ ├── api.php │ ├── console.php │ └── web.php ├── config │ ├── cors.php │ ├── services.php │ ├── view.php │ ├── hashing.php │ ├── broadcasting.php │ ├── sanctum.php │ ├── filesystems.php │ ├── cache.php │ ├── queue.php │ ├── mail.php │ ├── auth.php │ ├── logging.php │ ├── database.php │ ├── app.php │ └── session.php ├── phpunit.xml ├── .env.example ├── artisan ├── composer.json └── README.md ├── ui ├── .env ├── .browserslistrc ├── src │ ├── vite-env.d.ts │ ├── main.tsx │ ├── components │ │ ├── FlashStack.ts │ │ ├── Diff.tsx │ │ ├── DumpFrame.tsx │ │ ├── DumpsDiff.tsx │ │ ├── Dumps.tsx │ │ └── Logo.tsx │ ├── App.tsx │ ├── collections │ │ └── DumpsCollection.ts │ └── models │ │ └── Dump.ts ├── public │ ├── logo.png │ ├── screenshot_1.png │ ├── screenshot_2.png │ └── screenshot_3.png ├── tsconfig.node.json ├── vite.config.ts ├── index.html ├── tsconfig.json └── package.json ├── .github └── FUNDING.yml ├── .dockerignore ├── .gitignore ├── docker-compose.yaml ├── metadata.json ├── Makefile ├── README.md ├── Dockerfile └── docker.svg /backend/dump.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/.env: -------------------------------------------------------------------------------- 1 | BROWSER=none -------------------------------------------------------------------------------- /backend/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /backend/resources/css/app.css: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ui/.browserslistrc: -------------------------------------------------------------------------------- 1 | Electron 17.1.1 2 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [artifision] -------------------------------------------------------------------------------- /backend/database/.gitignore: -------------------------------------------------------------------------------- 1 | *.sqlite* 2 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | ui/node_modules 2 | backend/vendor -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | ui/build 3 | .idea -------------------------------------------------------------------------------- /backend/resources/js/app.js: -------------------------------------------------------------------------------- 1 | import './bootstrap'; 2 | -------------------------------------------------------------------------------- /backend/bootstrap/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /ui/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /backend/storage/framework/sessions/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/testing/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/views/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/cache/data/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !.gitignore 3 | -------------------------------------------------------------------------------- /backend/storage/framework/cache/.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !data/ 3 | !.gitignore 4 | -------------------------------------------------------------------------------- /ui/public/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artifision/php-dumper-docker-extension/HEAD/ui/public/logo.png -------------------------------------------------------------------------------- /ui/public/screenshot_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artifision/php-dumper-docker-extension/HEAD/ui/public/screenshot_1.png -------------------------------------------------------------------------------- /ui/public/screenshot_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artifision/php-dumper-docker-extension/HEAD/ui/public/screenshot_2.png -------------------------------------------------------------------------------- /ui/public/screenshot_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/artifision/php-dumper-docker-extension/HEAD/ui/public/screenshot_3.png -------------------------------------------------------------------------------- /docker-compose.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | php-dumper-docker-extension: 3 | image: ${DESKTOP_PLUGIN_IMAGE} 4 | ports: 5 | - "9912:9912" 6 | - "9913:9913" -------------------------------------------------------------------------------- /backend/storage/framework/.gitignore: -------------------------------------------------------------------------------- 1 | compiled.php 2 | config.php 3 | down 4 | events.scanned.php 5 | maintenance.php 6 | routes.php 7 | routes.scanned.php 8 | schedule-* 9 | services.json 10 | -------------------------------------------------------------------------------- /backend/docker/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | app: 5 | build: . 6 | volumes: 7 | - ../.:/var/www 8 | ports: 9 | - "9912:9912" 10 | - "9913:9913" 11 | -------------------------------------------------------------------------------- /backend/tests/TestCase.php: -------------------------------------------------------------------------------- 1 | assertTrue(true); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /ui/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | base: "./", 8 | build: { 9 | outDir: "build", 10 | }, 11 | server: { 12 | port: 3000, 13 | strictPort: true, 14 | } 15 | }); 16 | -------------------------------------------------------------------------------- /backend/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_size = 4 7 | indent_style = space 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [docker-compose.yml] 18 | indent_size = 4 19 | -------------------------------------------------------------------------------- /ui/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /backend/app/Http/Middleware/EncryptCookies.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /backend/app/Http/Middleware/VerifyCsrfToken.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /backend/tests/Feature/ExampleTest.php: -------------------------------------------------------------------------------- 1 | get('/'); 16 | 17 | $response->assertStatus(200); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /backend/app/Providers/BroadcastServiceProvider.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 16 | ]; 17 | } 18 | -------------------------------------------------------------------------------- /backend/tests/CreatesApplication.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class)->bootstrap(); 18 | 19 | return $app; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /backend/app/Http/Middleware/TrimStrings.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | 'current_password', 16 | 'password', 17 | 'password_confirmation', 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /backend/app/Http/Middleware/TrustHosts.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | public function hosts(): array 15 | { 16 | return [ 17 | $this->allSubdomainsOfApplicationUrl(), 18 | ]; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /backend/app/Providers/AppServiceProvider.php: -------------------------------------------------------------------------------- 1 | expectsJson() ? null : route('login'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /backend/app/Http/Middleware/ValidateSignature.php: -------------------------------------------------------------------------------- 1 | 13 | */ 14 | protected $except = [ 15 | // 'fbclid', 16 | // 'utm_campaign', 17 | // 'utm_content', 18 | // 'utm_medium', 19 | // 'utm_source', 20 | // 'utm_term', 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /backend/database/seeders/DatabaseSeeder.php: -------------------------------------------------------------------------------- 1 | create(); 16 | 17 | // \App\Models\User::factory()->create([ 18 | // 'name' => 'Test User', 19 | // 'email' => 'test@example.com', 20 | // ]); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /backend/routes/channels.php: -------------------------------------------------------------------------------- 1 | id === (int) $id; 18 | }); 19 | -------------------------------------------------------------------------------- /backend/app/Console/Commands/HealthcheckCommand.php: -------------------------------------------------------------------------------- 1 | pingOk()) { 17 | $healthcheck->sendStopSignal(); 18 | } 19 | 20 | sleep(Healthcheck::PING_CHECK_INTERVAL); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ESNext", 4 | "useDefineForClassFields": true, 5 | "lib": ["DOM", "DOM.Iterable", "ESNext"], 6 | "allowJs": false, 7 | "skipLibCheck": true, 8 | "esModuleInterop": false, 9 | "allowSyntheticDefaultImports": true, 10 | "strict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "module": "ESNext", 13 | "moduleResolution": "Node", 14 | "resolveJsonModule": true, 15 | "isolatedModules": true, 16 | "noEmit": true, 17 | "jsx": "react-jsx" 18 | }, 19 | "include": ["src"], 20 | "references": [{ "path": "./tsconfig.node.json" }] 21 | } 22 | -------------------------------------------------------------------------------- /backend/routes/api.php: -------------------------------------------------------------------------------- 1 | get('/user', function (Request $request) { 18 | return $request->user(); 19 | }); 20 | -------------------------------------------------------------------------------- /backend/app/Providers/AuthServiceProvider.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $policies = [ 16 | // 17 | ]; 18 | 19 | /** 20 | * Register any authentication / authorization services. 21 | */ 22 | public function boot(): void 23 | { 24 | // 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /backend/routes/console.php: -------------------------------------------------------------------------------- 1 | comment(Inspiring::quote()); 19 | })->purpose('Display an inspiring quote'); 20 | -------------------------------------------------------------------------------- /backend/public/.htaccess: -------------------------------------------------------------------------------- 1 | 2 | 3 | Options -MultiViews -Indexes 4 | 5 | 6 | RewriteEngine On 7 | 8 | # Handle Authorization Header 9 | RewriteCond %{HTTP:Authorization} . 10 | RewriteRule .* - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}] 11 | 12 | # Redirect Trailing Slashes If Not A Folder... 13 | RewriteCond %{REQUEST_FILENAME} !-d 14 | RewriteCond %{REQUEST_URI} (.+)/$ 15 | RewriteRule ^ %1 [L,R=301] 16 | 17 | # Send Requests To Front Controller... 18 | RewriteCond %{REQUEST_FILENAME} !-d 19 | RewriteCond %{REQUEST_FILENAME} !-f 20 | RewriteRule ^ index.php [L] 21 | 22 | -------------------------------------------------------------------------------- /backend/app/Console/Kernel.php: -------------------------------------------------------------------------------- 1 | command('inspire')->hourly(); 16 | } 17 | 18 | /** 19 | * Register the commands for the application. 20 | */ 21 | protected function commands(): void 22 | { 23 | $this->load(__DIR__.'/Commands'); 24 | 25 | require base_path('routes/console.php'); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ui/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import CssBaseline from "@mui/material/CssBaseline"; 4 | import { DockerMuiThemeProvider } from "@docker/docker-mui-theme"; 5 | 6 | import { App } from './App'; 7 | 8 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 9 | 10 | {/* 11 | If you eject from MUI (which we don't recommend!), you should add 12 | the `dockerDesktopTheme` class to your root element to get 13 | some minimal Docker theming. 14 | */} 15 | 16 | 17 | 18 | 19 | 20 | ); 21 | -------------------------------------------------------------------------------- /backend/app/Exceptions/Handler.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | protected $dontFlash = [ 16 | 'current_password', 17 | 'password', 18 | 'password_confirmation', 19 | ]; 20 | 21 | /** 22 | * Register the exception handling callbacks for the application. 23 | */ 24 | public function register(): void 25 | { 26 | $this->reportable(function (Throwable $e) { 27 | // 28 | }); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/app/Http/Middleware/TrustProxies.php: -------------------------------------------------------------------------------- 1 | |string|null 14 | */ 15 | protected $proxies; 16 | 17 | /** 18 | * The headers that should be used to detect proxies. 19 | * 20 | * @var int 21 | */ 22 | protected $headers = 23 | Request::HEADER_X_FORWARDED_FOR | 24 | Request::HEADER_X_FORWARDED_HOST | 25 | Request::HEADER_X_FORWARDED_PORT | 26 | Request::HEADER_X_FORWARDED_PROTO | 27 | Request::HEADER_X_FORWARDED_AWS_ELB; 28 | } 29 | -------------------------------------------------------------------------------- /backend/database/migrations/2014_10_12_100000_create_password_reset_tokens_table.php: -------------------------------------------------------------------------------- 1 | string('email')->primary(); 16 | $table->string('token'); 17 | $table->timestamp('created_at')->nullable(); 18 | }); 19 | } 20 | 21 | /** 22 | * Reverse the migrations. 23 | */ 24 | public function down(): void 25 | { 26 | Schema::dropIfExists('password_reset_tokens'); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /backend/routes/web.php: -------------------------------------------------------------------------------- 1 | view('test')); 22 | Route::get('/dump', [DumpsController::class, 'dump']); 23 | -------------------------------------------------------------------------------- /backend/docker/supervisord.conf: -------------------------------------------------------------------------------- 1 | [supervisord] 2 | nodaemon=true 3 | logfile=/dev/null 4 | logfile_maxbytes=0 5 | pidfile=/run/supervisord.pid 6 | 7 | [program:php-fpm] 8 | command=php-fpm -F 9 | stdout_logfile=/dev/stdout 10 | stdout_logfile_maxbytes=0 11 | stderr_logfile=/dev/stderr 12 | stderr_logfile_maxbytes=0 13 | autorestart=false 14 | startretries=0 15 | 16 | [program:nginx] 17 | command=nginx -g 'daemon off;' 18 | stdout_logfile=/dev/stdout 19 | stdout_logfile_maxbytes=0 20 | stderr_logfile=/dev/stderr 21 | stderr_logfile_maxbytes=0 22 | autorestart=false 23 | startretries=0 24 | 25 | [program:healthcheck] 26 | command=php /var/www/artisan app:healthcheck-command 27 | stdout_logfile=/dev/stdout 28 | stdout_logfile_maxbytes=0 29 | stderr_logfile=/dev/stderr 30 | stderr_logfile_maxbytes=0 31 | autorestart=true 32 | autostart=true 33 | startretries=0 34 | user=www-data 35 | -------------------------------------------------------------------------------- /ui/src/components/FlashStack.ts: -------------------------------------------------------------------------------- 1 | import {styled} from '@mui/material/styles'; 2 | import {keyframes} from '@mui/system'; 3 | import {blueGrey} from "@mui/material/colors"; 4 | import {Stack} from "@mui/material"; 5 | 6 | const backgroundFadeDark = keyframes` 7 | from { 8 | background-color: ${blueGrey[700]}; 9 | } 10 | to { 11 | background-color: ${blueGrey[900]}; 12 | } 13 | `; 14 | 15 | const backgroundFadeLight = keyframes` 16 | from { 17 | background-color: ${blueGrey[100]}; 18 | } 19 | to { 20 | background-color: ${blueGrey[50]}; 21 | } 22 | `; 23 | 24 | const FlashStack = styled(Stack)(({theme}) => ({ 25 | backgroundColor: blueGrey[theme.palette.mode === 'dark' ? 900 : 50], 26 | animation: `${theme.palette.mode === 'dark' ? backgroundFadeDark : backgroundFadeLight} 1s ease-out`, 27 | })); 28 | 29 | export default FlashStack; -------------------------------------------------------------------------------- /backend/app/Http/Controllers/Controller.php: -------------------------------------------------------------------------------- 1 | stream($callback, 200, [ 17 | 'Cache-Control' => 'no-cache', 18 | 'Content-Type' => 'text/event-stream', 19 | ]); 20 | } 21 | 22 | protected function sendEvent(mixed $data): void 23 | { 24 | echo 'data: ' . json_encode($data) . "\n\n"; 25 | ob_flush(); 26 | flush(); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /backend/app/Http/Middleware/RedirectIfAuthenticated.php: -------------------------------------------------------------------------------- 1 | check()) { 24 | return redirect(RouteServiceProvider::HOME); 25 | } 26 | } 27 | 28 | return $next($request); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/app/Services/Healthcheck.php: -------------------------------------------------------------------------------- 1 | cache->put(static::PING_CACHE_KEY, time(), static::OK_PING_INTERVAL); 21 | } 22 | 23 | public function pingOk(): bool 24 | { 25 | return (bool)$this->cache->get(static::PING_CACHE_KEY); 26 | } 27 | 28 | public function sendStopSignal(): void 29 | { 30 | ob_start(); 31 | dump(static::STOP_SERVER_KEY); 32 | ob_get_clean(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /backend/database/migrations/2014_10_12_000000_create_users_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('name'); 17 | $table->string('email')->unique(); 18 | $table->timestamp('email_verified_at')->nullable(); 19 | $table->string('password'); 20 | $table->rememberToken(); 21 | $table->timestamps(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('users'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /backend/database/migrations/2019_08_19_000000_create_failed_jobs_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->string('uuid')->unique(); 17 | $table->text('connection'); 18 | $table->text('queue'); 19 | $table->longText('payload'); 20 | $table->longText('exception'); 21 | $table->timestamp('failed_at')->useCurrent(); 22 | }); 23 | } 24 | 25 | /** 26 | * Reverse the migrations. 27 | */ 28 | public function down(): void 29 | { 30 | Schema::dropIfExists('failed_jobs'); 31 | } 32 | }; 33 | -------------------------------------------------------------------------------- /ui/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "ui", 3 | "version": "0.1.0", 4 | "private": true, 5 | "type": "module", 6 | "dependencies": { 7 | "@docker/docker-mui-theme": "<0.1.0", 8 | "@docker/extension-api-client": "0.3.4", 9 | "@emotion/react": "11.10.4", 10 | "@emotion/styled": "11.10.4", 11 | "@mui/icons-material": "^5.18.0", 12 | "@mui/material": "^5.18.0", 13 | "diff": "^5.1.0", 14 | "react": "^18.2.0", 15 | "react-dom": "^18.2.0" 16 | }, 17 | "scripts": { 18 | "dev": "vite", 19 | "build": "tsc && vite build", 20 | "test": "jest src" 21 | }, 22 | "devDependencies": { 23 | "@docker/extension-api-client-types": "0.3.4", 24 | "@types/diff": "^5.0.7", 25 | "@types/jest": "^29.1.2", 26 | "@types/node": "^18.7.18", 27 | "@types/react": "^18.0.17", 28 | "@types/react-dom": "^18.0.6", 29 | "@vitejs/plugin-react": "^2.1.0", 30 | "jest": "^29.1.2", 31 | "typescript": "^4.8.3", 32 | "vite": "^3.1.0" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /backend/config/cors.php: -------------------------------------------------------------------------------- 1 | ['api/*', 'sanctum/csrf-cookie'], 19 | 20 | 'allowed_methods' => ['*'], 21 | 22 | 'allowed_origins' => ['*'], 23 | 24 | 'allowed_origins_patterns' => [], 25 | 26 | 'allowed_headers' => ['*'], 27 | 28 | 'exposed_headers' => [], 29 | 30 | 'max_age' => 0, 31 | 32 | 'supports_credentials' => false, 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /backend/docker/nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 9913; 3 | 4 | index index.php; 5 | root /var/www/public; 6 | 7 | location ~ \.php$ { 8 | add_header Access-Control-Allow-Origin *; 9 | try_files $uri =404; 10 | fastcgi_split_path_info ^(.+\.php)(/.+)$; 11 | fastcgi_pass 127.0.0.1:9000; 12 | fastcgi_index index.php; 13 | include fastcgi_params; 14 | fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; 15 | fastcgi_param PATH_INFO $fastcgi_path_info; 16 | fastcgi_buffering off; 17 | 18 | proxy_connect_timeout 24h; 19 | proxy_send_timeout 24h; 20 | proxy_read_timeout 24h; 21 | send_timeout 24h; 22 | fastcgi_read_timeout 24h; 23 | fastcgi_send_timeout 24h; 24 | } 25 | 26 | location / { 27 | add_header Access-Control-Allow-Origin *; 28 | try_files $uri $uri/ /index.php?$query_string; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /backend/database/migrations/2019_12_14_000001_create_personal_access_tokens_table.php: -------------------------------------------------------------------------------- 1 | id(); 16 | $table->morphs('tokenable'); 17 | $table->string('name'); 18 | $table->string('token', 64)->unique(); 19 | $table->text('abilities')->nullable(); 20 | $table->timestamp('last_used_at')->nullable(); 21 | $table->timestamp('expires_at')->nullable(); 22 | $table->timestamps(); 23 | }); 24 | } 25 | 26 | /** 27 | * Reverse the migrations. 28 | */ 29 | public function down(): void 30 | { 31 | Schema::dropIfExists('personal_access_tokens'); 32 | } 33 | }; 34 | -------------------------------------------------------------------------------- /backend/app/Providers/EventServiceProvider.php: -------------------------------------------------------------------------------- 1 | > 16 | */ 17 | protected $listen = [ 18 | Registered::class => [ 19 | SendEmailVerificationNotification::class, 20 | ], 21 | ]; 22 | 23 | /** 24 | * Register any events for your application. 25 | */ 26 | public function boot(): void 27 | { 28 | // 29 | } 30 | 31 | /** 32 | * Determine if events and listeners should be automatically discovered. 33 | */ 34 | public function shouldDiscoverEvents(): bool 35 | { 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/config/services.php: -------------------------------------------------------------------------------- 1 | [ 18 | 'domain' => env('MAILGUN_DOMAIN'), 19 | 'secret' => env('MAILGUN_SECRET'), 20 | 'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'), 21 | 'scheme' => 'https', 22 | ], 23 | 24 | 'postmark' => [ 25 | 'token' => env('POSTMARK_TOKEN'), 26 | ], 27 | 28 | 'ses' => [ 29 | 'key' => env('AWS_ACCESS_KEY_ID'), 30 | 'secret' => env('AWS_SECRET_ACCESS_KEY'), 31 | 'region' => env('AWS_DEFAULT_REGION', 'us-east-1'), 32 | ], 33 | 34 | ]; 35 | -------------------------------------------------------------------------------- /backend/phpunit.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | tests/Unit 10 | 11 | 12 | tests/Feature 13 | 14 | 15 | 16 | 17 | app 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /backend/database/factories/UserFactory.php: -------------------------------------------------------------------------------- 1 | 10 | */ 11 | class UserFactory extends Factory 12 | { 13 | /** 14 | * Define the model's default state. 15 | * 16 | * @return array 17 | */ 18 | public function definition(): array 19 | { 20 | return [ 21 | 'name' => fake()->name(), 22 | 'email' => fake()->unique()->safeEmail(), 23 | 'email_verified_at' => now(), 24 | 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 25 | 'remember_token' => Str::random(10), 26 | ]; 27 | } 28 | 29 | /** 30 | * Indicate that the model's email address should be unverified. 31 | */ 32 | public function unverified(): static 33 | { 34 | return $this->state(fn (array $attributes) => [ 35 | 'email_verified_at' => null, 36 | ]); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /backend/app/Descriptors/HtmlDumpDescriptor.php: -------------------------------------------------------------------------------- 1 | dumper->setTheme($theme); 17 | } 18 | 19 | public function setMaxDepth(int $depth): void 20 | { 21 | $this->dumper->setDisplayOptions(['maxDepth' => $depth]); 22 | } 23 | 24 | public function describe(Data $data, array $context): array 25 | { 26 | if ($controller = $context['request']['controller'] ?? null) { 27 | $context['request']['controller_html'] = $this->dumper->dump($controller, true, ['maxDepth' => 0]); 28 | } 29 | 30 | return [ 31 | 'uid' => uniqid('dump-', true), 32 | 'timestamp' => (float)($context['timestamp'] ?: microtime(true)), 33 | 'context' => $context, 34 | 'html' => $this->dumper->dump($data, true), 35 | ]; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /backend/app/Models/User.php: -------------------------------------------------------------------------------- 1 | 19 | */ 20 | protected $fillable = [ 21 | 'name', 22 | 'email', 23 | 'password', 24 | ]; 25 | 26 | /** 27 | * The attributes that should be hidden for serialization. 28 | * 29 | * @var array 30 | */ 31 | protected $hidden = [ 32 | 'password', 33 | 'remember_token', 34 | ]; 35 | 36 | /** 37 | * The attributes that should be cast. 38 | * 39 | * @var array 40 | */ 41 | protected $casts = [ 42 | 'email_verified_at' => 'datetime', 43 | 'password' => 'hashed', 44 | ]; 45 | } 46 | -------------------------------------------------------------------------------- /backend/app/Console/Commands/ServerDumpCommand.php: -------------------------------------------------------------------------------- 1 | setDisplayOptions(['maxDepth' => 0]); 22 | $descriptor = new HtmlDumpDescriptor($htmlDumper); 23 | $io = new SymfonyStyle($this->input, $redisOutput); 24 | $server = new DumpServer('0.0.0.0:9912'); 25 | $server->start(); 26 | 27 | $server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) { 28 | $descriptor->describe($io, $data, $context, $clientId); 29 | }); 30 | 31 | return 0; 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /backend/config/view.php: -------------------------------------------------------------------------------- 1 | [ 17 | resource_path('views'), 18 | ], 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Compiled View Path 23 | |-------------------------------------------------------------------------- 24 | | 25 | | This option determines where all the compiled Blade templates will be 26 | | stored for your application. Typically, this is within the storage 27 | | directory. However, as usual, you are free to change this value. 28 | | 29 | */ 30 | 31 | 'compiled' => env( 32 | 'VIEW_COMPILED_PATH', 33 | realpath(storage_path('framework/views')) 34 | ), 35 | 36 | ]; 37 | -------------------------------------------------------------------------------- /backend/app/Providers/RouteServiceProvider.php: -------------------------------------------------------------------------------- 1 | by($request->user()?->id ?: $request->ip()); 29 | }); 30 | 31 | $this->routes(function () { 32 | Route::middleware('api') 33 | ->prefix('api') 34 | ->group(base_path('routes/api.php')); 35 | 36 | Route::middleware('web') 37 | ->group(base_path('routes/web.php')); 38 | }); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /backend/.env.example: -------------------------------------------------------------------------------- 1 | APP_NAME=Laravel 2 | APP_ENV=local 3 | APP_KEY= 4 | APP_DEBUG=true 5 | APP_URL=http://localhost 6 | 7 | LOG_CHANNEL=stack 8 | LOG_DEPRECATIONS_CHANNEL=null 9 | LOG_LEVEL=debug 10 | 11 | DB_CONNECTION=mysql 12 | DB_HOST=127.0.0.1 13 | DB_PORT=3306 14 | DB_DATABASE=example_laravel_app 15 | DB_USERNAME=root 16 | DB_PASSWORD= 17 | 18 | BROADCAST_DRIVER=log 19 | CACHE_DRIVER=file 20 | FILESYSTEM_DISK=local 21 | QUEUE_CONNECTION=sync 22 | SESSION_DRIVER=file 23 | SESSION_LIFETIME=120 24 | 25 | MEMCACHED_HOST=127.0.0.1 26 | 27 | REDIS_HOST=127.0.0.1 28 | REDIS_PASSWORD=null 29 | REDIS_PORT=6379 30 | 31 | MAIL_MAILER=smtp 32 | MAIL_HOST=mailpit 33 | MAIL_PORT=1025 34 | MAIL_USERNAME=null 35 | MAIL_PASSWORD=null 36 | MAIL_ENCRYPTION=null 37 | MAIL_FROM_ADDRESS="hello@example.com" 38 | MAIL_FROM_NAME="${APP_NAME}" 39 | 40 | AWS_ACCESS_KEY_ID= 41 | AWS_SECRET_ACCESS_KEY= 42 | AWS_DEFAULT_REGION=us-east-1 43 | AWS_BUCKET= 44 | AWS_USE_PATH_STYLE_ENDPOINT=false 45 | 46 | PUSHER_APP_ID= 47 | PUSHER_APP_KEY= 48 | PUSHER_APP_SECRET= 49 | PUSHER_HOST= 50 | PUSHER_PORT=443 51 | PUSHER_SCHEME=https 52 | PUSHER_APP_CLUSTER=mt1 53 | 54 | VITE_APP_NAME="${APP_NAME}" 55 | VITE_PUSHER_APP_KEY="${PUSHER_APP_KEY}" 56 | VITE_PUSHER_HOST="${PUSHER_HOST}" 57 | VITE_PUSHER_PORT="${PUSHER_PORT}" 58 | VITE_PUSHER_SCHEME="${PUSHER_SCHEME}" 59 | VITE_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" 60 | -------------------------------------------------------------------------------- /backend/resources/js/bootstrap.js: -------------------------------------------------------------------------------- 1 | /** 2 | * We'll load the axios HTTP library which allows us to easily issue requests 3 | * to our Laravel back-end. This library automatically handles sending the 4 | * CSRF token as a header based on the value of the "XSRF" token cookie. 5 | */ 6 | 7 | import axios from 'axios'; 8 | window.axios = axios; 9 | 10 | window.axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest'; 11 | 12 | /** 13 | * Echo exposes an expressive API for subscribing to channels and listening 14 | * for events that are broadcast by Laravel. Echo and event broadcasting 15 | * allows your team to easily build robust real-time web applications. 16 | */ 17 | 18 | // import Echo from 'laravel-echo'; 19 | 20 | // import Pusher from 'pusher-js'; 21 | // window.Pusher = Pusher; 22 | 23 | // window.Echo = new Echo({ 24 | // broadcaster: 'pusher', 25 | // key: import.meta.env.VITE_PUSHER_APP_KEY, 26 | // cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER ?? 'mt1', 27 | // wsHost: import.meta.env.VITE_PUSHER_HOST ? import.meta.env.VITE_PUSHER_HOST : `ws-${import.meta.env.VITE_PUSHER_APP_CLUSTER}.pusher.com`, 28 | // wsPort: import.meta.env.VITE_PUSHER_PORT ?? 80, 29 | // wssPort: import.meta.env.VITE_PUSHER_PORT ?? 443, 30 | // forceTLS: (import.meta.env.VITE_PUSHER_SCHEME ?? 'https') === 'https', 31 | // enabledTransports: ['ws', 'wss'], 32 | // }); 33 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IMAGE?=artifision/php-dumper-docker-extension 2 | TAG?=1.0.1 3 | 4 | BUILDER=buildx-multi-arch 5 | 6 | INFO_COLOR = \033[0;36m 7 | NO_COLOR = \033[m 8 | 9 | build-extension: ## Build service image to be deployed as a desktop extension 10 | docker build --tag=$(IMAGE):$(TAG) . 11 | 12 | install-extension: build-extension ## Install the extension 13 | docker extension install $(IMAGE):$(TAG) 14 | 15 | update-extension: build-extension ## Update the extension 16 | docker extension update $(IMAGE):$(TAG) 17 | 18 | remove-extension: ## Remove the extension 19 | docker extension rm $(IMAGE):$(TAG) 20 | 21 | prepare-buildx: ## Create buildx builder for multi-arch build, if not exists 22 | docker buildx inspect $(BUILDER) || docker buildx create --name=$(BUILDER) --driver=docker-container --driver-opt=network=host 23 | 24 | push-extension: prepare-buildx ## Build & Upload extension image to hub. Do not push if tag already exists: make push-extension tag=0.1 25 | docker pull $(IMAGE):$(TAG) && echo "Failure: Tag already exists" || docker buildx build --push --builder=$(BUILDER) --platform=linux/amd64,linux/arm64 --build-arg TAG=$(TAG) --tag=$(IMAGE):$(TAG) . 26 | 27 | help: ## Show this help 28 | @echo Please specify a build target. The choices are: 29 | @grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "$(INFO_COLOR)%-30s$(NO_COLOR) %s\n", $$1, $$2}' 30 | 31 | .PHONY: help 32 | -------------------------------------------------------------------------------- /backend/docker/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM php:8.2-fpm-alpine3.17 2 | 3 | WORKDIR /var/www 4 | 5 | # source: https://github.com/lorisleiva/laravel-docker/blob/main/8.2/Dockerfile 6 | 7 | # Add docker-php-extension-installer script 8 | ADD https://github.com/mlocati/docker-php-extension-installer/releases/latest/download/install-php-extensions /usr/local/bin/ 9 | 10 | # Install dependencies 11 | RUN apk add --no-cache \ 12 | bash \ 13 | curl \ 14 | freetype-dev \ 15 | g++ \ 16 | gcc \ 17 | gcompat \ 18 | icu-dev \ 19 | icu-libs \ 20 | libc-dev \ 21 | libzip-dev \ 22 | make \ 23 | oniguruma-dev \ 24 | openssh-client \ 25 | rsync \ 26 | zlib-dev \ 27 | nginx \ 28 | supervisor 29 | 30 | # Install php extensions 31 | RUN chmod +x /usr/local/bin/install-php-extensions && \ 32 | install-php-extensions \ 33 | @composer \ 34 | xdebug-stable \ 35 | bcmath \ 36 | calendar \ 37 | exif \ 38 | intl \ 39 | pcntl \ 40 | soap \ 41 | zip 42 | 43 | # Add local and global vendor bin to PATH. 44 | ENV PATH ./vendor/bin:/composer/vendor/bin:/root/.composer/vendor/bin:/usr/local/bin:$PATH 45 | 46 | RUN mv "$PHP_INI_DIR/php.ini-production" "$PHP_INI_DIR/php.ini" 47 | 48 | COPY supervisord.conf /etc/supervisor/conf.d/supervisord.conf 49 | COPY nginx.conf /etc/nginx/http.d/default.conf 50 | 51 | EXPOSE 9912 9913 52 | 53 | CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.conf"] 54 | -------------------------------------------------------------------------------- /backend/app/Http/Controllers/DumpsController.php: -------------------------------------------------------------------------------- 1 | setTheme(request('theme', 'dark')); 16 | $descriptor->setMaxDepth(request('depth', 1)); 17 | 18 | return $this->streamResponse(function () use ($descriptor) { 19 | $server = new DumpServer('0.0.0.0:9912'); 20 | $server->start(); 21 | 22 | $server->listen(function (Data $data, array $context, int $clientId) use ($descriptor) { 23 | if ($data->getValue() === Healthcheck::STOP_SERVER_KEY) { 24 | throw new \Exception('Stopping dump server...'); 25 | } 26 | 27 | $this->sendEvent(['dump' => json_encode($descriptor->describe($data, $context))]); 28 | }); 29 | }); 30 | } 31 | 32 | public function ping(Healthcheck $healthcheck): string 33 | { 34 | $healthcheck->ping(); 35 | 36 | return 'pong'; 37 | } 38 | 39 | public function stop(Healthcheck $healthcheck): string 40 | { 41 | $healthcheck->sendStopSignal(); 42 | 43 | return 'stopped'; 44 | } 45 | 46 | public function dump(): string 47 | { 48 | dump(request()); 49 | 50 | return 'dumped'; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /backend/config/hashing.php: -------------------------------------------------------------------------------- 1 | 'bcrypt', 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Bcrypt Options 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may specify the configuration options that should be used when 26 | | passwords are hashed using the Bcrypt algorithm. This will allow you 27 | | to control the amount of time it takes to hash the given password. 28 | | 29 | */ 30 | 31 | 'bcrypt' => [ 32 | 'rounds' => env('BCRYPT_ROUNDS', 10), 33 | ], 34 | 35 | /* 36 | |-------------------------------------------------------------------------- 37 | | Argon Options 38 | |-------------------------------------------------------------------------- 39 | | 40 | | Here you may specify the configuration options that should be used when 41 | | passwords are hashed using the Argon algorithm. These will allow you 42 | | to control the amount of time it takes to hash the given password. 43 | | 44 | */ 45 | 46 | 'argon' => [ 47 | 'memory' => 65536, 48 | 'threads' => 1, 49 | 'time' => 4, 50 | ], 51 | 52 | ]; 53 | -------------------------------------------------------------------------------- /backend/bootstrap/app.php: -------------------------------------------------------------------------------- 1 | singleton( 30 | Illuminate\Contracts\Http\Kernel::class, 31 | App\Http\Kernel::class 32 | ); 33 | 34 | $app->singleton( 35 | Illuminate\Contracts\Console\Kernel::class, 36 | App\Console\Kernel::class 37 | ); 38 | 39 | $app->singleton( 40 | Illuminate\Contracts\Debug\ExceptionHandler::class, 41 | App\Exceptions\Handler::class 42 | ); 43 | 44 | /* 45 | |-------------------------------------------------------------------------- 46 | | Return The Application 47 | |-------------------------------------------------------------------------- 48 | | 49 | | This script returns the application instance. The instance is given to 50 | | the calling script so we can separate the building of the instances 51 | | from the actual running of the application and sending responses. 52 | | 53 | */ 54 | 55 | return $app; 56 | -------------------------------------------------------------------------------- /backend/artisan: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | make(Illuminate\Contracts\Console\Kernel::class); 34 | 35 | $status = $kernel->handle( 36 | $input = new Symfony\Component\Console\Input\ArgvInput, 37 | new Symfony\Component\Console\Output\ConsoleOutput 38 | ); 39 | 40 | /* 41 | |-------------------------------------------------------------------------- 42 | | Shutdown The Application 43 | |-------------------------------------------------------------------------- 44 | | 45 | | Once Artisan has finished running, we will fire off the shutdown events 46 | | so that any final work may be done by the application before we shut 47 | | down the process. This is the last thing to happen to the request. 48 | | 49 | */ 50 | 51 | $kernel->terminate($input, $status); 52 | 53 | exit($status); 54 | -------------------------------------------------------------------------------- /backend/app/Outputs/RedisOutput.php: -------------------------------------------------------------------------------- 1 | write($messages, true, $options); 22 | } 23 | 24 | public function setVerbosity(int $level) 25 | { 26 | $this->verbosity = $level; 27 | } 28 | 29 | public function getVerbosity(): int 30 | { 31 | return $this->verbosity ?? self::VERBOSITY_NORMAL; 32 | } 33 | 34 | public function isQuiet(): bool 35 | { 36 | return false; 37 | } 38 | 39 | public function isVerbose(): bool 40 | { 41 | return false; 42 | } 43 | 44 | public function isVeryVerbose(): bool 45 | { 46 | return false; 47 | } 48 | 49 | public function isDebug(): bool 50 | { 51 | return false; 52 | } 53 | 54 | public function setDecorated(bool $decorated) 55 | { 56 | 57 | } 58 | 59 | public function isDecorated(): bool 60 | { 61 | return false; 62 | } 63 | 64 | public function setFormatter(\Symfony\Component\Console\Formatter\OutputFormatterInterface $formatter) 65 | { 66 | $this->formatter = $formatter; 67 | } 68 | 69 | public function getFormatter(): \Symfony\Component\Console\Formatter\OutputFormatterInterface 70 | { 71 | return $this->formatter ?? new OutputFormatter(); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /backend/public/index.php: -------------------------------------------------------------------------------- 1 | make(Kernel::class); 50 | 51 | $response = $kernel->handle( 52 | $request = Request::capture() 53 | )->send(); 54 | 55 | $kernel->terminate($request, $response); 56 | -------------------------------------------------------------------------------- /backend/app/Dumpers/HtmlCustomDumper.php: -------------------------------------------------------------------------------- 1 | [ 12 | 'default' => 'background-color:#263238; color:#FFFFFF; line-height:1.2em; font:14px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 13 | 'num' => 'font-weight:bold; color:#93c5fd', 14 | 'const' => 'font-weight:bold', 15 | 'str' => 'font-weight:bold; color:#7FA9FF', 16 | 'note' => 'color:#C792EA', 17 | 'ref' => 'color:#A0A0A0', 18 | 'public' => 'color:#22c55e', 19 | 'protected' => 'color:#fde047', 20 | 'private' => 'color:#ef4444', 21 | 'meta' => 'color:#818cf8', 22 | 'key' => 'color:#F78C6C', 23 | 'index' => 'color:#93c5fd', 24 | 'ellipsis' => 'color:#e879f9', 25 | 'ns' => 'user-select:none;', 26 | ], 27 | 28 | 'light' => [ 29 | 'default' => 'background-color:#F9F9F9; color:#6b7280; font-weight:bold; line-height:1.2em; font:14px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', 30 | 'num' => 'font-weight:bold; color:#60a5fa', 31 | 'const' => 'font-weight:bold', 32 | 'str' => 'font-weight:bold; color:#3b82f6', 33 | 'note' => 'color:#7c3aed', 34 | 'ref' => 'color:#6E6E6E', 35 | 'public' => 'font-weight:bold; color:#15803d', 36 | 'protected' => 'font-weight:bold; color:#f9a825', 37 | 'private' => 'font-weight:bold; color:#dc2626', 38 | 'meta' => 'color:#d946ef', 39 | 'key' => 'color:#64748b', 40 | 'index' => 'color:#60a5fa', 41 | 'ellipsis' => 'color:#CC7832', 42 | 'ns' => 'user-select:none;', 43 | ], 44 | ]; 45 | } 46 | -------------------------------------------------------------------------------- /backend/composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "laravel/laravel", 3 | "type": "project", 4 | "description": "The skeleton application for the Laravel framework.", 5 | "keywords": ["laravel", "framework"], 6 | "license": "MIT", 7 | "require": { 8 | "php": "^8.1", 9 | "guzzlehttp/guzzle": "^7.2", 10 | "laravel/framework": "^10.10", 11 | "laravel/sanctum": "^3.2", 12 | "laravel/tinker": "^2.8" 13 | }, 14 | "require-dev": { 15 | "fakerphp/faker": "^1.9.1", 16 | "laravel/pint": "^1.0", 17 | "mockery/mockery": "^1.4.4", 18 | "nunomaduro/collision": "^7.7", 19 | "phpunit/phpunit": "^10.1", 20 | "spatie/laravel-ignition": "^2.0", 21 | "symfony/var-dumper": "^6.3" 22 | }, 23 | "autoload": { 24 | "psr-4": { 25 | "App\\": "app/", 26 | "Database\\Factories\\": "database/factories/", 27 | "Database\\Seeders\\": "database/seeders/" 28 | } 29 | }, 30 | "autoload-dev": { 31 | "psr-4": { 32 | "Tests\\": "tests/" 33 | } 34 | }, 35 | "scripts": { 36 | "post-autoload-dump": [ 37 | "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", 38 | "@php artisan package:discover --ansi" 39 | ], 40 | "post-update-cmd": [ 41 | "@php artisan vendor:publish --tag=laravel-assets --ansi --force" 42 | ], 43 | "post-root-package-install": [ 44 | "@php -r \"file_exists('.env') || copy('.env.example', '.env');\"" 45 | ], 46 | "post-create-project-cmd": [ 47 | "@php artisan key:generate --ansi" 48 | ] 49 | }, 50 | "extra": { 51 | "laravel": { 52 | "dont-discover": [] 53 | } 54 | }, 55 | "config": { 56 | "optimize-autoloader": true, 57 | "preferred-install": "dist", 58 | "sort-packages": true, 59 | "allow-plugins": { 60 | "pestphp/pest-plugin": true, 61 | "php-http/discovery": true 62 | } 63 | }, 64 | "minimum-stability": "stable", 65 | "prefer-stable": true 66 | } 67 | -------------------------------------------------------------------------------- /ui/src/App.tsx: -------------------------------------------------------------------------------- 1 | import {Link, Stack, Typography, useTheme} from '@mui/material'; 2 | import {createDockerDesktopClient} from '@docker/extension-api-client'; 3 | import {blueGrey, pink} from '@mui/material/colors'; 4 | import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer'; 5 | import FavoriteIcon from '@mui/icons-material/Favorite'; 6 | 7 | import Dumps from "./components/Dumps"; 8 | import Logo from "./components/Logo"; 9 | 10 | // Note: This line relies on Docker Desktop's presence as a host application. 11 | // If you're running this React app in a browser, it won't work properly. 12 | const client = createDockerDesktopClient(); 13 | 14 | function useDockerDesktopClient() { 15 | return client; 16 | } 17 | 18 | export function App() { 19 | const ddClient = useDockerDesktopClient(); 20 | const theme = useTheme(); 21 | 22 | return ( 23 | 24 | 37 | 38 | PHP Dumper 39 | 40 | by ddClient.host.openExternal('https://artifision.com')}>Artifision 42 | 43 | 44 | 45 | 46 | ddClient.host.openExternal("https://github.com/sponsors/artifision")}> 48 | Sponsor 49 | 50 | ddClient.host.openExternal("https://forms.gle/72wpMPKJcnk81uDS9")}> 52 | Give Feedback 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ); 61 | } 62 | -------------------------------------------------------------------------------- /backend/config/broadcasting.php: -------------------------------------------------------------------------------- 1 | env('BROADCAST_DRIVER', 'null'), 19 | 20 | /* 21 | |-------------------------------------------------------------------------- 22 | | Broadcast Connections 23 | |-------------------------------------------------------------------------- 24 | | 25 | | Here you may define all of the broadcast connections that will be used 26 | | to broadcast events to other systems or over websockets. Samples of 27 | | each available type of connection are provided inside this array. 28 | | 29 | */ 30 | 31 | 'connections' => [ 32 | 33 | 'pusher' => [ 34 | 'driver' => 'pusher', 35 | 'key' => env('PUSHER_APP_KEY'), 36 | 'secret' => env('PUSHER_APP_SECRET'), 37 | 'app_id' => env('PUSHER_APP_ID'), 38 | 'options' => [ 39 | 'cluster' => env('PUSHER_APP_CLUSTER'), 40 | 'host' => env('PUSHER_HOST') ?: 'api-'.env('PUSHER_APP_CLUSTER', 'mt1').'.pusher.com', 41 | 'port' => env('PUSHER_PORT', 443), 42 | 'scheme' => env('PUSHER_SCHEME', 'https'), 43 | 'encrypted' => true, 44 | 'useTLS' => env('PUSHER_SCHEME', 'https') === 'https', 45 | ], 46 | 'client_options' => [ 47 | // Guzzle client options: https://docs.guzzlephp.org/en/stable/request-options.html 48 | ], 49 | ], 50 | 51 | 'ably' => [ 52 | 'driver' => 'ably', 53 | 'key' => env('ABLY_KEY'), 54 | ], 55 | 56 | 'redis' => [ 57 | 'driver' => 'redis', 58 | 'connection' => 'default', 59 | ], 60 | 61 | 'log' => [ 62 | 'driver' => 'log', 63 | ], 64 | 65 | 'null' => [ 66 | 'driver' => 'null', 67 | ], 68 | 69 | ], 70 | 71 | ]; 72 | -------------------------------------------------------------------------------- /ui/src/components/Diff.tsx: -------------------------------------------------------------------------------- 1 | import React, {useEffect, useState} from "react"; 2 | import * as diff from "diff"; 3 | import {Box, CircularProgress, useTheme} from "@mui/material"; 4 | import {Change} from "diff"; 5 | 6 | const styles = { 7 | added: { 8 | color: "green", 9 | backgroundColor: "#b5efdb" 10 | }, 11 | removed: { 12 | color: "red", 13 | backgroundColor: "#fec4c0" 14 | } 15 | }; 16 | 17 | type DiffProps = { 18 | string1: string; 19 | string2: string; 20 | mode?: "characters" | "words"; 21 | } 22 | 23 | const Diff = ({string1, string2, mode = 'characters'}: DiffProps) => { 24 | const [mappedNodes, setMappedNodes] = useState(null); 25 | const [identical, setIdentical] = useState(false); 26 | const theme = useTheme(); 27 | 28 | useEffect(() => { 29 | setMappedNodes(null); 30 | if (string1 === string2) { 31 | setIdentical(true); 32 | return; 33 | } 34 | setIdentical(false); 35 | 36 | setTimeout(() => { 37 | let groups: Change[] = []; 38 | 39 | if (mode === 'characters') { 40 | groups = diff.diffChars(string1, string2); 41 | } else if (mode === 'words') { 42 | groups = diff.diffWords(string1, string2); 43 | } 44 | 45 | setMappedNodes(groups.map((group: Change, index: number) => { 46 | const {value, added, removed} = group; 47 | let nodeStyles; 48 | if (added) { 49 | nodeStyles = styles.added; 50 | } 51 | if (removed) { 52 | nodeStyles = styles.removed; 53 | } 54 | return {value}; 55 | })); 56 | }, 100); 57 | 58 | }, [string1, string2, mode]); 59 | 60 | return 61 | {mappedNodes !== null ?
68 |         {mappedNodes}
69 |     
: 70 | 71 | {identical ?

The dumps are identical

: 72 | <> 73 | 74 |

75 | Computing difference... 76 |

77 | 78 | } 79 |
} 80 |
; 81 | }; 82 | 83 | export default Diff; 84 | -------------------------------------------------------------------------------- /ui/src/components/DumpFrame.tsx: -------------------------------------------------------------------------------- 1 | import {useEffect, memo} from 'react'; 2 | import {useTheme} from "@mui/material"; 3 | import Dump from "../models/Dump"; 4 | 5 | type DumpFrameProps = { 6 | dump: Dump 7 | } 8 | 9 | export default memo(function DumpFrame({dump} : DumpFrameProps) { 10 | const theme = useTheme(); 11 | const backgroundColor = theme.palette.mode === 'dark' ? '#263238' : '#F9F9F9'; 12 | 13 | const iframeCSS: string = ` 14 | body { 15 | margin: 0; 16 | overflow: hidden; 17 | background-color: ${backgroundColor}; 18 | font-size: 14px; 19 | } 20 | pre { 21 | margin: 0; 22 | } 23 | `; 24 | useEffect(() => { 25 | window.addEventListener('message', resizeIframe); 26 | 27 | return () => { 28 | window.removeEventListener('message', resizeIframe); 29 | } 30 | }, []); 31 | 32 | const resizeIframe = (event: MessageEvent) => { 33 | if (event.data && event.data.type === 'iframeResize' && event.data.height && event.data.uid === dump.getUid()) { 34 | const iframeElement = document.getElementById('dump-iframe-' + dump.getUid()); 35 | if (iframeElement) { 36 | iframeElement.style.height = event.data.height + 'px'; 37 | } 38 | } 39 | }; 40 | 41 | return ( 42 |