-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathHelpers.php
More file actions
187 lines (165 loc) · 6.57 KB
/
Helpers.php
File metadata and controls
187 lines (165 loc) · 6.57 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
<?php
namespace InternetGuru\LaravelCommon\Support;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Gate;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Storage;
use Throwable;
class Helpers
{
/**
* Get the app info as an array.
*
* Returns an array with the following keys:
* - app_name: Config app.name
* - environment: Config app.env
* - version: VERSION file content
* - branch: Git branch or [detached]
* - commit: Git commit
*/
public static function getAppInfoArray(): array
{
$info['app_name'] = config('app.name');
$info['environment'] = config('app.env');
// Using Storage to access root files
$info['version'] = trim(Storage::disk('root')->get('VERSION'));
$branch = '[detached]';
$commit = trim(Storage::disk('root')->get('.git/HEAD'));
if (substr($commit, 0, 10) == 'ref: refs/') {
$branch = $commit;
$commit = trim(Storage::disk('root')->get('.git/' . substr($branch, 5)));
}
$info['branch'] = basename($branch);
$info['commit'] = substr($commit, 0, 7);
return $info;
}
public static function getAppInfo(): string
{
return implode(' ', self::getAppInfoArray());
}
/**
* Parse the URL path and return an array of segments.
* If there is a short translation, it will be used instead of the full translation.
* If translation is not found, the segment will be marked with a text-danger class.
*
* Returns an array of segments with the following keys:
* - route: The route to the segment
* - translation: The translation of the segment
* - class: Additional classes to add to the segment
*/
public static function parseUrlPath(string $homeRoute = 'home', int $skipFirst = 0): array
{
$url = request()->path();
$urlParts = explode('/', trim($url, '/'));
// Skip first N segments
for ($i = 0; $i < $skipFirst; $i++) {
array_shift($urlParts);
}
// clean empty parts
$urlParts = array_filter($urlParts);
// Add root segment
array_unshift($urlParts, '');
// If error page or internal/system route, show error breadcrumb
$route = app('request')->route();
$skipPrefixes = config('ig-common.breadcrumb_skip_prefixes', []);
$shouldSkip = ! $route || collect($skipPrefixes)->contains(fn ($prefix) => str_starts_with($route->uri(), $prefix));
if ($shouldSkip) {
$urlParts = ['', 'error'];
}
$currentPath = '';
$segments = [];
$totalParts = count($urlParts);
$parameters = [];
foreach ($urlParts as $index => $segment) {
$currentPath = $currentPath == '/' ? $currentPath . $segment : $currentPath . '/' . $segment;
try {
$route = Route::getRoutes()->match(request()->create($currentPath));
$routeName = $route->getName();
$parameters = $route->parameters();
if ($routeName === null) {
// skip unnamed sub-routes
continue;
}
$uri = $currentPath;
foreach ($route->middleware() as $item) {
if ($item == 'auth') {
if (! auth()->check()) {
// If user is not authenticated, return the route name and empty URI
$uri = '';
}
} elseif (strpos($item, 'can:') === 0) {
[$permission, $model] = explode(',', substr($item, 4));
$modelInstance = array_key_exists($model, $parameters) ? $parameters[$model] : $model;
if (! Gate::allows($permission, $modelInstance)) {
// If user does not have permission, return the route name and empty URI
$uri = '';
}
}
}
} catch (Throwable $e) {
// If route not found skip it
continue;
}
$transKey = "ig-common::navig.$routeName";
if (! Lang::has($transKey)) {
$transKey = "navig.$routeName";
}
$parameters = $parameters['data'] ?? $parameters;
foreach ($parameters as $key => $value) {
if (Lang::has($transKey . '.' . $value)) {
$parameters[$key] = Lang::get($transKey . '.' . $value);
}
}
$translation = trans_choice($transKey, $totalParts - $index - $skipFirst, $parameters);
$segments[] = [
'uri' => $uri,
'translation' => $translation,
];
}
return $segments;
}
public static function createTitle(string $separator = ' – ', $homeRoute = 'home'): string
{
return implode(
$separator,
Arr::pluck(array_reverse(self::parseUrlPath($homeRoute)), 'translation')
);
}
public static function getEmailClientLink(): string
{
if (config('mail.mailers.' . config('mail.default') . '.host') != 'mailpit') {
return '';
}
// Generate link to Mailpit inbox
$link = config('app.url');
if (config('app.env') == 'local') {
$link = parse_url($link, PHP_URL_SCHEME) . '://' . parse_url($link, PHP_URL_HOST) . ':8025';
} else {
$link = preg_replace('/^(https?:\/\/)/', '$1mail.', config('app.url'));
}
return " <a href=\"$link\">" . __('ig-common::messages.inbox') . '</a>';
}
public static function verifyRequestSignature(Request $request): bool
{
if (! $request->hasHeader('X-Signature') || ! $request->hasHeader('X-Timestamp')) {
return false;
}
$clientTimestamp = (int) $request->header('X-Timestamp');
$clientSignature = $request->header('X-Signature');
$appKey = config('app.key');
// 1. Verify Freshness (Allow +/- 1 minutes for clock drift)
$diff = Carbon::now()->timestamp - $clientTimestamp;
if (abs($diff) > 60) {
return false;
}
// 2. Check signature
$serverSignature = hash_hmac('sha256', (string) $clientTimestamp, $appKey);
if (! hash_equals($serverSignature, $clientSignature)) {
return false;
}
return true;
}
}