mirror of
https://github.com/neocities/neocities.git
synced 2025-04-24 17:22:35 +02:00
beginnings of new API
This commit is contained in:
parent
138169ce6b
commit
3871c4d0fa
3 changed files with 137 additions and 32 deletions
114
app.rb
114
app.rb
|
@ -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
|
|
@ -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
47
views/api.erb
Normal 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 -->
|
Loading…
Add table
Reference in a new issue