Skip to content

Commit f04a572

Browse files
committed
WIP
1 parent 5bc8c97 commit f04a572

File tree

7 files changed

+251
-105
lines changed

7 files changed

+251
-105
lines changed

app/Console/Kernel.php

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ protected function schedule(Schedule $schedule): void
3131

3232
// Оптимизация SQLite каждую минуту смотри https://www.sqlite.org/pragma.html#pragma_optimize
3333
$schedule->command('sqlite:optimize')->everyMinute();
34+
$schedule->command('sqlite:vacuum')->everyFourHours();
3435

3536
// Перевод достижений каждую неделю по выходным
3637
$schedule->command('app:achievements-translation')

app/Docs.php

+107-53
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
namespace App;
44

55
use App\Models\Document;
6+
use App\Models\DocumentationSection;
67
use Exception;
8+
use Illuminate\Support\Arr;
79
use Illuminate\Support\Collection;
810
use Illuminate\Support\Facades\Cache;
911
use Illuminate\Support\Facades\Http;
@@ -44,11 +46,6 @@ class Docs
4446
*/
4547
protected array $variables = [];
4648

47-
/**
48-
* @var string The content of the Markdown file.
49-
*/
50-
protected $content;
51-
5249
/**
5350
* @var string The file name.
5451
*/
@@ -67,75 +64,82 @@ class Docs
6764
*/
6865
public function __construct(string $version, string $file)
6966
{
70-
$this->file = $file.'.md';
67+
$this->file = $file . '.md';
7168
$this->version = $version;
7269
$this->path = "/$version/$this->file";
73-
74-
$this->content();
7570
}
7671

7772
/**
78-
* @return array
73+
* @return string|null
7974
*/
80-
public function variables(): array
75+
public function raw()
8176
{
82-
return $this->variables;
77+
return once(function () {
78+
$raw = Storage::disk('docs')->get($this->path);
79+
80+
// Abort the request if the page doesn't exist
81+
abort_if(
82+
$raw === null,
83+
redirect(status: 300)->route('docs', ['version' => $this->version, 'page' => 'installation'])
84+
);
85+
86+
return $raw;
87+
});
8388
}
8489

8590
/**
86-
* @return string|null
91+
* @param string|null $key
92+
*
93+
* @return array
8794
*/
88-
public function content(): ?string
95+
public function variables(string $key = null): array
8996
{
90-
if ($this->content !== null) {
91-
return $this->content;
92-
}
97+
return once(function () use ($key) {
9398

94-
$raw = Cache::remember('doc-file-'.$this->path, now()->addMinutes(30), fn () => Storage::disk('docs')->get($this->path));
99+
$variables = Str::of($this->raw())->betweenFirst('---', '---');
95100

96-
// Abort the request if the page doesn't exist
97-
abort_if(
98-
$raw === null && Document::where('file', $this->file)->exists(),
99-
redirect(status: 300)->route('docs', ['version' => $this->version, 'page' => 'installation'])
100-
);
101+
try {
102+
$this->variables = Yaml::parse($variables);
103+
} catch (\Throwable) {
101104

102-
$variables = Str::of($raw)
103-
->after('---')
104-
->before('---');
105-
106-
try {
107-
$this->variables = Yaml::parse($variables);
108-
} catch (\Throwable) {
109-
110-
}
105+
}
111106

112-
$this->content = Str::of($raw)
113-
->replace('{{version}}', $this->version)
114-
->after('---')
115-
->after('---')
116-
->markdown();
107+
return Arr::get($this->variables, $key);
108+
});
109+
}
117110

118-
return $this->content;
111+
/**
112+
* @return string|null
113+
*/
114+
public function content(): ?string
115+
{
116+
return once(function () {
117+
return Str::of($this->raw())
118+
->replace('{{version}}', $this->version)
119+
->after('---')
120+
->after('---')
121+
->markdown();
122+
});
119123
}
120124

121125
/**
122126
* Get the rendered view of a documentation page.
123127
*
124128
* @param string $view The view name.
125129
*
130+
* @return \Illuminate\Contracts\View\View The rendered view of the documentation page.
126131
* @throws \Illuminate\Contracts\Filesystem\FileNotFoundException
127132
*
128-
* @return \Illuminate\Contracts\View\View The rendered view of the documentation page.
129133
*/
130134
public function view(string $view)
131135
{
132-
$all = collect()->merge($this->variables)->merge([
136+
$data = Cache::remember('doc-file-view-data' . $this->path, now()->addMinutes(30), fn() => collect()->merge($this->variables())->merge([
133137
'docs' => $this,
134138
'content' => $this->content(),
135139
'edit' => $this->getEditUrl(),
136-
]);
140+
]));
137141

138-
return view($view, $all);
142+
return view($view, $data);
139143
}
140144

141145
/**
@@ -145,8 +149,8 @@ public function view(string $view)
145149
*/
146150
public function getMenu(): array
147151
{
148-
return Cache::remember('doc-navigation-'.$this->version, now()->addHours(2), function () {
149-
$page = Storage::disk('docs')->get($this->version.'/documentation.md');
152+
return Cache::remember('doc-navigation-' . $this->version, now()->addHours(2), function () {
153+
$page = Storage::disk('docs')->get($this->version . '/documentation.md');
150154

151155
$html = Str::of($page)
152156
->after('---')
@@ -191,7 +195,7 @@ public function docsToArray(string $html): array
191195
$menu = [];
192196

193197
$crawler->filter('ul > li')->each(function (Crawler $node) use (&$menu) {
194-
$subList = $node->filter('ul > li')->each(fn (Crawler $subNode) => [
198+
$subList = $node->filter('ul > li')->each(fn(Crawler $subNode) => [
195199
'title' => $subNode->filter('a')->text(),
196200
'href' => url($subNode->filter('a')->attr('href')),
197201
]);
@@ -221,10 +225,10 @@ public static function every(string $version): Collection
221225
$files = Storage::disk('docs')->allFiles($version);
222226

223227
return collect($files)
224-
->filter(fn (string $path) => Str::of($path)->endsWith('.md'))
225-
->filter(fn (string $path) => ! Str::of($path)->endsWith(['readme.md', 'license.md']))
226-
->map(fn (string $path) => Str::of($path)->after($version.'/')->before('.md'))
227-
->map(fn (string $path) => new static($version, $path));
228+
->filter(fn(string $path) => Str::of($path)->endsWith('.md'))
229+
->filter(fn(string $path) => !Str::of($path)->endsWith(['readme.md', 'license.md']))
230+
->map(fn(string $path) => Str::of($path)->after($version . '/')->before('.md'))
231+
->map(fn(string $path) => new static($version, $path));
228232
}
229233

230234
/**
@@ -234,18 +238,18 @@ public static function every(string $version): Collection
234238
*/
235239
public function fetchBehind(): int
236240
{
237-
throw_unless(isset($this->variables['git']), new Exception("The document {$this->path} is missing a Git hash"));
241+
throw_unless($this->variables('git') === null, new Exception("The document {$this->path} is missing a Git hash"));
238242

239243
$response = $this->fetchGitHubDiff();
240244

241245
return $response
242-
->takeUntil(fn ($commit) => $commit['sha'] === $this->variables['git'])
246+
->takeUntil(fn($commit) => $commit['sha'] === $this->variables('git'))
243247
->count();
244248
}
245249

246250
public function fetchLastCommit(): string
247251
{
248-
throw_unless(isset($this->variables['git']), new Exception("The document {$this->path} is missing a Git hash"));
252+
throw_unless($this->variables('git') === null, new Exception("The document {$this->path} is missing a Git hash"));
249253

250254
$response = $this->fetchGitHubDiff();
251255

@@ -263,7 +267,7 @@ private function fetchGitHubDiff(?string $key = null): Collection
263267

264268
return Cache::remember("docs-diff-$this->version-$this->file-$hash",
265269
now()->addHours(2),
266-
fn () => Http::withBasicAuth('token', config('services.github.token'))
270+
fn() => Http::withBasicAuth('token', config('services.github.token'))
267271
->get("https://api.github.com/repos/laravel/docs/commits?sha={$this->version}&path={$this->file}")
268272
->collect($key)
269273
);
@@ -349,7 +353,57 @@ public function update()
349353
$this->getModel()->fill([
350354
'behind' => $this->fetchBehind(),
351355
'last_commit' => $this->fetchLastCommit(),
352-
'current_commit' => $this->variables['git'],
356+
'current_commit' => $this->variables('git'),
353357
])->save();
358+
359+
$this->updateSections();
360+
}
361+
362+
/**
363+
* Разбивает markdown файл на разделы по заголовкам.
364+
*
365+
* @return Массив разделов с заголовками и содержимым
366+
*/
367+
public function getSections()
368+
{
369+
// Разбиваем HTML содержимое на разделы по заголовкам
370+
preg_match_all('/<h(\d)>(.+)<\/h\d>(.*)/sU', $this->content(), $matches, PREG_SET_ORDER);
371+
372+
// Массив для хранения разделов
373+
$sections = collect();
374+
$prevEnd = 0;
375+
376+
foreach ($matches as $index => $match) {
377+
$sectionTitle = $match[2];
378+
379+
// Получаем начальную и конечную позицию текущего заголовка в тексте
380+
$startPos = strpos($this->content(), $match[0], $prevEnd);
381+
382+
// Получаем текст между текущим и предыдущим заголовком
383+
if ($index > 0) {
384+
$prevMatch = $matches[$index - 1];
385+
$prevEnd = strpos($this->content(), $prevMatch[0]) + strlen($prevMatch[0]);
386+
$sectionContent = substr($this->content(), $prevEnd, $startPos - $prevEnd);
387+
} else {
388+
$sectionContent = substr($this->content(), 0, $startPos);
389+
}
390+
391+
$sections->push([
392+
'title' => $sectionTitle,
393+
'slug' => Str::of($sectionTitle)->slug()->toString(),
394+
'content' => $sectionContent,
395+
'file' => $this->file,
396+
'version' => $this->version,
397+
'id' => Str::uuid(),
398+
]);
399+
}
400+
401+
return $sections;
402+
}
403+
404+
public function updateSections()
405+
{
406+
//DocumentationSection::where('file', $this->file)->where('version', $this->version)->delete();
407+
//DocumentationSection::insert($this->getSections()->toArray());
354408
}
355409
}

app/Exceptions/Handler.php

+32-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
namespace App\Exceptions;
44

55
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
6+
use Illuminate\Support\Str;
7+
use NotificationChannels\Telegram\TelegramMessage;
68
use Throwable;
79

810
class Handler extends ExceptionHandler
@@ -24,7 +26,36 @@ class Handler extends ExceptionHandler
2426
public function register(): void
2527
{
2628
$this->reportable(function (Throwable $e) {
27-
//
29+
$this->notificationToTelegram($e);
2830
});
2931
}
32+
33+
private function notificationToTelegram($exception)
34+
{
35+
if (config('app.env') == 'local') {
36+
return;
37+
}
38+
39+
// Send notification to Telegram
40+
try {
41+
TelegramMessage::create()
42+
->toNotGiven()
43+
->to(config('services.telegram-bot-api.chat_id'))
44+
->line('*⚠️ Ой-ой-ой!* Возникла неприятность в нашем коде. Пользователь столкнулся с неожиданной ошибкой на сайте.')
45+
->line('`')
46+
->escapedLine(Str::of($exception->getMessage())->ucfirst())
47+
->line('`')
48+
->escapedLine('📄 Код ошибки: '. $exception->getCode())
49+
->escapedLine('📂 Файл: '. Str::after($exception->getFile(), base_path()) .'#'. $exception->getLine())
50+
->line('')
51+
->line('*🔧 Что делать?*')
52+
->line('Давайте взглянем на этот участок кода внимательно и исправим проблему. С вашими умениями мы сможем преодолеть эту преграду!')
53+
->line('')
54+
->line('*💪 Не сдавайтесь!*')
55+
->line('Каждая ошибка - это шанс стать лучше. Давайте использовать этот момент, чтобы улучшить наш код и стать еще сильнее. Удачи!')
56+
->send();
57+
} catch (\Exception|Throwable $e) {
58+
// without recursive
59+
}
60+
}
3061
}

composer.json

+4-3
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,13 @@
2020
"php": ">=8.1",
2121
"cagilo/cagilo": "^3.2",
2222
"doctrine/dbal": "^3.7",
23+
"esplora/spire": "0.0.1",
2324
"guzzlehttp/guzzle": "^7.2",
2425
"hotwired/turbo-laravel": "^1.12",
2526
"intervention/image": "^2.7",
2627
"jolicode/jolitypo": "^1.4",
2728
"laminas/laminas-stdlib": "^3.18",
28-
"laragear/preload": "^1.1",
29+
"laravel-notification-channels/telegram": "^4.0",
2930
"laravel-notification-channels/webpush": "^7.1",
3031
"laravel/framework": "^10.31",
3132
"laravel/sanctum": "^3.2",
@@ -37,11 +38,11 @@
3738
"overtrue/laravel-like": "^5.2",
3839
"spatie/laravel-activitylog": "^4.7",
3940
"spatie/laravel-backup": "^8.6",
41+
"spatie/once": "^3.1",
4042
"symfony/dom-crawler": "^6.3",
4143
"symfony/yaml": "^6.3",
4244
"twbs/bootstrap-icons": "^1.11",
43-
"watson/active": "^7.0",
44-
"esplora/spire": "0.0.1"
45+
"watson/active": "^7.0"
4546
},
4647
"require-dev": {
4748
"deployer/deployer": "^7.3",

0 commit comments

Comments
 (0)