Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What's the most elegant way to handle users custom domains?

On my site, users have public profiles that can be accessed via http://mysite.com/vanity_url. I want to allow users to point their own domains to their profile page on my site. Just like Bandcamp does.

My Profile model has these two fields to deal with this: vanity_url, which is the normal username type of field; and a new custom_domain which is their own domains' name, eg, example.com.

This is what I have done so far, but I'm afraid it might not be the most elegant, safe, and efficient way to do it.

First, I made sure Apache's DocumentRoot is set to my app's webroot directory, so I can tell users to point their DNS to my site's IP.

Now, this is how the routing rules on my routes.php look like:

if (preg_match('/mysite\.com\.?$/', $_SERVER['SERVER_NAME'])){
    // Normal routes when visitors go to my domain
    Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
    Router::connect('/pages/**', array('controller' => 'pages', 'action' => 'display'));

    // Move all other actions to a separate '/app/' area
    Router::connect('/app/:controller/:action/**');
    Router::connect('/app/:controller/**');

    // Handle profile URLs
    Router::connect('/:profile/**', 
        array('controller' => 'profiles', 'action' => 'view'), 
        array('pass' => array('profile'), 'profile' => '[0-9a-zA-Z\-\_]+')
    );
}
else{
    // If visitors come via a URL different to mysite.com, I let 
    // the ProfilesController deal with it passing the current SERVER_NAME 
    // as a param to the 'view' action
    Router::connect('/', array(
        'controller' => 'profiles', 
        'action' => 'view', 
        $_SERVER['SERVER_NAME'], // 'url' param
        true // 'customDomain' param
    ));
    Router::redirect('/*', 'http://mysite.com');
}

And this is how the view action on the ProfilesController looks like:

public function view($url = null, $customDomain = false) {
    if ($url){
        // Find the profile by its vanity_url or its custom_domain
        $findOptions = array(
            'conditions' => $customDomain? 
                array('custom_domain' => $url) : 
                array('vanity_url' => $url) 
        );
        if ($profile = $this->Profile->find('first', $findOptions)) {
            $this->set('profile', $profile);
        }
    }
    else throw new NotFoundException(__('Invalid profile'));
}

What problems could I face with this approach?

Also, does anyone know why Bandcamp asks users to create a CNAME instead of an A record to set up a subdomain? Am I missing something I should consider here?

Edit Somebody helped me figure that last bit out: It seems you can't easily use an CNAME record to point a naked domain to another. The main question is still open.

like image 538
luchomolina Avatar asked Nov 04 '22 07:11

luchomolina


1 Answers

I did something similar using cake 1.3 a year ago

There are 4 major steps in this solution:

  1. Have a Domain model and domains datatable that records all the possible custom domains and Views for your users to enter their custom domains
  2. create domain checking code in beforeFilter in AppController and save User data to Session
  3. the Controller action that is responsible for viewing the public profile needs to reference the same User data that is saved to Session
  4. your users need to setup CNAME and A records correctly with their domain registrars

Step 1 Domain model and datatable

What I did was I had a table called domains

Each Domain contains the web address which I assumed to be just a http://....

Domain belongsTo User

User hasMany Domain

domains
==========
id    web_address    user_id    main

the main is a tinyint which indicates whether this is the main url to be used since User hasMany Domain.

so what happens is the user needs to create a new record or more for Domain.

Step 2 Code in beforeFilter of AppController to figure out which public profile to display Next, your code needs to be able to retrieve the User id based on the url submitted upon each http request.

The User from this point onwards refer to the User whose public profile is viewed. Do NOT confuse this with the loggedIn User, should you have one.

I suggest that you do this in your beforeFilter of AppController.

Something like this

  $currentUser = $this->User->getByDomain(FULL_BASE_URL);
  $this->Session->write('CurrentUser', $currentUser);

The code for getByDomain for User model is also an exercise for you. Should be easy enough given that I have explained the schema for Domains

You may need to check against the currentUser inside your Session data before writing the currentUser because you do not wish to write the Session data all the time especially when the visitor is visiting the same webpage again and again.

You may need to change the above code snippet to something like this:

  $currentUser = $this->Session->read('CurrentUser');

  if(empty($currentUser) OR (!$this->checkUrlAgainstDomain(FULL_BASE_URL, $currentUser['Domain']['domain']))) {
        $currentUser = $this->User->getByDomain(FULL_BASE_URL);
                    $this->Session->write('CurrentUser', $currentUser);
    }

Again the function checkUrlAgainstDomain is an exercise for you.

Step 3 Code in beforeFilter of AppController to figure out which public profile to display You will use the CurrentUser data saved in your Session to determine which User's public page you want to display.

I leave this as an exercise for you to figure out in your UsersController under action view

Step 4 your users need to enter the following in their domain registrars Then your users need to go to their domain registrars and do a similar thing as BandCamp users in the faq you have linked to.

  • User's "www" subdomain should point using CNAME to example.Lucho.com

  • User's root domain (without the www) should have an A Record pointing to IP Address of your server ie where your Lucho.com is residing in

  • User must add both both of these Domains management page as explained earlier on the top. It can take up to 48 hours for the domain to completely update.

like image 98
Kim Stacks Avatar answered Nov 26 '22 08:11

Kim Stacks