diff options
-rw-r--r-- | Gemfile | 5 | ||||
-rw-r--r-- | Gemfile.lock | 25 | ||||
-rw-r--r-- | README.md | 9 | ||||
-rw-r--r-- | app.rb | 103 | ||||
-rw-r--r-- | config.json.example | 8 | ||||
-rw-r--r-- | config.ru | 2 | ||||
-rw-r--r-- | views/index.erb | 65 |
7 files changed, 91 insertions, 126 deletions
diff --git a/Gemfile b/Gemfile deleted file mode 100644 index db87209..0000000 --- a/Gemfile +++ /dev/null @@ -1,5 +0,0 @@ -source "https://rubygems.org" - -gem "sinatra" -gem "oauth" -gem "rack-flash3" diff --git a/Gemfile.lock b/Gemfile.lock deleted file mode 100644 index 09f4a39..0000000 --- a/Gemfile.lock +++ /dev/null @@ -1,25 +0,0 @@ -GEM - remote: https://rubygems.org/ - specs: - oauth (0.5.1) - rack (1.6.4) - rack-flash3 (1.0.5) - rack - rack-protection (1.5.3) - rack - sinatra (1.4.7) - rack (~> 1.5) - rack-protection (~> 1.4) - tilt (>= 1.3, < 3) - tilt (2.0.2) - -PLATFORMS - ruby - -DEPENDENCIES - oauth - rack-flash3 - sinatra - -BUNDLED WITH - 1.11.2 @@ -1,9 +1,8 @@ -# icon +# icon.rhe.jp + Twitter のアイコンを変更するやつ ```sh -cp config.json.example config.json -vi config.json -bundle install -bundle exec rackup +gem install sinatra oauth rack-flash3 +DATADIR=some-dir CONSUMER_KEY=key CONSUMER_SECRET=secret ruby app.rb ``` @@ -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> diff --git a/config.json.example b/config.json.example deleted file mode 100644 index 2b46119..0000000 --- a/config.json.example +++ /dev/null @@ -1,8 +0,0 @@ -{ - "oauth": { - "consumer_key": "" - "consumer_secret": "" - }, - "secret": "", - "data": "/work/icon-rhe/data" -} diff --git a/config.ru b/config.ru deleted file mode 100644 index 78ed11b..0000000 --- a/config.ru +++ /dev/null @@ -1,2 +0,0 @@ -require "./app" -run Sinatra::Application diff --git a/views/index.erb b/views/index.erb deleted file mode 100644 index 0485ea5..0000000 --- a/views/index.erb +++ /dev/null @@ -1,65 +0,0 @@ -<!DOCTYPE html> -<html> - <head> - <title>icon</title> - <meta content="width=device-width" name="viewport"> - <style> - img { - width: 128px; - height: 128px; - } - - .container { - margin: 10px; - padding: 15px; - border: 1px solid #dddddd; - border-radius: 4px; - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.05); - } - - .container.flex { - display: flex; - flex-wrap: wrap; - } - - .item.flex { - margin: 5px; - } - - .item img { - vertical-align: middle; - } - </style> - </head> - <body> - <% if flash[:notice] %><div class="container"><%= flash[:notice] %></div><% end %> - <div class="container flex"> - <% ids.each do |id| %> - <div class="item flex"><a href="/update" data-id="<%= id %>"><img alt="<%= id %>" src="<%= user_id %>/<%= id %>"></a></div> - <% 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\"]"); - Array.prototype.forEach.call(document.querySelectorAll("a[href=\"/update\"]"), function(item) { - item.addEventListener("click", function(e) { - e.preventDefault(); - hidden.value = e.currentTarget.dataset.id; - form.submit(); - }, false); - }); - </script> - </body> -</html> |