User Tools

Site Tools


RecVi

RecVi - mini-tool for phone REcord VIsualisation and download, recvi for short. =)

It is minimal one-page application, written during ~1.5-2 hours.

recvi.pl.in
#!/usr/bin/env perl
 
#------------------
#--- CONTROLLER ---
#------------------
 
package Recvi::Controller;
 
use utf8;
use strict;
use warnings;
use Mojo::Base 'Mojolicious::Controller';
use Mojo::Util qw(md5_sum dumper quote encode);
use Mojo::JSON qw(encode_json decode_json);
use Apache::Htpasswd;
use File::Basename qw(fileparse);
 
sub hello {
    my $self = shift;
    $self->render(template => 'hello');
}
 
#--- controller::agent ---
 
sub renderFile {
        my $self = shift;
        my %args = @_;
 
        utf8::decode($args{filename}) if $args{filename} && !utf8::is_utf8($args{filename});
        utf8::decode($args{filepath}) if $args{filepath} && !utf8::is_utf8($args{filepath});
 
        my $filename = $args{filename};
        my $status = $args{status} || 200;
        my $content_disposition = $args{content_disposition}  || 'attachment';
        my $cleanup = $args{cleanup} // 0;
 
        # Content type based on format
        my $content_type;
        $content_type = $self->app->types->type( $args{format} ) if $args{format};
        $content_type = 'audio/vnd.wave';
 
        # Create asset
        my $asset;
        if ( my $filepath = $args{filepath} ) {
            unless ( -f $filepath && -r $filepath ) {
                $self->app->log->error("Cannot read file [$filepath]. error [$!]");
                return $self->rendered(404);
            }
            $filename ||= fileparse($filepath);
            $asset = Mojo::Asset::File->new( path => $filepath );
            $asset->cleanup($cleanup);
        } elsif ( $args{data} ) {
            $filename ||= $self->req->url->path->parts->[-1] || 'download';
            $asset = Mojo::Asset::Memory->new();
            $asset->add_chunk( $args{data} );
        } else {
            $self->app->log->error('You must provide "data" or "filepath" option');
            return;
        }
        # Set response headers
        my $headers = $self->res->content->headers();
 
        $filename = quote($filename); # quote the filename, per RFC 5987
        $filename = encode("UTF-8", $filename);
 
        $headers->add( 'Content-Type', $content_type . '; name=' . $filename );
        $headers->add( 'Content-Disposition', $content_disposition . '; filename=' . $filename );
 
        # Range, partially based on Mojolicious::Static
        if ( my $range = $self->req->headers->range ) {
            my $start = 0;
            my $size  = $asset->size;
            my $end   = $size - 1 >= 0 ? $size - 1 : 0;
 
            # Check range
            if ( $range =~ m/^bytes=(\d+)-(\d+)?/ && $1 <= $end ) {
                $start = $1;
                $end = $2 if defined $2 && $2 <= $end;
 
                $status = 206;
                $headers->add( 'Content-Length' => $end - $start + 1 );
                $headers->add( 'Content-Range'  => "bytes $start-$end/$size" );
            } else {
                # Not satisfiable
                return $self->rendered(416);
            }
            # Set range for asset
            $asset->start_range($start)->end_range($end);
        } else {
            $headers->add( 'Content-Length' => $asset->size );
        }
        # Stream content directly from file
        $self->res->content->asset($asset);
        return $self->rendered($status);
}
 
sub sessionAuth {
    return 1 if shift->session('username');
    return undef;
}
 
sub checkPassword {
    my ($self, $username, $password) = @_;
 
    return undef unless $username;
    return undef unless $password;
 
    my $passwdFile = $self->app->config('pwdfile');
    do { 
        $self->app->log->error("Cannot read password file '$passwdFile'");
        return undef;
    } unless -r $passwdFile;
    my $result = undef;
    eval {
        my $ht = Apache::Htpasswd->new({ passwdFile => $passwdFile, ReadOnly => 1 });
        $result = $ht->htCheckPassword($username, $password);
    };
    do { $self->app->log->debug("Auth module error: $@"); return undef; } if $@;
 
    return 1 if $result;
    $self->app->log->info("Bad auth from ".$self->tx->remote_address);
    return undef;
}
 
sub login {
    my $self = shift;
 
    return $self->redirect_to('/hello') if $self->sessionAuth;
 
    my $username = $self->req->param('username') || undef;
    my $password = $self->req->param('password') || undef;
 
    my $auth =  $self->checkPassword($username, $password);
    if ($auth) {
        $self->session(username => $username);
        return $self->redirect_to('/hello');
    }
 
    $self->render(template => 'login', req => $self->req);
}
 
sub dataList {
    my $self = shift;
    $self->render(template => 'dataList');
}
 
sub dataDown {
    my $self = shift;
    my $name = $self->req->param('name');
 
    return $self->render(template => 'not_found.production') unless $name;
 
    my $datadir = $self->app->config("datadir");
    my $file = "/$datadir/$name";
 
    return $self->render(template => 'not_found.production') unless -r $file;
    return $self->render(template => 'not_found.production') if -d $file;
    $self->renderFile(filepath => "$file");
}
 
 
1;
#-----------
#--- APP ---
#-----------
package Recvi;
 
use utf8;
use strict;
use warnings;
use Mojo::Base 'Mojolicious';
 
sub startup {
    my $self = shift;
}
 
1;
#------------
#--- MAIN ---
#------------
use strict;
use warnings;
use utf8;
 
use POSIX qw(setuid setgid);
use Mojo::Server::Prefork;
use Mojo::IOLoop::Subprocess;
use Mojo::Util qw(monkey_patch b64_encode b64_decode md5_sum getopt dumper);
use File::Basename;
use Sys::Hostname qw(hostname);
use File::Basename qw(basename dirname);
use Apache::Htpasswd;
use Cwd qw(getcwd abs_path);
 
 
my $appfile = abs_path(__FILE__);
my $appname = basename($appfile, ".pl");
$0 = $appfile;
 
getopt
    'h|help' => \my $help,
    '4|ipv4listen=s' => \my $ipv4listen,
    '6|ipv6listen=s' => \my $ipv6listen,
    'c|config=s' => \my $conffile,
    'p|pwdfile=s' => \my $pwdfile,
    'd|datadir=s' => \my $datadir,
    'l|logfile=s' => \my $logfile,
    'i|pidfile=s' => \my $pidfile,
    'v|loglevel=s' => \my $loglevel,
    'f|nofork' => \my $nofork,
    'u|user=s' => \my $user,
    'g|group=s' => \my $group;
 
 
if ($help) {
    print qq(
Usage: app [OPTIONS]
 
Options
    -h | --help                      This help
    -4 | --ipv4listen=address:port      Listen address and port, defaults 127.0.0.1:5100
    -6 | --ipv6listen=[address]:port    Listen address and port, defaults [::1]:5100
 
    -c | --config=path    Path to config file
    -p | --pwdfile=path   Path to user password file
    -d | --datadir=path    Path to application files 
    -l | --logfile=path   Path to log file
    -i | --pidfile=path   Path to process ID file
    -v | --loglevel=level  Verbose level: debug, info, warn, error, fatal
    -u | --user=user      System owner of process
    -g | --group=group    System group 
    -f | --nofork         Dont fork process, for debugging
All path option override option from configuration file
 
    )."\n";
    exit 0;
}
 
my $server = Mojo::Server::Prefork->new;
my $app = $server->build_app('Recvi');
$app = $app->controller_class('Recvi::Controller');
 
$app->config(
    hostname => hostname,
    datadir => $datadir || "/data",
    listenIPv4 => $ipv4listen || "0.0.0.0",
    listenIPv6 => $ipv6listen || "[::]",
    listenPort => "3005",
 
    pwdfile => $pwdfile || "@APP_CONFDIR@/$appname.pw",
    pidfile => $pidfile || "@APP_RUNDIR@/$appname.pid",
    logfile => $logfile || "@APP_LOGDIR@/$appname.log",
    conffile => $conffile || "@APP_CONFDIR@/$appname.conf",
    maxrequestsize => 1024*1024*1024,
    tlscert => "@APP_CONFDIR@/$appname.crt",
    tlskey => "@APP_CONFDIR@/$appname.key",
    appuser => $user || "@APP_USER@",
    appgroup => $group || "@APP_GROUP@",
    mode => 'production',
    loglevel => $loglevel || 'info',
    libdir => '@APP_LIBDIR@',
);
 
$conffile = $app->config('conffile');
do {
    $app->log->debug("Load configuration from $conffile ");
    my $config = $app->plugin( 'JSONConfig', { file => $conffile } );
} if -r $conffile;
 
$app->log->level($app->config('loglevel'));
 
my $tlscert = $app->config('tlscert');
my $tlskey = $app->config('tlskey');
$datadir = $app->config('datadir');
my $rundir = dirname ($app->config('pidfile'));
my $logdir = dirname ($app->config('logfile'));
$pwdfile = $app->config('pwdfile');
 
do { print "Error: Cannot read from data direcory $datadir\n"; exit 1; } unless -r $datadir;
do { print "Error: Cannot write to run direcory $rundir\n"; exit 1; } unless -w $rundir;
do { print "Error: Cannot write to log direcory $logdir\n"; exit 1; } unless -w $logdir;
 
do { print "Error: Cannot read TLS certificate $tlscert\n"; exit 1; } unless -r $tlscert;
do { print "Error: Cannot read TLS key $tlskey\n"; exit 1; } unless -r $tlskey;
do { print "Error: Cannot read password file $pwdfile\n"; exit 1; } unless -r $pwdfile;
 
my $appUser = $app->config('appuser');
my $appGroup = $app->config('appgroup');
 
my $appUID = getpwnam($appUser);
my $appGID = getgrnam($appGroup);
 
do { print "System user $appUser not exist.\n"; exit 1; } unless $appUID;
do { print "System group $appGroup not exist.\n"; exit 1; } unless $appGID;
 
$app->moniker($appname);
$app->mode($app->config("mode"));
$app->secrets(['xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx']);
 
$app->hook(before_dispatch => sub {
        my $c = shift;
 
        my $remoteIPaddr = $c->tx->remote_address;
        my $method = $c->req->method;
 
        my $base = $c->req->url->base->to_string;
        my $path = $c->req->url->path->to_string;
 
        my $loglevel = $c->app->log->level;
        unless ($loglevel eq 'debug') {
            my $url = $c->req->url->to_abs->to_string;
            $c->app->log->info("$method $url from $remoteIPaddr");
        }
        if ($loglevel eq 'debug') {
            my $url = $c->req->url->to_abs->to_string;
            $c->app->log->debug("$method $url from $remoteIPaddr");
        }
});
 
$app->static->paths->[0] = $app->config('libdir').'/public';
$app->renderer->paths->[0] = $app->config('libdir').'/templs';
 
my $r = $app->routes;
 
$r->add_condition(
    auth => sub {
        my ($route, $c, $captures, $hash) = @_;
        return 1 if $c->sessionAuth;
        return undef;
    }
);
 
$r->any('/login')->to('Controller#login');
$r->any('/hello')->over('auth')->to('Controller#hello');
$r->any('/data/list')->over('auth')->to('Controller#dataList');
$r->any('/data/down')->over('auth')->to('Controller#dataDown');
 
$app->helper('reply.not_found' => sub { 
        my $c = shift; 
        return $c->redirect_to('/login') unless $c->sessionAuth; 
        $c->render(template => 'not_found.production');
});
 
my $tlsParam .= '?';
$tlsParam .= 'cert='.$tlscert;
$tlsParam .= '&key='.$tlskey;
 
my $listenPort = $app->config('listenPort');
my $listenIPv4 = $app->config('listenIPv4');
my $listenIPv6 = $app->config('listenIPv6');
 
$server->listen([
    "https://$listenIPv4:$listenPort$tlsParam",
]);
 
$server->pid_file($app->config('pidfile'));
 
$server->heartbeat_interval(3);
$server->heartbeat_timeout(60);
 
unless ($nofork) {
    my $pid = fork;
    if ($pid == 0) {
        setuid($appUID) if $appUID;
        setgid($appGID) if $appGID;
        $app->log(Mojo::Log->new( path => $app->config('logfile') ));
        open (my $STDOUT2, '>&', STDOUT); open (STDOUT, '>>', '/dev/null');
        open (my $STDERR2, '>&', STDERR); open (STDERR, '>>', '/dev/null');
        chdir($datadir);
        local $SIG{HUP} = sub {
                $app->log->info('Catch HUP signal'); 
                $app->log(Mojo::Log->new(
                        path => $app->config('logfile'),
                        level => $app->config('loglevel'),
                ));
        };
        $server->run;
    }
} else {
    setuid($appUID) if $appUID;
    setgid($appGID) if $appGID;
    $server->run;
}
#EOF
dataList.html.ep
%#
%# $Id$
%#
% layout 'default';
% title 'RecVi';
% use Mojo::Util qw(dumper);
% use File::Basename;
% use File::stat;
% use POSIX;
 
% use utf8;
% use strict;
 
% my $datadir = $self->app->config("datadir");
% unless (-d $datadir || -r $datadir) {
    <div class="callout alert">
         Cannot read data directory. Не могу прочитать каталог данных.
    </div>
%}
 
% if (-d $datadir || -r $datadir) {
 
<h5 class="text-center">
    Record list&nbsp;
    <a href="/data/list"><i class="fi-refresh" style="font-size:1.3rem;"></i></a>
</h5>
 
%     my $num = 1;
    <table id="table" class="display" >
        <thead>
    	    <tr>
    		<td>#</td>
    		<td>date</td>
    		<td>source</td>
    		<td>dest</td>
    		<td>duration</td>
    		<td>size</td>
    	    </tr>
    </thead>
    <tbody>
%     opendir(my $dh, $datadir);
 
%       while (my $name = readdir($dh)) {
%           next if ($name =~ m/^\./);
%           my $datafile = "$datadir/$name";
%           next if -d $datafile;
%           next unless -r $datafile;
%           my $st = stat($datafile);
%           my $size = $st->size;
%           next if $size < 101;
%           my $mtime = strftime("%Y-%m-%d %H:%M:%S %Z", localtime($st->mtime));
%           my $rday = strftime("%j", localtime($st->mtime));
%           my $cday = strftime("%j", localtime(time));
%           my $diff = $cday-$rday;
%           next if ($diff > 45);
%           my ($Y, $M, $D, $h, $m, $s, $src, $dst) =
%              $name =~ /rec-([0-9]{4})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{2})-([0-9]{3})-([0-9]{1,64})/g;
%           my $timestamp = "$Y-$M-$D $h:$m";
 
          <tr>
            <td><%= $num %></td>
            <td><a target="_blank" href="/data/down?name=<%= $name %>"><%= $timestamp %></a></td>
            <td><%= $src %></a></td>
            <td><%= $dst %></a></td>
            <td><%= int($size/16000+0.5) %></td>
            <td><%= $size %></td>
          </tr>
%       $num++;
%       }
%       closedir $dh;
        </tbody>
    </table>
 
    <script>
        $.extend(true, $.fn.dataTable.defaults, {
            "searching": true,
            "ordering": true
        } );
 
        $(document).ready(function() {
            $('#table').DataTable();
        });
    </script>
% }
%#EOF
configure.ac
AC_INIT(recvi.pl)
AM_INIT_AUTOMAKE(recvi,0.01)
AC_PREFIX_DEFAULT(/usr/local)
 
PACKAGE=recvi
 
AC_CHECK_PROG(HAVE_PERL, perl, true, false, /usr/local/bin /usr/bin)
if test "x$HAVE_PERL" = "xfalse"; then
	AC_MSG_ERROR([Requested program perl not found])
fi
AC_SUBST(PERL, perl)
AC_PATH_PROG([PERL],[perl])
 
AC_PROG_INSTALL
 
AC_CANONICAL_HOST
 
case $host_os in
    *freebsd* )
	AC_SUBST(ROOT_GROUP, "wheel")
	AM_CONDITIONAL(FREEBSD_OS, true)
	AM_CONDITIONAL(LINUX_OS, false)
	OSNAME=freebsd
	ROOT_GROUP=wheel
        ;;
    *linux* )
	AC_SUBST(ROOT_GROUP, "root")
	AM_CONDITIONAL(FREEBSD_OS, false)
	AM_CONDITIONAL(LINUX_OS, true)
	OSNAME=linux
	ROOT_GROUP=root
        ;;
esac
 
AC_ARG_WITH(confdir,
	AS_HELP_STRING([--with-confdir=PATH],[set configuration dir to PATH (default: "${ac_default_prefix}"/etc/${PACKAGE})]),
	[ if test ! -z "$with_confdir" ; then
		case $with_app_confdir in
			/*)
				APP_CONFDIR="$with_confdir"
				;;
			*)
                                AC_MSG_ERROR(You must specify an absolute path to --with-confdir=PATH)
				;;
		esac
	else
		APP_CONFDIR="$ac_default_prefix/etc/${PACKAGE}"
	fi ],
	[
	APP_CONFDIR="$ac_default_prefix/etc/${PACKAGE}"
	])
 
AC_DEFINE_UNQUOTED(APP_CONFDIR, "$APP_CONFDIR", [location of configuration files for ${PACKAGE}])
AC_SUBST(APP_CONFDIR, "$APP_CONFDIR")
 
AC_ARG_WITH(logdir,
	AS_HELP_STRING([--with-logdir=PATH],[set file path for source logdir (default: /var/log/${PACKAGE}/${PACKAGE}.log)]),
	[ if test ! -z "$with_logdir" ; then
		case $with_logdir in
			/*)
				APP_LOGDIR="$with_logdir"
				;;
			*)
                                AC_MSG_ERROR(You must specify an absolute path to --with-logdir=PATH)
				;;
		esac
	else
		APP_LOGDIR="/var/log/${PACKAGE}"
	fi ],
	[ 
	APP_LOGDIR="/var/log/${PACKAGE}"
	])
 
AC_DEFINE_UNQUOTED(APP_LOGDIR, "$APP_LOGDIR", [location of ${PACKAGE} logdir])
AC_SUBST(APP_LOGDIR, "$APP_LOGDIR")
 
AC_ARG_WITH(rundir,
	AS_HELP_STRING([--with-rundir=PATH],[set file path for source rundir (default: /var/run/${PACKAGE})]),
	[ if test ! -z "$with_rundir" ; then
		case $with_rundir in
			/*)
				APP_RUNDIR="$with_rundir"
				;;
			*)
                                AC_MSG_ERROR(You must specify an absolute path to --with-rundir=PATH)
				;;
		esac
	else
		APP_RUNDIR="/var/run/${PACKAGE}"
	fi ],
	[
	APP_RUNDIR="/var/run/${PACKAGE}"
	])
 
AC_DEFINE_UNQUOTED(APP_RUNDIR, "$APP_RUNDIR", [location of pid file])
AC_SUBST(APP_RUNDIR, "$APP_RUNDIR")
 
dnl AC_ARG_WITH(dbdir,
dnl 	AS_HELP_STRING([--with-dbdir=PATH],[set file path for data files (default: "/var/db/${PACKAGE}")]),
dnl 	[ if test ! -z "$with_dbdir" ; then
dnl 		case $with_dbdir in
dnl 			/*)
dnl 				APP_DBDIR="$with_dbdir"
dnl 				;;
dnl 			*)
dnl                                 AC_MSG_ERROR(You must specify an absolute path to --with-dbdir=PATH)
dnl 				;;
dnl 		esac
dnl 	else
dnl 		APP_DBDIR="/var/db/${PACKAGE}"
dnl 	fi ],
dnl 	[ APP_DBDIR="/var/db/${PACKAGE}" ])
dnl AC_DEFINE_UNQUOTED(APP_DBDIR, "$APP_DBDIR", [location of application data])
dnl AC_SUBST(APP_DBDIR, "$APP_DBDIR")
 
default_storedir="/var/pgstore"
 
AC_ARG_WITH(storedir,
	AS_HELP_STRING([--with-storedir=PATH],[set data directory for pgstore (default: $default_storedir)]),
	[ if test ! -z "$with_storedir" ; then
		case $with_storedir in
			/*)
				PGSTORE_DATADIR="$with_storedir"
				;;
			*)
                                AC_MSG_ERROR(You must specify an absolute path to --with-storedir=PATH)
				;;
		esac
	else
		PGSTORE_DATADIR="$default_storedir"
	fi ],
	[
	PGSTORE_DATADIR="$default_storedir"
	])
 
AC_DEFINE_UNQUOTED(PGSTORE_DATADIR, "$PGSTORE_DATADIR", [location of pgstore data dir])
AC_SUBST(PGSTORE_DATADIR, "$PGSTORE_DATADIR")
 
case $host_os in
    *freebsd* )
	default_user="www"
	default_group="www"
        ;;
    *linux* )
	default_user="www-data"
	default_group="www-data"
        ;;
esac
 
AC_ARG_WITH(user,
	AS_HELP_STRING([--with-user=${PACKAGE}],[set executing user name]),
	[ if test ! -z "$with_user" ; then
		case $with_user in
			"")
				AC_MSG_ERROR(You must specify user name)
				;;
			*)
				APP_USER="$with_user"
				;;
		esac
	else
		APP_USER="$default_user"
	fi ],
	[ APP_USER="$default_user" ])
AC_DEFINE_UNQUOTED(APP_USER, "$APP_USER", [effective user])
AC_SUBST(APP_USER, "$APP_USER")
 
AC_ARG_WITH(group,
	AS_HELP_STRING([--with-group=${PACKAGE}],[set executing group name]),
	[ if test ! -z "$with_group" ; then
		case $with_group in
			"")
				AC_MSG_ERROR(You must specify group name)
				;;
			*)
				APP_GROUP="$with_group"
				;;
		esac
	else
		APP_GROUP="$default_group"
	fi ],
	[ APP_GROUP="$default_group" ])
AC_DEFINE_UNQUOTED(APP_GROUP, "$APP_GROUP", [effective group id])
AC_SUBST(APP_GROUP, "$APP_GROUP")
 
 
AC_DEFINE_UNQUOTED(APP_LIBDIR, ${ac_default_prefix}/share/${PACKAGE}, [application lib directory])
AC_SUBST(APP_LIBDIR, ${ac_default_prefix}/share/${PACKAGE})
 
 
AC_DEFUN([AC_PERL_MODULES],[
ac_perl_modules="$1"
for ac_perl_module in $ac_perl_modules; do
AC_MSG_CHECKING(for perl module $ac_perl_module)
perl "-M$ac_perl_module" -e exit > /dev/null 2>&1
if test $? -ne 0; then
    AC_MSG_RESULT(no);
    AC_MSG_ERROR(You must install perl module $ac_perl_module)
  else
    AC_MSG_RESULT(ok);
fi
done])
 
AC_PERL_MODULES([
POSIX
Apache::Htpasswd
DBI
File::Basename
File::stat
Sys::Hostname
Mojo::Base
Mojo::Home
Mojo::IOLoop::Subprocess
Mojo::JSON
Mojo::Server::Prefork
Mojo::UserAgent
Mojo::Util
])
 
AC_OUTPUT([
Makefile 
recvi:recvi.pl
rc.d/recvi
])
 
dnl EOF
Makefile.am
#
# $Id: Makefile.am 633 2017-04-15 13:51:07Z ziggi $
#
AUTOMAKE_OPTIONS = foreign no-dependencies no-installinfo
 
EXTRA_DIST = \
	LICENSE
 
install-data-hook:
if FREEBSD_OS
	chmod a+x $(DESTDIR)/${etcdir}/rc.d/recvi
endif
	$(INSTALL) -d -m 750 -o $(APP_USER) -g $(APP_GROUP) $(DESTDIR)$(APP_LOGDIR)
	$(INSTALL) -d -m 750 -o $(APP_USER) -g $(APP_GROUP) $(DESTDIR)$(APP_RUNDIR)
	for data in $(nobase_conf_DATA);do \
	  chmod 0644 $(DESTDIR)$(APP_CONFDIR)/$$data; \
	done
 
if FREEBSD_OS
etcdir = @prefix@/etc
nobase_etc_SCRIPTS = rc.d/recvi
endif
 
sbin_SCRIPTS = recvi
 
confdir = @APP_CONFDIR@
nobase_conf_DATA = \
	recvi.pw.example \
	recvi.crt.example \
	recvi.conf.example \
	recvi.key.example
 
nobase_dist_pkgdata_DATA = \
	public/css/app.css \
	public/css/datatables.css \
	public/css/datatables.min.css \
	public/css/foundation-float.css \
	public/css/foundation-float.min.css \
	public/css/foundation-prototype.css \
	public/css/foundation-prototype.min.css \
	public/css/foundation-rtl.css \
	public/css/foundation-rtl.min.css \
	public/css/foundation.css \
	public/css/foundation.min.css \
	public/favicon.ico \
	public/favicon.png \
	public/icons/foundation-icons.css \
	public/icons/foundation-icons.eot \
	public/icons/foundation-icons.svg \
	public/icons/foundation-icons.ttf \
	public/icons/foundation-icons.woff \
	public/images/sort_asc_disabled.png \
	public/images/sort_asc.png \
	public/images/sort_both.png \
	public/images/sort_desc_disabled.png \
	public/images/sort_desc.png \
	public/js/app.js \
	public/js/datatables.js \
	public/js/datatables.min.js \
	public/js/foundation.js \
	public/js/foundation.min.js \
	public/js/jquery.js \
	public/js/jquery.min.js \
	public/js/what-input.js \
	\
	templs/dataList.html.ep \
	templs/exception.development.html.ep \
	templs/exception.production.html.ep \
	templs/hello.html.ep \
	templs/layouts/default.html.ep \
	templs/not_found.development.html.ep \
	templs/not_found.production.html.ep \
	templs/login.html.ep
#EOF
default.html.ep
<!doctype html>
<html class="no-js" lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title><%= title %></title>
        <link rel="stylesheet" href="/css/foundation-float.min.css">
        <link rel="stylesheet" href="/css/datatables.css"/>
        <link rel="stylesheet" href="/css/app.css">
 
        <link rel="stylesheet" href="/icons/foundation-icons.css">
 
        <script src="/js/jquery.min.js"></script>
        <script src="/js/datatables.min.js"></script>
        <script src="/js/foundation.min.js"></script>
 
    </head>
 
    <body>
 
        <div class="title-bar" data-responsive-toggle="topbar-menu" data-hide-for="medium">
            <button class="menu-icon" type="button" data-toggle="topbar-menu"></button>
            <div class="title-bar-title">Menu</div>
        </div>
 
        <div class="top-bar" id="topbar-menu">
            <div class="top-bar-left">
                <ul class="dropdown menu" data-dropdown-menu>
                    <li class="menu-text">RecVi</li>
                    <li><a href="/data/list">Data</a></li>
                </ul>
          </div>
        </div>
 
        <div class="row">&nbsp;</div>
        <div class="row">
<!- end of head template ->
 
<%= content %>
 
<!- begin of tail template ->
            </div>
        </div>
        <hr/>
        <div class="row">
            <p class="text-center"><small>Made by <a href="http://wiki.unix7.org">Borodin Oleg</a></small></p>
        </div>
 
        <script src="/js/app.js"></script>
    </body>
</html>
<!- end of tail template ->
<!- EOF ->
login.html.ep
%#
%# $Id: login.html.ep 634 2017-04-15 13:55:49Z ziggi $
%#
<html class="no-js" lang="en" dir="ltr">
    <head>
        <meta charset="utf-8">
        <meta http-equiv="x-ua-compatible" content="ie=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Login</title>
        <link rel="stylesheet" href="/css/foundation-float.min.css">
        <link rel="stylesheet" href="/css/app.css">
 
        <link rel="stylesheet" href="/icons/foundation-icons.css">
 
        <script src="/js/jquery.min.js"></script>
        <script src="/js/foundation.min.js"></script>
    </head>
    <body>
        <div class="text-center" style="margin:5em 5em 5em 5em;">
            <form accept-charset="UTF-8" method="post" action="/login">
                <div class="row column">
                    <h4 class="text-center">Login with your username</h4>
                    <label>Username
                        <input type="text" name="username" placeholder="somename" />
                    </label>
                    <label>Password
                        <input type="password" name="password" placeholder="password" />
                    </label>
                    <p class="text-center">
                        <button type="submit" class="button">LogIn</button>
                    </p>
            </form>
        </div>
 
        <hr/>
        <div class="row">
            <p class="text-center"><small>Made by <a href="http://wiki.unix7.org">Borodin Oleg</a></small></p>
        </div>
 
        <script src="/js/app.js"></script>
    </body>
</html>
<!- EOF ->
%# EOF

First PagePrevious PageBack to overviewNext PageLast Page