JSON.pm 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. package Trog::Routes::JSON;
  2. use strict;
  3. use warnings;
  4. no warnings 'experimental';
  5. use feature qw{signatures state};
  6. use Clone qw{clone};
  7. use JSON::MaybeXS();
  8. use Scalar::Util();
  9. use Trog::Utils();
  10. use Trog::Config();
  11. use Trog::Auth();
  12. use Trog::Routes::HTML();
  13. use Trog::Log::Metrics();
  14. my $conf = Trog::Config::get();
  15. # TODO de-duplicate this, it's shared in html
  16. my $theme_dir = '';
  17. $theme_dir = "themes/" . $conf->param('general.theme') if $conf->param('general.theme') && -d "www/themes/" . $conf->param('general.theme');
  18. our %routes = (
  19. '/api/catalog' => {
  20. method => 'GET',
  21. callback => \&catalog,
  22. parameters => {},
  23. },
  24. '/api/webmanifest' => {
  25. method => 'GET',
  26. callback => \&webmanifest,
  27. parameters => {},
  28. },
  29. '/api/version' => {
  30. method => 'GET',
  31. callback => \&version,
  32. parameters => {},
  33. },
  34. '/api/auth_change_request/(.*)' => {
  35. method => 'GET',
  36. callback => \&process_auth_change_request,
  37. captures => ['token'],
  38. parameters => {
  39. token => sub { my $tok = shift; $tok =~ m/[a-f|0-9|-]+/; },
  40. },
  41. noindex => 1,
  42. robot_name => '/api/auth_change_request/*',
  43. },
  44. '/api/requests_per' => {
  45. method => 'GET',
  46. auth => 1,
  47. parameters => {
  48. period => sub {
  49. grep {
  50. my $valid = $_;
  51. List::Util::any { $_ eq $valid } @_
  52. } qw{second minute hour day week month year};
  53. },
  54. num_periods => \&Scalar::Util::looks_like_number,
  55. before => \&Scalar::Util::looks_like_number,
  56. code => \&Scalar::Util::looks_like_number,
  57. },
  58. callback => \&requests_per,
  59. },
  60. );
  61. # Clone / redact for catalog
  62. my $cloned = clone( \%routes );
  63. foreach my $r ( keys(%$cloned) ) {
  64. delete $cloned->{$r}{callback};
  65. }
  66. my $enc = JSON::MaybeXS->new( utf8 => 1 );
  67. # Note to authors, don't forget to update this
  68. sub _version () {
  69. return '1.0';
  70. }
  71. # Special case of a non data-structure JSON return
  72. sub version ($query) {
  73. state $ret = [ 200, [ 'Content-type' => "application/json", ETag => 'version-' . _version() ], [ _version() ] ];
  74. return $ret;
  75. }
  76. sub catalog ($query) {
  77. return _render( 200, { ETag => 'catalog-' . _version() }, %$cloned );
  78. }
  79. sub webmanifest ($query) {
  80. state $headers = { ETag => 'manifest-' . _version() };
  81. state %manifest = (
  82. "icons" => [
  83. { "src" => "$theme_dir/img/icon/favicon-32.png", "type" => "image/png", "sizes" => "32x32" },
  84. { "src" => "$theme_dir/img/icon/favicon-48.png", "type" => "image/png", "sizes" => "48x48" },
  85. { "src" => "$theme_dir/img/icon/favicon-167.png", "type" => "image/png", "sizes" => "167x167" },
  86. { "src" => "$theme_dir/img/icon/favicon-180.png", "type" => "image/png", "sizes" => "180x180" },
  87. { "src" => "$theme_dir/img/icon/favicon-192.png", "type" => "image/png", "sizes" => "192x192" },
  88. { "src" => "$theme_dir/img/icon/favicon-512.png", "type" => "image/png", "sizes" => "512x512" },
  89. ],
  90. );
  91. return _render( 200, $headers, %manifest );
  92. }
  93. sub process_auth_change_request ($query) {
  94. my $token = $query->{token};
  95. my $msg = Trog::Auth::process_change_request($token);
  96. return Trog::Routes::HTML::forbidden($query) unless $msg;
  97. return _render(
  98. 200, undef,
  99. message => $msg,
  100. result => 'success',
  101. );
  102. }
  103. sub requests_per ($query) {
  104. my $code = Trog::Utils::coerce_array( $query->{code} );
  105. return _render(
  106. 200, undef,
  107. %{ Trog::Log::Metrics::requests_per( $query->{period}, $query->{num_periods}, $query->{before}, @$code ) }
  108. );
  109. }
  110. sub _render ( $code, $headers, %data ) {
  111. return Trog::Renderer->render(
  112. code => 200,
  113. data => \%data,
  114. template => 'bogus.tx',
  115. contenttype => 'application/json',
  116. headers => $headers,
  117. );
  118. }
  119. 1;