Laravel custom routing files not working with new routes - api

I am building a CMS SPA with Laravel 8 and I wanted to seperate the routes by category (admin, api, auth) in seperate route files with the use of custom slugs (/admin/settings , /auth/login) but I cant seem to get it to work so what am I doing wrong? It all did work when it was the API file but when dividing it over seprate files it wont work.
routes/admin.php
Route::group(['middleware' => 'auth:sanctum'], function () {
Route::get('settings', [SettingsController::class, 'index'])->name('settings');
});
routeServiceProvider.php
public function boot()
{
$this->configureRateLimiting();
$this->routes(function () {
Route::middleware('web')
->group(base_path('routes/web.php'));
Route::prefix('api')
->middleware('api')
->group(base_path('routes/api.php'));
Route::prefix('auth')
->middleware('auth')
->namespace($this->namespace)
->group(base_path('routes/auth.php'));
Route::prefix('admin')
->middleware('admin')
->namespace($this->namespace)
->group(base_path('routes/admin.php'));
});
}
routes/web.php
Route::get('/{any}', function () {
return view('layouts.vue');
})->where('any', '^(?!api|admin|auth).*$');
kernel.php
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'admin' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
'auth' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
'throttle:60,1',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
]
];
Component.vue
const response = axios.get('/admin/settings')// not giving the data back
const response = axios.get('/api/settings')// in the past did work

While someone else gives a better answer about your approach. As simple way to achieve /admin/settings in the web/routes.php file would be to use prefixes
Route::prefix('admin')->middleware(['auth:sanctum'])->group(function() {
Route::get('settings', [SettingsController::class, 'index']);
});
With this approach you won't need to make changes to your app\Http\kernel.php or routeServiceProvider.php files.
Also if you want to abstract it away, you could put your it in a new file and just require require_once __DIR__ . '/admin.php'; as you would a regular php file.

Related

Commitlint - Allow '/' in scope-enum

In my Angular project, I want to extend #commitlint/config-conventional with some pre-defined scopes.
The Angular project has a library for UI components (generated via ng generate library) and a default app which consumes the UI library.
In commitlint.config.js I've added the following lines:
module.exports = {
extends: ['#commitlint/config-conventional'],
rules: {
'scope-enum': [
2,
'always',
[
'ui-components',
'ui-components/badge',
'ui-components/button',
'ui-components/tooltip',
'core',
'account',
'plugins',
'settings',
'projects',
'shared',
'styles'
]
]
}
};
However, when I try to commit something with the scope: 'ui-components/tooltip':
fix(ui-components/tooltip): fix border
I get a commitlint error, saying that:
⧗ input: fix(ui-components/tooltip): fix border
✖ scope must be one of [ui-components, ui-components/badge, ui/button, ui-components/tooltip, core, account, plugins, settings, projects, shared, styles] [scope-enum]
✖ found 1 problems, 0 warnings
Unfortunately slashes aren't allowed in scopes.
To get around this I replace / with two dashes (--).
I wrote a script to grab subfolders and return an array:
https://gist.github.com/trevor-coleman/51f1730044e14081faaff098618aba36
[
'ui-components',
'ui-components--badge',
'ui-components--button',
'ui-components--tooltip',
...
]
According to source code, Commitlint use / for multiple scopes.
It means, you can commit like fix(core/account): fix border but you can't commit fix(ui-components/tooltip): fix border because you need to add tooltip in to your scopes first.
Here is source code: https://github.com/conventional-changelog/commitlint/blob/master/%40commitlint/rules/src/scope-enum.ts
Also, it is mentioned in here: https://github.com/conventional-changelog/commitlint/blob/master/docs/concepts-commit-conventions.md#multiple-scopes
You can write your own custom plugin to check scopes, I had the same issue, so I wrote one to solve this problem, see example commitlint.config.js below:
module.exports = {
extends: ["#commitlint/config-conventional"],
rules: {
"enhanced-scope-enum": [
2,
"always",
[
"ui-components",
"ui-components/badge",
"ui-components/button",
"ui-components/tooltip",
"core",
"account",
"plugins",
"settings",
"projects",
"shared",
"styles",
],
],
},
plugins: [
{
rules: {
"enhanced-scope-enum": (parsed, when = "always", value = []) => {
if (!parsed.scope) {
return [true, ""];
}
// only use comma sign as seperator
const scopeSegments = parsed.scope.split(",");
const check = (value, enums) => {
if (value === undefined) {
return false;
}
if (!Array.isArray(enums)) {
return false;
}
return enums.indexOf(value) > -1;
};
const negated = when === "never";
const result =
value.length === 0 ||
scopeSegments.every((scope) => check(scope, value));
return [
negated ? !result : result,
`scope must ${negated ? `not` : null} be one of [${value.join(
", "
)}]`,
];
},
},
},
],
}

About the implementation of Remember Me using AuthenticationPlugin's Cookie Authenticator

I use CakePHP's AuthenticationPlugin. I was trying to implement RememberMe functionality into this.
I found the following article when I was reading the Cakephp documentation.
Cookie Authenticator aka “Remember Me”
However, the documentation here is difficult for me to understand. I have no idea what to do with it.
I've successfully implemented EncryptedCookieMiddleware. I have no idea what to do after that.
I don't know how to use rememberMeField, how to use fields and how to use cookies.
$this->Authentication->rememberMeField
$this->Authentication->fields
I tried to see if I could use it like these, but it was still no good.
Please let me know how to use these.
Also, do you know of any RememberMe tutorials?
How do I implement it?
Sorry. Please help me...
// in config/app.php
'Security' => [
.....
'cookieKey' => env('SECURITY_COOKIE_KEY', 'AnyString'), // <- add
],
// in src/Application.php
use Cake\Http\Middleware\EncryptedCookieMiddleware; // <- add
// in middleware()
public function middleware(MiddlewareQueue $middlewareQueue): MiddlewareQueue
{
$cookies = new EncryptedCookieMiddleware( // <- add
['mail', 'password'],
Configure::read('Security.cookieKey')
);
$middlewareQueue
// . . .
->add($cookies) // <-add
->add(new AuthenticationMiddleware($this));
So far I've been able to implement it myself. I'm confident.
The problem is after this. We have no idea what to do with it...
A Remember me checkbox was implemented in the template Form.
$this->request->getData('rememberMe'); to get it.
If this is 1, the checkbox was pressed.
// in src/Controller/UsersController
public function login()
{
$this->request->allowMethod(['get', 'post']);
if ($this->request->is('post')) {
$result = $this->Authentication->getResult();
// If the user is logged in, whether POST or GET, we will redirect
$requestGetData = $this->request->getData('rememberMe');
if ($requestGetData['rememberMe'] == 1){
$this->Authentication->cookie['name'] = $requestGetData['mail'];
$this->Authentication->cookie['name'] = $requestGetData['password']
}
if ($result->isValid()) {
$redirect = $this->request->getQuery('redirect', [
'controller' => 'Stores',
'action' => 'index',
]);
return $this->redirect($redirect);
}
// If the user fails to authenticate after submitting, an error is displayed.
if (!$result->isValid()) {
$this->Flash->error(__('Your email address or password is incorrect.'));
}
}
$title = $this->config('Users.title.login');
$message = $this->config('Users.message.login');
$this->set(compact('login_now', 'title', 'message'));
}
I know that's not true. But I tried to implement something like this just in case.
Please help me!
Changed around the login.
public function login()
{
$this->request->allowMethod(['get', 'post']);
if ($this->request->is('post')) {
$result = $this->Authentication->getResult();
$requestData = $this->request->getData();
if ($result->isValid()) {
$redirect = $this->request->getQuery('redirect', [
'controller' => 'Stores',
'action' => 'index',
]);
$this->Authentication->getAuthenticationService()->loadAuthenticator( 'Authentication.Cookie', [
'fields' => ['mail', 'password']
]
);
return $this->redirect($redirect);
}
if ($this->request->is('post') && !$result->isValid()) {
$this->Flash->error(__('Your email address or password is incorrect.'));
}
}
$title = $this->config('Users.title.login');
$message = $this->config('Users.message.login');
$this->set(compact('title', 'message'));
}
You're not supposed to load authenticators in your controllers, authentication happens at middleware level, before any of your controllers are being invoked.
The cookie authenticator is ment to be loaded and configured just like any other authenticator, that is where you create the authentication service, usually in Application::getAuthenticationService() in src/Application.php.
By default the field in the form must be remember_me, not rememberMe, that is unless you would configure the cookie authenticator's rememberMeField option otherwise.
Furthermore the default cookie name of the cookie authenticator is CookieAuth, so if you wanted to encrypt it, you'd have to use that name in the EncryptedCookieMiddleware config accordingly.
tl;dr
Remove all cookie related code from your controller, and load the authenticator in your Application::getAuthenticationService() method:
use Authentication\Identifier\IdentifierInterface;
// ...
public function getAuthenticationService(ServerRequestInterface $request): AuthenticationServiceInterface
{
$service = new AuthenticationService();
// ...
// The cookie authenticator should be loaded _after_ the session authenticator,
// and _before_ other authenticators like the form authenticator
$service->loadAuthenticator('Authentication.Cookie', [
// 'rememberMeField' => 'custom_form_field_name', // if you want to change the default
'fields' => [
IdentifierInterface::CREDENTIAL_USERNAME => 'mail',
IdentifierInterface::CREDENTIAL_PASSWORD => 'password',
],
]);
// ...
return $service;
}
set the authentication cookie name in the EncryptedCookieMiddleware config:
$cookies = new EncryptedCookieMiddleware(
['CookieAuth'],
Configure::read('Security.cookieKey')
);
and change the field name in your form to remember_me if you're using the cookie authenticator's defaults:
echo $this->Form->control('remember_me', [
'type' => 'checkbox'
]);
That's all that should be required, if you tick the checkbox in your login form, then the authentication middleware will set a cookie after successful authentication accordingly, and it will pick up the cookie if it's present on a request and no other authenticator successfully authenticates the request first (like the session authenticator for example).

Directus Hooks - how to use the "item.read"

Anyone know how the "item.read" hooks is meant to work?
return [
'filters' => [
'item.update.table:before' => function (\Directus\Hook\Payload $payload) {
$payload->set('field', my_encrypt($payload->get('password'), $key));
return $payload;
},
'item.read.table:before' => function(\Directus\Hook\Payload $payload){
<how to set the 'field' before view??>
return $payload;
},
],
];
I need to unecrypt the stored field for view....
I found the way.
First you need the
'item.read.coll' => function ($payload)
Second you get the data from the payload - alter the data and replace the data in the payload - like this
$data = $payload->getData();
$data[0]['field'] = "NEW DATA";
$payload->replace($data);
return $payload;

CakePHP3 CRUD API and API Routing

I am using the Friends of Cake CRUD plugin for my back-end API. I am also using API prefixes for my routes:
Router::prefix('Api', function ($routes) {
$routes->extensions(['json', 'xml', 'ajax']);
$routes->resources('Messages');
$routes->resources('ReportedListings');
$routes->fallbacks('InflectedRoute');
});
So far so good. My controller is as follows:
namespace App\Controller\Api;
use App\Controller\AppController;
use Cake\Event\Event;
use Cake\Core\Exception\Exception;
class MessagesController extends AppController {
use \Crud\Controller\ControllerTrait;
public function initialize() {
parent::initialize();
$this->loadComponent(
'Crud.Crud', [
'actions' => [
'Crud.Add',
'update' => ['className' => 'Crud.Edit']
],
'listeners' => ['Crud.Api'],
]
,'RequestHandler'
);
$this->Crud->config(['listeners.api.exceptionRenderer' => 'App\Error\ExceptionRenderer']);
$this->Crud->addListener('relatedModels', 'Crud.RelatedModels');
}
public function beforeFilter(Event $event){
parent::beforeFilter($event);
}
public function add() {
return $this->Crud->execute();
}
When I make a call as follows:
[POST] /api/messages.json
I get an error:
Action MessagesController::index() could not be found, or is not accessible.
I instead use:
[POST] /messages.json
I don't get the error and I can add a message. So the question is why with my api prefix routing does the CRUD look for index and how can I avoid this behavior?
I found the issue:
Router::prefix('Api', function ($routes) {
....
}
The 'Api' should be lowercase!
Router::prefix('api', function ($routes) {
....
}

How to override error handler from a module

To override error handler from a module based this samdark guid:
class Module extends \yii\base\Module
{
public function init()
{
parent::init();
\Yii::configure($this, [
'components' => [
'errorHandler' => [
'class' => ErrorHandler::className(),
]
],
]);
/** #var ErrorHandler $handler */
$handler = $this->get('errorHandler');
\Yii::$app->set('errorHandler', $handler);
$handler->register();
}
}
This method only worked for valid module controllers, but can not handle invalid module controllers. Even affects all over the application so that module error handler will be default error handler all over the application. i was confused how can solve it!
Example: I was willing www.site.com/module/x handled with module error handler(www.site.com/module/default/error),where x is not a valid module controller. If that was not the case, it was handled with app error handler(www.site.com/site/error).
Finally i used this code: (admin is my module name). Know everything is fine.
class AdminModule extends \yii\base\Module
{
public function init()
{
parent::init();
// Is better used regex method, temporally i used this method
$r = Yii::$app->urlManager->parseRequest(Yii::$app->request)[0];
$r_array = explode('/',$r);
if($r_array[0] === 'admin'){
Yii::configure($this, [
'components' => [
'errorHandler' => [
'class' => ErrorHandler::className(),
'errorAction' => '/admin/default/error',
]
],
]);
$handler = $this->get('errorHandler');
Yii::$app->set('errorHandler', $handler);
$handler->register();
}
}
}
If you know a better way please share with me. Thanks