aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKazuki Yamaguchi <k@rhe.jp>2016-09-15 02:51:04 +0900
committerKazuki Yamaguchi <k@rhe.jp>2016-09-15 02:51:04 +0900
commit7257fccb70976f2103341a38341a3290603cd764 (patch)
tree4b25f5fc9fd6ee69cd1671727eef03dc5d11d85e
parentb354f8ffc8378d4c0790f674267c47614d584d3e (diff)
downloadicon-7257fccb70976f2103341a38341a3290603cd764.tar.gz
compress into 1 file
-rw-r--r--Gemfile5
-rw-r--r--Gemfile.lock25
-rw-r--r--README.md9
-rw-r--r--app.rb103
-rw-r--r--config.json.example8
-rw-r--r--config.ru2
-rw-r--r--views/index.erb65
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
diff --git a/README.md b/README.md
index 1665711..5bda68b 100644
--- a/README.md
+++ b/README.md
@@ -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
```
diff --git a/app.rb b/app.rb
index f8c2acc..2feead6 100644
--- a/app.rb
+++ b/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>