beginnings of new API

This commit is contained in:
Kyle Drake 2014-04-06 23:57:35 -04:00
parent 138169ce6b
commit 3871c4d0fa
3 changed files with 137 additions and 32 deletions

114
app.rb
View file

@ -12,40 +12,14 @@ use Rack::Recaptcha, public_key: $config['recaptcha_public_key'], private_key: $
helpers Rack::Recaptcha::Helpers
before do
if is_http_auth
login_http_auth
if request.path.match(/^\/api\//i)
content_type :json
else
content_type :html, 'charset' => 'utf-8'
redirect '/' if request.post? && !csrf_safe?
end
end
def is_http_auth
return true if request.env['HTTP_AUTHORIZATION']
false
end
def login_http_auth
if auth = request.env['HTTP_AUTHORIZATION']
@api = true
user, pass = Base64.decode64(auth.match(/Basic (.+)/)[1]).split(':')
if Site.valid_login? user, pass
site = Site[username: user]
if site.is_banned
json [result: 'error', message: 'not found'].to_json
end
session[:id] = site.id
else
json [result: 'error', message: 'not found'].to_json
end
end
end
not_found do
slim :'not_found'
end
@ -95,6 +69,10 @@ get '/browse' do
erb :browse
end
get '/api' do
erb :'api'
end
get '/tutorials' do
erb :'tutorials'
end
@ -228,7 +206,7 @@ post '/change_name' do
flash[:error] = 'You already have this name.'
redirect '/settings'
end
current_site.username = params[:name]
if current_site.valid?
@ -301,9 +279,7 @@ post '/site_files/upload' do
halt http_error_code, 'File size must be smaller than available space.'
end
mime_type = Magic.guess_file_mime_type params[:newfile][:tempfile].path
unless (Site::VALID_MIME_TYPES.include?(mime_type) || mime_type =~ /text/) && Site::VALID_EXTENSIONS.include?(File.extname(params[:newfile][:filename]).sub(/^./, '').downcase)
unless Site.valid_file_type? params[:newfile]
@errors << 'File must me one of the following: HTML, Text, Image (JPG PNG GIF JPEG SVG), JS, CSS, Markdown.'
halt http_error_code, 'File type is not supported.' # slim(:'site_files/new')
end
@ -579,6 +555,45 @@ post '/contact' do
end
end
post '/api/upload' do
require_api_credentials
files = []
params.each do |k,v|
next unless v[:tempfile]
files << {filename: k.to_s, tempfile: v[:tempfile]}
end
uploaded_size = files.collect {|f| f[:tempfile].size}.inject{|sum,x| sum + x }
if (uploaded_size + current_site.total_space) > Site::MAX_SPACE
api_error 'too_large', 'files are too large to fit in your space, try uploading less (or smaller) files'
end
files.each do |file|
if !Site.valid_file_type?(file)
api_error 'invalid_file_type', "#{file[:filename]} is not a valid file type, files have not been uploaded"
end
end
files.each do |file|
current_site.store_file file[:filename], file[:tempfile]
end
api_success 'your file(s) have been successfully uploaded'
end
# Catch-all for missing api calls
get '/api/:name' do
api_not_found
end
post '/api/:name' do
api_not_found
end
def require_admin
redirect '/' unless signed_in? && current_site.is_admin
end
@ -618,4 +633,39 @@ def encoding_fix(file)
return Rack::Utils.escape_html(file.force_encoding('BINARY')) if e.message =~ /invalid byte sequence in UTF-8/
fail
end
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', 'could not parse your auth credentials - please check your username and password'
end
if Site.valid_login? user, pass
site = Site[username: user]
if site.nil? || site.is_banned
api_error 'invalid_credentials', 'invalid credentials - please check your username and password'
end
session[:id] = site.id
else
api_error 'invalid_credentials', 'invalid credentials - please check your username and password'
end
end
end
def api_success(message)
halt({result: 'success', message: message}.to_json)
end
def api_error(error_type, message)
halt({result: 'error', error_type: error_type, message: message}.to_json)
end
def api_not_found
api_error 'not_found', 'the requested api call does not exist'
end

View file

@ -125,6 +125,14 @@ class Site < Sequel::Model
}
end
def self.valid_file_type?(uploaded_file)
mime_type = Magic.guess_file_mime_type uploaded_file[:tempfile].path
return true if (Site::VALID_MIME_TYPES.include?(mime_type) || mime_type =~ /text/) &&
Site::VALID_EXTENSIONS.include?(File.extname(uploaded_file[:filename]).sub(/^./, '').downcase)
false
end
def store_file(filename, uploaded)
FileUtils.mv uploaded.path, file_path(filename)
File.chmod(0640, file_path(filename))

47
views/api.erb Normal file
View file

@ -0,0 +1,47 @@
<div class="content single-Col">
<h2 class="alpha">NeoCities Developers API</h2>
<p><img src="/assets/img/cat-larger.png"></p>
<p>
<strong>Our Developers API allows you to make changes to your site remotely with programming languages!</strong>
</p>
<h3>Ideas</h3>
<p>
<ul>
<li>Create your own blog, and use a script to add new blog posts.</li>
<li>Integrate external HTML editors / upload tools.</li>
<li>Provide updated air quality information from a gauge at your house.</li>
<li>Conduct regular backups of your site.</li>
</ul>
</p>
<h3>Rules</h3>
<p>
<ul>
<li>Do not spam the server with tons of API requests.</li>
<li>Try to limit site updates to one per minute.</li>
<li>Do not use the API to "game" the site (increase ranking by manipulating our algorithms).</li>
<li>Do not use the API to data mine / rip all of the sites.</li>
</ul>
</p>
<h2>API Documentation</h2>
<p>
The NeoCities API is a <a href="http://en.wikipedia.org/wiki/Representational_state_transfer" target="_blank">REST</a> API, which uses query parameters for input, and returns data in the <a href="http://en.wikipedia.org/wiki/JSON" target="_blank">JSON</a> format (except for file downloads). It uses client-side <a href="http://en.wikipedia.org/wiki/Basic_access_authentication" target="_blank">HTTP AUTH</a> to authenticate, using your user/site name and password as the credentials. It is designed to play nicely with the most common HTTP libraries available in programming languages, and can be easily used with <strong>cURL</strong> (a command-line tool for making HTTP requests you can use by opening a terminal on your computer).
</p>
<p>
<strong>That's a lot of buzz words if you're new to programming.</strong> Don't worry, it's easier than it sounds! We'll walk you through some working examples you can get started with.
</p>
<h3>POST /api/upload</h3>
<p>
Uploads files to your site. You can upload as many files as you want with a single query, as long as the entire request stays within the disk space limit. The parameter name should be the name of the file, with the extension so we know what kind of file it is (index<strong>.html</strong>).
</p>
<h4>Examples</h4>
<p>Using cURL to upload a single local file (<strong>local.html</strong>), which will be <strong>index.html</strong> on the server:</p>
<code>$ curl -F index.html=@local.html https://USER:PASS@neocities.org/api/upload</code>
</div> <!-- end .content -->