Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
Github/Database/.DS_Store
Jira/*
vendor
File renamed without changes.
70 changes: 70 additions & 0 deletions Console/InstallCommand.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Modules\Github\Console;

use App\Misc\Helper;
use Illuminate\Console\Command;
use Nwidart\Modules\Facades\Module;

class InstallCommand extends Command
{
/**
* The name and signature of the console command.
*
* @var string
*/
protected $signature = 'freescout-github:install {--force : Force the operation to run when in production}';

/**
* The console command description.
*
* @var string
*/
protected $description = 'Run the database migrations for the FreeScout GitHub module.';

/**
* Execute the console command.
*
* @return int
*/
public function handle()
{
$moduleName = 'Github';

$module = Module::find($moduleName);

if (!$module) {
$this->error('The GitHub module is not registered.');

return 1;
}

$this->info('Running migrations for the GitHub module...');

$parameters = ['module' => $moduleName];

$force = $this->option('force');

if ($force) {
$parameters['--force'] = true;
}

try {
$exitCode = $this->call('module:migrate', $parameters);
} catch (\Throwable $exception) {
$this->error('Running GitHub module migrations failed: '.$exception->getMessage());
Helper::logException($exception, '[GitHub] install command');

return 1;
}

if ($exitCode === 0) {
$this->info('GitHub module migrations completed successfully.');
} else {
$this->error('GitHub module migrations finished with errors.');
}

return $exitCode;
}
}

File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,42 @@
use Modules\Github\Entities\GithubIssue;
use Modules\Github\Entities\GithubLabelMapping;
use App\Conversation;
use Modules\Github\Support\RepositoryCache;

class GithubController extends Controller
{
/**
* Normalize request values to boolean for older Laravel versions.
*
* @param mixed $value
* @param bool $default
* @return bool
*/
private function toBoolean($value, bool $default = false): bool
{
if (is_null($value)) {
return $default;
}

if (is_bool($value)) {
return $value;
}

if (is_string($value)) {
$value = strtolower(trim($value));
if ($value === '') {
return $default;
}
return in_array($value, ['1', 'true', 'on', 'yes'], true);
}

if (is_numeric($value)) {
return (int) $value === 1;
}

return $default;
}

/**
* Test GitHub connection
*/
Expand Down Expand Up @@ -56,25 +89,33 @@ public function testConnection(Request $request)
public function getRepositories(Request $request)
{
try {
$result = GithubApiClient::getRepositories();

\Helper::log('github_debug', 'Repository fetch result: ' . json_encode([
'status' => $result['status'],
'data_count' => isset($result['data']) ? count($result['data']) : 'no data',
'message' => $result['message'] ?? 'no message'
]));

if ($this->toBoolean($request->input('refresh'))) {
RepositoryCache::clear();
}

$result = RepositoryCache::getRepositories(true);

if ($result['status'] === 'success') {
return response()->json([
'status' => 'success',
'repositories' => $result['data']
'repositories' => $result['repositories'],
'source' => $result['source'] ?? 'cache',
'fetched_at' => $result['fetched_at'] ?? null,
]);
} else {
}

if ($result['status'] === 'throttled') {
return response()->json([
'status' => 'error',
'message' => $result['message']
]);
'status' => 'throttled',
'message' => $result['message'],
'retry_after' => $result['retry_after'] ?? 60,
], 429);
}

return response()->json([
'status' => 'error',
'message' => $result['message'] ?? 'Failed to load repositories',
], 400);
} catch (\Exception $e) {
\Helper::logException($e, '[GitHub] Get Repositories Error');
return response()->json([
Expand All @@ -84,6 +125,89 @@ public function getRepositories(Request $request)
}
}

/**
* Refresh repository cache.
*/
public function refreshRepositories(): \Illuminate\Http\JsonResponse
{
try {
RepositoryCache::clear();

return response()->json([
'status' => 'success',
'message' => 'Repository cache cleared.',
]);
} catch (\Exception $e) {
\Helper::logException($e, '[GitHub] Refresh Repositories Error');

return response()->json([
'status' => 'error',
'message' => 'Failed to clear repository cache.',
], 500);
}
}

/**
* Search repositories with cache + throttled API fallback.
*/
public function searchRepositories(Request $request): \Illuminate\Http\JsonResponse
{
try {
$query = (string) $request->input('q', $request->input('term', ''));
$limit = (int) $request->input('limit', 20);

$result = RepositoryCache::search($query, $limit);

if ($result['status'] === 'success') {
$repositories = $result['repositories'];

$formatted = array_map(function ($repo) {
return [
'id' => $repo['full_name'],
'text' => $repo['full_name'],
'name' => $repo['name'] ?? $repo['full_name'],
'private' => $repo['private'] ?? false,
'has_issues' => $repo['has_issues'] ?? true,
];
}, $repositories);

return response()->json([
'status' => 'success',
'results' => $formatted,
'meta' => [
'count' => count($formatted),
'source' => $result['source'] ?? 'cache',
'fetched_at' => $result['fetched_at'] ?? null,
'throttled' => $result['throttled'] ?? false,
'retry_after' => $result['retry_after'] ?? null,
],
]);
}

if ($result['status'] === 'throttled') {
return response()->json([
'status' => 'throttled',
'message' => $result['message'],
'retry_after' => $result['retry_after'] ?? 60,
], 429);
}

$message = $result['message'] ?? 'Failed to search repositories.';

return response()->json([
'status' => 'error',
'message' => $message,
], 400);
} catch (\Exception $e) {
\Helper::logException($e, '[GitHub] Search Repositories Error');

return response()->json([
'status' => 'error',
'message' => 'Failed to search repositories: ' . $e->getMessage(),
], 500);
}
}

/**
* Get repository labels
*/
Expand Down
2 changes: 2 additions & 0 deletions Github/Http/routes.php → Http/routes.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
// Settings routes
Route::post('/github/test-connection', 'GithubController@testConnection')->name('github.test_connection');
Route::post('/github/repositories', 'GithubController@getRepositories')->name('github.repositories');
Route::get('/github/repositories/search', 'GithubController@searchRepositories')->name('github.repositories.search');
Route::post('/github/repositories/refresh', 'GithubController@refreshRepositories')->name('github.repositories.refresh');
Route::get('/github/labels/{repository}', 'GithubController@getLabels')->name('github.labels')->where('repository', '.*');
Route::post('/github/save-settings', 'GithubController@saveSettings')->name('github.save_settings');

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
namespace Modules\Github\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;
use Modules\Github\Console\InstallCommand;

// Module alias
define('GITHUB_MODULE', 'github');
Expand All @@ -23,6 +23,10 @@ public function boot()
$this->loadMigrations();
$this->registerHooks();
$this->loadAssets();

if ($this->app->runningInConsole()) {
$this->registerCommands();
}
}

/**
Expand Down Expand Up @@ -88,14 +92,30 @@ protected function registerHooks()
{
// Add module's CSS file to the application layout
\Eventy::addFilter('stylesheets', function($styles) {
$styles[] = \Module::getPublicPath(GITHUB_MODULE).'/css/module.css';
$cssPath = \Module::getPublicPath(GITHUB_MODULE).'/css/module.css';
if (file_exists(public_path($cssPath))) {
$styles[] = $cssPath;
} else {
\Log::warning('[GitHub] Public CSS asset not found: '.public_path($cssPath));
}
return $styles;
});

// Add module's JS file to the application layout
\Eventy::addFilter('javascripts', function($javascripts) {
$javascripts[] = \Module::getPublicPath(GITHUB_MODULE).'/js/laroute.js';
$javascripts[] = \Module::getPublicPath(GITHUB_MODULE).'/js/module.js';
$jsFiles = [
\Module::getPublicPath(GITHUB_MODULE).'/js/laroute.js',
\Module::getPublicPath(GITHUB_MODULE).'/js/module.js',
];

foreach ($jsFiles as $jsPath) {
if (file_exists(public_path($jsPath))) {
$javascripts[] = $jsPath;
} else {
\Log::warning('[GitHub] Public JS asset not found: '.public_path($jsPath));
}
}

return $javascripts;
});

Expand Down Expand Up @@ -187,4 +207,16 @@ protected function loadAssets()
{
// Assets are loaded via Eventy filters in registerHooks()
}

/**
* Register Artisan commands provided by the module.
*
* @return void
*/
protected function registerCommands()
{
$this->commands([
InstallCommand::class,
]);
}
}
File renamed without changes.
8 changes: 8 additions & 0 deletions Github/Public/js/laroute.js → Public/js/laroute.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,14 @@
"uri": "github\/repositories",
"name": "github.repositories"
},
{
"uri": "github\/repositories\/search",
"name": "github.repositories.search"
},
{
"uri": "github\/repositories\/refresh",
"name": "github.repositories.refresh"
},
{
"uri": "github\/labels\/{repository}",
"name": "github.labels"
Expand Down
Loading