|
|
@@ -0,0 +1,257 @@ |
|
|
|
#!/usr/bin/perl |
|
|
|
|
|
|
|
use strict; |
|
|
|
use FCGI; |
|
|
|
use JSON; |
|
|
|
use DBI; |
|
|
|
use Data::Dumper; |
|
|
|
use warnings; |
|
|
|
|
|
|
|
$Data::Dumper::Sortkeys = 1; |
|
|
|
|
|
|
|
my $conf = load_conf("../etc/autodoc.json"); |
|
|
|
my $dbh = sqlconnect($conf->{sql}); |
|
|
|
|
|
|
|
my %map = ( |
|
|
|
api_v1_POST_documents => &api_v1_POST_documents, |
|
|
|
api_v1_POST_documents_id_data => &api_v1_POST_documents_id_data, |
|
|
|
api_v1_GET_documents_id_image => &api_v1_GET_documents_id_image, |
|
|
|
api_v1_GET_pages_image => &api_v1_GET_pages_image, |
|
|
|
api_v1_GET_documents => &api_v1_GET_documents, |
|
|
|
api_v1_GET_documents_id => &api_v1_GET_documents_id, |
|
|
|
api_v1_GET_pages_id => &api_v1_GET_pages_id, |
|
|
|
api_v1_PATCH_documents_id => &api_v1_PATCH_documents_id, |
|
|
|
); |
|
|
|
|
|
|
|
my $request = FCGI::Request(); |
|
|
|
|
|
|
|
while($request->Accept() >= 0) { |
|
|
|
|
|
|
|
my $user = $ENV{REMOTE_USER} || 'undefined'; |
|
|
|
my $qs = parse_querystring($ENV{QUERY_STRING}); |
|
|
|
my $method = $ENV{REQUEST_METHOD}; |
|
|
|
|
|
|
|
my $path = [ split(/\//,$ENV{SCRIPT_NAME}) ] if exists $ENV{SCRIPT_NAME}; |
|
|
|
shift(@{$path}); |
|
|
|
|
|
|
|
my $post = parse_post( |
|
|
|
\*STDIN, |
|
|
|
exists $ENV{CONTENT_LENGTH} ? $ENV{CONTENT_LENGTH} : 0, |
|
|
|
exists $ENV{CONTENT_TYPE} ? $ENV{CONTENT_TYPE} : 0 |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
my($code, $type, $data) = process_query($method, $path, $qs, $post, $user); |
|
|
|
|
|
|
|
if ( $type eq 'application/json' ) { |
|
|
|
$data = to_json($data); |
|
|
|
} |
|
|
|
|
|
|
|
printf("Status: %s\r\n", $code); |
|
|
|
printf("Content-type: %s\r\n", $type); |
|
|
|
printf("Content-length: %i\r\n", length($data)); |
|
|
|
printf("\r\n"); |
|
|
|
print $data; |
|
|
|
} |
|
|
|
|
|
|
|
sub load_conf { |
|
|
|
my($file) = @_; |
|
|
|
|
|
|
|
my $x=''; |
|
|
|
|
|
|
|
open(F,"$file") || fatal_api_error(500,"Failed to load configuration file"); |
|
|
|
while(<F>) { $x.=$_; } |
|
|
|
close(F); |
|
|
|
|
|
|
|
return from_json($x); |
|
|
|
} |
|
|
|
|
|
|
|
sub process_query { |
|
|
|
my($method, $path, $qs, $post, $user) = @_; |
|
|
|
|
|
|
|
my ($api_version, $path_main, $path_id, $path_sub) = @{$path}; |
|
|
|
|
|
|
|
my $func = sprintf("api_%s_%s_%s", |
|
|
|
defined $api_version ? $api_version : "unknown", |
|
|
|
defined $method ? $method : "unknown", |
|
|
|
defined $path_main ? $path_main : "unknown" |
|
|
|
); |
|
|
|
|
|
|
|
$func .= '_id' if defined $path_id; |
|
|
|
$func .= '_'.$path_sub if defined $path_sub; |
|
|
|
|
|
|
|
return $map{$func}->($path_id, $qs, $post, $user) if exists $map{$func}; |
|
|
|
return api_error(404); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
sub db_get_document_object { |
|
|
|
my($id) = @_; |
|
|
|
|
|
|
|
my $document; |
|
|
|
my @pages; |
|
|
|
my @pageids; |
|
|
|
|
|
|
|
my $q = sqlquery($dbh, "SELECT * FROM documents WHERE id = ?", $id); |
|
|
|
while(my $hash = $q->fetchrow_hashref()) { $document = $hash; } |
|
|
|
|
|
|
|
$q = sqlquery($dbh, "SELECT * FROM pages WHERE documentId = ?", $id); |
|
|
|
while(my $hash = $q->fetchrow_hashref()) { push @pages, $hash; push @pageids, $hash->{id}; } |
|
|
|
|
|
|
|
my %lang; |
|
|
|
|
|
|
|
foreach my $page ( @pages ) { |
|
|
|
$q = sqlquery($dbh, "SELECT * FROM pages_lang WHERE pageId = ?", $page->{id}); |
|
|
|
while(my $hash = $q->fetchrow_hashref()) { |
|
|
|
$lang{$hash->{language}}++; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
my $out = { |
|
|
|
id => $document->{id}, |
|
|
|
pageId => [ @pageids ], |
|
|
|
name => $document->{name}, |
|
|
|
created => $document->{created}, |
|
|
|
owner => $document->{owner}, |
|
|
|
status => $document->{status}, |
|
|
|
languages => [ keys %lang ], |
|
|
|
}; |
|
|
|
|
|
|
|
return (200, "application/json", $out); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
# create an empty document object. |
|
|
|
sub api_v1_POST_documents { |
|
|
|
my($id, $qs, $post, $user) = @_; |
|
|
|
|
|
|
|
my $q = sqlquery($dbh, " |
|
|
|
INSERT INTO documents |
|
|
|
SET |
|
|
|
owner = ?, |
|
|
|
status = 'nodata' |
|
|
|
", $user); |
|
|
|
$q = sqlquery($dbh, "SELECT LAST_INSERT_ID()"); |
|
|
|
while(my($lastid) = $q->fetchrow_array()) { |
|
|
|
$id = $lastid; |
|
|
|
} |
|
|
|
|
|
|
|
return db_get_document_object($id); |
|
|
|
} |
|
|
|
|
|
|
|
sub api_v1_POST_documents_id_data { return api_error(501,"Not yet implemented"); } |
|
|
|
sub api_v1_GET_documents_id_image { return api_error(501,"Not yet implemented"); } |
|
|
|
sub api_v1_GET_pages_image { return api_error(501,"Not yet implemented"); } |
|
|
|
|
|
|
|
# get a list of document objects |
|
|
|
sub api_v1_GET_documents { |
|
|
|
my($id, $qs, $post, $user) = @_; |
|
|
|
|
|
|
|
$qs->{pageSize} = $conf->{query}{pageSize} if !exists $qs->{pageSize}; |
|
|
|
$qs->{pageIndex} = $conf->{query}{pageIndex} if !exists $qs->{pageIndex}; |
|
|
|
|
|
|
|
my @out; |
|
|
|
|
|
|
|
my $q = sqlquery($dbh, "SELECT id FROM documents LIMIT ?,?", |
|
|
|
$qs->{pageSize} * $qs->{pageIndex}, |
|
|
|
$qs->{pageSize}); |
|
|
|
while(my ($id) = $q->fetchrow_array()) { |
|
|
|
my ($code, $ct, $body) = db_get_document_object($id); |
|
|
|
push @out, $body; |
|
|
|
} |
|
|
|
|
|
|
|
return ( |
|
|
|
200, |
|
|
|
"application/json", |
|
|
|
\@out |
|
|
|
); |
|
|
|
} |
|
|
|
|
|
|
|
# get a single document object |
|
|
|
sub api_v1_GET_documents_id { |
|
|
|
my($id, $qs, $post, $user) = @_; |
|
|
|
return db_get_document_object($id); |
|
|
|
} |
|
|
|
|
|
|
|
sub api_v1_GET_pages_id { return api_error(501,"Not yet implemented"); } |
|
|
|
sub api_v1_PATCH_documents_id { return api_error(501,"Not yet implemented"); } |
|
|
|
|
|
|
|
sub fatal_api_error { |
|
|
|
my($code,$type,$body)=api_error(@_); |
|
|
|
|
|
|
|
printf("Status: %s\r\n", $code); |
|
|
|
printf("Content-type: %s\r\n", $type); |
|
|
|
printf("Content-length: %i\r\n", length($body)); |
|
|
|
printf("\r\n"); |
|
|
|
print $body; |
|
|
|
exit; |
|
|
|
} |
|
|
|
|
|
|
|
sub api_error { |
|
|
|
my($code, $text)=@_; |
|
|
|
|
|
|
|
my %deftext = ( |
|
|
|
"000" => "An error produced an internal error, cascading errors over errors", |
|
|
|
"404" => "No such API path" |
|
|
|
); |
|
|
|
|
|
|
|
$code = "000" if !defined $code; |
|
|
|
$text = $deftext{$code} if ( !defined $text || $code eq "000" ); |
|
|
|
|
|
|
|
return ( $code, "text/plain", $text ); |
|
|
|
} |
|
|
|
|
|
|
|
sub parse_querystring { |
|
|
|
my($in) = @_; |
|
|
|
|
|
|
|
my %out; |
|
|
|
|
|
|
|
if ( defined $in && length $in ) { |
|
|
|
foreach(split(/&/,$in)) { |
|
|
|
my($name,$value) = split(/=/); |
|
|
|
$value =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; |
|
|
|
$out{$name}=$value; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
return \%out; |
|
|
|
} |
|
|
|
|
|
|
|
sub parse_post { |
|
|
|
my($fd, $len, $ct) = @_; |
|
|
|
|
|
|
|
my $data = ''; |
|
|
|
while ( $len > 0 ) { |
|
|
|
my $buf; |
|
|
|
my $read = read($fd, $buf, $len); |
|
|
|
$len -= $read; |
|
|
|
$data .= $buf; |
|
|
|
} |
|
|
|
|
|
|
|
if ( $ct eq 'application/json' ) { |
|
|
|
my $tmp = from_json($data); |
|
|
|
$data = $tmp; |
|
|
|
} |
|
|
|
|
|
|
|
return $data; |
|
|
|
} |
|
|
|
|
|
|
|
sub sqlconnect { |
|
|
|
my($sql) = @_; |
|
|
|
|
|
|
|
my $dsn = "DBI:mysql:database=$sql->{base};host=$sql->{host}"; |
|
|
|
my $dbh = DBI->connect($dsn, $sql->{user}, $sql->{pass}) || \\ |
|
|
|
fatal_api_error(500,"Failed to connect to database"); |
|
|
|
|
|
|
|
return $dbh; |
|
|
|
} |
|
|
|
|
|
|
|
sub sqlquery { |
|
|
|
my $dbh = shift; |
|
|
|
my $query = shift; |
|
|
|
my @args = @_; |
|
|
|
|
|
|
|
my $sth = $dbh->prepare($query) || fatal_api_error(500,"Failed to execute SQL query"); |
|
|
|
$sth->execute(@args) || fatal_api_error(500,"Failed to execute SQL query"); |
|
|
|
return $sth; |
|
|
|
} |