This commit is contained in:
Kyle Drake 2014-04-11 15:51:09 -07:00
parent 7cd7973144
commit d8da8de5b3
No known key found for this signature in database
GPG key ID: 8BE721072E1864BE
4 changed files with 127 additions and 32 deletions

89
app.rb
View file

@ -594,7 +594,7 @@ post '/api/upload' do
api_success 'your file(s) have been successfully uploaded'
end
post '/api/:delete' do
post '/api/delete' do
require_api_credentials
api_error 'missing_filenames', 'you must provide files to delete' if params[:filenames].nil? || params[:filenames].empty?
@ -603,15 +603,15 @@ post '/api/:delete' do
params[:filenames].each do |filename|
unless filename.is_a?(String) && Site.valid_filename?(filename)
api_error 'bad_filename', "#{filename} is not a valid filename, canceled all deletes"
api_error 'bad_filename', "#{filename} is not a valid filename, canceled deleting"
end
if !current_site.file_exists?(filename)
api_error 'missing_files', "#{filename} was not found on your site, canceled all deletes"
api_error 'missing_files', "#{filename} was not found on your site, canceled deleting"
end
if filename == 'index.html'
api_error 'cannot_delete_index', 'you cannot delete your index.html file, canceled all deletes'
api_error 'cannot_delete_index', 'you cannot delete your index.html file, canceled deleting'
end
filenames << filename
@ -621,7 +621,31 @@ post '/api/:delete' do
current_site.delete_file(filename)
end
api_success 'files have been deleted'
api_success 'file(s) have been deleted'
end
get '/api/info' do
if params[:sitename]
site = Site[username: params[:sitename]]
api_error 'site_not_found', "could not find site #{params[:sitename]}" if site.nil?
api_success api_info_for(site)
else
init_api_credentials
api_success api_info_for(current_site)
end
end
def api_info_for(site)
{
info: {
sitename: site.username,
hits: site.hits,
created_at: site.created_at.rfc2822,
last_updated: site.updated_at.rfc2822,
domain: site.domain,
tags: site.tags.collect {|t| t.name}
}
}
end
# Catch-all for missing api calls
@ -676,32 +700,45 @@ def encoding_fix(file)
end
def require_api_credentials
if auth = request.env['HTTP_AUTHORIZATION']
begin
user, pass = Base64.decode64(auth.match(/Basic (.+)/)[1]).split(':')
rescue
api_error_invalid_auth
end
if Site.valid_login? user, pass
site = Site[username: user]
if site.nil? || site.is_banned
api_error_invalid_auth
end
session[:id] = site.id
else
api_error_invalid_auth
end
if !request.env['HTTP_AUTHORIZATION'].nil?
init_api_credentials
else
api_error_invalid_auth
end
end
def api_success(message)
halt({result: 'success', message: message}.to_json)
def init_api_credentials
auth = request.env['HTTP_AUTHORIZATION']
begin
user, pass = Base64.decode64(auth.match(/Basic (.+)/)[1]).split(':')
rescue
api_error_invalid_auth
end
if Site.valid_login? user, pass
site = Site[username: user]
if site.nil? || site.is_banned
api_error_invalid_auth
end
session[:id] = site.id
else
api_error_invalid_auth
end
end
def api_success(message_or_obj)
output = {result: 'success'}
if message_or_obj.is_a?(String)
output[:message] = message_or_obj
else
output.merge! message_or_obj
end
halt output.to_json
end
def api_error(error_type, message)

View file

@ -73,10 +73,11 @@ class Site < Sequel::Model
end
def new_tags=(tags_string)
tags_string.gsub! /[^a-zA-Z0-9, ]/, ''
tags_string.gsub! ', ', ','
tags = tags_string.split ','
tags_string.gsub! /[^a-zA-Z0-9, ]/, ''
tags.collect! {|c| (c.match(/^\w+\s\w+/) || c.match(/^\w+/)).to_s }
@new_tag_strings = tags
@new_tag_strings = tags.sort
end
def before_validation

View file

@ -14,6 +14,45 @@ def create_site
@pass = site_attr[:password]
end
describe 'api info' do
it 'fails for no input' do
get '/api/info'
res[:error_type] = 'missing_sitename'
end
it 'fails for nonexistent site' do
get '/api/info', sitename: 'notexist'
res[:error_type].must_equal 'site_not_found'
end
it 'succeeds for valid sitename' do
create_site
@site.update hits: 31337, domain: 'derp.com', new_tags: 'derpie, man'
get '/api/info', sitename: @user
res[:result].must_equal 'success'
res[:info][:sitename].must_equal @site.username
res[:info][:hits].must_equal 31337
res[:info][:created_at].must_equal @site.created_at.rfc2822
res[:info][:last_updated].must_equal @site.updated_at.rfc2822
res[:info][:domain].must_equal 'derp.com'
res[:info][:tags].must_equal ['derpie', 'man']
end
it 'fails for bad auth' do
basic_authorize 'derp', 'fake'
get '/api/info'
res[:error_type].must_equal 'invalid_auth'
end
it 'succeeds for api auth' do
create_site
@site.update hits: 12345
basic_authorize @user, @pass
get '/api/info'
res[:info][:hits] == 12345
end
end
describe 'api delete' do
it 'fails with no or bad auth' do
post '/api/delete', filenames: ['hi.html']

View file

@ -52,10 +52,10 @@ var NeoCities = require('neocities')
var api = new NeoCities('YOURUSERNAME', 'YOURPASSWORD')
// Upload a file called local.html from your computer,
// which will be named newfile.html on the site.
api.uploadFile('newfile.html', './local.html', function(resp) {
api.uploadFiles([{
name: 'newfileonsite.html'
path: './local.html'
}], function(resp) {
if(resp.result == 'error')
throw new Error(resp.error_type+' - '+resp.message)
@ -64,6 +64,10 @@ api.uploadFile('newfile.html', './local.html', function(resp) {
})
</pre>
<p>
</p>
<h3>POST /api/delete</h3>
<p>
Deletes files from your site. Provide a <strong>filenames</strong> argument with an array of filenames you wish to delete. You can delete any files except index.html.
@ -80,4 +84,18 @@ api.uploadFile('newfile.html', './local.html', function(resp) {
<code>
curl -d "filenames[]=img1.jpg" -d "filenames[]=img2.jpg" \<br> https://YOURUSER:YOURPASS@neocities.org/api/delete
</code>
<h3>GET /api/info</h3>
<p>
This call lets you retreive information about a web site. This call does not require site authorization if you provide a <strong>sitename</strong> argument. Note that the sitename is the same as a username. If you provide auth credentials, you will receive the info for the auth user's site.
</p>
<h4>Examples</h4>
<h6>Using cURL</h6>
<code>
$ curl https://neocities.org/api/info?sitename=youpi
</code>
<code>
$ curl https://YOURUSER:YOURPASS@neocities.org/api/info
</code>
</div> <!-- end .content -->