From 9c7e4937456a6d743491e0517e44c983415deada Mon Sep 17 00:00:00 2001 From: bangnokia Date: Sun, 7 Apr 2024 23:56:25 +0700 Subject: [PATCH 1/9] add websocket server --- app/Commands/WebsocketServeCommand.php | 116 +++++++++++++++++++++++++ app/Socket.php | 36 ++++++++ app/Watcher.php | 37 ++++++++ composer.json | 6 +- 4 files changed, 193 insertions(+), 2 deletions(-) create mode 100644 app/Commands/WebsocketServeCommand.php create mode 100644 app/Socket.php create mode 100644 app/Watcher.php diff --git a/app/Commands/WebsocketServeCommand.php b/app/Commands/WebsocketServeCommand.php new file mode 100644 index 0000000..7f429c8 --- /dev/null +++ b/app/Commands/WebsocketServeCommand.php @@ -0,0 +1,116 @@ +loop = Loop::get(); + + $this->loop->futureTick(function () { + $this->line("Starting websocket server: ws://{$this->host()}:{$this->port()}"); + }); + + $this->startWatcher() + ->startServer(); + } + + protected function startWatcher(): static + { + $dirs = $this->dirs(); + + if (empty($dirs)) { + $this->warn('No directory to watch, please check you are in the correct directory.'); + return $this; + } + + $finder = (new Finder())->files()->in($this->dirs()); + + (new Watcher($this->loop, $finder)) + ->startWatching(function () { + collect(Socket::$clients) + ->map(function (ConnectionInterface $client) { + $this->info('Changes detected, reloading...'); + $client->send('reload'); + }); + }); + + return $this; + } + + protected function startServer(): static + { + try { + $this->server = new IoServer( + new HttpServer(new WsServer(new Socket())), + new Reactor("{$this->host()}:{$this->port()}", [], $this->loop), + $this->loop + ); + + $this->server->run(); + } catch (\Exception $exception) { + if (static::$portOffset < 10) { + static::$portOffset++; + $this->startServer(); + } + } + + return $this; + } + + public function host() + { + return '127.0.0.1'; + } + + public static function port() + { + return static::$port + static::$portOffset; + } + + protected function dirs(): array + { + $currentDir = getcwd(); + + $proposalDirs = [ + $currentDir . '/content', + $currentDir . '/public', + $currentDir . '/resources/views', + ]; + + $realDirs = []; + + foreach ($proposalDirs as $dir) { + if (is_dir($dir)) { + $realDirs[] = $dir; + } + } + + return $realDirs; + } +} diff --git a/app/Socket.php b/app/Socket.php new file mode 100644 index 0000000..3803a5f --- /dev/null +++ b/app/Socket.php @@ -0,0 +1,36 @@ +attach($conn); + } + + function onClose(ConnectionInterface $conn) + { + static::$clients->detach($conn); + } + + function onError(ConnectionInterface $conn, \Exception $e) + { + } + + public function onMessage(ConnectionInterface $conn, MessageInterface $msg) + { + } +} diff --git a/app/Watcher.php b/app/Watcher.php new file mode 100644 index 0000000..f5d9124 --- /dev/null +++ b/app/Watcher.php @@ -0,0 +1,37 @@ +loop = $loop; + $this->finder = $finder; + } + + public function startWatching($callback) + { + $watcher = new ResourceWatcher( + new ResourceCacheMemory(), + $this->finder, + new Crc32ContentHash() + ); + + $this->loop->addPeriodicTimer(1, function () use ($watcher, $callback) { + Cache::put('serve_websockets_running', true, 5); + if ($watcher->findChanges()->hasChanges()) { + call_user_func($callback); + } + }); + } + +} diff --git a/composer.json b/composer.json index b43f196..29a704a 100644 --- a/composer.json +++ b/composer.json @@ -19,13 +19,15 @@ ], "require": { "php": "^8.3", + "cboden/ratchet": "^0.4.4", "erusev/parsedown": "^v1.7.2", "erusev/parsedown-extra": "^0.8.1", "illuminate/view": "^v10.0", "keinos/parsedown-toc": "^1.1", "laravel-zero/framework": "^v10.3", - "symfony/http-foundation": "^7.0", - "symfony/yaml": "^7.0", + "linaphp/resource-watcher": "^0.1.1", + "symfony/http-foundation": "^6.0", + "symfony/yaml": "^6.0", "tempest/highlight": "1.*" }, "require-dev": { From d6c31a3997dbda28aa0236aa8791d4d66ff838ae Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 00:01:48 +0700 Subject: [PATCH 2/9] update skeleton --- app/Commands/WebsocketServeCommand.php | 2 +- skeleton/content/index.md | 5 +---- skeleton/resources/views/post.blade.php | 3 +-- 3 files changed, 3 insertions(+), 7 deletions(-) diff --git a/app/Commands/WebsocketServeCommand.php b/app/Commands/WebsocketServeCommand.php index 7f429c8..e2b524b 100644 --- a/app/Commands/WebsocketServeCommand.php +++ b/app/Commands/WebsocketServeCommand.php @@ -53,9 +53,9 @@ protected function startWatcher(): static (new Watcher($this->loop, $finder)) ->startWatching(function () { + $this->info('Changes detected, reloading...'); collect(Socket::$clients) ->map(function (ConnectionInterface $client) { - $this->info('Changes detected, reloading...'); $client->send('reload'); }); }); diff --git a/skeleton/content/index.md b/skeleton/content/index.md index 5da124c..b2f1660 100644 --- a/skeleton/content/index.md +++ b/skeleton/content/index.md @@ -3,9 +3,6 @@ title: Welcome to Lina layout: home --- -Welcome to Lina, a simple and lightweight blog platform built on top of Laravel Blade. Lina is designed to be easy to use and. +Welcome to Lina, a simple and blazing fast blog platform built on top of Laravel Blade. Lina is designed to be easy to use and. Checkout the [documentation](https://github.com/bangnokia/lina) on our Github repository to get started. - - - diff --git a/skeleton/resources/views/post.blade.php b/skeleton/resources/views/post.blade.php index f588e88..1741e92 100644 --- a/skeleton/resources/views/post.blade.php +++ b/skeleton/resources/views/post.blade.php @@ -2,10 +2,9 @@ @section('content')

{{ $data->title }}

- +
{!! $data->content !!} -{{-- {{ $data->content }}--}}
@endsection From 5ba2ba63a3bf9488f14ba9e742f252039b2a4de9 Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 00:05:17 +0700 Subject: [PATCH 3/9] update to separate 2 serve http and websocket command --- app/Commands/{ServeCommand.php => HttpServeCommand.php} | 4 ++-- app/Commands/WebsocketServeCommand.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) rename app/Commands/{ServeCommand.php => HttpServeCommand.php} (95%) diff --git a/app/Commands/ServeCommand.php b/app/Commands/HttpServeCommand.php similarity index 95% rename from app/Commands/ServeCommand.php rename to app/Commands/HttpServeCommand.php index 75418fe..5fc5076 100644 --- a/app/Commands/ServeCommand.php +++ b/app/Commands/HttpServeCommand.php @@ -6,9 +6,9 @@ use Symfony\Component\Process\PhpExecutableFinder; use Symfony\Component\Process\Process; -class ServeCommand extends Command +class HttpServeCommand extends Command { - protected $signature = 'serve'; + protected $signature = 'serve:http'; protected $description = 'Start simple web server for development'; diff --git a/app/Commands/WebsocketServeCommand.php b/app/Commands/WebsocketServeCommand.php index e2b524b..1ed9025 100644 --- a/app/Commands/WebsocketServeCommand.php +++ b/app/Commands/WebsocketServeCommand.php @@ -16,7 +16,7 @@ class WebsocketServeCommand extends Command { - protected $signature = 'ws:serve'; + protected $signature = 'serve:ws'; protected $description = 'Start websocket server for development'; From 73db09e2579768ea083bf9a878e30c7ea6964be1 Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 00:23:50 +0700 Subject: [PATCH 4/9] add type --- app/Commands/HttpServeCommand.php | 2 ++ app/Commands/WebsocketServeCommand.php | 11 +++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/app/Commands/HttpServeCommand.php b/app/Commands/HttpServeCommand.php index 5fc5076..c042ce9 100644 --- a/app/Commands/HttpServeCommand.php +++ b/app/Commands/HttpServeCommand.php @@ -12,6 +12,8 @@ class HttpServeCommand extends Command protected $description = 'Start simple web server for development'; + protected $hidden = true; + protected int $portOffset = 0; public function handle() diff --git a/app/Commands/WebsocketServeCommand.php b/app/Commands/WebsocketServeCommand.php index 1ed9025..aec1ff1 100644 --- a/app/Commands/WebsocketServeCommand.php +++ b/app/Commands/WebsocketServeCommand.php @@ -9,6 +9,7 @@ use Ratchet\Server\IoServer; use Ratchet\WebSocket\WsServer; use React\EventLoop\Loop; +use React\EventLoop\LoopInterface; use Symfony\Component\Finder\Finder; use Ratchet\ConnectionInterface; use React\Socket\SocketServer as Reactor; @@ -20,13 +21,15 @@ class WebsocketServeCommand extends Command protected $description = 'Start websocket server for development'; - protected $loop; + protected $hidden = true; - protected $server; + protected LoopInterface $loop; - public static $port = 9696; + protected IoServer $server; - public static $portOffset = 0; + public static int $port = 9696; + + public static int $portOffset = 0; public function handle() { From 09e687cbd211b87d32151be383fac15fc0254475 Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 02:12:02 +0700 Subject: [PATCH 5/9] serve command --- app/Commands/ServeCommand.php | 33 +++++++++++++++++++++++++++++++++ skeleton/content/index.md | 4 +++- 2 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 app/Commands/ServeCommand.php diff --git a/app/Commands/ServeCommand.php b/app/Commands/ServeCommand.php new file mode 100644 index 0000000..21d2c6d --- /dev/null +++ b/app/Commands/ServeCommand.php @@ -0,0 +1,33 @@ +find(); + // get the current php binary path which is running the command + $pool = Process::pool(function (Pool $pool) use ($phpBinary) { + $pool->path(getcwd())->command([$phpBinary, base_path('lina'), 'serve:http']); + $pool->path(getcwd())->command([$phpBinary, base_path('lina'), 'serve:ws']); + })->start(function (string $type, string $output, string $key) { + $this->output->write($output); + }); + + while ($pool->running()->isNotEmpty()) { + usleep(0.5 * 1000000); + } + + $pool->wait(); + } +} diff --git a/skeleton/content/index.md b/skeleton/content/index.md index b2f1660..1264c58 100644 --- a/skeleton/content/index.md +++ b/skeleton/content/index.md @@ -3,6 +3,8 @@ title: Welcome to Lina layout: home --- -Welcome to Lina, a simple and blazing fast blog platform built on top of Laravel Blade. Lina is designed to be easy to use and. +Welcome to [Lina](https://lina.daudau.cc), a simple and blazing fast blog platform built on top of Laravel Blade. + +Lina is designed to be easy to use and. Checkout the [documentation](https://github.com/bangnokia/lina) on our Github repository to get started. From 7df540884a7d6e8d9b74fe740c769a0965133ef3 Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 02:13:11 +0700 Subject: [PATCH 6/9] cleanup --- app/MarkdownParser.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/app/MarkdownParser.php b/app/MarkdownParser.php index 8eee382..9368ed9 100644 --- a/app/MarkdownParser.php +++ b/app/MarkdownParser.php @@ -3,7 +3,6 @@ namespace BangNokia\Lina; use BangNokia\Lina\Contracts\MarkdownParser as MarkdownParserContract; -use ParsedownToC; class MarkdownParser implements MarkdownParserContract { @@ -16,8 +15,6 @@ public function __construct() public function parse(string $text): string { - $content = trim($this->driver->text($text)); -// dd($content); - return $content; + return trim($this->driver->text($text)); } } From f7f985f7c3a03cf660ebc71fdec17a894d7dd77b Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 02:36:34 +0700 Subject: [PATCH 7/9] wont log http request --- app/Commands/HttpServeCommand.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/Commands/HttpServeCommand.php b/app/Commands/HttpServeCommand.php index c042ce9..1552c90 100644 --- a/app/Commands/HttpServeCommand.php +++ b/app/Commands/HttpServeCommand.php @@ -42,7 +42,7 @@ protected function startProcess() $process = new Process($this->serverCommand(), timeout: 0); $process->start(function ($type, $data) { - $this->output->write($data); +// $this->output->write($data); }); // Stop the server when the user hits Ctrl+C From a0a06c809717b836a78684600f43444c7bfe3a41 Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 02:41:01 +0700 Subject: [PATCH 8/9] implement livew reloading --- app/Commands/WebsocketServeCommand.php | 12 +++++++++-- app/MarkdownRenderer.php | 5 ++--- app/Router.php | 28 +++++++++++++++++++++++++- skeleton/content/index.md | 2 -- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/app/Commands/WebsocketServeCommand.php b/app/Commands/WebsocketServeCommand.php index aec1ff1..b6bd79c 100644 --- a/app/Commands/WebsocketServeCommand.php +++ b/app/Commands/WebsocketServeCommand.php @@ -4,6 +4,7 @@ use BangNokia\Lina\Socket; use BangNokia\Lina\Watcher; +use Illuminate\Support\Facades\Cache; use LaravelZero\Framework\Commands\Command; use Ratchet\Http\HttpServer; use Ratchet\Server\IoServer; @@ -39,7 +40,8 @@ public function handle() $this->line("Starting websocket server: ws://{$this->host()}:{$this->port()}"); }); - $this->startWatcher() + $this + ->startWatcher() ->startServer(); } @@ -74,6 +76,7 @@ protected function startServer(): static new Reactor("{$this->host()}:{$this->port()}", [], $this->loop), $this->loop ); + $this->loop->addPeriodicTimer(1, fn() => Cache::put('ws_is_running', true, 5)); $this->server->run(); } catch (\Exception $exception) { @@ -86,7 +89,12 @@ protected function startServer(): static return $this; } - public function host() + public static function isRunning(): bool + { + return Cache::get('ws_is_running', false); + } + + public static function host() { return '127.0.0.1'; } diff --git a/app/MarkdownRenderer.php b/app/MarkdownRenderer.php index 234bfc8..d56c8fe 100644 --- a/app/MarkdownRenderer.php +++ b/app/MarkdownRenderer.php @@ -3,7 +3,6 @@ namespace BangNokia\Lina; use BangNokia\Lina\Contracts\Renderer; -use Illuminate\Support\Facades\Blade; class MarkdownRenderer implements Renderer { @@ -17,9 +16,9 @@ public function __construct(protected string $rootDir) config(['view.compiled' => $this->rootDir . '/resources/cache']); } - public function render(string $realPath): string + public function render(string $file): string { - $content = app(ContentFinder::class)->get($realPath, true); + $content = app(ContentFinder::class)->get($file, true); return view($content->layout, [ 'data' => $content, diff --git a/app/Router.php b/app/Router.php index 83711ab..bfa7368 100644 --- a/app/Router.php +++ b/app/Router.php @@ -2,6 +2,8 @@ namespace BangNokia\Lina; +use BangNokia\Lina\Commands\WebsocketServeCommand; +use Illuminate\Support\Facades\Cache; use Symfony\Component\Finder\Finder; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -29,13 +31,37 @@ public function parse(Request $request): Response $contentFileRealPath = $this->contentFinder->tryFind($path); + $html = app(MarkdownRenderer::class)->render($contentFileRealPath); + + // we don't have middleware so let inject the websocket script here + $html = $this->injectWebSocketScript($html); + return new Response( - app(MarkdownRenderer::class)->render($contentFileRealPath), + $html, 200, ['Content-Type' => 'text/html'] ); } + protected function injectWebSocketScript(string $html): string + { + $port = WebsocketServeCommand::port(); + + $script = << + (new WebSocket('ws://127.0.0.1:$port')).onmessage = function (message) { + if (message.data === 'reload') { + window.location.reload(true); + } + }; + +JS; + + $html = $html . $script; // so who care about well-formed html here xD! + + return $html; + } + protected function isStaticFile(string $path): bool { return in_array(pathinfo($path, PATHINFO_EXTENSION), ['css', 'js', 'png', 'jpg', 'jpeg', 'gif', 'svg']); diff --git a/skeleton/content/index.md b/skeleton/content/index.md index 1264c58..756ba50 100644 --- a/skeleton/content/index.md +++ b/skeleton/content/index.md @@ -5,6 +5,4 @@ layout: home Welcome to [Lina](https://lina.daudau.cc), a simple and blazing fast blog platform built on top of Laravel Blade. -Lina is designed to be easy to use and. - Checkout the [documentation](https://github.com/bangnokia/lina) on our Github repository to get started. From 44b48d997175410af3085c75b47f82a3c802aa2f Mon Sep 17 00:00:00 2001 From: bangnokia Date: Mon, 8 Apr 2024 02:45:05 +0700 Subject: [PATCH 9/9] lgtm --- skeleton/content/index.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/skeleton/content/index.md b/skeleton/content/index.md index 756ba50..c8c9634 100644 --- a/skeleton/content/index.md +++ b/skeleton/content/index.md @@ -5,4 +5,10 @@ layout: home Welcome to [Lina](https://lina.daudau.cc), a simple and blazing fast blog platform built on top of Laravel Blade. -Checkout the [documentation](https://github.com/bangnokia/lina) on our Github repository to get started. +## Get started + +- **content** folder: contains all the markdown files content. +- **resources/views** folder: contains all the views. +- **public** folder: contains all the assets such as css, js, images. + +Checkout the [documentation](https://github.com/bangnokia/lina) on our Github repository for more information.