diff options
Diffstat (limited to 'app.rb')
-rw-r--r-- | app.rb | 103 |
1 files changed, 87 insertions, 16 deletions
@@ -1,24 +1,40 @@ +gem "sinatra" +gem "oauth" +gem "rack-flash3" + require "sinatra" -require "tilt/erb" +require "erb" require "fileutils" require "rack/flash" require "oauth" require "json" +require "uri" +require "securerandom" + +include ERB::Util -CONFIG = JSON.parse(File.read(File.expand_path("../config.json", __FILE__)), symbolize_names: true).freeze +DATADIR = ENV["DATADIR"] || File.join(__dir__, "data") +CONSUMER_KEY = ENV["CONSUMER_KEY"] +CONSUMER_SECRET = ENV["CONSUMER_SECRET"] +SESSION_SECRET = SecureRandom.random_bytes(32) -set :public_folder, CONFIG[:data] -use Rack::Session::Cookie, { expire_after: (86400 * 7), - secret: CONFIG[:secret] } +set :public_folder, DATADIR +use Rack::Session::Cookie, expire_after: (86400 * 7), secret: SESSION_SECRET use Rack::Flash +def user_id + id = session[:user_id] + raise "not logged in" unless id + raise "broken format user id" if /\A\d+\z/ !~ id + id +end + def logged_in? !!session[:user_id] end def consumer - $consumer ||= OAuth::Consumer.new(CONFIG[:oauth][:consumer_key], - CONFIG[:oauth][:consumer_secret], + $consumer ||= OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, site: "https://api.twitter.com", authorize_path: "/oauth/authenticate") end @@ -29,10 +45,13 @@ end get "/" do if logged_in? - ids = Dir.glob(File.join(CONFIG[:data], session[:user_id], "*")).map { |path| path.split("/").last } + ids = Dir.glob(File.join(DATADIR, session[:user_id], "*")).map { |path| + File.basename(path) + } erb :index, locals: { ids: ids, user_id: session[:user_id] } else - req = consumer.get_request_token(oauth_callback: request.url.sub(/\/[^\/]*\z/, "/callback")) + uri = URI.parse(request.url).merge!("./callback") + req = consumer.get_request_token(oauth_callback: uri.to_s) session[:req] = JSON.generate(token: req.token, secret: req.secret) redirect req.authorize_url end @@ -46,7 +65,7 @@ get "/callback" do acc = req.get_access_token(oauth_verifier: params[:oauth_verifier]) session[:user_id] = acc.params[:user_id] session[:acc] = JSON.generate(token: acc.token, secret: acc.secret) - FileUtils.mkdir_p(File.join(CONFIG[:data], session[:user_id])) + FileUtils.mkdir_p(File.join(DATADIR, user_id)) end end redirect "/" @@ -54,13 +73,13 @@ end post "/update" do authenticate! - return 400 unless /\A[0-9]+-[a-f0-9]{40}\z/ =~ params[:id].to_s - path = File.join(CONFIG[:data], session[:user_id], params[:id]) + return 400 unless /\A\d+-[a-f0-9]{40}\z/ =~ params[:id].to_s + path = File.join(DATADIR, user_id, params[:id]) return 404 unless File.exist?(path) encoded = [File.read(path)].pack("m0") - json = JSON.parse(session[:acc], symbolize_names: true) + json = JSON.parse(session.fetch(:acc), symbolize_names: true) acc = OAuth::AccessToken.new(consumer, json[:token], json[:secret]) acc.post("/1.1/account/update_profile_image.json", image: encoded) @@ -70,13 +89,65 @@ end post "/upload" do authenticate! - return 400 unless (params.dig(:file, :tempfile) rescue nil) - tempfile = params[:file][:tempfile] + tempfile = params.dig(:file, :tempfile) + return 400 unless tempfile return 400 if tempfile.size > 5 * 1024 * 1024 digest = Digest::SHA1.file(tempfile).hexdigest id = Time.now.to_i.to_s + "-" + digest - File.write(File.join(CONFIG[:data], session[:user_id], id), tempfile.read) + File.write(File.join(DATADIR, user_id, id), tempfile.read) flash[:notice] = "Successfully uploaded" redirect "/" end + +__END__ + +@@ index +<!DOCTYPE html> +<html> + <head> + <title>icon.rhe.jp</title> + <meta content="width=device-width" name="viewport"> + <style> + .container { + display: flex; + flex-wrap: wrap; + margin: 10px; + padding: 15px; + border: 1px solid #dddddd; + box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); + } + a.icon { display: block; margin: 5px; } + img { vertical-align: middle; width: 128px; height: 128px; } + </style> + </head> + <body> + <% if flash[:notice] %><div class="container"><%=h flash[:notice] %></div><% end %> + <div class="container"> + <% ids.each do |id| %> + <a class="icon" href="/update" data-id="<%=h id %>"><img alt="<%=h id %>" src="<%=h user_id %>/<%=h id %>"></a> + <% end %> + </div> + <div class="container"> + <form action="/upload" method="post" enctype="multipart/form-data"> + <input type="file" name="file"> + <input type="submit" value="Submit"> + </form> + </div> + <form action="/update" method="post"> + <input type="hidden" name="id"> + </form> + <script> + var form = document.querySelector('form[action="/update"]'); + var hidden = form.querySelector('input[type="hidden"]'); + var icons = document.querySelectorAll('a[href="/update"]') + Array.prototype.forEach.call(icons, function(item) { + item.addEventListener("click", function(e) { + e.preventDefault(); + hidden.value = e.currentTarget.dataset.id; + form.submit(); + }, false); + }); + </script> + </body> +</html> |