Skip to content

Commit

Permalink
WIP DNM Support uploads using the "put" saver
Browse files Browse the repository at this point in the history
The upload.js saver is old and clunky so it would be nice to switch
over to the put.js saver.

- Add a bogus WebDAV header for TiddlyWiki's put saver
- Add a controller method for the "put" saves and related routes
- Make it a configurable option for now, but still thinking about
  how to roll it out to prod, (hence the WIP).
- Add ETag header

There's a related PR for TiddlyWiki that would improve the UX by
showing the error explanations to the user.

Issue: #148
  • Loading branch information
simonbaird committed Apr 4, 2022
1 parent 1c00abc commit 3cde18c
Show file tree
Hide file tree
Showing 9 changed files with 72 additions and 12 deletions.
4 changes: 4 additions & 0 deletions rails/app/controllers/concerns/subdomain_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,8 @@ def manage_iframe_header
response.headers.except!('X-Frame-Options') if @site.try(:allow_in_iframe?)
end

def etag_header
response.set_header 'ETag', @site.etag
end

end
2 changes: 2 additions & 0 deletions rails/app/controllers/tiddlyspot_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,13 @@ def home

def serve
update_access_count_and_timestamp
etag_header
render html: @site.html_content.html_safe, layout: false
end

def download
update_access_count_and_timestamp
etag_header
download_html_content(@site.html_content, @site.name)
end

Expand Down
40 changes: 38 additions & 2 deletions rails/app/controllers/tiddlywiki_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class TiddlywikiController < ApplicationController
before_action :find_site

# TiddlyWiki can't provide the token for saving so we need to skip it
skip_before_action :verify_authenticity_token, only: :save
skip_before_action :verify_authenticity_token, only: [:upload_save, :put_save]

# Rails wants a token for options requests, which TiddlyWiki similarly can't provide
skip_before_action :verify_authenticity_token,
Expand All @@ -20,6 +20,11 @@ class TiddlywikiController < ApplicationController
def serve
return site_not_available unless site_visible?

# Convince TiddlyWiki it can use the put saver
dummy_webdav_header if request.options? && @site.enable_put_saver?

etag_header

# Avoid site download for head or options requests
return head 200 if request.head? || request.options?

Expand All @@ -35,6 +40,8 @@ def serve
def json_content
return site_not_available unless site_visible?

etag_header

# Return empty body for options request with CORS headers
return head 200 if request.options?

Expand Down Expand Up @@ -62,6 +69,8 @@ def tid_content
# If we get nil, assume the tiddler doesn't exist
return head 404 unless tiddler_data

etag_header

# Return empty body for options request with CORS headers
return head 200 if request.options?

Expand All @@ -86,7 +95,8 @@ def download
download_html_content(@site.download_content, @site.name)
end

def save
# Using the "upload" saver
def upload_save
begin
if site_saveable?
@site.file_upload(params[:userfile])
Expand All @@ -102,6 +112,27 @@ def save
end
end

# Using the "put" saver
def put_save
begin
if site_saveable?
if request.headers['If-Match'].presence && request.headers['If-Match'] != @site.etag
render plain: "The site has been updated since you first loaded it. " +
"Saving now would cause the other changes to be lost.\n", status: 412
else
@site.file_upload(request.body)
@site.increment_save_count
head 204
end
else
render plain: "If this is your site please log in at\n#{main_site_url} and try again.\n", status: 403
end
rescue => e
# Todo: Should probably give a generic "Save failed!" message, and log the real problem
render plain: "#{e.class.name} #{e.message}\n", status: 500
end
end

private

def update_view_count_and_access_timestamp
Expand Down Expand Up @@ -194,4 +225,9 @@ def cors_headers
response.set_header 'Access-Control-Allow-Headers', 'X-Requested-With'
end

# TiddlyWiki just checks if the header it exists so the value doesn't matter
def dummy_webdav_header
response.set_header 'dav', "Dummy WebDAV header to enable TiddlyWiki's PUT saver"
end

end
5 changes: 5 additions & 0 deletions rails/app/models/concerns/site_common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,11 @@ def site_cache(cache_type, &blk)
Rails.cache.fetch(site_content_cache_key, expires_in: 4.weeks.from_now, &blk)
end

# Should be a good enough for ETag headers
def etag
Digest::SHA256.base64digest("#{id}#{updated_at}#{blob.key}")
end

def download_url
"#{url}/download"
end
Expand Down
3 changes: 2 additions & 1 deletion rails/app/models/site.rb
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ def looks_valid?
end

def html_content(signed_in_user: nil)
th_file.apply_tiddlyhost_mods(name, signed_in_user: signed_in_user).to_html
th_file.apply_tiddlyhost_mods(name,
signed_in_user: signed_in_user, enable_put_saver: enable_put_saver).to_html
end

def json_data(opts={})
Expand Down
3 changes: 2 additions & 1 deletion rails/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@
get '/favicon.ico', to: 'tiddlywiki#favicon'
get '/download', to: 'tiddlywiki#download'

post '/', to: 'tiddlywiki#save'
post '/', to: 'tiddlywiki#upload_save'
put '/', to: 'tiddlywiki#put_save'
end

#
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddPutSaverOptionToSites < ActiveRecord::Migration[6.1]
def change
add_column :sites, :enable_put_saver, :boolean, default: false
end
end
3 changes: 2 additions & 1 deletion rails/db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2022_03_30_234401) do
ActiveRecord::Schema.define(version: 2022_04_04_022507) do

# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
Expand Down Expand Up @@ -70,6 +70,7 @@
t.integer "raw_byte_size"
t.string "tw_version"
t.boolean "allow_in_iframe", default: false
t.boolean "enable_put_saver", default: false
t.index ["empty_id"], name: "index_sites_on_empty_id"
t.index ["name"], name: "index_sites_on_name", unique: true
t.index ["user_id"], name: "index_sites_on_user_id"
Expand Down
19 changes: 12 additions & 7 deletions rails/lib/th_file.rb
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,22 @@ def self.from_empty(empty_type)
from_file(empty_path(empty_type))
end

def apply_tiddlyhost_mods(site_name, for_download: false, signed_in_user: nil)
def apply_tiddlyhost_mods(site_name, for_download: false, enable_put_saver: false, signed_in_user: nil)
if is_tw5?

upload_url = if !for_download
# The url for uploads is the same as the site url
Settings.subdomain_site_url(site_name)
else
# Clear $:/UploadURL so the save button in the downloaded file will not try
# to use upload.js. It should use another save method, probably download to file.
upload_url = if for_download || enable_put_saver
# Clear $:/UploadURL for downloads so the save button in the downloaded
# file will not try to use upload.js. It should use another save
# method, probably download to file.
#
# Todo: Consider if we should do that also when signed_in_user is nil.
#
# Clear $:/UploadURL when using the put saver otherwise TW will
# prioritize the upload saver
""
else
# The url for uploads is the same as the site url
Settings.subdomain_site_url(site_name)
end

if !for_download && signed_in_user
Expand Down

0 comments on commit 3cde18c

Please sign in to comment.