From 95491e39dc80963af0276ac4ff8aa7a1529e3856 Mon Sep 17 00:00:00 2001 From: Eric Wang <37554696+ericwang401@users.noreply.github.com> Date: Wed, 29 Nov 2023 19:27:05 -0600 Subject: [PATCH] Add backend support for Coterm --- .../Controllers/Admin/CotermController.php | 81 +++++- .../Admin/Coterms/DeleteCotermRequest.php | 14 + .../Admin/Coterms/StoreCotermRequest.php | 6 +- .../Admin/Coterms/UpdateCotermRequest.php | 6 +- app/Models/Filters/FiltersCoterm.php | 15 ++ app/Policies/CotermPolicy.php | 23 ++ app/Services/Coterm/CotermJWTService.php | 38 +-- app/Transformers/Admin/CotermTransformer.php | 31 +++ .../scripts/api/admin/coterms/createCoterm.ts | 32 +++ .../scripts/api/admin/coterms/deleteCoterm.ts | 5 + .../api/admin/coterms/getAttachedNodes.ts | 33 +++ .../scripts/api/admin/coterms/getCoterms.ts | 52 ++++ .../api/admin/coterms/resetCotermToken.ts | 12 + .../api/admin/coterms/updateAttachedNodes.ts | 15 ++ .../scripts/api/admin/coterms/updateCoterm.ts | 29 +++ .../api/admin/coterms/useCotermsSWR.ts | 12 + routes/api-admin.php | 245 ++++++++++++++---- 17 files changed, 578 insertions(+), 71 deletions(-) create mode 100644 app/Http/Requests/Admin/Coterms/DeleteCotermRequest.php create mode 100644 app/Models/Filters/FiltersCoterm.php create mode 100644 app/Policies/CotermPolicy.php create mode 100644 app/Transformers/Admin/CotermTransformer.php create mode 100644 resources/scripts/api/admin/coterms/createCoterm.ts create mode 100644 resources/scripts/api/admin/coterms/deleteCoterm.ts create mode 100644 resources/scripts/api/admin/coterms/getAttachedNodes.ts create mode 100644 resources/scripts/api/admin/coterms/getCoterms.ts create mode 100644 resources/scripts/api/admin/coterms/resetCotermToken.ts create mode 100644 resources/scripts/api/admin/coterms/updateAttachedNodes.ts create mode 100644 resources/scripts/api/admin/coterms/updateCoterm.ts create mode 100644 resources/scripts/api/admin/coterms/useCotermsSWR.ts diff --git a/app/Http/Controllers/Admin/CotermController.php b/app/Http/Controllers/Admin/CotermController.php index ab9c17676b7..fd2fb657dba 100644 --- a/app/Http/Controllers/Admin/CotermController.php +++ b/app/Http/Controllers/Admin/CotermController.php @@ -2,34 +2,109 @@ namespace Convoy\Http\Controllers\Admin; +use Convoy\Http\Controllers\ApplicationApiController; use Convoy\Http\Requests\Admin\Coterms\StoreCotermRequest; use Convoy\Http\Requests\Admin\Coterms\UpdateAttachedNodesRequest; use Convoy\Http\Requests\Admin\Coterms\UpdateCotermRequest; use Convoy\Models\Coterm; +use Convoy\Models\Filters\FiltersCoterm; +use Convoy\Models\Filters\FiltersNode; +use Convoy\Services\Coterm\CotermTokenCreationService; +use Convoy\Transformers\Admin\CotermTransformer; +use Convoy\Transformers\Admin\NodeTransformer; use Illuminate\Http\Request; +use Spatie\QueryBuilder\AllowedFilter; +use Spatie\QueryBuilder\QueryBuilder; -class CotermController +class CotermController extends ApplicationApiController { - public function index() + public function __construct(private CotermTokenCreationService $cotermTokenCreator) { - + } + + public function index(Request $request) + { + $addressPools = QueryBuilder::for(Coterm::query()) + ->withCount(['nodes']) + ->defaultSort('-id') + ->allowedFilters( + ['name', AllowedFilter::custom( + '*', new FiltersCoterm(), + )], + ) + ->paginate(min($request->query('per_page', 50), 100))->appends( + $request->query(), + ); + + return fractal($addressPools, new CotermTransformer())->respond(); + } + + public function show(Coterm $coterm) + { + $coterm->loadCount(['nodes']); + + return fractal($coterm, new CotermTransformer())->respond(); } public function store(StoreCotermRequest $request) { + $coterm = Coterm::create($request->safe()->except('node_ids')); + if ($request->node_ids !== null) { + $coterm->nodes()->attach($request->node_ids); + } + $coterm->loadCount(['nodes']); + return fractal($coterm, new CotermTransformer())->respond(); } public function update(UpdateCotermRequest $request, Coterm $coterm) { + $coterm->update($request->validated()); + if ($request->node_ids !== null) { + $coterm->nodes()->sync($request->node_ids); + } + $coterm->loadCount(['nodes']); + + return fractal($coterm, new CotermTransformer())->respond(); + } + + public function getAttachedNodes(Request $request, Coterm $coterm) + { + $nodes = QueryBuilder::for($coterm->nodes()) + ->withCount('servers') + ->allowedFilters( + ['name', 'fqdn', AllowedFilter::exact( + 'location_id', + ), AllowedFilter::custom('*', new FiltersNode())], + ) + ->paginate(min($request->query('per_page', 50), 100))->appends( + $request->query(), + ); + + return fractal($nodes, new NodeTransformer())->respond(); } public function updateAttachedNodes(UpdateAttachedNodesRequest $request, Coterm $coterm) { + $coterm->nodes()->sync($request->node_ids); + $coterm->loadCount(['nodes']); + + return fractal($coterm, new CotermTransformer())->respond(); + } + + public function resetCotermToken(Coterm $coterm) + { + $creds = $this->cotermTokenCreator->handle(); + $coterm->update([ + 'token_id' => $creds['token_id'], + 'token' => $creds['token'], + ]); + return fractal($coterm, new CotermTransformer())->parseIncludes('token')->respond(); } public function destroy(Coterm $coterm) { + return $this->returnNoContent(); } } diff --git a/app/Http/Requests/Admin/Coterms/DeleteCotermRequest.php b/app/Http/Requests/Admin/Coterms/DeleteCotermRequest.php new file mode 100644 index 00000000000..27cc37c64e6 --- /dev/null +++ b/app/Http/Requests/Admin/Coterms/DeleteCotermRequest.php @@ -0,0 +1,14 @@ +user()->can('delete', $this->parameter('coterm', Coterm::class)); + } +} diff --git a/app/Http/Requests/Admin/Coterms/StoreCotermRequest.php b/app/Http/Requests/Admin/Coterms/StoreCotermRequest.php index 972efdb13ec..f75c42ed6ce 100644 --- a/app/Http/Requests/Admin/Coterms/StoreCotermRequest.php +++ b/app/Http/Requests/Admin/Coterms/StoreCotermRequest.php @@ -12,6 +12,10 @@ public function rules(): array { $rules = Coterm::getRules(); - return Arr::only($rules, ['name', 'is_tls_enabled', 'fqdn', 'port']); + return [ + ...Arr::only($rules, ['name', 'is_tls_enabled', 'fqdn', 'port']), + 'node_ids' => ['nullable', 'array'], + 'node_ids.*' => ['required', 'integer', 'exists:nodes,id'], + ]; } } diff --git a/app/Http/Requests/Admin/Coterms/UpdateCotermRequest.php b/app/Http/Requests/Admin/Coterms/UpdateCotermRequest.php index 7725f6a38c2..d1d0e7e95a7 100644 --- a/app/Http/Requests/Admin/Coterms/UpdateCotermRequest.php +++ b/app/Http/Requests/Admin/Coterms/UpdateCotermRequest.php @@ -13,6 +13,10 @@ public function rules(): array $coterm = $this->parameter('coterm', Coterm::class); $rules = Coterm::getRulesForUpdate($coterm); - return Arr::only($rules, ['name', 'is_tls_enabled', 'fqdn', 'port']); + return [ + ...Arr::only($rules, ['name', 'is_tls_enabled', 'fqdn', 'port']), + 'node_ids' => ['nullable', 'array'], + 'node_ids.*' => ['required', 'integer', 'exists:nodes,id'], + ]; } } diff --git a/app/Models/Filters/FiltersCoterm.php b/app/Models/Filters/FiltersCoterm.php new file mode 100644 index 00000000000..f372d49465c --- /dev/null +++ b/app/Models/Filters/FiltersCoterm.php @@ -0,0 +1,15 @@ +where('id', $value) + ->orWhereRaw('LOWER(name) LIKE ?', ["%$value%"]); + } +} \ No newline at end of file diff --git a/app/Policies/CotermPolicy.php b/app/Policies/CotermPolicy.php new file mode 100644 index 00000000000..88ce5b83fb0 --- /dev/null +++ b/app/Policies/CotermPolicy.php @@ -0,0 +1,23 @@ +loadCount(['nodes']); + + if ($coterm->nodes_count > 0) { + $this->deny('Cannot delete an instance of Coterm with nodes attached to it.'); + } + + return true; + } +} diff --git a/app/Services/Coterm/CotermJWTService.php b/app/Services/Coterm/CotermJWTService.php index 5cdeed072a9..f537b29a570 100644 --- a/app/Services/Coterm/CotermJWTService.php +++ b/app/Services/Coterm/CotermJWTService.php @@ -2,31 +2,39 @@ namespace Convoy\Services\Coterm; -use Convoy\Models\User; -use Convoy\Models\Server; use Carbon\CarbonImmutable; -use Webmozart\Assert\Assert; -use Lcobucci\JWT\Token\Plain; -use Convoy\Services\Api\JWTService; use Convoy\Enums\Server\ConsoleType; +use Convoy\Models\Coterm; +use Convoy\Models\Server; +use Convoy\Models\User; +use Convoy\Services\Api\JWTService; +use Lcobucci\JWT\Token\Plain; +use Webmozart\Assert\Assert; class CotermJWTService { - public function __construct(private JWTService $JWTService) {} + public function __construct(private JWTService $JWTService) + { + } public function handle(Server $server, User $user, ConsoleType $consoleType): Plain { - Assert::notEmpty($server->node->coterm_fqdn, 'Coterm FQDN isn\'t defined. Is Coterm enabled?'); - Assert::notEmpty($server->node->coterm_token, 'Coterm token isn\'t defined. Is Coterm enabled?'); + Assert::isInstanceOf( + $server->node->coterm, Coterm::class, + 'The server\'s node does not have a Coterm instance.', + ); $token = $this->JWTService - ->setExpiresAt(CarbonImmutable::now()->addSeconds(30)) - ->setUser($user) - ->setClaims([ - 'server_uuid' => $server->uuid, - 'console_type' => $consoleType->value, - ]) - ->handle($server->node->coterm_token, $server->node->getCotermConnectionAddress(), $user->uuid . $server->uuid); + ->setExpiresAt(CarbonImmutable::now()->addSeconds(30)) + ->setUser($user) + ->setClaims([ + 'server_uuid' => $server->uuid, + 'console_type' => $consoleType->value, + ]) + ->handle( + $server->node->coterm->token, $server->node->getCotermConnectionAddress(), + $user->uuid . $server->uuid, + ); return $token; } diff --git a/app/Transformers/Admin/CotermTransformer.php b/app/Transformers/Admin/CotermTransformer.php new file mode 100644 index 00000000000..aab776a3760 --- /dev/null +++ b/app/Transformers/Admin/CotermTransformer.php @@ -0,0 +1,31 @@ + (int)$coterm->id, + 'name' => $coterm->name, + 'is_tls_enabled' => (boolean)$coterm->is_tls_enabled, + 'fqdn' => $coterm->fqdn, + 'port' => (int)$coterm->port, + 'nodes_count' => (int)$coterm->nodes_count, + ]; + } + + public function includeToken(Coterm $coterm): array + { + return [ + 'token_id' => $coterm->token_id, + 'token' => $coterm->token, + ]; + } +} diff --git a/resources/scripts/api/admin/coterms/createCoterm.ts b/resources/scripts/api/admin/coterms/createCoterm.ts new file mode 100644 index 00000000000..be91c04c954 --- /dev/null +++ b/resources/scripts/api/admin/coterms/createCoterm.ts @@ -0,0 +1,32 @@ +import { rawDataToCoterm } from '@/api/admin/coterms/getCoterms' +import http from '@/api/http' + +interface CreateCotermParameters { + name: string + isTlsEnabled: boolean + fqdn: string + port: number + nodeIds?: number[] | null +} + +const createCoterm = async ({ + name, + isTlsEnabled, + fqdn, + port, + nodeIds, +}: CreateCotermParameters) => { + const { + data: { data }, + } = await http.post('/api/admin/coterms', { + name, + is_tls_enabled: isTlsEnabled, + fqdn, + port, + node_ids: nodeIds, + }) + + return rawDataToCoterm(data) +} + +export default createCoterm \ No newline at end of file diff --git a/resources/scripts/api/admin/coterms/deleteCoterm.ts b/resources/scripts/api/admin/coterms/deleteCoterm.ts new file mode 100644 index 00000000000..9f3bb32a7de --- /dev/null +++ b/resources/scripts/api/admin/coterms/deleteCoterm.ts @@ -0,0 +1,5 @@ +import http from '@/api/http' + +const deleteCoterm = (id: string) => http.delete(`/admin/coterms/${id}`) + +export default deleteCoterm \ No newline at end of file diff --git a/resources/scripts/api/admin/coterms/getAttachedNodes.ts b/resources/scripts/api/admin/coterms/getAttachedNodes.ts new file mode 100644 index 00000000000..d4649682e20 --- /dev/null +++ b/resources/scripts/api/admin/coterms/getAttachedNodes.ts @@ -0,0 +1,33 @@ +import { NodeResponse, rawDataToNode } from '@/api/admin/nodes/getNodes' +import http, { getPaginationSet } from '@/api/http' + + +export interface QueryParams { + query?: string | null + fqdn?: string | null + locationId?: number | null + page?: number | null + perPage?: number | null +} + +const getAttachedNodes = async ( + id: number, + { query, fqdn, locationId, page, perPage = 50 }: QueryParams +): Promise => { + const { data } = await http.get(`/api/admin/coterms/${id}/nodes`, { + params: { + query, + fqdn, + location_id: locationId, + page, + per_page: perPage, + }, + }) + + return { + items: data.data.map(rawDataToNode), + pagination: getPaginationSet(data.meta.pagination), + } +} + +export default getAttachedNodes \ No newline at end of file diff --git a/resources/scripts/api/admin/coterms/getCoterms.ts b/resources/scripts/api/admin/coterms/getCoterms.ts new file mode 100644 index 00000000000..1d46e38b6ff --- /dev/null +++ b/resources/scripts/api/admin/coterms/getCoterms.ts @@ -0,0 +1,52 @@ +import http, { PaginatedResult, getPaginationSet } from '@/api/http' + +export interface Coterm { + id: number + name: string + isTlsEnabled: boolean + fqdn: string + port: number + nodesCount: number + tokenId?: string + token?: string +} + +export type CotermResponse = PaginatedResult + +export interface QueryParams { + query?: string + page?: number + perPage?: number +} + +const getCoterms = async ({ + query, + page, + perPage = 50, +}: QueryParams): Promise => { + const { data } = await http.get('/api/admin/coterms', { + params: { + 'filter[*]': query, + page, + 'per_page': perPage, + }, + }) + + return { + items: data.data.map(rawDataToCoterm), + pagination: getPaginationSet(data.meta.pagination), + } +} + +export const rawDataToCoterm = (data: any): Coterm => ({ + id: data.id, + name: data.name, + isTlsEnabled: Boolean(data.is_tls_enabled), + fqdn: data.fqdn, + port: data.port, + nodesCount: data.nodes_count, + tokenId: data.token_id, + token: data.token, +}) + +export default getCoterms \ No newline at end of file diff --git a/resources/scripts/api/admin/coterms/resetCotermToken.ts b/resources/scripts/api/admin/coterms/resetCotermToken.ts new file mode 100644 index 00000000000..b1acbec209f --- /dev/null +++ b/resources/scripts/api/admin/coterms/resetCotermToken.ts @@ -0,0 +1,12 @@ +import { rawDataToCoterm } from '@/api/admin/coterms/getCoterms' +import http from '@/api/http' + +const resetCotermToken = async (id: number) => { + const { + data: { data }, + } = await http.post(`/api/admin/coterm/${id}/settings/reset-coterm-token`) + + return rawDataToCoterm(data) +} + +export default resetCotermToken \ No newline at end of file diff --git a/resources/scripts/api/admin/coterms/updateAttachedNodes.ts b/resources/scripts/api/admin/coterms/updateAttachedNodes.ts new file mode 100644 index 00000000000..a0693c2553b --- /dev/null +++ b/resources/scripts/api/admin/coterms/updateAttachedNodes.ts @@ -0,0 +1,15 @@ +import { rawDataToCoterm } from '@/api/admin/coterms/getCoterms' +import http from '@/api/http' + + +const updateAttachedNodes = async (id: number, nodeIds: number[]) => { + const { + data: { data }, + } = await http.put(`/api/admin/coterms/${id}/nodes`, { + node_ids: nodeIds, + }) + + return rawDataToCoterm(data) +} + +export default updateAttachedNodes \ No newline at end of file diff --git a/resources/scripts/api/admin/coterms/updateCoterm.ts b/resources/scripts/api/admin/coterms/updateCoterm.ts new file mode 100644 index 00000000000..6f256e2e2a1 --- /dev/null +++ b/resources/scripts/api/admin/coterms/updateCoterm.ts @@ -0,0 +1,29 @@ +import { rawDataToCoterm } from '@/api/admin/coterms/getCoterms' +import http from '@/api/http' + +interface UpdateCotermParameters { + name: string + isTlsEnabled: boolean + fqdn: string + port: number + nodeIds?: number[] | null +} + +const updateCoterm = async ( + id: number, + { name, isTlsEnabled, fqdn, port, nodeIds }: UpdateCotermParameters +) => { + const { + data: { data }, + } = await http.put(`/admin/coterms/${id}`, { + name, + is_tls_enabled: isTlsEnabled, + fqdn, + port, + node_ids: nodeIds, + }) + + return rawDataToCoterm(data) +} + +export default updateCoterm \ No newline at end of file diff --git a/resources/scripts/api/admin/coterms/useCotermsSWR.ts b/resources/scripts/api/admin/coterms/useCotermsSWR.ts new file mode 100644 index 00000000000..0ecdb96317e --- /dev/null +++ b/resources/scripts/api/admin/coterms/useCotermsSWR.ts @@ -0,0 +1,12 @@ +import useSWR from 'swr' + + + +import getCoterms, { CotermResponse, QueryParams } from "@/api/admin/coterms/getCoterms"; + + +const useCotermsSWR = ({ page, query, ...params }: QueryParams) => { + return useSWR(['admin.coterms', page, query], () => getCoterms({ page, query, ...params })) +} + +export default useCotermsSWR \ No newline at end of file diff --git a/routes/api-admin.php b/routes/api-admin.php index 6bc927855b1..80c0ef42818 100644 --- a/routes/api-admin.php +++ b/routes/api-admin.php @@ -4,66 +4,209 @@ use Convoy\Http\Middleware\Admin\Server\ValidateServerStatusMiddleware; use Illuminate\Support\Facades\Route; -Route::resource('locations', Admin\LocationController::class) - ->only(['index', 'store', 'update', 'destroy']); - -Route::resource('nodes', Admin\Nodes\NodeController::class) - ->only(['index', 'show', 'store', 'update', 'destroy']); - -Route::prefix('/nodes/{node}')->group(function () { - Route::resource('/isos', Admin\Nodes\IsoController::class) - ->only(['index', 'store', 'update', 'destroy']); - - Route::resource('template-groups', Admin\Nodes\TemplateGroupController::class) - ->only(['index', 'store', 'update', 'destroy']); - Route::post('/template-groups/reorder', [Admin\Nodes\TemplateGroupController::class, 'updateOrder']); - - Route::resource('template-groups.templates', Admin\Nodes\TemplateController::class) - ->only(['index', 'store', 'update', 'destroy']); - Route::post( - '/template-groups/{template_group}/templates/reorder', - [Admin\Nodes\TemplateController::class, 'updateOrder'], - ); - - Route::resource('addresses', Admin\Nodes\AddressController::class) - ->only(['index', 'store', 'update', 'destroy']); - - Route::get('/tools/query-remote-file', [Admin\Nodes\IsoController::class, 'queryLink']); - - Route::prefix('/settings')->group(function () { - Route::patch('/coterm', [Admin\Nodes\NodeController::class, 'updateCoterm']); - Route::post('/reset-coterm-token', [Admin\Nodes\NodeController::class, 'resetCotermToken']); +/* +|-------------------------------------------------------------------------- +| Location Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/locations +| +*/ +Route::prefix('/locations')->group(function () { + Route::get('/', [Admin\LocationController::class, 'index']); + Route::post('/', [Admin\LocationController::class, 'store']); + + Route::prefix('/{location}')->group(function () { + Route::put('/', [Admin\LocationController::class, 'update']); + Route::delete('/', [Admin\LocationController::class, 'destroy']); }); }); -Route::get('/servers', [Admin\ServerController::class, 'index']); -Route::post('/servers', [Admin\ServerController::class, 'store']); -Route::prefix('/servers/{server}')->middleware(ValidateServerStatusMiddleware::class)->group(function () { - Route::get('/', [Admin\ServerController::class, 'show'])->withoutMiddleware(ValidateServerStatusMiddleware::class); - Route::patch('/', [Admin\ServerController::class, 'update'])->withoutMiddleware( - ValidateServerStatusMiddleware::class, - ); - Route::delete('/', [Admin\ServerController::class, 'destroy']); - - Route::prefix('/settings')->group(function () { - Route::patch('/build', [Admin\ServerController::class, 'updateBuild']); - - Route::post('/suspend', [Admin\ServerController::class, 'suspend']); - Route::post('/unsuspend', [Admin\ServerController::class, 'unsuspend']); +/* +|-------------------------------------------------------------------------- +| Node Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/nodes +| +*/ +Route::prefix('/nodes')->group(function () { + Route::get('/', [Admin\Nodes\NodeController::class, 'index']); + Route::post('/', [Admin\Nodes\NodeController::class, 'store']); + + Route::prefix('/{node}')->group(function () { + Route::put('/', [Admin\Nodes\NodeController::class, 'update']); + Route::delete('/', [Admin\Nodes\NodeController::class, 'destroy']); + + /* + |-------------------------------------------------------------------------- + | Node ISOs Controller Routes + |-------------------------------------------------------------------------- + | + | Endpoint: /api/admin/nodes/{node}/isos + | + */ + Route::resource('/isos', Admin\Nodes\IsoController::class) + ->only(['index', 'store', 'update', 'destroy']); + + /* + |-------------------------------------------------------------------------- + | Node Templates Controller Routes + |-------------------------------------------------------------------------- + | + | Endpoint: /api/admin/nodes/{node}/template-groups + | + */ + Route::prefix('/template-groups')->group(function () { + Route::get('/', [Admin\Nodes\TemplateGroupController::class, 'index']); + Route::post('/', [Admin\Nodes\TemplateGroupController::class, 'store']); + Route::post( + '/reorder', [Admin\Nodes\TemplateGroupController::class, 'updateOrder'], + ); + + Route::prefix('/{template_group}')->group(function () { + Route::put('/', [Admin\Nodes\TemplateGroupController::class, 'update']); + Route::delete('/', [Admin\Nodes\TemplateGroupController::class, 'destroy']); + Route::prefix('/templates')->group(function () { + Route::get('/', [Admin\Nodes\TemplateController::class, 'index']); + Route::post('/', [Admin\Nodes\TemplateController::class, 'store']); + Route::post( + '/reorder', + [Admin\Nodes\TemplateController::class, 'updateOrder'], + ); + + Route::get('/{template}', [Admin\Nodes\TemplateController::class, 'show']); + Route::delete('/{template}', [Admin\Nodes\TemplateController::class, 'destroy'], + ); + }); + + Route::resource('templates', Admin\Nodes\TemplateController::class) + ->only(['index', 'store', 'update', 'destroy']); + }); + }); + + /* + |-------------------------------------------------------------------------- + | Node Addresses Controller Routes + |-------------------------------------------------------------------------- + | + | Endpoint: /api/admin/nodes/{node}/addresses + | + */ + Route::resource('addresses', Admin\Nodes\AddressController::class) + ->only(['index', 'store', 'update', 'destroy']); + + /* + |-------------------------------------------------------------------------- + | Node Helpers Routes + |-------------------------------------------------------------------------- + */ + Route::get('/tools/query-remote-file', [Admin\Nodes\IsoController::class, 'queryLink']); }); }); -Route::resource('address-pools', Admin\AddressPools\AddressPoolController::class) - ->only(['index', 'show', 'store', 'update', 'destroy']); -Route::prefix('/address-pools/{address_pool}')->group(function () { - Route::get('/attached-nodes', [Admin\AddressPools\AddressPoolController::class, 'getAttachedNodes']); +/* +|-------------------------------------------------------------------------- +| Server Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/servers +| +*/ +Route::prefix('/servers')->group(function () { + Route::get('/', [Admin\ServerController::class, 'index']); + Route::post('/', [Admin\ServerController::class, 'store']); + + Route::group(['prefix' => '/{server}', 'middleware' => ValidateServerStatusMiddleware::class], + function () { + Route::get('/', [Admin\ServerController::class, 'show'])->withoutMiddleware( + ValidateServerStatusMiddleware::class, + ); + Route::patch('/', [Admin\ServerController::class, 'update'])->withoutMiddleware( + ValidateServerStatusMiddleware::class, + ); + Route::delete('/', [Admin\ServerController::class, 'destroy']); + + Route::prefix('/settings')->group(function () { + Route::patch('/build', [Admin\ServerController::class, 'updateBuild']); + + Route::post('/suspend', [Admin\ServerController::class, 'suspend']); + Route::post('/unsuspend', [Admin\ServerController::class, 'unsuspend']); + }); + }); }); -Route::resource('address-pools.addresses', Admin\AddressPools\AddressController::class) - ->only(['index', 'store', 'update', 'destroy']); +/* +|-------------------------------------------------------------------------- +| Address Pool Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/address-pools +| +*/ +Route::prefix('/address-pools')->group(function () { + Route::get('/', [Admin\AddressPools\AddressPoolController::class, 'index']); + Route::post('/', [Admin\AddressPools\AddressPoolController::class, 'store']); + + Route::prefix('/{address_pool}')->group(function () { + Route::get('/', [Admin\AddressPools\AddressPoolController::class, 'show']); + Route::put('/', [Admin\AddressPools\AddressPoolController::class, 'update']); + Route::delete('/', [Admin\AddressPools\AddressPoolController::class, 'destroy']); + Route::get( + '/attached-nodes', + [Admin\AddressPools\AddressPoolController::class, 'getAttachedNodes'], + ); + + Route::prefix('/addresses')->group(function () { + Route::get('/', [Admin\AddressPools\AddressController::class, 'index']); + Route::post('/', [Admin\AddressPools\AddressController::class, 'store']); + Route::put('/{address}', [Admin\AddressPools\AddressController::class, 'update']); + Route::delete('/{address}', [Admin\AddressPools\AddressController::class, 'destroy']); + }); + }); +}); +/* +|-------------------------------------------------------------------------- +| User Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/users +| +*/ Route::resource('users', Admin\UserController::class) - ->only(['index', 'show', 'store', 'update', 'destroy']); + ->only(['index', 'show', 'store', 'update', 'destroy']); + +/* +|-------------------------------------------------------------------------- +| Coterm Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/coterms +| +*/ + +Route::prefix('/coterms')->group(function () { + Route::get('/', [Admin\CotermController::class, 'index']); + Route::post('/', [Admin\CotermController::class, 'store']); + + Route::prefix('/{coterm}')->group(function () { + Route::get('/', [Admin\CotermController::class, 'show']); + Route::put('/', [Admin\CotermController::class, 'update']); + Route::post('/reset-coterm-token', [Admin\CotermController::class, 'resetCotermToken']); + Route::delete('/', [Admin\CotermController::class, 'destroy']); + Route::get('/nodes', [Admin\CotermController::class, 'getAttachedNodes']); + Route::put('/nodes', [Admin\CotermController::class, 'updateAttachedNodes']); + }); +}); +/* +|-------------------------------------------------------------------------- +| API Token Controller Routes +|-------------------------------------------------------------------------- +| +| Endpoint: /api/admin/tokens +| +*/ Route::resource('tokens', Admin\TokenController::class) - ->only(['index', 'store', 'destroy']); + ->only(['index', 'store', 'destroy']);