Permalink
Browse files

initial commit

  • Loading branch information...
0 parents commit de6d1d4283e30a7219c08ad52f9c539a02feb84d @dragon3 committed Jun 19, 2011
@@ -0,0 +1,9 @@
+Makefile
+inc/
+MANIFEST
+*.bak
+*.old
+nytprof.out
+nytprof/
+development.db
+test.db
@@ -0,0 +1,29 @@
+use ExtUtils::MakeMaker;
+
+WriteMakefile(
+ NAME => 'CloudWatchViewer',
+ AUTHOR => 'Ryuzo Yamamoto <ryuzo.yamamoto@gmail.com>',
+ VERSION_FROM => 'lib/CloudWatchViewer.pm',
+ PREREQ_PM => {
+ 'Amon2' => '2.38',
+ 'Amon2::DBI' => '0.05',
+ 'Text::Xslate' => '1.1005',
+ 'Text::Xslate::Bridge::TT2Like' => '0.00008',
+ 'Plack::Middleware::ReverseProxy' => '0.09',
+ 'Plack::Middleware::Auth::Basic' => 0,
+ 'HTML::FillInForm::Lite' => '1.09',
+ 'Time::Piece' => '1.20',
+ 'Log::Minimal' => '0.08',
+ 'JSON::Syck' => '0.40',
+ 'Furl' => '0.32',
+ 'URI::Escape' => '3.30',
+ 'IO::Socket::SSL' => '1.44',
+ 'DBD::SQLite' => 0,
+ 'DBD::mysql' => 0,
+ 'Config::Pit' => 0,
+ },
+ MIN_PERL_VERSION => '5.008001',
+ ( -d 'xt' and $ENV{AUTOMATED_TESTING} || $ENV{RELEASE_TESTING} )
+ ? ( test => { TESTS => 't/*.t xt/*.t', }, )
+ : (),
+);
@@ -0,0 +1,31 @@
+use File::Spec;
+use File::Basename;
+use lib File::Spec->catdir( dirname(__FILE__), 'extlib', 'lib', 'perl5' );
+use lib File::Spec->catdir( dirname(__FILE__), 'lib' );
+use CloudWatchViewer::Web;
+use CloudWatchViewer::API;
+use Plack::Builder;
+
+use Config::Pit;
+my $config = pit_get(
+ "cloudwatchviewer",
+ require => {
+ ba_user => 'basic auth user',
+ ba_pass => 'basic auth pass',
+ }
+);
+
+builder {
+ enable 'Plack::Middleware::Static',
+ path => qr{^(?:/static/|/robot\.txt$|/favicon.ico$)},
+ root => File::Spec->catdir( dirname(__FILE__) );
+ enable 'Plack::Middleware::ReverseProxy';
+ mount '/api/' => CloudWatchViewer::API->to_app();
+ mount '/' => builder {
+ enable "Auth::Basic", authenticator => sub {
+ my ( $u, $p ) = @_;
+ return $u eq $config->{ba_user} && $p eq $config->{ba_pass};
+ };
+ CloudWatchViewer::Web->to_app();
+ };
+};
@@ -0,0 +1,18 @@
+use Config::Pit;
+my $config = pit_get(
+ "cloudwatchviewer",
+ require => {
+ db_user => 'db user',
+ db_pass => 'db pass',
+ db_port => 'db port',
+ }
+);
+
+return +{
+ 'DBI' => [
+ 'dbi:mysql:dbname=cwv;host=cwvdb.dragon3.dotcloud.com;port='
+ . $config->{db_port},
+ $config->{db_user}, $config->{db_pass},
+ +{ mysql_enable_utf8 => 1, }
+ ],
+};
@@ -0,0 +1,15 @@
++{
+ 'DBI' =>
+ [ 'dbi:SQLite:dbname=development.db', '', '', +{ sqlite_unicode => 1, } ],
+ 'Log::Dispatch' => {
+ outputs => [
+ [
+ 'Screen::Color',
+ min_level => 'debug',
+ name => 'debug',
+ stderr => 1,
+ color => { debug => { text => 'green', } }
+ ],
+ ],
+ },
+};
@@ -0,0 +1,10 @@
++{
+ 'DBI' => [
+ 'dbi:SQLite:dbname=test.db',
+ '',
+ '',
+ +{
+ sqlite_unicode => 1,
+ }
+ ],
+};
@@ -0,0 +1,10 @@
+package CloudWatchViewer;
+use strict;
+use warnings;
+use parent qw/Amon2/;
+our $VERSION = '0.01';
+use 5.008001;
+
+__PACKAGE__->load_plugin(qw/DBI/);
+
+1;
@@ -0,0 +1,22 @@
+package CloudWatchViewer::API;
+use strict;
+use warnings;
+use parent qw/CloudWatchViewer Amon2::Web/;
+use File::Spec;
+
+# load all controller classes
+use Module::Find ();
+Module::Find::useall("CloudWatchViewer::API::C");
+
+# dispatcher
+use CloudWatchViewer::API::Dispatcher;
+
+sub dispatch {
+ return CloudWatchViewer::API::Dispatcher->dispatch( $_[0] )
+ or die "response is not generated";
+}
+
+# load plugins
+__PACKAGE__->load_plugins('Web::JSON');
+
+1;
@@ -0,0 +1,51 @@
+package CloudWatchViewer::API::Dispatcher;
+use strict;
+use warnings;
+use Amon2::Web::Dispatcher::Lite;
+use Log::Minimal;
+use JSON::Syck;
+use Furl;
+use URI::Escape;
+
+any '/subscribe' => sub {
+ my ($c) = @_;
+ my $content = $c->req->content;
+ infof($content);
+
+ my $data = JSON::Syck::Load($content);
+
+ # TODO : check signature
+
+ # TODO : Notification
+ if ( $data->{Type} eq 'Notification' ) {
+ }
+
+ # SubscriptionConfirmation
+ elsif ( $data->{Type} eq 'SubscriptionConfirmation' ) {
+ my $url = $data->{SigningCertURL};
+ $url =~ s|\.amazonaws\.com/.+$|.amazonaws.com/|;
+ $url .= '?Action=ConfirmSubscription&Version=2010-03-31';
+ $url .= '&Token=' . uri_escape( $data->{Token} );
+ $url .= '&TopicArn=' . uri_escape( $data->{TopicArn} );
+ infof( 'SubscriptionConfirmation request: ' . $url );
+
+ my $furl = Furl->new( timeout => 30, );
+ my $res = $furl->get($url);
+ warnf( $res->status_line ) unless $res->is_success;
+ infof( $res->content );
+ }
+
+ # store data
+ _store( $c->dbh, $content );
+
+ $c->render_json( {} );
+};
+
+sub _store {
+ my ( $dbh, $content ) = @_;
+ my $sth = $dbh->prepare("INSERT INTO notifications VALUES (?,?)");
+ $sth->execute( time, $content );
+ $sth->finish;
+}
+
+1;
@@ -0,0 +1,71 @@
+package CloudWatchViewer::Web;
+use strict;
+use warnings;
+use parent qw/CloudWatchViewer Amon2::Web/;
+use File::Spec;
+
+# load all controller classes
+use Module::Find ();
+Module::Find::useall("CloudWatchViewer::Web::C");
+
+# dispatcher
+use CloudWatchViewer::Web::Dispatcher;
+
+sub dispatch {
+ return CloudWatchViewer::Web::Dispatcher->dispatch( $_[0] )
+ or die "response is not generated";
+}
+
+# setup view class
+use Text::Xslate;
+{
+ my $view_conf = __PACKAGE__->config->{'Text::Xslate'} || +{};
+ unless ( exists $view_conf->{path} ) {
+ $view_conf->{path} =
+ [ File::Spec->catdir( __PACKAGE__->base_dir(), 'tmpl' ) ];
+ }
+ my $view = Text::Xslate->new(
+ +{
+ 'syntax' => 'TTerse',
+ 'module' => ['Text::Xslate::Bridge::TT2Like'],
+ 'function' => {
+ c => sub { Amon2->context() },
+ uri_with => sub { Amon2->context()->req->uri_with(@_) },
+ uri_for => sub { Amon2->context()->uri_for(@_) },
+ },
+ %$view_conf
+ }
+ );
+ sub create_view { $view }
+}
+
+# load plugins
+use HTTP::Session::Store::File;
+__PACKAGE__->load_plugins(
+ 'Web::FillInFormLite',
+ 'Web::NoCache', # do not cache the dynamic content by default
+ 'Web::CSRFDefender',
+ 'Web::HTTPSession' => {
+ state => 'Cookie',
+ store => HTTP::Session::Store::File->new( dir => File::Spec->tmpdir(), )
+ },
+);
+
+# for your security
+__PACKAGE__->add_trigger(
+ AFTER_DISPATCH => sub {
+ my ( $c, $res ) = @_;
+ $res->header( 'X-Content-Type-Options' => 'nosniff' );
+ },
+);
+
+__PACKAGE__->add_trigger(
+ BEFORE_DISPATCH => sub {
+ my ($c) = @_;
+
+ # ...
+ return;
+ },
+);
+
+1;
@@ -0,0 +1,42 @@
+package CloudWatchViewer::Web::Dispatcher;
+use strict;
+use warnings;
+use Amon2::Web::Dispatcher::Lite;
+use JSON::Syck;
+
+any '/' => sub {
+ my ($c) = @_;
+ my $sth =
+ $c->dbh->prepare("SELECT ts, data FROM notifications ORDER BY ts DESC");
+ $sth->execute;
+ my @notifications = ();
+ while ( my $ref = $sth->fetchrow_arrayref ) {
+ push( @notifications, _parse_notification( $ref->[0], $ref->[1] ) );
+ }
+ $sth->finish;
+ $c->render( 'index.tt', { notifications => \@notifications } );
+};
+
+sub _parse_notification {
+ my ( $ts, $json ) = @_;
+ my $data = JSON::Syck::Load($json);
+ my $notification = {
+ ts => $ts,
+ Type => $data->{Type},
+ Timestamp => $data->{Timestamp},
+ MessageId => $data->{MessageId},
+ Subject => $data->{Subject} || '',
+ Message => $data->{Message},
+ UnsubscribeURL => $data->{UnsubscribeURL} || '',
+ };
+
+ # ALARM
+ if ( $notification->{Message} =~ /AlarmName/ ) {
+ if ( $notification->{Subject} =~ /\[(OK|ALARM|INSUFFICIENT_DATA)\]/ ) {
+ $notification->{AlarmType} = $1;
+ }
+ }
+ return $notification;
+}
+
+1;
9 readme
@@ -0,0 +1,9 @@
+* webapp
+dotcloud deploy -t perl dragon3.cwv
+
+* mysql
+dotcloud deploy -t mysql dragon3.cwvdb
+dotcloud info dragon3.cwvdb
+dotcloud run louis.db -- mysql -uroot \'-p$ROOT_PASSWORD\'
+create database cwv;
+
@@ -0,0 +1,4 @@
+CREATE TABLE notifications (
+ ts int(11) PRIMARY KEY,
+ data MEDIUMTEXT
+);
@@ -0,0 +1,4 @@
+CREATE TABLE notifications IF NOT EXISTS (
+ ts INTEGER PRIMARY KEY,
+ data TEXT
+ );
@@ -0,0 +1,26 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8 />
+ <style type="text/css">
+ body {
+ text-align: center;
+ font-family: 'Menlo', 'Monaco', Courier, monospace;
+ background-color: whitesmoke;
+ padding-top: 10%;
+ }
+ .number {
+ font-size: 800%;
+ font-weight: bold;
+ margin-bottom: 40px;
+ }
+ .message {
+ font-size: 400%;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="number">404</div>
+ <div class="message">Not Found</div>
+ </body>
+</html>
@@ -0,0 +1,26 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8 />
+ <style type="text/css">
+ body {
+ text-align: center;
+ font-family: 'Menlo', 'Monaco', Courier, monospace;
+ background-color: whitesmoke;
+ padding-top: 10%;
+ }
+ .number {
+ font-size: 800%;
+ font-weight: bold;
+ margin-bottom: 40px;
+ }
+ .message {
+ font-size: 400%;
+ }
+ </style>
+ </head>
+ <body>
+ <div class="number">500</div>
+ <div class="message">Internal Server Error</div>
+ </body>
+</html>
Oops, something went wrong.

0 comments on commit de6d1d4

Please sign in to comment.