rpaproxy.rb
# -*- coding: utf-8 -*-
require 'sinatra'
require 'mongoid'
require 'omniauth'
require 'omniauth-twitter'
require 'haml'
require './models/user.rb'
require './models/proxy.rb'
require './models/log.rb'
require './models/stat.rb'
require './models/client.rb'
if production?
require 'memcachier'
require 'rack/session/dalli'
use Rack::Session::Dalli, cache: Dalli::Client.new, expire_after: 2592000
else
use Rack::Session::Pool, expire_after: 2592000
end
use OmniAuth::Builder do
provider :twitter, ENV['TWITTER_KEY'], ENV['TWITTER_SECRET']
provider :developer unless production?
end
set :haml, { format: :html5, escape_html: true }
configure do
Mongoid.load!("config/mongoid.yml")
Mongo::Logger.level = Logger::FATAL
end
configure :production do
require 'newrelic_rpm'
require 'newrelic_moped'
raise StandardError.new("not found ENV['TWITTER_KEY']") unless ENV['TWITTER_KEY']
raise StandardError.new("not found ENV['TWITTER_SECRET']") unless ENV['TWITTER_SECRET']
end
helpers do
def current_user
@current_user ||= User.where(uid: session[:user_id]).first if session[:user_id]
end
def locales
['jp', 'us', 'ca', 'de', 'fr', 'uk', 'es', 'it', 'cn']
end
def forbidden?
client = Client.find_or_initialize_by(atag: params['AssociateTag'])
client.update_status
if client.status == Client::Status::ACTIVE
client.save if client.changed? and client.created_at
false
else
client.save if client.changed?
true
end
end
end
before do
content_type :html, 'charset' => 'utf-8'
end
before '/profile*' do
redirect '/' unless current_user
end
before '/proxy*' do
redirect '/' unless current_user
end
# ログアウト
get '/logout' do
session[:user_id] = nil
redirect '/'
end
# トップページ
get '/' do
haml :index
end
# ログイン処理
get '/auth/twitter/callback' do
auth = request.env['omniauth.auth']
@current_user = User.find_or_create_with_omniauth(auth)
session[:user_id] = @current_user.uid
request.logger.info "[INFO] @#{current_user.screen_name} logged in"
redirect '/profile'
end
# ログイン処理(開発用)
post '/auth/developer/callback' do
auth = request.env['omniauth.auth']
auth['uid'] = auth['info']['nickname'] = auth['info']['name']
request.logger.info request.env['omniauth.auth'].inspect
@current_user = User.find_or_create_with_omniauth(auth)
session[:user_id] = @current_user.uid
request.logger.info "[INFO] @#{current_user.screen_name} logged in"
redirect '/profile'
end
# ログイン後の画面
get '/profile' do
haml :profile
end
# プロフィール更新
put '/profile/:id' do
user = User.find(params[:id])
raise StandardError.new("error") unless current_user.id == user.id
user.name = params[:name]
user.url = params[:url]
if user.save
request.logger.info "[INFO] updated profile by @#{current_user.screen_name}"
else
request.logger.error "[ERROR] failed to update profile. #{user.errors.full_messages}"
end
redirect '/profile'
end
# プロキシ一覧
get '/proxies' do
@proxies = Proxy.all
haml :proxies
end
post '/proxy' do
# create a new proxy
# TODO: エラー処理
proxy = Proxy.new_with_yaml(params[:endpoint])
proxy.user = current_user
begin
if proxy.valid_endpoint? && proxy.save
request.logger.info "[INFO] added proxy by @#{current_user.screen_name}"
else
request.logger.error "[ERROR] failed to add proxy. #{proxy.errors.full_messages}"
end
rescue StandardError => e
request.logger.error "[ERROR] failed to add proxy. #{e.class}: #{e.message}"
end
redirect '/profile'
end
put '/proxy/:id' do
# update an existing proxy
proxy = Proxy.find(params[:id])
raise StandardError.new("error") unless current_user.id == proxy.user.id
proxy.name = params[:name]
# proxy.endpoint = params[:endpoint]
begin
if proxy.save
request.logger.info "[INFO] updated proxy by @#{current_user.screen_name}"
else
request.logger.error "[ERROR] failed to update proxy. #{proxy.errors.full_messages}"
end
rescue StandardError => e
request.logger.error "[ERROR] failed to update proxy. #{e.class}: #{e.message}"
end
redirect '/profile'
end
delete '/proxy/:id' do
proxy = Proxy.find(params[:id])
raise StandardError.new("error") unless current_user.id == proxy.user.id
proxy.destroy
request.logger.info "[INFO] deleted proxy by @#{current_user.screen_name}"
redirect '/profile'
end
# リバースプロキシ http://rpaproxy.heroku.com/rpaproxy/jp/
get %r{/rpaproxy/([\w]{2})/} do |locale|
# deny bot
if forbidden?
env['QUERY_STRING'] = request.params.reject{|k,v| k == 'AssociateTag' }.to_query
end
# FIXME: 全件取得しているのを最適化したい
proxies = Proxy.random(locale)
res = nil
proxies.each do |proxy|
start_time = Time.now
res = proxy.fetch(locale, request.query_string)
if res
Log.create(
atag: params['AssociateTag'],
locale: locale,
created_at: Time.now,
response: Time.now - start_time,
proxy: proxy,
success: true)
break
end
end
unless res
# TODO: トータルの失敗回数を増分
Log.create(
atag: params['AssociateTag'],
locale: locale,
created_at: Time.now,
success: false)
request.logger.error "[ERROR] failed to return response"
halt 503, "proxy unavailable"
end
redirect res['location'], 302
end
get '/stats' do
Stat.destroy_all
@stats = locales.map{|locale| Stat.create_by_logs(locale) }
haml :stats
end
get '/logs' do
redirect '/' unless current_user
@logs = Log.desc('$natural').limit(100).reverse
haml :logs
end
get '/users' do
redirect '/' unless current_user
@users = User.all.select{|user| user.proxies.size > 0 }
haml :users
end
get '/debug' do
raise StandardError.new('デバッグ')
end