Skip to content

Commit 6e631cc

Browse files
committed
Allow error pipe customization
1 parent 58262be commit 6e631cc

File tree

5 files changed

+186
-3
lines changed

5 files changed

+186
-3
lines changed

src/WebView/Api/Schemes/Handler/ErrorHandlerInterface.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44

55
namespace Boson\WebView\Api\Schemes\Handler;
66

7+
use Boson\Component\Http\Component\StatusCode;
8+
use Boson\Contracts\Http\Component\StatusCodeInterface;
79
use Boson\Contracts\Http\RequestInterface;
810
use Boson\Contracts\Http\ResponseInterface;
911
use Boson\WebView\WebView;
1012

1113
interface ErrorHandlerInterface
1214
{
15+
public const StatusCodeInterface DEFAULT_ERROR_CODE = StatusCode::InternalServerError;
16+
1317
public function handle(WebView $context, RequestInterface $request, \Throwable $exception): ?ResponseInterface;
1418
}
Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\WebView\Api\Schemes\Handler;
6+
7+
use Boson\Component\Http\Response;
8+
use Boson\Contracts\Http\Component\StatusCodeInterface;
9+
use Boson\Contracts\Http\RequestInterface;
10+
use Boson\Contracts\Http\ResponseInterface;
11+
use Boson\WebView\WebView;
12+
13+
final readonly class ProductionErrorHandler implements ErrorHandlerInterface
14+
{
15+
private const string ERROR_TEMPLATE = <<<'HTML'
16+
<!doctype html>
17+
<html lang="en">
18+
<head>
19+
<meta charset="UTF-8">
20+
<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" />
21+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
22+
<title>An Internal Error Occurred</title>
23+
<link rel="preconnect" href="https://fonts.googleapis.com">
24+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
25+
<link href="https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&family=JetBrains+Mono:ital,wght@0,100..800;1,100..800&family=Roboto+Condensed:wght@100..900&display=swap" rel="stylesheet">
26+
<style>
27+
:root {
28+
--color-bg: #0d1119;
29+
--color-bg-button: #3A1309;
30+
--color-bg-button-hover: #601A08;
31+
--color-text: rgba(255, 255, 255, 0.7);
32+
--color-text-brand: #F93904;
33+
--color-text-button: #F93904;
34+
35+
--font-fallback-main: BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
36+
--font-fallback-mono: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
37+
38+
--font-title: 'Roboto Condensed', var(--font-fallback-main);
39+
--font-mono: 'JetBrains Mono', var(--font-fallback-mono);
40+
--font-main: Inter, var(--font-fallback-main);
41+
42+
--font-size: 17px;
43+
--font-line-height: 1.7;
44+
45+
--font-size-h1: 44px;
46+
--font-size-h2: 24px;
47+
}
48+
49+
html,
50+
body {
51+
background: var(--color-bg);
52+
color: var(--color-text);
53+
font-family: var(--font-main), sans-serif;
54+
font-size: var(--font-size);
55+
line-height: var(--font-line-height);
56+
margin: 0;
57+
padding: 0;
58+
transition: opacity 1s ease;
59+
}
60+
61+
h1, h2, h3, h4, h5, h6 {
62+
font-family: var(--font-title), sans-serif;
63+
color: var(--color-text);
64+
margin: 0;
65+
padding: 0;
66+
}
67+
68+
h1 {
69+
font-size: var(--font-size-h1);
70+
font-weight: 600;
71+
font-style: normal;
72+
color: var(--color-text-brand);
73+
display: block;
74+
}
75+
76+
h2 {
77+
font-size: var(--font-size-h2);
78+
font-weight: 300;
79+
font-style: normal;
80+
display: block;
81+
}
82+
83+
button {
84+
font-family: var(--font-mono), sans-serif;
85+
cursor: pointer;
86+
border: none;
87+
border-radius: 3px;
88+
letter-spacing: 1px;
89+
color: inherit;
90+
transition-duration: 0.1s;
91+
background: var(--color-bg-button);
92+
text-transform: uppercase;
93+
padding: 0 2em;
94+
display: inline-block;
95+
line-height: 56px;
96+
margin-top: 50px;
97+
height: 56px;
98+
gap: 1em;
99+
justify-content: inherit;
100+
align-items: center;
101+
white-space: nowrap;
102+
}
103+
104+
button:hover {
105+
color: var(--color-text-button);
106+
background: var(--color-bg-button-hover);
107+
}
108+
109+
.layout {
110+
padding: 25px;
111+
box-sizing: border-box;
112+
width: 100vw;
113+
height: 100vh;
114+
display: flex;
115+
align-items: center;
116+
justify-content: center;
117+
flex-direction: column;
118+
}
119+
</style>
120+
</head>
121+
<body style="opacity: 0">
122+
<main class="layout">
123+
<h1>An Internal Error Occurred</h1>
124+
<h2>To solve the problem, please contact the developer</h2>
125+
<p>
126+
<button onclick="document.location.reload()">reload</button>
127+
</p>
128+
</main>
129+
<script>
130+
setTimeout(function () {
131+
document.body.style.removeProperty('opacity');
132+
}, 100);
133+
</script>
134+
</body>
135+
</html>
136+
HTML;
137+
138+
public function __construct(
139+
private StatusCodeInterface $status = self::DEFAULT_ERROR_CODE,
140+
) {}
141+
142+
public function handle(WebView $context, RequestInterface $request, \Throwable $exception): ResponseInterface
143+
{
144+
return new Response(self::ERROR_TEMPLATE, $this->status);
145+
}
146+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace Boson\WebView\Api\Schemes\Handler;
6+
7+
use Boson\Contracts\Http\RequestInterface;
8+
use Boson\WebView\WebView;
9+
10+
final readonly class StderrErrorHandler implements ErrorHandlerInterface
11+
{
12+
/**
13+
* @var resource
14+
*/
15+
public const mixed DEFAULT_ERROR_PIPE = \STDERR;
16+
17+
public function __construct(
18+
/**
19+
* @var resource
20+
*/
21+
private mixed $pipe = self::DEFAULT_ERROR_PIPE,
22+
) {}
23+
24+
public function handle(WebView $context, RequestInterface $request, \Throwable $exception): null
25+
{
26+
if (\is_resource($this->pipe)) {
27+
\fwrite($this->pipe, $exception . "\n");
28+
}
29+
30+
return null;
31+
}
32+
}

src/WebView/Api/Schemes/Handler/WhoopsErrorHandler.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44

55
namespace Boson\WebView\Api\Schemes\Handler;
66

7-
use Boson\Component\Http\Component\StatusCode;
87
use Boson\Component\Http\Response;
98
use Boson\Contracts\Http\Component\StatusCodeInterface;
109
use Boson\Contracts\Http\RequestInterface;
@@ -15,8 +14,6 @@
1514

1615
final readonly class WhoopsErrorHandler implements ErrorHandlerInterface
1716
{
18-
private const StatusCodeInterface DEFAULT_ERROR_CODE = StatusCode::InternalServerError;
19-
2017
private ?WhoopsHandler $handler;
2118

2219
public function __construct(

src/WebView/Api/Schemes/SchemesExtension.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
use Boson\Extension\Attribute\AvailableAs;
1010
use Boson\Extension\Extension;
1111
use Boson\WebView\Api\Schemes\Handler\ErrorHandlerInterface;
12+
use Boson\WebView\Api\Schemes\Handler\ProductionErrorHandler;
13+
use Boson\WebView\Api\Schemes\Handler\StderrErrorHandler;
1214
use Boson\WebView\Api\Schemes\Handler\WhoopsErrorHandler;
1315
use Boson\WebView\WebView;
1416

@@ -18,7 +20,9 @@
1820
// 2) Only define-like constants allow object instances.
1921
//
2022
\define($_ = 'Boson\WebView\Api\Schemes\DEFAULT_ERROR_HANDLERS', [
23+
new StderrErrorHandler(),
2124
new WhoopsErrorHandler(),
25+
new ProductionErrorHandler(),
2226
]);
2327

2428
/**

0 commit comments

Comments
 (0)