gitprep / lib / Gitprep.pm /
c128bed 11 years ago
1 contributor
247 lines | 5.533kb
use 5.008007;
package Gitprep;

our $VERSION = '0.01';

use Mojo::Base 'Mojolicious';
use Gitprep::Git;
use DBIx::Custom;
use Validator::Custom;
use Encode qw/encode decode/;
use Mojo::JSON;
use Gitprep::API;
use Carp 'croak';
use Gitprep::RepManager;
use Scalar::Util 'weaken';
use Carp 'croak';

has 'git';
has 'dbi';
has 'validator';
has 'manager';

sub startup {
  my $self = shift;
  
  # Config
  $self->plugin('INIConfig', {ext => 'conf'});
  
  # My Config(Development)
  my $my_conf_file = $self->home->rel_file('gitprep.my.conf');
  $self->plugin('INIConfig', {file => $my_conf_file}) if -f $my_conf_file;
  
  # Config
  my $conf = $self->config;
  $conf->{hypnotoad} ||= {listen => ["http://*:10020"]};
  my $listen = $conf->{hypnotoad}{listen} || '';
  if ($listen ne '' && ref $listen ne 'ARRAY') {
    $listen = [ split /,/, $listen ];
  }
  $conf->{hypnotoad}{listen} = $listen;
  
  # Git command
  my $git = Gitprep::Git->new;
  my $git_bin = $conf->{basic}{git_bin} ? $conf->{basic}{git_bin} : $git->search_bin;
  if (!$git_bin || ! -e $git_bin) {
    $git_bin ||= '';
    my $error = "Can't detect or found git command ($git_bin). set git_bin in gitprep.conf";
    $self->log->error($error);
    croak $error;
  }
  $git->bin($git_bin);

  # Repository Manager
  my $manager = Gitprep::RepManager->new(app => $self);
  weaken $manager->{app};
  $self->manager($manager);
  
  # Repository home
  my $rep_home = $self->home->rel_file('rep');
  $git->rep_home($rep_home);
  unless (-d $rep_home) {
    mkdir $rep_home
      or croak "Can't create directory $rep_home: $!";
  }
  $self->git($git);

  # DBI
  my $db_file = $self->home->rel_file('db/gitprep.db');
  my $dbi = DBIx::Custom->connect(
    dsn => "dbi:SQLite:database=$db_file",
    connector => 1,
    option => {sqlite_unicode => 1}
  );
  $self->dbi($dbi);

  # Create user table
  eval {
    my $sql = <<"EOS";
create table user (
  row_id integer primary key autoincrement,
  id not null unique,
  config not null
);
EOS
    $dbi->execute($sql);
  };
  
  # Create project table
  eval {
    my $sql = <<"EOS";
create table project (
  row_id integer primary key autoincrement,
  user_id not null,
  name not null,
  config not null,
  unique(user_id, name)
);
EOS
    $dbi->execute($sql);
  };
  
  # Model
  my $models = [
    {table => 'user', primary_key => 'id'},
    {table => 'project', primary_key => ['user_id', 'name']}
  ];
  $dbi->create_model($_) for @$models;

  # Fiter
  $dbi->register_filter(json => sub {
    my $value = shift;
    
    if (ref $value) {
      return decode('UTF-8', Mojo::JSON->new->encode($value));
    }
    else {
      return Mojo::JSON->new->decode(encode('UTF-8', $value));
    }
  });
  
  # Validator
  my $validator = Validator::Custom->new;
  $self->validator($validator);
  $validator->register_constraint(
    user_name => sub {
      my $value = shift;
      
      return ($value || '') =~ /^[a-zA-Z0-9_\-]+$/
    },
    project_name => sub {
      my $value = shift;
      
      return ($value || '') =~ /^[a-zA-Z0-9_\-]+$/
    }
  );
  
  # Helper
  $self->helper(gitprep_api => sub { Gitprep::API->new(shift) });

  # DBViewer(only development)
  if ($self->mode eq 'development') {
    eval {
      $self->plugin(
        'DBViewer',
        dsn => "dbi:SQLite:database=$db_file",
      );
    };
  }
  
  # Routes
  my $r = $self->routes;
  
  # Auto route
  {
    my $r = $r->under(sub {
      my $self = shift;
      
      my $api = $self->gitprep_api;
      
      # Admin page authentication
      {
        my $path = $self->req->url->path->parts->[0] || '';

        if ($path eq '_admin' && !$api->logined_admin) {
          $self->redirect_to('/');
          return;
        }
      }
      
      return 1; 
    });
    $self->plugin('AutoRoute', route => $r);
  }
  
  # User defined Routes
  {
    # User
    my $r = $r->route('/:user');
    {
      # Home
      $r->get('/')->name('user');
      
      # Settings
      $r->get('/_settings')->name('user-settings');
    }
    
    # Project
    {
      my $r = $r->route('/:project');
      
      # Home
      $r->get('/')->name('project');
      
      # Commit
      $r->get('/commit/#diff')->name('commit');
      
      # Commits
      $r->get('/commits/#rev', {id => 'HEAD'})->name('commits');
      $r->get('/commits/#rev/(*blob)')->name('commits');
      
      # Branches
      $r->get('/branches')->name('branches');

      # Tags
      $r->get('/tags')->name('tags');

      # Tree
      $r->get('/tree/(*object)')->name('tree');
      
      # Blob
      $r->get('/blob/(*object)')->name('blob');
      
      # Blob diff
      $r->get('/blobdiff/(#diff)/(*file)')->name('blobdiff');
      
      # Raw
      $r->get('/raw/(*object)')->name('raw');
      
      # Archive
      $r->get('/archive/(#rev).tar.gz')->name('archive')->to(archive_type => 'tar');
      $r->get('/archive/(#rev).zip')->name('archive')->to(archive_type => 'zip');
      
      # Compare
      $r->get('/compare/(#rev1)...(#rev2)')->name('compare');
      
      # Settings
      $r->any('/settings')->name('project-settings');
      
      # Fork
      $r->any('/fork')->name('fork');
    }
  }

  # Reverse proxy support
  $ENV{MOJO_REVERSE_PROXY} = 1;
  $self->hook('before_dispatch' => sub {
    my $self = shift;
    
    if ($self->req->headers->header('X-Forwarded-Host')) {
      my $prefix = shift @{$self->req->url->path->parts};
      push @{$self->req->url->base->path->parts}, $prefix
        if defined $prefix;
    }
  });
}

1;