From a52b5af595059d9e4e05cca4471630d62a51fd56 Mon Sep 17 00:00:00 2001 From: MD Mahbub Hasan Date: Tue, 15 Oct 2024 23:04:02 +0600 Subject: [PATCH 1/2] Update service Application FQDN by UUID. --- .../Controllers/Api/ServicesController.php | 114 ++++++++++++++++++ routes/api.php | 2 +- 2 files changed, 115 insertions(+), 1 deletion(-) diff --git a/app/Http/Controllers/Api/ServicesController.php b/app/Http/Controllers/Api/ServicesController.php index 89418517ba..da5f15948a 100644 --- a/app/Http/Controllers/Api/ServicesController.php +++ b/app/Http/Controllers/Api/ServicesController.php @@ -1240,4 +1240,118 @@ public function action_restart(Request $request) ); } + + #[OA\Patch( + summary: 'Update Service Application FQDN', + description: 'Update service Application FQDN by UUID.', + path: '/services/{uuid}/fqdn', + operationId: 'update-service-fqdn-by-uuid', + security: [ + ['bearerAuth' => []], + ], + tags: ['Services'], + parameters: [ + new OA\Parameter( + name: 'uuid', + in: 'path', + description: 'UUID of the service.', + required: true, + schema: new OA\Schema( + type: 'string', + format: 'uuid', + ) + ), + ], + requestBody: new OA\RequestBody( + description: 'FQDN update and application UUID to exclude.', + required: true, + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'fqdn' => ['type' => 'string', 'description' => 'Comma-separated FQDNs.'], + 'applications_uuid' => ['type' => 'string', 'description' => 'UUID of the application to exclude.'], + ], + required: ['fqdn', 'applications_uuid'], + ), + ), + ], + ), + responses: [ + new OA\Response( + response: 200, + description: 'FQDN updated.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'uuid' => ['type' => 'string'], + 'fqdn' => ['type' => 'string'], + ] + ) + ), + ]), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 404, + ref: '#/components/responses/404', + ), + ] + )] + + public function update_service_fqdn_by_uuid(Request $request, $uuid) + { + $teamId = getTeamIdFromToken(); + if (is_null($teamId)) { + return invalidTokenResponse(); + } + + // Find the service with the specified UUID and teamId + $service = Service::whereRelation('environment.project.team', 'id', $teamId)->whereUuid($uuid)->first(); + + // If service not found, return error response + if (!$service) { + return response()->json(['message' => 'Service not found.'], 404); + } + + // Validate the request input + $validator = customApiValidator($request->all(), [ + 'fqdn' => 'required|string|max:255', + 'applications_uuid' => 'required|string|max:255', + ]); + + // If validation fails, return error response + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 422); + } + + // Find the application with the specified applications_uuid + $application = $service->applications()->where('uuid',$request->applications_uuid)->first(); + + // If application not found, return error response + if (!$application) { + return response()->json(['message' => 'Application not found.'], 404); + } + + // Update the FQDN for the found application + $application->fqdn = $request->fqdn; + $application->save(); // Save the updated application + + // Return the updated application with 200 response + return response()->json($application, 200); + } } diff --git a/routes/api.php b/routes/api.php index 57f45be5da..066a33c4ed 100644 --- a/routes/api.php +++ b/routes/api.php @@ -116,7 +116,7 @@ Route::get('/services/{uuid}', [ServicesController::class, 'service_by_uuid']); // Route::patch('/services/{uuid}', [ServicesController::class, 'update_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); Route::delete('/services/{uuid}', [ServicesController::class, 'delete_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - + Route::patch('/services/{uuid}/fqdn', [ServicesController::class, 'update_service_fqdn_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); Route::get('/services/{uuid}/envs', [ServicesController::class, 'envs']); Route::post('/services/{uuid}/envs', [ServicesController::class, 'create_env'])->middleware([IgnoreReadOnlyApiToken::class]); Route::patch('/services/{uuid}/envs/bulk', [ServicesController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); From 91d9d5e6a7df749ef7e3878139e8d3a91c16274c Mon Sep 17 00:00:00 2001 From: MD Mahbub Hasan Date: Wed, 16 Oct 2024 00:30:20 +0600 Subject: [PATCH 2/2] Added Github Controller API for create load repo and load branches --- app/Http/Controllers/Api/GithubController.php | 320 ++++++++++++++++++ routes/api.php | 6 +- 2 files changed, 325 insertions(+), 1 deletion(-) create mode 100644 app/Http/Controllers/Api/GithubController.php diff --git a/app/Http/Controllers/Api/GithubController.php b/app/Http/Controllers/Api/GithubController.php new file mode 100644 index 0000000000..e37ec45dca --- /dev/null +++ b/app/Http/Controllers/Api/GithubController.php @@ -0,0 +1,320 @@ + []], + ], + tags: ['GitHub Apps'], + requestBody: new OA\RequestBody( + description: 'GitHub app creation payload.', + required: true, + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'name' => ['type' => 'string', 'description' => 'Name of the GitHub app.'], + 'organization' => ['type' => 'string', 'nullable' => true, 'description' => 'Organization to associate the app with.'], + 'api_url' => ['type' => 'string', 'description' => 'API URL for the GitHub app.'], + 'html_url' => ['type' => 'string', 'description' => 'HTML URL for the GitHub app.'], + 'custom_user' => ['type' => 'string', 'description' => 'Custom user for the app.'], + 'custom_port' => ['type' => 'integer', 'description' => 'Custom port for the app.'], + 'is_system_wide' => ['type' => 'boolean', 'description' => 'Is this app system-wide.'], + ], + required: ['name', 'api_url', 'html_url', 'custom_user', 'custom_port'], + ), + ), + ], + ), + responses: [ + new OA\Response( + response: 201, + description: 'GitHub app created successfully.', + content: [ + new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'id' => ['type' => 'integer'], + 'uuid' => ['type' => 'string'], + 'name' => ['type' => 'string'], + 'organization' => ['type' => 'string', 'nullable' => true], + 'api_url' => ['type' => 'string'], + 'html_url' => ['type' => 'string'], + 'custom_user' => ['type' => 'string'], + 'custom_port' => ['type' => 'integer'], + 'team_id' => ['type' => 'integer'], + ] + ) + ), + ] + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + ] + )] + + public function createGitHubApp(Request $request){ + // Extract the team ID from the current session or token + $teamId = currentTeam()->id; + + try { + + + $validator = customApiValidator($request->all(), [ + 'name' => 'required|string|max:255', + 'organization' => 'nullable|string|max:255', + 'api_url' => 'required|string|url', + 'html_url' => 'required|string|url', + 'custom_user' => 'required|string|max:255', + 'custom_port' => 'required|integer', + 'is_system_wide' => 'required|boolean', + + ]); + + // If validation fails, return a 400 error with validation messages + if ($validator->fails()) { + return response()->json([ + 'message' => 'Validation failed.', + 'errors' => $validator->errors(), + ], 400); + } + + // Prepare the payload for creating the GitHub app + $payload = [ + 'name' => $request->input('name'), + 'organization' => $request->input('organization'), + 'api_url' => $request->input('api_url'), + 'html_url' => $request->input('html_url'), + 'custom_user' => $request->input('custom_user'), + 'custom_port' => $request->input('custom_port'), + 'team_id' => $teamId, + ]; + + // If running in cloud environment, include 'is_system_wide' + if (isCloud()) { + $payload['is_system_wide'] = $request->input('is_system_wide'); + } + + // Create the GitHub app in the database + $githubApp = GithubApp::create($payload); + + // Return the newly created GitHub app with a 201 status + return response()->json([ + 'message' => 'GitHub app created successfully.', + 'data' => $githubApp, + ], 201); + + } catch (\Throwable $e) { + // Handle any errors that occur during the process + return response()->json([ + 'message' => 'An error occurred while creating the GitHub app.', + 'error' => $e->getMessage(), + ], 500); + } + } + + #[OA\Get( + path: '/github-apps/{github_app_id}/repositories', + summary: 'Load Repositories for a GitHub App', + description: 'Fetch repositories from GitHub for a given GitHub app.', + operationId: 'load-repositories', + tags: ['GitHub Repositories'], + security: [ + ['bearerAuth' => []], + ], + parameters: [ + new OA\Parameter( + name: 'github_app_id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer'), + description: 'GitHub App ID' + ) + ], + responses: [ + new OA\Response( + response: 200, + description: 'Repositories loaded successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'repositories' => new OA\Items( + type: 'array', + items: new OA\Schema(type: 'object') + ) + ] + ) + ) + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ) + ] + )] + public function loadRepositories($github_app_id) + { + try { + // Fetch GitHub app by ID + $githubApp = GithubApp::findOrFail($github_app_id); + $token = generate_github_installation_token($githubApp); + $repositories = collect(); + $page = 1; + + // Fetch repositories in a loop to handle pagination + do { + $response = Http::withToken($token)->get("{$githubApp->api_url}/installation/repositories", [ + 'per_page' => 100, + 'page' => $page + ]); + + if ($response->status() !== 200) { + return response()->json([ + 'message' => $response->json()['message'], + ], $response->status()); + } + + $json = $response->json(); + $repositories = $repositories->concat($json['repositories']); + $page++; + } while (count($json['repositories']) > 0); + + return response()->json([ + 'message' => 'Repositories loaded successfully.', + 'repositories' => $repositories->sortBy('name')->values(), + ], 200); + } catch (\Throwable $e) { + return response()->json([ + 'message' => 'An error occurred while loading repositories.', + 'error' => $e->getMessage(), + ], 500); + } + } + + #[OA\Get( + path: '/github-apps/{github_app_id}/repositories/{owner}/{repo}/branches', + summary: 'Load Branches for a GitHub Repository', + description: 'Fetch branches from GitHub for a given repository.', + operationId: 'load-branches', + tags: ['GitHub Branches'], + security: [ + ['bearerAuth' => []], + ], + parameters: [ + new OA\Parameter( + name: 'github_app_id', + in: 'path', + required: true, + schema: new OA\Schema(type: 'integer'), + description: 'GitHub App ID' + ), + new OA\Parameter( + name: 'owner', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string'), + description: 'Repository owner' + ), + new OA\Parameter( + name: 'repo', + in: 'path', + required: true, + schema: new OA\Schema(type: 'string'), + description: 'Repository name' + ), + ], + responses: [ + new OA\Response( + response: 200, + description: 'Branches loaded successfully.', + content: new OA\MediaType( + mediaType: 'application/json', + schema: new OA\Schema( + type: 'object', + properties: [ + 'branches' => new OA\Items( + type: 'array', + items: new OA\Schema(type: 'object') + ) + ] + ) + ) + ), + new OA\Response( + response: 400, + ref: '#/components/responses/400', + ), + new OA\Response( + response: 401, + ref: '#/components/responses/401', + ), + ] + )] + public function loadBranches($github_app_id, $owner, $repo) + { + try { + // Fetch the GitHub App + $githubApp = GithubApp::findOrFail($github_app_id); + $token = generate_github_installation_token($githubApp); + + // API call to GitHub to load branches + $response = Http::withToken($token)->get("{$githubApp->api_url}/repos/{$owner}/{$repo}/branches"); + + // Handle the response from GitHub API + if ($response->status() !== 200) { + return response()->json([ + 'message' => 'Error loading branches from GitHub.', + 'error' => $response->json('message') + ], $response->status()); + } + + $branches = $response->json(); + + return response()->json([ + 'message' => 'Branches loaded successfully.', + 'branches' => $branches, + ], 200); + + } catch (\Throwable $e) { + return response()->json([ + 'message' => 'An error occurred while loading branches.', + 'error' => $e->getMessage(), + ], 500); + } + } + + +} \ No newline at end of file diff --git a/routes/api.php b/routes/api.php index 066a33c4ed..9b6a29f3b9 100644 --- a/routes/api.php +++ b/routes/api.php @@ -10,6 +10,7 @@ use App\Http\Controllers\Api\ServersController; use App\Http\Controllers\Api\ServicesController; use App\Http\Controllers\Api\TeamController; +use App\Http\Controllers\Api\GithubController; use App\Http\Middleware\ApiAllowed; use App\Http\Middleware\IgnoreReadOnlyApiToken; use App\Http\Middleware\OnlyRootApiToken; @@ -122,7 +123,10 @@ Route::patch('/services/{uuid}/envs/bulk', [ServicesController::class, 'create_bulk_envs'])->middleware([IgnoreReadOnlyApiToken::class]); Route::patch('/services/{uuid}/envs', [ServicesController::class, 'update_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); Route::delete('/services/{uuid}/envs/{env_uuid}', [ServicesController::class, 'delete_env_by_uuid'])->middleware([IgnoreReadOnlyApiToken::class]); - + Route::post('/github-apps', [GithubController::class, 'createGitHubApp'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/github-apps/{github_app_id}/repositories', [GithubController::class, 'loadRepositories'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::get('/github-apps/{github_app_id}/repositories/{owner}/{repo}/branches', [GithubController::class, 'loadBranches'])->middleware([IgnoreReadOnlyApiToken::class]); + Route::match(['get', 'post'], '/services/{uuid}/start', [ServicesController::class, 'action_deploy'])->middleware([IgnoreReadOnlyApiToken::class]); Route::match(['get', 'post'], '/services/{uuid}/restart', [ServicesController::class, 'action_restart'])->middleware([IgnoreReadOnlyApiToken::class]); Route::match(['get', 'post'], '/services/{uuid}/stop', [ServicesController::class, 'action_stop'])->middleware([IgnoreReadOnlyApiToken::class]);