Skip to content

Commit

Permalink
React server rendering, pt. 1
Browse files Browse the repository at this point in the history
Adds infrastructure for react server rendering, and makes our 404 page use it.
(Gotta start somewhere!)

If a catalyst action has a NewTemplate attribute, it'll take whatever action
was requested, e.g. root/main/404, and tell server.js to render
root/main/404.js instead.

The macros uri_for and uri_for_action are supported in React templates via
regexp magic.

The formatUserDate module has been moved to root/utility, since it uses
server-only data.
  • Loading branch information
mwiencek committed Oct 15, 2015
1 parent 9f8fd92 commit 8bcf268
Show file tree
Hide file tree
Showing 31 changed files with 1,623 additions and 73 deletions.
14 changes: 14 additions & 0 deletions app.psgi
Expand Up @@ -25,6 +25,20 @@ BEGIN {

use MusicBrainz::Server;

BEGIN {
use LWP::UserAgent;
use MusicBrainz::Server::Renderer qw( create_request );

if (DBDefs->RENDERER_HOST eq '') {
# Check if the renderer is already running.
my $response = LWP::UserAgent->new->request(create_request('/', undef, ''));

if ($response->code == 500 && !fork) {
exec './script/start_renderer.pl';
}
}
}

debug_method_calls() if DBDefs->CATALYST_DEBUG;

builder {
Expand Down
8 changes: 8 additions & 0 deletions lib/DBDefs/Default.pm
Expand Up @@ -435,6 +435,14 @@ sub EMAIL_BUGS { undef }
sub HTML_VALIDATOR { 'http://validator.w3.org/nu/?out=json' }
# sub HTML_VALIDATOR { 'http://localhost:8888?out=json' }

# We use a small HTTP server (root/server.js) to render React.js templates.
# These configure the host/port it listens on. If RENDERER_HOST is '', then
# musicbrainz-server will fork & exec root/server.js for us (convenient on
# development servers). Otherwise, it'll assume the service is running
# separately.
sub RENDERER_HOST { '' }
sub RENDERER_PORT { 9009 }

################################################################################
# Profiling
################################################################################
Expand Down
7 changes: 7 additions & 0 deletions lib/MusicBrainz/Server.pm
Expand Up @@ -428,6 +428,13 @@ has json => (
}
);

has json_utf8 => (
is => 'ro',
default => sub {
return JSON->new->utf8->allow_blessed->convert_blessed;
}
);

=head1 NAME
MusicBrainz::Server - Catalyst-based MusicBrainz server
Expand Down
29 changes: 19 additions & 10 deletions lib/MusicBrainz/Server/Controller/Root.pm
Expand Up @@ -110,7 +110,7 @@ this means serving a 404 error page.
=cut

sub default : Path
sub default : Path NewTemplate
{
my ($self, $c) = @_;
$c->detach('/error_404');
Expand Down Expand Up @@ -207,6 +207,11 @@ sub begin : Private
$alert = l('Our Redis server appears to be down; some features may not work as intended or expected.');
warn "Redis connection to get alert failed: $_";
};

# For displaying which git branch is active as well as last commit information
# (only shown on staging servers)
my ($git_branch, $git_sha, $git_msg) = DBDefs->GIT_BRANCH;

$c->stash(
wiki_server => DBDefs->WIKITRANS_SERVER,
server_languages => Translation->instance->all_languages(),
Expand All @@ -216,13 +221,20 @@ sub begin : Private
is_slave_db => DBDefs->REPLICATION_TYPE == RT_SLAVE,
read_only => DBDefs->DB_READ_ONLY,
alert => $alert,
alert_mtime => $alert_mtime
alert_mtime => $alert_mtime,
git => {
branch => $git_branch,
sha => $git_sha,
msg => $git_msg,
},
},
favicon_css_classes => FAVICON_CLASSES,
);

# Setup the searchs on the sidebar
$c->form( sidebar_search => 'Search::Search' );
unless (exists $c->action->attributes->{NewTemplate}) {
$c->form(sidebar_search => 'Search::Search');
}

# Edit implies RequireAuth
if (!exists $c->action->attributes->{RequireAuth} && exists $c->action->attributes->{Edit}) {
Expand Down Expand Up @@ -315,6 +327,10 @@ sub begin : Private
if (DBDefs->REPLICATION_TYPE == RT_SLAVE) {
$c->stash( last_replication_date => $c->model('Replication')->last_replication_date );
}

if (exists $c->action->attributes->{NewTemplate}) {
MusicBrainz::Server::Renderer::handle_request($c);
}
}

=head2 end
Expand All @@ -340,13 +356,6 @@ sub end : ActionClass('RenderView')
is_beta => DBDefs->IS_BETA
};

# For displaying which git branch is active as well as last commit information
# (only shown on staging servers)
my ($git_branch, $git_sha, $git_msg) = DBDefs->GIT_BRANCH;
$c->stash->{server_details}->{git}->{branch} = $git_branch;
$c->stash->{server_details}->{git}->{sha} = $git_sha;
$c->stash->{server_details}->{git}->{msg} = $git_msg;

$c->stash->{google_analytics_code} = DBDefs->GOOGLE_ANALYTICS_CODE;

# For displaying release attributes
Expand Down
11 changes: 11 additions & 0 deletions lib/MusicBrainz/Server/MergeQueue.pm
@@ -1,5 +1,6 @@
package MusicBrainz::Server::MergeQueue;
use Moose;
use MusicBrainz::Server::Data::Utils qw( model_to_type boolean_to_json );
use namespace::autoclean;

has 'type' => (
Expand Down Expand Up @@ -42,4 +43,14 @@ sub remove_entities {
])
}

sub TO_JSON {
my ($self) = @_;

return {
type => $self->type,
entities => $self->entities,
ready_to_merge => boolean_to_json($self->ready_to_merge),
};
}

1;
89 changes: 89 additions & 0 deletions lib/MusicBrainz/Server/Renderer.pm
@@ -0,0 +1,89 @@
package MusicBrainz::Server::Renderer;

use strict;
use warnings;

use base 'Exporter';
use charnames ':alias' => {INFO_SEP => 'INFORMATION SEPARATOR ONE'};
use DBDefs;
use Encode;
use HTML::Entities qw( encode_entities decode_entities );
use HTTP::Request;
use JSON -convert_blessed_universally;
use MusicBrainz::Server::Data::Utils qw( boolean_to_json );
use URI;

my %URI_DELIMITERS = (
uri_for => "\N{INFO_SEP}__URI_FOR__\N{INFO_SEP}",
uri_for_action => "\N{INFO_SEP}__URI_FOR_ACTION__\N{INFO_SEP}",
);

our @EXPORT_OK = qw(
create_request
);

sub create_request {
my ($path, $headers, $body) = @_;

my $uri = URI->new;
$uri->scheme('http');
$uri->host(DBDefs->RENDERER_HOST || '127.0.0.1');
$uri->port(DBDefs->RENDERER_PORT);
$uri->path($path);

HTTP::Request->new('GET', $uri, $headers, $body);
}

sub replace_uri {
my ($c, $method, $args) = @_;

$args = JSON->new->decode(decode_entities($args));
return encode_entities($c->$method(@{$args}));
}

sub handle_request {
my ($c) = @_;

my $user;
if ($c->user_exists) {
$user = {%{$c->user->TO_JSON}, preferences => $c->user->preferences};
}

my $body = $c->json_utf8->encode({
context => {
user => $user,
debug => boolean_to_json($c->debug),
stash => $c->stash,
sessionid => scalar($c->sessionid),
session => $c->session,
flash => $c->flash,
}});

my $response = $c->model('MB')->context->lwp->request(
create_request($c->req->path, $c->req->headers->clone, $body)
);

my $content = decode('utf-8', $response->content);

# URI replacement magic.
for my $method (keys %URI_DELIMITERS) {
my $delimiter = $URI_DELIMITERS{$method};

$content =~ s/$delimiter ([^\N{INFO_SEP}]+) $delimiter
/replace_uri($c, $method, $1)/xmseg;
}

$c->res->status($response->code);
$c->res->body($content);
}

1;

=head1 COPYRIGHT
This file is part of MusicBrainz, the open internet music database.
Copyright (C) 2015 MetaBrainz Foundation
Licensed under the GPL version 2, or (at your option) any later version:
http://www.gnu.org/licenses/gpl-2.0.txt
=cut

0 comments on commit 8bcf268

Please sign in to comment.