diff --git a/.dancer b/.dancer deleted file mode 100644 index e69de29..0000000 diff --git a/TODO b/TODO index 0df49df..c274ccd 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,6 @@ - thumbnailer / caching component - query media by hash - media upload -- consider mojolicious - account registration - use email for confirmation and reset, but don't store email; instead store a crypt(3) of it diff --git a/bin/app.psgi b/bin/app.psgi deleted file mode 100755 index bbfc804..0000000 --- a/bin/app.psgi +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/env perl - -use strict; -use warnings; -use FindBin; -use lib "$FindBin::Bin/../lib"; - -use PoorBooru; -use PoorBooru::API::V0; -use Plack::Builder; - -builder { - mount "/" => PoorBooru->to_app; - mount "/api/v0" => PoorBooru::API::V0->to_app; -}; diff --git a/config.yml b/config.yml deleted file mode 100644 index f8cc123..0000000 --- a/config.yml +++ /dev/null @@ -1,68 +0,0 @@ -# This is the main configuration file of your Dancer2 app -# env-related settings should go to environments/$env.yml -# all the settings in this file will be loaded at Dancer's startup. - -# === Basic configuration === - -# Your application's name -appname: "PoorBooru" - -# The default layout to use for your application (located in -# views/layouts/main.tt) -layout: "main" - -# when the charset is set to UTF-8 Dancer2 will handle for you -# all the magic of encoding and decoding. You should not care -# about unicode within your app when this setting is set (recommended). -charset: "UTF-8" - -# === Engines === -# -# NOTE: All the engine configurations need to be under a single "engines:" -# key. If you uncomment engine configurations below, make sure to delete -# all "engines:" lines except the first. Otherwise, only the last -# "engines:" block will take effect. - -# template engine -# simple: default and very basic template engine -# template_toolkit: TT - -template: "tiny" - -# template: "template_toolkit" -# engines: -# template: -# template_toolkit: -# # Note: start_tag and end_tag are regexes -# start_tag: '<%' -# end_tag: '%>' - -# session engine -# -# Simple: in-memory session store - Dancer2::Session::Simple -# YAML: session stored in YAML files - Dancer2::Session::YAML -# -# Check out metacpan for other session storage options: -# https://metacpan.org/search?q=Dancer2%3A%3ASession&search_type=modules -# -# Default value for 'cookie_name' is 'dancer.session'. If you run multiple -# Dancer apps on the same host then you will need to make sure 'cookie_name' -# is different for each app. -# -#engines: -# session: -# Simple: -# cookie_name: testapp.session -# -#engines: -# session: -# YAML: -# cookie_name: eshop.session -# is_secure: 1 -# is_http_only: 1 - -plugins: - DBIC: - default: - dsn: "dbi:SQLite:dbname=db/PoorBooru.db" - schema_class: PoorBooru::Schema diff --git a/cpanfile b/cpanfile deleted file mode 100644 index 52876da..0000000 --- a/cpanfile +++ /dev/null @@ -1,34 +0,0 @@ -requires "Dancer2" => "0.400000"; - -recommends "YAML" => "0"; -recommends "URL::Encode::XS" => "0"; -recommends "CGI::Deurl::XS" => "0"; -recommends "CBOR::XS" => "0"; -recommends "YAML::XS" => "0"; -recommends "Class::XSAccessor" => "0"; -recommends "Crypt::URandom" => "0"; -recommends "HTTP::XSCookies" => "0"; -recommends "HTTP::XSHeaders" => "0"; -recommends "Math::Random::ISAAC::XS" => "0"; -recommends "MooX::TypeTiny" => "0"; -recommends "Type::Tiny::XS" => "0"; - -feature 'accelerate', 'Accelerate Dancer2 app performance with XS modules' => sub { - requires "URL::Encode::XS" => "0"; - requires "CGI::Deurl::XS" => "0"; - requires "YAML::XS" => "0"; - requires "Class::XSAccessor" => "0"; - requires "Cpanel::JSON::XS" => "0"; - requires "Crypt::URandom" => "0"; - requires "HTTP::XSCookies" => "0"; - requires "HTTP::XSHeaders" => "0"; - requires "Math::Random::ISAAC::XS" => "0"; - requires "MooX::TypeTiny" => "0"; - requires "Type::Tiny::XS" => "0"; -}; - -on "test" => sub { - requires "Test::More" => "0"; - requires "HTTP::Request::Common" => "0"; -}; - diff --git a/environments/development.yml b/environments/development.yml deleted file mode 100644 index 04d65e7..0000000 --- a/environments/development.yml +++ /dev/null @@ -1,22 +0,0 @@ -# configuration file for development environment - -# the logger engine to use -# console: log messages to STDOUT (your console where you started the -# application server) -# file: log message to a file in log/ -logger: "console" - -# the log level for this environment -# core is the lowest, it shows Dancer2's core log messages as well as yours -# (debug, info, warning and error) -log: "core" - -# should Dancer2 show a stacktrace when an 5xx error is caught? -# if set to yes, public/500.html will be ignored and either -# views/500.tt, 'error_template' template, or a default error template will be used. -show_errors: 1 - -# print the banner -startup_info: 1 - -poorbooru_api: "http://localhost:8080/api/v0" diff --git a/environments/production.yml b/environments/production.yml deleted file mode 100644 index d86c30c..0000000 --- a/environments/production.yml +++ /dev/null @@ -1,13 +0,0 @@ -# configuration file for production environment - -# only log warning and error messsages -log: "warning" - -# log message to a file in logs/ -logger: "file" - -# hide errors -show_errors: 0 - -# disable server tokens in production environments -no_server_tokens: 1 diff --git a/lib/PoorBooru.pm b/lib/PoorBooru.pm deleted file mode 100644 index f9ee01e..0000000 --- a/lib/PoorBooru.pm +++ /dev/null @@ -1,169 +0,0 @@ -package PoorBooru; -use v5.32; -use Dancer2; -use HTTP::Tiny; - - -our $VERSION = v0.0.1; - -my $POORBOORU_API = setting("poorbooru_api"); - - -sub validate_page_number ($) { $_[0] =~ /^[1-9][0-9]*$/ } - -sub api_request ($$;%) -{ - my ($method, $path, $params_hashref) = @_; - - my $http = HTTP::Tiny->new( - timeout => 15, - verify_SSL => true, - agent => setting("appname") . " backend " . - version::->parse($VERSION)->normal, - - default_headers => { - "Accept" => "application/json", - "Content-Type" => "application/json", - }, - ); - my $params = $http->www_form_urlencode($params_hashref // {}); - - return $http->request($method, "$POORBOORU_API$path" . - ($params ne "" ? "?$params" : "")); -} - -sub api_get ($;%) { api_request("GET", shift, @_) } - -sub nav_pager ($$) -{ - my ($p, $uri) = @_; - - my (%pager, %res); - %pager = %$p; - $res{first} = $pager{first} != $pager{cur} ? - uri_for($uri, { page => $pager{first} }) : undef; - $res{prev} = defined($pager{prev}) && $pager{prev} != $pager{first} ? - uri_for($uri, { page => $pager{prev} }) : undef; - $res{cur} = $pager{cur}; - $res{next} = defined($pager{next}) && $pager{next} != $pager{last} ? - uri_for($uri, { page => $pager{next} }) : undef; - $res{last} = $pager{last} != $pager{cur} ? - uri_for($uri, { page => $pager{last} }) : undef; - - return \%res; -} - - -hook before_template_render => sub { - my $tokens = shift; - - $tokens->{uris}->{root} = uri_for("/"); - $tokens->{uris}->{login} = uri_for("/login"); - $tokens->{uris}->{logout} = uri_for("/logout"); - $tokens->{uris}->{random} = uri_for("/random/media"); - $tokens->{uris}->{tags} = uri_for("/tags"); -}; - -get "/" => sub { - my $page = query_parameters->get("page") // 1; - send_error("Invalid page number", 400) if !validate_page_number($page); - - my $res = api_get("/media", { page => $page }); - send_error("API error", 500) if !$res->{success}; - - my $data = decode_json($res->{content}); - my @media = map +{ - # XXX point to a cache - image_src => "$POORBOORU_API/download/$_", - media_uri => uri_for("/media/$_"), - }, @{$data->{media}}; - - template "gallery" => { - title => "PoorBooru", - media => \@media, - pager => nav_pager($data->{pager}, "/"), - }; -}; - -get "/tags" => sub { - my $page = query_parameters->get("page") // 1; - send_error("Invalid page number", 400) if !validate_page_number($page); - - my $res = api_get("/tags", { page => $page }); - send_error("API error", 500) if !$res->{success}; - - my $data = decode_json($res->{content}); - my @tags = map +{ - name => $_->{name}, - count => $_->{count}, - uri => uri_for("/tag/$_->{name}"), - }, @{$data->{tags}}; - - template "tags" => { - title => "Tags", - tags => \@tags, - pager => nav_pager($data->{pager}, "/tags"), - }; -}; - -get "/tag/:tag_id_or_name" => sub { - my $page = query_parameters->get("page") // 1; - send_error("Invalid page number", 400) if !validate_page_number($page); - - my $tag_id_or_name = route_parameters->get("tag_id_or_name"); - - my $res = api_get("/tag/$tag_id_or_name", { page => $page }); - send_error("API error", 500) if !$res->{success}; - - my $data = decode_json($res->{content}); - my @media = map +{ - # XXX point to a cache - image_src => "$POORBOORU_API/download/$_", - media_uri => uri_for("/media/$_"), - }, @{$data->{media}}; - - template "gallery" => { - title => $data->{name}, - media => \@media, - pager => nav_pager($data->{pager}, "/tag/$tag_id_or_name"), - }; -}; - -get "/media/:media_id" => sub { - my $media_id = route_parameters->get("media_id"); - - my $res = api_get("/media/$media_id"); - send_error("API error", 500) if !$res->{success}; - - my $data = decode_json($res->{content}); - - template "media" => { - no_title => 1, - title => $data->{id}, - image_src => "$POORBOORU_API/download/$data->{id}", - media_id => $data->{id}, - media_filename => $data->{filename}, - media_size => $data->{size}, - media_tags => [], - }; -}; - -get "/random/media" => sub { - my $res = api_get("/random/media"); - send_error("API error", 500) if !$res->{success}; - - my $data = decode_json($res->{content}); - # XXX body content. - redirect "/media/$data->{id}"; -}; - -get "/random/tag" => sub { - my $res = api_get("/random/tag"); - send_error("API error", 500) if !$res->{success}; - - my $data = decode_json($res->{content}); - # XXX body content. - redirect "/tag/$data->{name}"; -}; - -true; diff --git a/lib/PoorBooru/API/V0.pm b/lib/PoorBooru/API/V0.pm deleted file mode 100644 index 311aecf..0000000 --- a/lib/PoorBooru/API/V0.pm +++ /dev/null @@ -1,176 +0,0 @@ -package PoorBooru::API::V0; -use v5.32; -use Dancer2; -use Dancer2::Plugin::DBIC; - - -use constant { - DEFAULT_CONTENT_TYPE => "application/json", -}; - -our $VERSION = v0.0.1; - -my $MEDIA_SEARCH_OPTS = { - order_by => { -desc => "media_id" }, - rows => 20, -}; -my $TAGS_COUNT_VIEW_SEARCH_OPTS = { - order_by => { -desc => "count" }, - rows => 100, -}; - - -sub validate_page_number ($) { $_[0] =~ /^[1-9][0-9]*$/ } -sub validate_tag_name ($) { $_[0] !~ /^[1-9][0-9]*$/ && $_[0] =~ /^\w+$/ } - -sub mkpager ($) -{ - my $pager = $_[0]->pager; - return { - first => $pager->first_page, - prev => $pager->previous_page, - cur => $pager->current_page, - next => $pager->next_page, - last => $pager->last_page, - }; -} - - -set serializer => "JSON"; - - -get "/meta" => sub { - return [ - { path => "/meta", verb => "GET" }, - { path => "/tags", verb => "GET" }, - { path => "/tags", verb => "POST" }, - { path => "/tag/:tag_id_or_name", verb => "GET" }, - { path => "/media/:media_id", verb => "GET" }, - { path => "/download/:media_id", verb => "GET" }, - { path => "/random", verb => "GET" }, - ], -}; - -get "/tags" => sub { - my $page = query_parameters->get("page") // 1; - send_error("Invalid page number", 400) if !validate_page_number($page); - - my $paged_tags = schema("default")->resultset("TagsCountView") - ->search({}, $TAGS_COUNT_VIEW_SEARCH_OPTS)->page($page); - my @tags = map +{ - id => $_->tag_id, - name => $_->name, - count => $_->count, - }, $paged_tags->all; - - return { - pager => mkpager($paged_tags), - tags => \@tags, - } -}; - -post "/tags" => sub { - my @tag_names = body_parameters->get_all("name"); - send_error("No tags provided", 400) if @tag_names == 0; - send_error("Too many tags provided", 400) if @tag_names > 100; - send_error("Invalid tag names", 400) if - grep { validate_tag_name($_) } @tag_names; - - my @tags; - eval { - @tags = map +{ - id => $_->tag_id, - name => $_->name, - }, schema("default")->resultset("Tag") - ->populate([map +{ name => $_ }, @tag_names]); - } or send_error("Tag exists", 409); - - return \@tags; -}; - -get "/tag/:tag_id_or_name" => sub { - my $page = query_parameters->get("page") // 1; - send_error("Invalid page number", 400) if !validate_page_number($page); - - my $tag_id_or_name = route_parameters->get("tag_id_or_name"); - - my $query = [ - { tag_id => $tag_id_or_name }, - { name => $tag_id_or_name }, - ]; - my $tag = schema("default")->resultset("Tag")->single($query); - send_error("Tag not found", 404) if !defined($tag); - - my $paged_media = schema("default")->resultset("MediaTag") - ->search({ tag_id => $tag->tag_id }, $MEDIA_SEARCH_OPTS) - ->page($page); - my @media = map { $_->media_id } $paged_media->all; - - return { - id => $tag->tag_id, - name => $tag->name, - pager => mkpager($paged_media), - media => \@media, - }; -}; - -get "/random/tag" => sub { - my $tag = schema("default")->resultset("Tag") - ->search({}, { order_by => \"random()", limit => 1 })->single; - - send_error("Tag not found", 404) if !defined($tag); - - forward "/tag/" . $tag->tag_id; -}; - -get "/media" => sub { - my $page = query_parameters->get("page") // 1; - send_error("Invalid page number", 400) if !validate_page_number($page); - - my $paged_media = schema("default")->resultset("Media") - ->search({}, $MEDIA_SEARCH_OPTS)->page($page); - my @media = map { $_->media_id } $paged_media->all; - - return { - pager => mkpager($paged_media), - media => \@media, - }; -}; - -get "/media/:media_id" => sub { - my $media = schema("default")->resultset("Media") - ->single({ media_id => route_parameters->get("media_id") }); - - send_error("Media not found", 404) if !defined($media); - - return { - id => $media->media_id, - filename => $media->filename, - size => length($media->content), - download_path => "/download/" . $media->media_id, - }; -}; - -get "/download/:media_id" => sub { - my $media = schema("default")->resultset("Media") - ->single({ media_id => route_parameters->get("media_id") }); - - send_error("Media not found", 404) if !defined($media); - - send_file( - \$media->content, - content_type => $media->content_type // DEFAULT_CONTENT_TYPE, - filename => $media->filename, - ); -}; - -get "/random/media" => sub { - my $media = schema("default")->resultset("Media") - ->search({}, { order_by => \"random()", limit => 1 })->single; - - send_error("Media not found", 404) if !defined($media); - - forward "/media/" . $media->media_id; -}; - -true; diff --git a/views/gallery.tt b/views/gallery.tt deleted file mode 100644 index e77a8c6..0000000 --- a/views/gallery.tt +++ /dev/null @@ -1,7 +0,0 @@ - diff --git a/views/layouts/main.tt b/views/layouts/main.tt deleted file mode 100644 index e1fef2b..0000000 --- a/views/layouts/main.tt +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - [% settings.appname %] - [% title %] - - - -
- -
-
-
-[% UNLESS no_title %] -

[% title %]

-[% END %] -
[% content %]
-[% IF pager %] - -[% END %] -
-
- - - diff --git a/views/media.tt b/views/media.tt deleted file mode 100644 index b986531..0000000 --- a/views/media.tt +++ /dev/null @@ -1,21 +0,0 @@ -
- -
- -

metadata

- -
-
ID
-
[% media_id %]
-
Filename
-
[% media_filename %]
-
Size
-
[% media_size %]
-
Tags
-[% UNLESS media_tags %] -
no tags
-[% END %] -[% FOREACH tag IN media_tags -%] -
[% tag.name %]
-[% END -%] -
diff --git a/views/tags.tt b/views/tags.tt deleted file mode 100644 index 78e6fc1..0000000 --- a/views/tags.tt +++ /dev/null @@ -1,5 +0,0 @@ -

-[% FOREACH tag IN tags -%] -[% tag.name %] ([% tag.count %]) -[% END -%] -