API Decoupling, Part 1

The first part of improving the API is removing all admin functions from
the front end so those no admin methods will be available client side.

The urls in the FipamoAdmin js file have been changed to post directly to the
system and they are handled from there. To account for this change
controller routes for every standard method have been created for better
organization and readability.

The FipamoAdmin js file will be integrated with the rest of the front
end code and will not be seperate library
This commit is contained in:
ro 2024-07-13 14:23:01 -06:00
parent 458b076f73
commit 3d17771f76
No known key found for this signature in database
GPG key ID: 29B551CDBD4D3B50
9 changed files with 224 additions and 70 deletions

View file

@ -26,6 +26,10 @@ class DashController extends Controller
$this->sort = $sortingService;
}
//---
// GET
//---
public function init($second, $third, $fourth)
{
switch ($second) {
@ -52,18 +56,6 @@ class DashController extends Controller
}
}
public function login()
{
if ($this->member::status()) {
return redirect('dashboard');
} else {
return view('back.login', [
"status" => $this->member::status(),
"title" => "Hi!"
]);
}
}
public function start()
{
$result = [];
@ -119,6 +111,30 @@ class DashController extends Controller
return view('back.settings', $this->sort->settings());
}
//---
// POST
//---
//---
// PUT
//---
//---
// AUTH
//---
public function login()
{
if ($this->member::status()) {
return redirect('dashboard');
} else {
return view('back.login', [
"status" => $this->member::status(),
"title" => "Hi!"
]);
}
}
public function logout()
{
session()->flush();

View file

@ -0,0 +1,29 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Interfaces\PageRepositoryInterface;
class RouteDeleteController extends Controller
{
protected $page;
public function __construct(
PageRepositoryInterface $pageRepo
) {
$this->page = $pageRepo;
}
public function handleRequest(Request $request)
{
$path = explode('/', $request->path());
switch ($path[0]) {
case 'page':
$body = json_decode($request->getContent());
$result = $this->page->delete($body);
return response()->json($result)->header('Content-Type', 'application/json');
break;
}
}
}

View file

@ -3,9 +3,8 @@
namespace App\Http\Controllers;
use App\Interfaces\MemberRepositoryInterface;
use Illuminate\Http\Request;
class RouteController extends Controller
class RouteGetController extends Controller
{
protected $dash;
protected $gate;
@ -27,7 +26,7 @@ class RouteController extends Controller
$this->member = $memberRepo;
}
public function get($first = null, $second = null, $third = null, $fourth = null)
public function handleRequest($first = null, $second = null, $third = null, $fourth = null)
{
if (isset($first) && !is_numeric($first)) {
switch ($first) {
@ -58,13 +57,4 @@ class RouteController extends Controller
return $this->front->index($first, $second, $third);
}
}
public function post(Request $request)
{
switch ($request->path()) {
case 'login':
return $this->gate->enter($request);
break;
}
}
}

View file

@ -0,0 +1,60 @@
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
use App\Mail\SystemEmail;
use App\Interfaces\PageRepositoryInterface;
class RoutePostController extends Controller
{
protected $page;
public function __construct(
PageRepositoryInterface $pageRepo,
AuthController $authController,
) {
$this->page = $pageRepo;
$this->gate = $authController;
}
public function handleRequest(Request $request)
{
$path = explode('/', $request->path());
switch ($path[0]) {
case 'login':
return $this->gate->enter($request);
break;
case 'page':
$body = json_decode($request->getContent());
$result = $this->page->create($body);
return response()->json($result)->header('Content-Type', 'application/json');
break;
case 'settings':
if ($path[1] == 'mailer') {
return $this->sendNotify($request);
}
break;
}
}
private function sendNotify($request)
{
$result = [];
try {
Mail::to(env('ADMIN_EMAIL'))->send(new SystemEmail($request->content));
$result = [
'type' => 'mail_good',
'message' => 'Mail Sent',
];
} catch (TransportException $e) {
$result = [
'type' => 'mail_not_good',
'message' => 'Mail Not Sent. It\'s cool. Just check mail settings in the .env',
];
}
return response()->json($result)->header('Content-Type', 'application/json');
}
}

View file

@ -0,0 +1,71 @@
<?php
namespace App\Http\Controllers;
use App\Interfaces\PageRepositoryInterface;
use App\Services\Assets\AssetService;
use App\Services\Assets\RenderService;
use App\Interfaces\MemberRepositoryInterface;
use App\Services\Data\SettingsService;
use Illuminate\Http\Request;
class RoutePutController extends Controller
{
protected $page;
protected $assets;
protected $render;
protected $settings;
protected $member;
public function __construct(
PageRepositoryInterface $pageRepo,
AssetService $assetService,
RenderService $renderService,
SettingsService $settingsService,
MemberRepositoryInterface $memberRepo,
) {
$this->page = $pageRepo;
$this->assets = $assetService;
$this->render = $renderService;
$this->settings = $settingsService;
$this->member = $memberRepo;
}
public function handleRequest(Request $request)
{
$path = explode('/', $request->path());
switch ($path[0]) {
case 'page':
$body = json_decode($request->getContent());
$result = $this->page->update($body);
return response()->json($result)->header('Content-Type', 'application/json');
break;
case 'settings':
return $this->settingsTasks($request, $path[1]);
break;
}
}
private function settingsTasks($request, $task)
{
$result = [];
switch ($task) {
case 'publish':
$this->assets->moveToTheme(true);
$result = $this->render->publishAll();
break;
case 'sync':
$body = json_decode($request->getContent());
//update member if needed
$this->member->update($body->member);
//sync settings
$result = $this->settings->sync($body);
break;
case 'nav-sync':
$body = json_decode($request->getContent());
$result = $this->settings->navSync($body);
break;
}
return response()->json($result)->header('Content-Type', 'application/json');
}
}

View file

@ -6,20 +6,21 @@ export const REQUEST_TYPE_DELETE = 'DELETE';
//** POST CONTENT TYPES **//
export const CONTENT_TYPE_JSON = 'json';
export const CONTENT_TYPE_FORM = 'x-www-form-urlencoded';
//** API URLS **//
export const API_STATUS = '/api/v1/status';
export const API_GET_SETTINGS = '/api/v1/settings/site';
export const API_GET_MEMBER_INFO = '/api/v1/settings/member';
export const API_NEW_PAGE = '/api/v1/page/create';
export const API_EDIT_PAGE = '/api/v1/page/write';
export const API_DELETE_PAGE = '/api/v1/page/delete';
export const API_SETTINGS_SYNC = '/api/v1/settings/sync';
export const API_PUBLISH_PAGES = '/api/v1/settings/publish';
export const API_NAV_SYNC = '/api/v1/settings/nav-sync';
export const API_REINDEX_PAGES = '/api/v1/settings/reindex';
export const API_SEND_MAIL = '/api/v1/mailer';
export const API_LOGIN = '/api/v1/login';
//** API TASKS **//
//** ACTIONS URLS **//
export const API_NEW_PAGE = '/page/create';
export const API_EDIT_PAGE = '/page/write';
export const API_DELETE_PAGE = '/page/delete';
export const API_GET_SETTINGS = '/settings/site';
export const API_SETTINGS_SYNC = '/settings/sync';
export const API_PUBLISH_PAGES = '/settings/publish';
export const API_NAV_SYNC = '/settings/nav-sync';
export const API_GET_MEMBER_INFO = '/settings/member';
export const API_REINDEX_PAGES = '/settings/reindex';
export const API_SEND_MAIL = '/settings/mailer';
export const API_LOGIN = '/login';
//** ACTIONS TASKS **//
export const AUTH_STATUS = 'getAuthStatus';
export const TASK_SETTINGS_WRITE = 'writeSettings';
export const TASK_PUBLISH_SITE = 'publishSite';
@ -32,7 +33,7 @@ export const TASK_SYNC_SETTNIGS = 'syncSite';
export const TASK_SYNC_NAV = 'syncNav';
export const TASK_GET_SETTINGS = 'getSiteSettings';
export const TASK_GET_MEMBER_INFO = 'getMemberInfo';
//** API STATUS **//
//** ACTIONS STATUS **//
export const API_ACCESS_GOOD = 'apiUseAuthorized';
export const API_ACCESS_BAD = 'apiUseNotAuthorized';
@ -52,17 +53,6 @@ class FipamoAdminAPI {
this.progressBar = progressBar;
this.status = false;
if (baseURL) this.baseURL = baseURL;
//asks server if a session is active
this._request(this.baseURL ? this.baseURL + API_STATUS : API_STATUS).then(
response => {
if (response.type === API_ACCESS_GOOD) {
this.token = response.token;
} else {
//don't set token
//console.log("NO TOKEN");
}
}
);
}
/**
* Promise method for authenticating and starting a session\
@ -375,6 +365,10 @@ class FipamoAdminAPI {
self.handleLoadProgress(e, self.progressBar)
);
request.open(requestType, requestURL, true);
request.setRequestHeader(
'X-CSRF-TOKEN',
document.querySelector('meta[name="csrf-token"]').content
);
request.onload = () => {
if (request.status == 200) {
let response = JSON.parse(request['response']);
@ -389,16 +383,6 @@ class FipamoAdminAPI {
requestType == REQUEST_TYPE_POST ||
requestType == REQUEST_TYPE_DELETE
) {
if (
eventType === TASK_SETTINGS_WRITE ||
eventType === TASK_PAGE_EDIT ||
eventType === TASK_PAGE_CREATE ||
eventType === TASK_PAGE_DELETE ||
eventType === TASK_PUBLISH_SITE ||
eventType === TASK_REINDEX_PAGE
)
request.setRequestHeader('fipamo-access-token', self.token);
switch (contentType) {
case CONTENT_TYPE_JSON:
request.setRequestHeader(
@ -412,12 +396,6 @@ class FipamoAdminAPI {
break;
}
} else {
if (
eventType === TASK_GET_SETTINGS ||
eventType === TASK_GET_MEMBER_INFO
) {
request.setRequestHeader('fipamo-access-token', self.token);
}
request.send();
}
});

View file

@ -3,7 +3,6 @@
<img alt="fipamo logo" id="the-logo" class="logo-medium" src="/assets/images/global/fipamo-logo-secondary.svg"/>
</div>
<form action="/login" method="post" enctype="multipart/form-data">
@csrf
<label class="inline">handle</label><input class="input-light" type="text" name="handle" class="inline" placeholder="Handle" required/>
<label class="inline">password</label><input class="input-light" type="password" name="password" class="inline" placeholder="Password" required/>
@if($errors->any())
@ -11,5 +10,6 @@
@else
<input type="submit" value="Knock Knock" name="submit_button">
@endif
@csrf
</form>
</section>

View file

@ -5,6 +5,7 @@
<meta charset="UTF-8">
<meta name="theme-color" content="#d66365" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="csrf-token" content="{{ csrf_token() }}">
<title>
@yield('title')
</title>

View file

@ -1,7 +1,11 @@
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\RouteController;
use App\Http\Controllers\RouteGetController;
use App\Http\Controllers\RoutePostController;
use App\Http\Controllers\RoutePutController;
use App\Http\Controllers\RouteDeleteController;
use App\Http\Middleware\VerifyCsrfToken;
/*
|--------------------------------------------------------------------------
@ -15,5 +19,10 @@ use App\Http\Controllers\RouteController;
*/
//routing needs a bit more nuance, so requests are sent to a controller to sort traffic
Route::get("/{first?}/{second?}/{third?}/{four?}", [RouteController::class, 'get']);
Route::post("/{first?}/{second?}/{third?}", [RouteController::class, 'post']);
Route::get("/{first?}/{second?}/{third?}/{four?}", [RouteGetController::class, 'handleRequest']);
Route::post("/{first?}/{second?}/{third?}", [RoutePostController::class, 'handleRequest'])
->middleware(VerifyCsrfToken::class);
Route::put("/{first?}/{second?}/{third?}", [RoutePutController::class, 'handleRequest'])
->middleware(VerifyCsrfToken::class);
Route::delete("/{first?}/{second?}/{third?}", [RouteDeleteController::class, 'handleRequest'])
->middleware(VerifyCsrfToken::class);