Googlebot wreaking havoc

Tuesday 30 May 2017

This morning I wake up to emails saying that our Mailgun account has been disabled due to high volumes of email and high volumes of bounces. The logs indicate that way more emails have been sent than we had visitors. Emails are only sent as the result of a user clicking a link on the site, so I have no idea how this is possible.

After a few hours of investigation, it turns out Googlebot was crawling our site and kept following links and buttons that send emails. To prevent this from happening again I took a couple different precautions:

  • Added rel="nofollow" to the links and buttons in question.
  • Changed any link that generates an email from a GET to a POST
  • Added meta robots tags to the pages in question with "nofollow"

Hopefully that will prevent this sort of thing from happening again.

Labels: coding
No comments

Domain Routing in Laravel

Monday 15 May 2017

For quite a while I have been struggling to get domain routing working in Laravel. Subdomain routing comes out of the box, and what I read said that adding domain routing should be fairly easy.

The first thing to do is get the full domain passed into the router. By default Laravel only takes what comes before the first ".", so to get the full domain passed in you need to add this to your Providers/RouteServiceProvider.php file:

Route::pattern('domain', '[a-z0-9.\-]+');
 parent::boot();

Now you can access the full domain in the routes file, and you can do so by adding a route group:

Route::group(['domain' => '{domain}'], function ($domain) {

I tried to get the routes to take in the domain as a parameter and create themselves dynamically, but that did not work. So I ended up creating the route group. My issue was that some domains just use the normal route, and others were to have their own custom routes. I spent a while trying to get that working before just adding the route group. Inside the route group I check the database to see if this domain uses the normal routes or gets a special route. 

The next issue was how to pass the variables from the routes to the controller, when they do not come from the URL. The special routes can be accessed via URL: http://www.maindomain.com/site/1. I wanted to be able to map via http://www.customdomain.com to that URL, but to do so I needed to pass the parameters from the URL into the controller when the parameters do not exist in the URL. That took some more figuring out, but it turns out you can do it like this:

$app = app();
$controller = $app->make('App\Http\Controllers\WhateverController');
return $controller->callAction('show', ['request' => $request, 'id' => $id]);

The controller expects a Request object, and to get that passed in you need to add it to the route explicity:

Route::get('/', function ($domain, \Illuminate\Http\Request $request) {

With that addition I am able to map the custom domain to a specific controller and pass in variables which are determined in the routes file.

The final code looks like this:

Route::group(['domain' => '{domain}'], function ($domain) {
    Route::get('/', function ($domain, \Illuminate\Http\Request $request) {
        $site = \App\Website::where('host', $domain)->first();
        if($site){
            $app = app();
            $controller = $app->make('App\Http\Controllers\DomainController');
            return $controller->callAction('show', ['request' => $request, 'id' => $site->store_id]);
        } else {
            $app = app();
            $controller = $app->make('App\Http\Controllers\HomeController');
            return $controller->callAction('index', []);
        }
    });
});

So for route "/" it checks to see if the domain exists in a table in the database, if so calls DomainController with a parameter from the DB and the request. If not it calls HomeController.

After a long time spent trying to figure this out it turns out to be a lot simpler than I thought it would be. However now I need to add specific routes to some domains but not others. I don't expect that will be too different than the method I am currently using.

Labels: coding, laravel
No comments

Domain Routing in Laravel

Monday 15 May 2017

For quite a while I have been struggling to get domain routing working in Laravel. Subdomain routing comes out of the box, and what I read said that adding domain routing should be fairly easy.

The first thing to do is get the full domain passed into the router. By default Laravel only takes what comes before the first ".", so to get the full domain passed in you need to add this to your Providers/RouteServiceProvider.php file:

Route::pattern('domain', '[a-z0-9.\-]+');
 parent::boot();

Now you can access the full domain in the routes file, and you can do so by adding a route group:

Route::group(['domain' => '{domain}'], function ($domain) {

I tried to get the routes to take in the domain as a parameter and create themselves dynamically, but that did not work. So I ended up creating the route group. My issue was that some domains just use the normal route, and others were to have their own custom routes. I spent a while trying to get that working before just adding the route group. Inside the route group I check the database to see if this domain uses the normal routes or gets a special route. 

The next issue was how to pass the variables from the routes to the controller, when they do not come from the URL. The special routes can be accessed via URL: http://www.maindomain.com/site/1. I wanted to be able to map via http://www.customdomain.com to that URL, but to do so I needed to pass the parameters from the URL into the controller when the parameters do not exist in the URL. That took some more figuring out, but it turns out you can do it like this:

$app = app();
$controller = $app->make('App\Http\Controllers\WhateverController');
return $controller->callAction('show', ['request' => $request, 'id' => $id]);

The controller expects a Request object, and to get that passed in you need to add it to the route explicity:

Route::get('/', function ($domain, \Illuminate\Http\Request $request) {

With that addition I am able to map the custom domain to a specific controller and pass in variables which are determined in the routes file.

The final code looks like this:

Route::group(['domain' => '{domain}'], function ($domain) {
    Route::get('/', function ($domain, \Illuminate\Http\Request $request) {
        $site = \App\Website::where('host', $domain)->first();
        if($site){
            $app = app();
            $controller = $app->make('App\Http\Controllers\DomainController');
            return $controller->callAction('show', ['request' => $request, 'id' => $site->store_id]);
        } else {
            $app = app();
            $controller = $app->make('App\Http\Controllers\HomeController');
            return $controller->callAction('index', []);
        }
    });
});

So for route "/" it checks to see if the domain exists in a table in the database, if so calls DomainController with a parameter from the DB and the request. If not it calls HomeController.

After a long time spent trying to figure this out it turns out to be a lot simpler than I thought it would be. However now I need to add specific routes to some domains but not others. I don't expect that will be too different than the method I am currently using.

Labels: coding, laravel
No comments

Queries by Key in Laravel

Sunday 07 May 2017

One of my greatest frustrations with Eloquent collections has been that I needed to loop through the collection and either create a new collection or array if I wanted to have the results in a format where I could access them by a value in the query results. That is to say if I want to be able to access the results by say primary key, I would need to loop through the Collection returned by the query and create a new object with the key as whatever I wanted it to be.

I just learned that there is a much easier way to do this: 

    Model::all()->keyBy('whatever');

This will return the collection with "whatever" as the key, which makes life so much easier and code so much cleaner.

Labels: coding, laravel
No comments

Sizing Google Recaptchas

Tuesday 25 April 2017

I was struggling today trying to get a Google Recaptcha integrated into a site. The space was smaller than the recaptcha size, and I couldn't figure out how to size it down. I found this article that explains very easily how to do it.

<div class="g-recaptcha" data-theme="light" data-sitekey="XXXXXXXXXXXXX" style="transform:scale(0.77);-webkit-transform:scale(0.77);transform-origin:0 0;-webkit-transform-origin:0 0;"></div>

Labels: coding
No comments

Archives