One of the core concepts of
doodlekit is the multi-site environment. It was very important to me that setting up a doodlekit website could be completely automated. Even the domain name registration is automatic. While it has certainly added complexity to the code, it's not really been that bad.
Doodlekit is divided into three tiers, system, admin, and public. There is one system level login that has access to all sites. It can create sites and layouts(templates), and see the admin pages for everything. The admin and public levels apply to each individual site, where the admin can modify anything on the site, and public is read only. There are other security roles, but that's not relevant.
The database schema looks like it typically would for a one site install, except that every top-level object has a site_id. The sites table holds all the information for each site, including the name, and what I call a 'handle'. The handle is used for many things, but mainly for the sub-domain. There is a separate field for the domain name, so that we can support both the sub-domain, and a custom domain name at the same time.
For every doodlekit request, a before filter is run that looks something like this...
def load_site
if request.domain == DOODLEKIT_DOMAIN
sub_domain = request.subdomains.last || 'default'
@site = Site.find_by_handle(sub_domain)
else
@site = Site.find_by_domain(request.domain)
end
if @site.nil?
@site = Site.find_default
end
if session[:current_user] &&
session[:current_user].site_id != @site.id &&
!session[:current_user].is_system
session[:current_user] = nil
end
end Basically if the root domain name is 'doodlekit.com', then it takes the last sub-domain element and looks up that handle. Otherwise it looks it up by the domain name. If both of these fail, then just use the default site. The @site instance variable is then used to pull up features, menus, pages, layouts, etc. Whenever I decide to implement object caching, the Site object will definitely be the first to go in there.
As far as DNS, all I have to do is setup everything as a CNAME to doodlekit.com. There's no Apache config or anything.
The last piece in the code above is the first security check that ensures you're user account matches the site your trying to access. Security has been a big issue as many holes start to open up in this type of system. I've had to analyze every single query to ensure that it's not possible to access or modify data across sites. Basically every query for a top level object is also constrained by the site id. But that's not enough on it's own. For example, in the forums you have topics and messages, which are relatively constrained by the site id. However, since we allow you to move topics to other forums, it's possible to change the forum id to that of another site. Well it's actually not possible in doodlekit, but those are the types of potential situations I've had to consider.
Ultimately I'm very pleased with how well everything is performing. We're currently running close to 50 sites under one install. It runs on four mongrel process on a
Rails Machine , which I'm
very happy with. I tried not to think about performance very much when writing doodlekit, because I didn't want that to hold me back from implementing cool ideas. Honestly I am very surprised by how quickly things run in spite of that. I've recently done a round of optimizations, but they have been fairly minor.
The
doodlekit core is solid, and probably won't change much. I've created a plugin architecture for add-ons like the
new form builder . Everything is very stable, and very easy to maintain, and I couldn't be happier or prouder.
Post a Comment