diff --git a/application.conf.example b/application.conf.example index 490b847..078d6e5 100644 --- a/application.conf.example +++ b/application.conf.example @@ -1,4 +1,4 @@ { - password => '', db_file => 'sql/fotostore.db', + invite_code => 'very_secure_invite_code', } \ No newline at end of file diff --git a/fotostore.pl b/fotostore.pl old mode 100644 new mode 100755 index 543edb5..a3a559d --- a/fotostore.pl +++ b/fotostore.pl @@ -81,6 +81,45 @@ get '/logout' => sub { $self->render( text => 'bye' ); }; +get '/register' => ( authenticated => 0 ) => sub { + +}; + +post '/register' => ( authenticated => 0 ) => sub { + my $self = shift; + my $username = $self->req->param('username'); + my $password = $self->req->param('password'); + my $fullname = $self->req->param('fullname'); + my $invite = $self->req->param('invite'); + + if ($invite eq $config->{'invite_code'}) { + #chek that username is not taken + my $user = $db->get_user($username); + if ($user->{'user_id'} > 0) { + $self->render(template => 'error', message => 'Username already taken!'); + return 0; + } + + if ($fullname eq '') { + $fullname = $username; + } + + my $digest = $sha->add($password); + $db->add_user($username, $digest->hexdigest(), $fullname); + + #Authenticate user after add + if ( $self->authenticate( $username, $password ) ) { + $self->redirect_to('/'); + } + else { + $self->render( text => 'Login failed :(' ); + } + + } else { + $self->render(template => 'error', message => 'invalid invite code'); + } +}; + # Display top page get '/' => sub { my $self = shift; diff --git a/lib/FotoStore/DB.pm b/lib/FotoStore/DB.pm new file mode 100644 index 0000000..1ea57ed --- /dev/null +++ b/lib/FotoStore/DB.pm @@ -0,0 +1,67 @@ +package FotoStore::DB; + +use strict; +use warnings; + +use feature qw(signatures); +no warnings qw(experimental::signatures); + +sub new { + my $class = shift; + my $db_file = shift; + + my $dbh = DBI->connect(sprintf('dbi:SQLite:dbname=%s', $db_file),"",""); + my $self = { + dbh => $dbh, + }; + bless $self, $class; + return $self; +} + +sub check_user ($self, $nickname, $password) { + print STDERR "[$nickname][$password]"; + my ($user_id) = $self->{'dbh'}->selectrow_array(q~select user_id from users where nickname=? and password=?~, undef, ($nickname, $password)); + return $user_id; +} + +sub get_user ($self, $user_id) { + if ($user_id =~ /^\d+$/) { + return $self->_get_user_by_user_id($user_id); + } else { + return $self->_get_user_by_username($user_id); + } +} + +sub _get_user_by_user_id ($self, $user_id) { + my $user_data = $self->{'dbh'}->selectrow_hashref(q~select user_id, nickname, fullname, timestamp from users where user_id=?~, {}, ($user_id)); + return $user_data; +} + +sub _get_user_by_username($self, $username) { + my $user_data = $self->{'dbh'}->selectrow_hashref(q~select user_id, nickname, fullname, timestamp from users where nickname=?~, {}, ($username)); + return $user_data; +} + + +sub add_user($self, $username, $password, $fullname) { + my $rows = $self->{'dbh'}->do(q~insert into users (nickname, password, fullname) values (?, ?, ?)~, undef, ($username, $password, $fullname)); + if ($self->{'dbh'}->errstr) { + die $self->{'dbh'}->errstr; + } + return $rows; +} + + +sub add_file($self, $user_id, $filename) { + my $rows = $self->{'dbh'}->do(q~insert into images (owner_id, file_name) values (?, ?)~, undef, ($user_id, $filename)); + if ($self->{'dbh'}->errstr) { + die $self->{'dbh'}->errstr; + } + return $rows; +} + +sub get_files($self, $user_id, $count=20, $start_at=0) { + return $self->{'dbh'}->selectall_arrayref(q~select * from images where owner_id=? order by created_time desc~, { Slice => {} }, $user_id ); +} + +1; \ No newline at end of file diff --git a/public/css/main.css b/public/css/main.css new file mode 100644 index 0000000..58d8c9b --- /dev/null +++ b/public/css/main.css @@ -0,0 +1,9 @@ +.foto-block { + /* border: 1px solid black; */ +} +.foto-block .image { + padding: 5px; +} +.foto-block .foto-notes { + padding: 5px; +} \ No newline at end of file diff --git a/public/file_uploader/.gitignore b/public/file_uploader/.gitignore new file mode 100755 index 0000000..29a41a8 --- /dev/null +++ b/public/file_uploader/.gitignore @@ -0,0 +1,3 @@ +.DS_Store +*.pyc +node_modules diff --git a/public/file_uploader/.jshintrc b/public/file_uploader/.jshintrc new file mode 100755 index 0000000..4ad82e6 --- /dev/null +++ b/public/file_uploader/.jshintrc @@ -0,0 +1,81 @@ +{ + "bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.) + "camelcase" : true, // true: Identifiers must be in camelCase + "curly" : true, // true: Require {} for every new block or scope + "eqeqeq" : true, // true: Require triple equals (===) for comparison + "forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty() + "immed" : true, // true: Require immediate invocations to be wrapped in parens + // e.g. `(function () { } ());` + "indent" : 4, // {int} Number of spaces to use for indentation + "latedef" : true, // true: Require variables/functions to be defined before being used + "newcap" : true, // true: Require capitalization of all constructor functions e.g. `new F()` + "noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee` + "noempty" : true, // true: Prohibit use of empty blocks + "nonew" : true, // true: Prohibit use of constructors for side-effects (without assignment) + "plusplus" : false, // true: Prohibit use of `++` & `--` + "quotmark" : "single", // Quotation mark consistency: + // false : do nothing (default) + // true : ensure whatever is used is consistent + // "single" : require single quotes + // "double" : require double quotes + "undef" : true, // true: Require all non-global variables to be declared (prevents global leaks) + "unused" : true, // true: Require all defined variables be used + "strict" : true, // true: Requires all functions run in ES5 Strict Mode + "trailing" : true, // true: Prohibit trailing whitespaces + "maxparams" : false, // {int} Max number of formal params allowed per function + "maxdepth" : false, // {int} Max depth of nested blocks (within functions) + "maxstatements" : false, // {int} Max number statements per function + "maxcomplexity" : false, // {int} Max cyclomatic complexity per function + "maxlen" : false, // {int} Max number of characters per line + + // Relaxing + "asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons) + "boss" : false, // true: Tolerate assignments where comparisons would be expected + "debug" : false, // true: Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // true: Tolerate use of `== null` + "es5" : false, // true: Allow ES5 syntax (ex: getters and setters) + "esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`) + "moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features) + // (ex: `for each`, multiple try/catch, function expression…) + "evil" : false, // true: Tolerate use of `eval` and `new Function()` + "expr" : false, // true: Tolerate `ExpressionStatement` as Programs + "funcscope" : false, // true: Tolerate defining variables inside control statements" + "globalstrict" : false, // true: Allow global "use strict" (also enables 'strict') + "iterator" : false, // true: Tolerate using the `__iterator__` property + "lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block + "laxbreak" : false, // true: Tolerate possibly unsafe line breakings + "laxcomma" : false, // true: Tolerate comma-first style coding + "loopfunc" : false, // true: Tolerate functions being defined in loops + "multistr" : false, // true: Tolerate multi-line strings + "proto" : false, // true: Tolerate using the `__proto__` property + "scripturl" : false, // true: Tolerate script-targeted URLs + "smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment + "shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;` + "sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation + "supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;` + "validthis" : false, // true: Tolerate using this in a non-constructor function + + // Environments + "browser" : false, // Web Browser (window, document, etc) + "couch" : false, // CouchDB + "devel" : false, // Development/debugging (alert, confirm, etc) + "dojo" : false, // Dojo Toolkit + "jquery" : false, // jQuery + "mootools" : false, // MooTools + "node" : false, // Node.js + "nonstandard" : false, // Widely adopted globals (escape, unescape, etc) + "prototypejs" : false, // Prototype and Scriptaculous + "rhino" : false, // Rhino + "worker" : false, // Web Workers + "wsh" : false, // Windows Scripting Host + "yui" : false, // Yahoo User Interface + + // Legacy + "nomen" : true, // true: Prohibit dangling `_` in variables + "onevar" : true, // true: Allow only one `var` statement per function + "passfail" : false, // true: Stop on first error + "white" : true, // true: Check against strict whitespace and indentation rules + + // Custom Globals + "globals" : {} // additional predefined global variables +} diff --git a/public/file_uploader/.npmignore b/public/file_uploader/.npmignore new file mode 100755 index 0000000..0530f5d --- /dev/null +++ b/public/file_uploader/.npmignore @@ -0,0 +1,20 @@ +* +!css/jquery.fileupload-noscript.css +!css/jquery.fileupload-ui-noscript.css +!css/jquery.fileupload-ui.css +!css/jquery.fileupload.css +!img/loading.gif +!img/progressbar.gif +!js/cors/jquery.postmessage-transport.js +!js/cors/jquery.xdr-transport.js +!js/vendor/jquery.ui.widget.js +!js/jquery.fileupload-angular.js +!js/jquery.fileupload-audio.js +!js/jquery.fileupload-image.js +!js/jquery.fileupload-jquery-ui.js +!js/jquery.fileupload-process.js +!js/jquery.fileupload-ui.js +!js/jquery.fileupload-validate.js +!js/jquery.fileupload-video.js +!js/jquery.fileupload.js +!js/jquery.iframe-transport.js diff --git a/sql/deploy/album_images.sql b/sql/deploy/album_images.sql new file mode 100644 index 0000000..f9a6765 --- /dev/null +++ b/sql/deploy/album_images.sql @@ -0,0 +1,14 @@ +-- Deploy fotostore:album_images to sqlite +-- requires: images +-- requires: albums + +BEGIN; + +CREATE TABLE album_images ( + record_id INTEGER PRIMARY KEY AUTOINCREMENT, + album_id INTEGER REFERENCES albums (album_id), + image_id INTEGER REFERENCES images (file_id) +); + + +COMMIT; diff --git a/sql/deploy/albums.sql b/sql/deploy/albums.sql new file mode 100644 index 0000000..7232617 --- /dev/null +++ b/sql/deploy/albums.sql @@ -0,0 +1,14 @@ +-- Deploy fotostore:albums to sqlite + +BEGIN; + +CREATE TABLE albums ( + album_id INTEGER PRIMARY KEY AUTOINCREMENT, + name STRING NOT NULL, + description TEXT, + created DATETIME DEFAULT (CURRENT_TIMESTAMP), + modified DATETIME DEFAULT (CURRENT_TIMESTAMP), + deleted BOOLEAN +); + +COMMIT; diff --git a/sql/deploy/user_albums.sql b/sql/deploy/user_albums.sql new file mode 100644 index 0000000..d20541d --- /dev/null +++ b/sql/deploy/user_albums.sql @@ -0,0 +1,14 @@ +-- Deploy fotostore:user_albums to sqlite +-- requires: users +-- requires: albums + +BEGIN; + +CREATE TABLE user_albums ( + record_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER REFERENCES users (user_id), + album_id INTEGER REFERENCES albums (album_id) +); + + +COMMIT; diff --git a/sql/deploy/user_images.sql b/sql/deploy/user_images.sql new file mode 100644 index 0000000..098ecb3 --- /dev/null +++ b/sql/deploy/user_images.sql @@ -0,0 +1,12 @@ +-- Deploy fotostore:user_images to sqlite + +BEGIN; + +CREATE TABLE user_images ( + record_id INTEGER PRIMARY KEY AUTOINCREMENT, + user_id INTEGER REFERENCES users (user_id) ON DELETE CASCADE, + image_id INTEGER REFERENCES images (file_id) ON DELETE CASCADE +); + + +COMMIT; diff --git a/sql/revert/album_images.sql b/sql/revert/album_images.sql new file mode 100644 index 0000000..2771ccf --- /dev/null +++ b/sql/revert/album_images.sql @@ -0,0 +1,7 @@ +-- Revert fotostore:album_images from sqlite + +BEGIN; + +DROP TABLE album_images; + +COMMIT; diff --git a/sql/revert/albums.sql b/sql/revert/albums.sql new file mode 100644 index 0000000..53e8afa --- /dev/null +++ b/sql/revert/albums.sql @@ -0,0 +1,7 @@ +-- Revert fotostore:albums from sqlite + +BEGIN; + +DROP TABLE albums; + +COMMIT; diff --git a/sql/revert/user_albums.sql b/sql/revert/user_albums.sql new file mode 100644 index 0000000..d769c9b --- /dev/null +++ b/sql/revert/user_albums.sql @@ -0,0 +1,7 @@ +-- Revert fotostore:user_albums from sqlite + +BEGIN; + +DROP TABLE user_albums; + +COMMIT; diff --git a/sql/revert/user_images.sql b/sql/revert/user_images.sql new file mode 100644 index 0000000..8c29ff3 --- /dev/null +++ b/sql/revert/user_images.sql @@ -0,0 +1,7 @@ +-- Revert fotostore:user_images from sqlite + +BEGIN; + +DROP TABLE user_images; + +COMMIT; diff --git a/sql/verify/album_images.sql b/sql/verify/album_images.sql new file mode 100644 index 0000000..45067f5 --- /dev/null +++ b/sql/verify/album_images.sql @@ -0,0 +1,7 @@ +-- Verify fotostore:album_images on sqlite + +BEGIN; + +select record_id, album_id, image_id from album_images where 0; + +ROLLBACK; diff --git a/sql/verify/albums.sql b/sql/verify/albums.sql new file mode 100644 index 0000000..e45afb0 --- /dev/null +++ b/sql/verify/albums.sql @@ -0,0 +1,7 @@ +-- Verify fotostore:albums on sqlite + +BEGIN; + +select album_id, name, description, created, modified, deleted from albums where 0; + +ROLLBACK; diff --git a/sql/verify/user_albums.sql b/sql/verify/user_albums.sql new file mode 100644 index 0000000..241f528 --- /dev/null +++ b/sql/verify/user_albums.sql @@ -0,0 +1,7 @@ +-- Verify fotostore:user_albums on sqlite + +BEGIN; + +select record_id, album_id, user_id from user_albums where 0; + +ROLLBACK; diff --git a/sql/verify/user_images.sql b/sql/verify/user_images.sql new file mode 100644 index 0000000..50183a4 --- /dev/null +++ b/sql/verify/user_images.sql @@ -0,0 +1,7 @@ +-- Verify fotostore:user_images on sqlite + +BEGIN; + +select record_id, user_id, image_id from user_images where 0; + +ROLLBACK; diff --git a/templates/error.html.ep b/templates/error.html.ep new file mode 100644 index 0000000..a57d2e8 --- /dev/null +++ b/templates/error.html.ep @@ -0,0 +1,10 @@ + + + + Error + + + <%= $message %> + + + diff --git a/templates/images_list.html.ep b/templates/images_list.html.ep new file mode 100644 index 0000000..865ef6e --- /dev/null +++ b/templates/images_list.html.ep @@ -0,0 +1,54 @@ +
+
Logout
+
+ + + + + + +
+
+
+
+
+
+ + +
+
+<% foreach my $image (@$images) { %> +
+
+ "> +
+
+ Image original + <% for my $scale (@$scales) { %> + <%= $scale %> + <% } %> +
+ +
+<% } %> +
+
\ No newline at end of file diff --git a/templates/index.html.ep b/templates/index.html.ep new file mode 100644 index 0000000..bcf2fac --- /dev/null +++ b/templates/index.html.ep @@ -0,0 +1,18 @@ +% layout 'base'; +

Rough, Slow, Stupid, Contrary Photohosting

+ +<% if (is_user_authenticated()) { %> + %= include 'images_list' +<% } else { %> + +
+
+ + + +
+
+ +<% } %> + + diff --git a/templates/layouts/base.html.ep b/templates/layouts/base.html.ep new file mode 100644 index 0000000..a2dc11e --- /dev/null +++ b/templates/layouts/base.html.ep @@ -0,0 +1,29 @@ + + + + Rough, Slow, Stupid, Contrary Photohosting + + + + + + + + + + + + + + + + + + <%= content %> + + \ No newline at end of file diff --git a/templates/no_logged.html.ep b/templates/no_logged.html.ep new file mode 100644 index 0000000..a63909b --- /dev/null +++ b/templates/no_logged.html.ep @@ -0,0 +1,17 @@ + + + + Rough, Slow, Stupid, Contrary Photohosting + + +

Rough, Slow, Stupid, Contrary Photohosting

+
+
+ + + +
+
+ + + diff --git a/templates/register.html.ep b/templates/register.html.ep new file mode 100644 index 0000000..09f7823 --- /dev/null +++ b/templates/register.html.ep @@ -0,0 +1,25 @@ +% layout 'base'; + +
+
+
+
+

+ Login: + +

+ Fullname: + +

+ Password: + +

+ Invite code: + +

+ +

+
+
+
+
\ No newline at end of file