#!/usr/bin/perl

# ABSTRACT: provide a web interface to a Pinto repository
# PODNAME: pinto-server

use strict;
use warnings;

use Pod::Usage;
use Getopt::Long;
use Path::Class;
use File::Temp;
use Pinto 0.020;

use Dancer ':syntax';

#-----------------------------------------------------------------------------

our $VERSION = '0.018'; # VERSION

#-----------------------------------------------------------------------------
# Globals

my $REPOS;

#----------------------------------------------------------------------------
# Dancer routes

post '/action/add' => sub {

    my $author = param('author')
      or (status 500 and return 'No author supplied');

    my $dist   = upload('dist')
      or (status 500 and return 'No dist file supplied');

    my $tempdir = dir( File::Temp::tempdir(CLEANUP=>1) );
    my $dist_file = $tempdir->file( $dist->basename() );
    $dist->copy_to( $dist_file );

    my $pinto = pinto();
    $pinto->new_action_batch(noinit => 1);
    $pinto->add_action('Add', dist => $dist_file, author => $author);
    my $result = $pinto->run_actions();

    status 200 and return if $result->is_success();
    status 500 and return $result->to_string();

};

#----------------------------------------------------------------------------

post '/action/remove' => sub {

    my $author  = param('author')
      or (status 500 and return 'No author supplied');

    my $package = param('package')
      or ( status 500 and return 'No package supplied');

    my $pinto = pinto();
    $pinto->new_action_batch(noinit => 1);
    $pinto->add_action('Remove', package => $package, author => $author);
    my $result = $pinto->run_actions();

    status 200 and return if $result->is_success();
    status 500 and return $result->to_string();
};

#----------------------------------------------------------------------------

post '/action/list' => sub {

    my $buffer = '';
    my $type = ucfirst param('type') || 'All';

    my $pinto = pinto();
    $pinto->new_action_batch(noinit => 1);
    $pinto->add_action("List::$type", out => \$buffer);
    my $result = $pinto->run_actions();

    status 200 and return $buffer if $result->is_success();
    status 500 and return $result->to_string;
};

#----------------------------------------------------------------------------
# Route for indexes and dists

get qr{^ /(authors|modules)/(.+) }x => sub {
     my $file =  file( $REPOS, request->uri() );
     status 404 and return 'Not found' if not -e $file;
     return send_file( $file, system_path => 1 );
};

#----------------------------------------------------------------------------
# Ping route

get '/' => sub {
    status 200;
    return 'Pinto OK';
};

#-----------------------------------------------------------------------------
# Fallback route

any qr{ .* }x => sub {
    status 404;
    return 'Not found';
};

#-----------------------------------------------------------------------------
# Main

GetOptions( 'daemon'  => sub { setting(@_) },
            'port=i'  => sub { setting(@_) },
            'repos=s' => \$REPOS ) or pod2usage();

pod2usage(-message => 'Must specify a repository') if not $REPOS;

Dancer::dance();

#----------------------------------------------------------------------------

sub pinto {
    return Pinto->new(repos => $REPOS, quiet => 1);
}

#----------------------------------------------------------------------------



=pod

=for :stopwords Jeffrey Ryan Thalhammer Imaginative Software Systems

=head1 NAME

pinto-server - provide a web interface to a Pinto repository

=head1 VERSION

version 0.018

=head1 SYNOPSIS

  pinto-server --repos=/path/to/repository [--daemon] [--port=N]

=head1 DESCRIPTION

Before running L<pinto-server> you must first create a Pinto
repository.  See L<pinto-admin> for instructions on that.

L<pinto-server> provides a web API to a L<Pinto> repository.  Remote
clients (like L<pinto-remote>) can use this API to add distributions,
remove packages, and list the contents of the Pinto repository.  In
addition, L<pinto-server> exposes the contents of your repository, so
you can use it as the source of distributions for L<cpan> or L<cpanm>.

=head1 ARGUMENTS

=over 4

=item --repos=PATH

The path to the Pinto repository you wish to serve.

=back

=head1 OPTIONS

=over 4

=item --daemon

Tells L<pinto-server> to fork and run in a separate process.

=item --port=N

Sets the port number for the server to listen on.  Default is 3000.

=back

=head1 DEPLOYMENT

L<pinto-server> can be run as a stand-alone server as shown in the
L</"SYNOPSIS">.  However, this may not perform well under heavy load.

L<pinto-server> is also PSGI compatible, so if you need a more
powerful robust server, you can run it under L<Plack> like this:

  $> plackup [OPTIONS] /path/to/pinto-server

=head1 CAVEATS

If you are running L<pinto-server> and have configured L<Pinto> to use
a VCS-based store, such as L<Pinto::Store::VCS::Svn> or
L<Pinto::Store::VCS::Git>, then you must not mess with the VCS
directly (at least not the part of the VCS that holds the Pinto
repository).  This is because L<pinto-server> only initializes the
working copy of the repository at startup.  Thereafter, it assumes
that it is the only actor affecting the Pinto repository in the VCS.
If you start modifying Pinto's area of the VCS directly, then the
working copy for L<pinto-server> will become out of date and conflicts
will happen.

=head1 LIMITATIONS

L<pinto-server> speaks HTTP, but does not actually serve HTML.  At the
moment, it is geared toward command line tools like L<pinto-remote> so
it just returns plain text.  This will probably change as
L<pinto-server> evolves into a real web application.

=head1 AUTHOR

Jeffrey Ryan Thalhammer <jeff@imaginative-software.com>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2011 by Imaginative Software Systems.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut


__END__


