diff --git a/app/app/src/Application/Bootloader/GithubBootloader.php b/app/app/src/Application/Bootloader/GithubBootloader.php index 9e84e9c..7b83e7d 100644 --- a/app/app/src/Application/Bootloader/GithubBootloader.php +++ b/app/app/src/Application/Bootloader/GithubBootloader.php @@ -8,6 +8,7 @@ use App\Github\Client; use App\Github\ClientInterface; use App\Github\WebhookGate; +use Psr\Log\LoggerInterface; use Spiral\Boot\Bootloader\Bootloader; use Spiral\Boot\EnvironmentInterface; use Spiral\Cache\CacheStorageProviderInterface; @@ -19,6 +20,7 @@ public function defineSingletons(): array return [ ClientInterface::class => static fn( CacheStorageProviderInterface $cache, + LoggerInterface $logger, EnvironmentInterface $env, ) => new CacheableClient( client: new Client( @@ -29,6 +31,7 @@ public function defineSingletons(): array 'Authorization' => 'Bearer ' . $env->get('GITHUB_TOKEN'), ], ]), + logger: $logger, ), cache: $cache->storage('github'), ttl: 300, diff --git a/app/app/src/Application/Event/Liked.php b/app/app/src/Application/Event/Liked.php new file mode 100644 index 0000000..b05f88d --- /dev/null +++ b/app/app/src/Application/Event/Liked.php @@ -0,0 +1,34 @@ + $this->key, + ]; + } +} diff --git a/app/app/src/Endpoint/Centrifugo/RPCService.php b/app/app/src/Endpoint/Centrifugo/RPCService.php index 1e25aac..38795dd 100644 --- a/app/app/src/Endpoint/Centrifugo/RPCService.php +++ b/app/app/src/Endpoint/Centrifugo/RPCService.php @@ -57,7 +57,8 @@ public function createHttpRequest(Request\RPC $request): ServerRequestInterface [$method, $uri] = \explode(':', $request->method, 2); $method = \strtoupper($method); - $httpRequest = $this->requestFactory->createServerRequest(\strtoupper($method), \ltrim($uri, '/')) + $httpRequest = $this->requestFactory + ->createServerRequest(\strtoupper($method), \ltrim($uri, '/')) ->withHeader('Content-Type', 'application/json'); $data = $request->getData(); diff --git a/app/app/src/Endpoint/Event/LikeListener.php b/app/app/src/Endpoint/Event/LikeListener.php new file mode 100644 index 0000000..775144e --- /dev/null +++ b/app/app/src/Endpoint/Event/LikeListener.php @@ -0,0 +1,132 @@ +key)); + } +} diff --git a/app/app/src/Endpoint/Http/Controller/LikeAction.php b/app/app/src/Endpoint/Http/Controller/LikeAction.php new file mode 100644 index 0000000..a7905cc --- /dev/null +++ b/app/app/src/Endpoint/Http/Controller/LikeAction.php @@ -0,0 +1,32 @@ +events->dispatch(new Liked(key: $request->key)); + + return $this->response->create(200); + } +} diff --git a/app/app/src/Endpoint/Http/Filter/LikeRequest.php b/app/app/src/Endpoint/Http/Filter/LikeRequest.php new file mode 100644 index 0000000..a76ed8e --- /dev/null +++ b/app/app/src/Endpoint/Http/Filter/LikeRequest.php @@ -0,0 +1,24 @@ + 'required|string', + ]); + } +} diff --git a/app/app/src/Github/Client.php b/app/app/src/Github/Client.php index c9e8614..792a12b 100644 --- a/app/app/src/Github/Client.php +++ b/app/app/src/Github/Client.php @@ -7,11 +7,13 @@ use App\Github\Entity\Issue; use Carbon\Carbon; use GuzzleHttp\Psr7\Request; +use Psr\Log\LoggerInterface; final readonly class Client implements ClientInterface { public function __construct( private \Psr\Http\Client\ClientInterface $client, + private LoggerInterface $logger, ) { } @@ -26,7 +28,7 @@ public function getStars(string $repository): int $data = \json_decode($response->getBody()->getContents(), true); - return $data['stargazers_count']; + return $data['stargazers_count'] ?? 0; } public function getLastVersion(string $repository): string @@ -40,7 +42,7 @@ public function getLastVersion(string $repository): string $data = \json_decode($response->getBody()->getContents(), true); - return $data['tag_name']; + return $data['tag_name'] ?? '0.0.0'; } public function getIssuesForContributors(): array @@ -71,6 +73,16 @@ private function fetchRepositoryIssues(string $repository): array ), ); + if ($response->getStatusCode() !== 200) { + $this->logger->error('Failed to fetch issues', [ + 'repository' => $repository, + 'status' => $response->getStatusCode(), + 'body' => $response->getBody()->getContents(), + ]); + + return []; + } + $data = \json_decode($response->getBody()->getContents(), true); return \array_map( diff --git a/spa/app/api/Api.ts b/spa/app/api/Api.ts index fdb9584..f31a73e 100644 --- a/spa/app/api/Api.ts +++ b/spa/app/api/Api.ts @@ -6,6 +6,10 @@ type SettingsApi = { get: () => SettingsResponse, } +type DataApi = { + like: () => void, +} + type ExamplesApi = { call: (action: string) => void, } @@ -29,6 +33,12 @@ export default class Api { this._examples_url = examples_url; } + get data(): DataApi { + return { + like: apiMethods.like(this.rpc), + } + } + get settings(): SettingsApi { return { get: apiMethods.settings(this.rpc), diff --git a/spa/app/api/methods.ts b/spa/app/api/methods.ts index 404195a..2b211cc 100644 --- a/spa/app/api/methods.ts +++ b/spa/app/api/methods.ts @@ -14,6 +14,8 @@ const team = (rpc: RPCClient) => () => rpc.call('get:api/team') const issuesForContributors = (rpc: RPCClient) => () => rpc.call('get:api/issues/for-contributors') .then((response: ServerResponse) => response.data.data); +const like = (rpc: RPCClient) => (key: string) => rpc.call('post:api/like', {key}); + const callExampleAction = (host: string) => (action: string) => { action = action.toLowerCase(); @@ -32,5 +34,6 @@ export default { settings, team, callExampleAction, - issuesForContributors + issuesForContributors, + like } diff --git a/spa/components/Common/Likeable.vue b/spa/components/Common/Likeable.vue new file mode 100644 index 0000000..6f7d604 --- /dev/null +++ b/spa/components/Common/Likeable.vue @@ -0,0 +1,89 @@ + + + + + \ No newline at end of file diff --git a/spa/components/v1/Contribution.vue b/spa/components/v1/Contribution.vue index f4f607b..036376d 100644 --- a/spa/components/v1/Contribution.vue +++ b/spa/components/v1/Contribution.vue @@ -15,7 +15,7 @@ const redirectTo = (url: string) => {