Initial import
This commit is contained in:
commit
1ff831f80d
19 changed files with 956 additions and 0 deletions
37
lib/PoorBooru.pm
Normal file
37
lib/PoorBooru.pm
Normal file
|
@ -0,0 +1,37 @@
|
|||
package PoorBooru;
|
||||
use Dancer2;
|
||||
use HTTP::Tiny;
|
||||
|
||||
our $VERSION = v0.1;
|
||||
|
||||
my $POORBOORU_API = setting("poorbooru_api");
|
||||
|
||||
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");
|
||||
$tokens->{uris}->{tags} = uri_for("/tags");
|
||||
};
|
||||
|
||||
get "/" => sub {
|
||||
template "index" => {
|
||||
"title" => "main",
|
||||
};
|
||||
};
|
||||
|
||||
get "/tags" => sub {
|
||||
};
|
||||
|
||||
get "/tag/:tag_id" => sub {
|
||||
};
|
||||
|
||||
get "/random" => sub {
|
||||
};
|
||||
|
||||
get "/image/:image_id" => sub {
|
||||
};
|
||||
|
||||
true;
|
113
lib/PoorBooru/API/V0.pm
Normal file
113
lib/PoorBooru/API/V0.pm
Normal file
|
@ -0,0 +1,113 @@
|
|||
package PoorBooru::API::V0;
|
||||
use Dancer2;
|
||||
use Dancer2::Plugin::DBIC;
|
||||
|
||||
our $VERSION = v0;
|
||||
|
||||
set serializer => "JSON";
|
||||
set database => "db/booru.db";
|
||||
|
||||
my $DEFAULT_CONTENT_TYPE = "application/json";
|
||||
my @ROUTES = (
|
||||
{ path => "/meta", verb => "GET" },
|
||||
{ path => "/tags", verb => "GET" },
|
||||
{ path => "/tags/new", verb => "POST" },
|
||||
{ path => "/tag/:tag_id_or_name", verb => "GET" },
|
||||
{ path => "/random", verb => "GET" },
|
||||
{ path => "/media/:media_id", verb => "GET" },
|
||||
{ path => "/download/:media_id", verb => "GET" },
|
||||
);
|
||||
|
||||
get "/meta" => sub {
|
||||
return \@ROUTES;
|
||||
};
|
||||
|
||||
get "/tags" => sub {
|
||||
my @tags = schema("default")->resultset("TagsCountView")->all;
|
||||
|
||||
return [
|
||||
map +( {
|
||||
id => $_->tag_id,
|
||||
name => $_->name,
|
||||
count => $_->count,
|
||||
} ), @tags,
|
||||
];
|
||||
};
|
||||
|
||||
post "/tags/new" => sub {
|
||||
my $tag;
|
||||
|
||||
eval {
|
||||
$tag = schema("default")->resultset("Tag")
|
||||
->create({ name => body_parameters->get("name") });
|
||||
} or send_error("Tag exists", 409);
|
||||
|
||||
return {
|
||||
id => $tag->tag_id,
|
||||
name => $tag->name,
|
||||
};
|
||||
};
|
||||
|
||||
get "/tag/:tag_id_or_name" => sub {
|
||||
my ($tag, $tag_rset, @media);
|
||||
my $tag_id_or_name = route_parameters->get("tag_id_or_name");
|
||||
|
||||
$tag_rset = schema("default")->resultset("Tag");
|
||||
$tag = $tag_rset->single({ tag_id => $tag_id_or_name }) //
|
||||
$tag_rset->single({ name => $tag_id_or_name });
|
||||
|
||||
send_error("Tag not found", 404) if !defined($tag);
|
||||
|
||||
@media = map { $_->media_id } schema("default")->resultset("MediaTag")
|
||||
->search({ tag_id => $tag->tag_id })->all;
|
||||
|
||||
return {
|
||||
id => $tag->tag_id,
|
||||
name => $tag->name,
|
||||
media => [
|
||||
map +( {
|
||||
id => $_,
|
||||
uri => uri_for("/media/$_"),
|
||||
donwload_uri => uri_for("/download/$_"),
|
||||
} ), @media,
|
||||
],
|
||||
};
|
||||
};
|
||||
|
||||
get "/random" => 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;
|
||||
};
|
||||
|
||||
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_uri => uri_for("/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,
|
||||
);
|
||||
};
|
||||
|
||||
true;
|
20
lib/PoorBooru/Schema.pm
Normal file
20
lib/PoorBooru/Schema.pm
Normal file
|
@ -0,0 +1,20 @@
|
|||
use utf8;
|
||||
package PoorBooru::Schema;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Schema';
|
||||
|
||||
__PACKAGE__->load_namespaces;
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2023-02-18 09:09:31
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0PabNwBpp04P3y4a8O5MtA
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
108
lib/PoorBooru/Schema/Result/Media.pm
Normal file
108
lib/PoorBooru/Schema/Result/Media.pm
Normal file
|
@ -0,0 +1,108 @@
|
|||
use utf8;
|
||||
package PoorBooru::Schema::Result::Media;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
PoorBooru::Schema::Result::Media
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<media>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("media");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 media_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 content
|
||||
|
||||
data_type: 'blob'
|
||||
is_nullable: 0
|
||||
size: 10485760
|
||||
|
||||
=head2 filename
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 0
|
||||
size: 255
|
||||
|
||||
=head2 content_type
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
size: 255
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"media_id",
|
||||
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
|
||||
"content",
|
||||
{ data_type => "blob", is_nullable => 0, size => 10485760 },
|
||||
"filename",
|
||||
{ data_type => "text", is_nullable => 0, size => 255 },
|
||||
"content_type",
|
||||
{ data_type => "text", is_nullable => 1, size => 255 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</media_id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("media_id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 media_tags
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<PoorBooru::Schema::Result::MediaTag>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"media_tags",
|
||||
"PoorBooru::Schema::Result::MediaTag",
|
||||
{ "foreign.media_id" => "self.media_id" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 tags
|
||||
|
||||
Type: many_to_many
|
||||
|
||||
Composing rels: L</media_tags> -> tag
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->many_to_many("tags", "media_tags", "tag");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2023-02-18 09:09:31
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:X0A6CSw6yWOYvxhMNRoS0g
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
99
lib/PoorBooru/Schema/Result/MediaTag.pm
Normal file
99
lib/PoorBooru/Schema/Result/MediaTag.pm
Normal file
|
@ -0,0 +1,99 @@
|
|||
use utf8;
|
||||
package PoorBooru::Schema::Result::MediaTag;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
PoorBooru::Schema::Result::MediaTag
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<media_tags>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("media_tags");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 media_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 tag_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_foreign_key: 1
|
||||
is_nullable: 0
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"media_id",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
|
||||
"tag_id",
|
||||
{ data_type => "integer", is_foreign_key => 1, is_nullable => 0 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</media_id>
|
||||
|
||||
=item * L</tag_id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("media_id", "tag_id");
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 media
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<PoorBooru::Schema::Result::Media>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"media",
|
||||
"PoorBooru::Schema::Result::Media",
|
||||
{ media_id => "media_id" },
|
||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
|
||||
);
|
||||
|
||||
=head2 tag
|
||||
|
||||
Type: belongs_to
|
||||
|
||||
Related object: L<PoorBooru::Schema::Result::Tag>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
"tag",
|
||||
"PoorBooru::Schema::Result::Tag",
|
||||
{ tag_id => "tag_id" },
|
||||
{ is_deferrable => 0, on_delete => "CASCADE", on_update => "NO ACTION" },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2023-02-18 09:09:31
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CaXiI8+eaCoDbot368Hjyw
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
106
lib/PoorBooru/Schema/Result/Tag.pm
Normal file
106
lib/PoorBooru/Schema/Result/Tag.pm
Normal file
|
@ -0,0 +1,106 @@
|
|||
use utf8;
|
||||
package PoorBooru::Schema::Result::Tag;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
PoorBooru::Schema::Result::Tag
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
|
||||
=head1 TABLE: C<tags>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("tags");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 tag_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_auto_increment: 1
|
||||
is_nullable: 0
|
||||
|
||||
=head2 name
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 0
|
||||
size: 255
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"tag_id",
|
||||
{ data_type => "integer", is_auto_increment => 1, is_nullable => 0 },
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 0, size => 255 },
|
||||
);
|
||||
|
||||
=head1 PRIMARY KEY
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</tag_id>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->set_primary_key("tag_id");
|
||||
|
||||
=head1 UNIQUE CONSTRAINTS
|
||||
|
||||
=head2 C<name_unique>
|
||||
|
||||
=over 4
|
||||
|
||||
=item * L</name>
|
||||
|
||||
=back
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_unique_constraint("name_unique", ["name"]);
|
||||
|
||||
=head1 RELATIONS
|
||||
|
||||
=head2 media_tags
|
||||
|
||||
Type: has_many
|
||||
|
||||
Related object: L<PoorBooru::Schema::Result::MediaTag>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->has_many(
|
||||
"media_tags",
|
||||
"PoorBooru::Schema::Result::MediaTag",
|
||||
{ "foreign.tag_id" => "self.tag_id" },
|
||||
{ cascade_copy => 0, cascade_delete => 0 },
|
||||
);
|
||||
|
||||
=head2 medias
|
||||
|
||||
Type: many_to_many
|
||||
|
||||
Composing rels: L</media_tags> -> media
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->many_to_many("medias", "media_tags", "media");
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2023-02-18 09:09:31
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:tlbIFVg6S6LAWWiR0ioHFw
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
60
lib/PoorBooru/Schema/Result/TagsCountView.pm
Normal file
60
lib/PoorBooru/Schema/Result/TagsCountView.pm
Normal file
|
@ -0,0 +1,60 @@
|
|||
use utf8;
|
||||
package PoorBooru::Schema::Result::TagsCountView;
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader
|
||||
# DO NOT MODIFY THE FIRST PART OF THIS FILE
|
||||
|
||||
=head1 NAME
|
||||
|
||||
PoorBooru::Schema::Result::TagsCountView
|
||||
|
||||
=cut
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use base 'DBIx::Class::Core';
|
||||
__PACKAGE__->table_class("DBIx::Class::ResultSource::View");
|
||||
|
||||
=head1 TABLE: C<tags_count_view>
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->table("tags_count_view");
|
||||
|
||||
=head1 ACCESSORS
|
||||
|
||||
=head2 tag_id
|
||||
|
||||
data_type: 'integer'
|
||||
is_nullable: 1
|
||||
|
||||
=head2 name
|
||||
|
||||
data_type: 'text'
|
||||
is_nullable: 1
|
||||
size: 255
|
||||
|
||||
=head2 count
|
||||
|
||||
data_type: (empty string)
|
||||
is_nullable: 1
|
||||
|
||||
=cut
|
||||
|
||||
__PACKAGE__->add_columns(
|
||||
"tag_id",
|
||||
{ data_type => "integer", is_nullable => 1 },
|
||||
"name",
|
||||
{ data_type => "text", is_nullable => 1, size => 255 },
|
||||
"count",
|
||||
{ data_type => "", is_nullable => 1 },
|
||||
);
|
||||
|
||||
|
||||
# Created by DBIx::Class::Schema::Loader v0.07049 @ 2023-02-18 09:09:31
|
||||
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:9NUzqweyXVu/G84fxb3eew
|
||||
|
||||
|
||||
# You can replace this text with custom code or comments, and it will be preserved on regeneration
|
||||
1;
|
Reference in a new issue