Permalink
Browse files

5f/webapp

  • Loading branch information...
shirokanezoo
shirokanezoo committed Oct 31, 2015
1 parent d9ee9c7 commit 3d20016d7b103ea45e3f58501a749724004d987e
Showing with 41,216 additions and 0 deletions.
  1. +2 −0 5f/webapp/.gitignore
  2. +6 −0 5f/webapp/env.sh
  3. +13 −0 5f/webapp/golang/README.md
  4. +1 −0 5f/webapp/golang/src/github.com/gorilla/context
  5. +1 −0 5f/webapp/golang/src/github.com/gorilla/mux
  6. +1 −0 5f/webapp/golang/src/github.com/gorilla/securecookie
  7. +1 −0 5f/webapp/golang/src/github.com/gorilla/sessions
  8. +1 −0 5f/webapp/golang/src/github.com/lib/pq
  9. +8 −0 5f/webapp/ruby/Gemfile
  10. +45 −0 5f/webapp/ruby/Gemfile.lock
  11. +232 −0 5f/webapp/ruby/app.rb
  12. +3 −0 5f/webapp/ruby/config.ru
  13. +4 −0 5f/webapp/ruby/unicorn_config.rb
  14. +27 −0 5f/webapp/ruby/views/login.erb
  15. +74 −0 5f/webapp/ruby/views/main.erb
  16. +110 −0 5f/webapp/ruby/views/modify.erb
  17. +36 −0 5f/webapp/ruby/views/signup.erb
  18. +1 −0 5f/webapp/ruby/views/userjs.erb
  19. 0 5f/webapp/sql/.hold
  20. +20,019 −0 5f/webapp/sql/initialize.sql
  21. +31 −0 5f/webapp/sql/schema.sql
  22. 0 5f/webapp/static/.hold
  23. +587 −0 5f/webapp/static/css/bootstrap-theme.css
  24. +1 −0 5f/webapp/static/css/bootstrap-theme.css.map
  25. +5 −0 5f/webapp/static/css/bootstrap-theme.min.css
  26. +6,800 −0 5f/webapp/static/css/bootstrap.css
  27. +1 −0 5f/webapp/static/css/bootstrap.css.map
  28. +5 −0 5f/webapp/static/css/bootstrap.min.css
  29. +81 −0 5f/webapp/static/css/jumbotron-narrow.css
  30. +40 −0 5f/webapp/static/css/signin.css
  31. BIN 5f/webapp/static/fonts/glyphicons-halflings-regular.eot
  32. +288 −0 5f/webapp/static/fonts/glyphicons-halflings-regular.svg
  33. BIN 5f/webapp/static/fonts/glyphicons-halflings-regular.ttf
  34. BIN 5f/webapp/static/fonts/glyphicons-halflings-regular.woff
  35. BIN 5f/webapp/static/fonts/glyphicons-halflings-regular.woff2
  36. +58 −0 5f/webapp/static/js/airisu.js
  37. +2,363 −0 5f/webapp/static/js/bootstrap.js
  38. +7 −0 5f/webapp/static/js/bootstrap.min.js
  39. +10,351 −0 5f/webapp/static/js/jquery-1.11.3.js
  40. +13 −0 5f/webapp/static/js/npm.js
View
@@ -0,0 +1,2 @@
*.so
golang/app
View
@@ -0,0 +1,6 @@
#!/bin/sh
export PATH=/usr/local/bin:/home/isucon/.local/ruby/bin:/home/isucon/.local/node/bin:/home/isucon/.local/python3/bin:/home/isucon/.local/perl/bin:/home/isucon/.local/php/bin:/home/isucon/.local/php/sbin:/home/isucon/.local/go/bin:/home/isucon/.local/scala/bin:$PATH
export GOPATH=/home/isucon/gocode
export _JAVA_OPTIONS="-Dfile.encoding=UTF-8"
exec $*
View
@@ -0,0 +1,13 @@
## 前提
- Go がインストールされており、`go` にパスが通っていること
- $GOPATH がセットされていること
## 実行
```
go get github.com/tagomoris/isucon5-final
cd $GOPATH/github.com/tagomoris/isucon5-final/webapp/golang
go get
go run app.go
```
Submodule context added at 1c83b3
Submodule mux added at ad4d7a
Submodule securecookie added at e95799
Submodule sessions added at f72618
Submodule pq added at 83c4f4
View
@@ -0,0 +1,8 @@
source "https://rubygems.org"
gem "sinatra"
gem "sinatra-contrib"
gem "pg"
gem "erubis"
gem "unicorn"
gem "httpclient"
@@ -0,0 +1,45 @@
GEM
remote: https://rubygems.org/
specs:
backports (3.6.6)
erubis (2.7.0)
httpclient (2.6.0.1)
kgio (2.10.0)
multi_json (1.11.2)
pg (0.18.3)
rack (1.6.4)
rack-protection (1.5.3)
rack
rack-test (0.6.3)
rack (>= 1.0)
raindrops (0.15.0)
sinatra (1.4.6)
rack (~> 1.4)
rack-protection (~> 1.4)
tilt (>= 1.3, < 3)
sinatra-contrib (1.4.6)
backports (>= 2.0)
multi_json
rack-protection
rack-test
sinatra (~> 1.4.0)
tilt (>= 1.3, < 3)
tilt (2.0.1)
unicorn (4.9.0)
kgio (~> 2.6)
rack
raindrops (~> 0.7)
PLATFORMS
ruby
DEPENDENCIES
erubis
httpclient
pg
sinatra
sinatra-contrib
unicorn
BUNDLED WITH
1.10.6
View
@@ -0,0 +1,232 @@
require 'sinatra/base'
require 'sinatra/contrib'
require 'pg'
require 'tilt/erubis'
require 'erubis'
require 'json'
require 'httpclient'
require 'openssl'
# bundle config build.pg --with-pg-config=<path to pg_config>
# bundle install
module Isucon5f
module TimeWithoutZone
def to_s
strftime("%F %H:%M:%S")
end
end
::Time.prepend TimeWithoutZone
end
class Isucon5f::WebApp < Sinatra::Base
use Rack::Session::Cookie, secret: (ENV['ISUCON5_SESSION_SECRET'] || 'tonymoris')
set :erb, escape_html: true
set :public_folder, File.expand_path('../../static', __FILE__)
SALT_CHARS = [('a'..'z'),('A'..'Z'),('0'..'9')].map(&:to_a).reduce(&:+)
helpers do
def config
@config ||= {
db: {
host: ENV['ISUCON5_DB_HOST'] || 'localhost',
port: ENV['ISUCON5_DB_PORT'] && ENV['ISUCON5_DB_PORT'].to_i,
username: ENV['ISUCON5_DB_USER'] || 'isucon',
password: ENV['ISUCON5_DB_PASSWORD'],
database: ENV['ISUCON5_DB_NAME'] || 'isucon5f',
},
}
end
def db
return Thread.current[:isucon5_db] if Thread.current[:isucon5_db]
conn = PG.connect(
host: config[:db][:host],
port: config[:db][:port],
user: config[:db][:username],
password: config[:db][:password],
dbname: config[:db][:database],
connect_timeout: 3600
)
Thread.current[:isucon5_db] = conn
conn
end
def authenticate(email, password)
query = <<SQL
SELECT id, email, grade FROM users WHERE email=$1 AND passhash=digest(salt || $2, 'sha512')
SQL
user = nil
db.exec_params(query, [email, password]) do |result|
result.each do |tuple|
user = {id: tuple['id'].to_i, email: tuple['email'], grade: tuple['grade']}
end
end
session[:user_id] = user[:id] if user
user
end
def current_user
return @user if @user
return nil unless session[:user_id]
@user = nil
db.exec_params('SELECT id,email,grade FROM users WHERE id=$1', [session[:user_id]]) do |r|
r.each do |tuple|
@user = {id: tuple['id'].to_i, email: tuple['email'], grade: tuple['grade']}
end
end
session.clear unless @user
@user
end
def generate_salt
salt = ''
32.times do
salt << SALT_CHARS[rand(SALT_CHARS.size)]
end
salt
end
end
get '/signup' do
session.clear
erb :signup
end
post '/signup' do
email, password, grade = params['email'], params['password'], params['grade']
salt = generate_salt
insert_user_query = <<SQL
INSERT INTO users (email,salt,passhash,grade) VALUES ($1,$2,digest($3 || $4, 'sha512'),$5) RETURNING id
SQL
default_arg = {}
insert_subscription_query = <<SQL
INSERT INTO subscriptions (user_id,arg) VALUES ($1,$2)
SQL
db.transaction do |conn|
user_id = conn.exec_params(insert_user_query, [email,salt,salt,password,grade]).values.first.first
conn.exec_params(insert_subscription_query, [user_id, default_arg.to_json])
end
redirect '/login'
end
post '/cancel' do
redirect '/signup'
end
get '/login' do
session.clear
erb :login
end
post '/login' do
authenticate params['email'], params['password']
halt 403 unless current_user
redirect '/'
end
get '/logout' do
session.clear
redirect '/login'
end
get '/' do
unless current_user
return redirect '/login'
end
erb :main, locals: {user: current_user}
end
get '/user.js' do
halt 403 unless current_user
erb :userjs, content_type: 'application/javascript', locals: {grade: current_user[:grade]}
end
get '/modify' do
user = current_user
halt 403 unless user
query = <<SQL
SELECT arg FROM subscriptions WHERE user_id=$1
SQL
arg = db.exec_params(query, [user[:id]]).values.first[0]
erb :modify, locals: {user: user, arg: arg}
end
post '/modify' do
user = current_user
halt 403 unless user
service = params["service"]
token = params.has_key?("token") ? params["token"].strip : nil
keys = params.has_key?("keys") ? params["keys"].strip.split(/\s+/) : nil
param_name = params.has_key?("param_name") ? params["param_name"].strip : nil
param_value = params.has_key?("param_value") ? params["param_value"].strip : nil
select_query = <<SQL
SELECT arg FROM subscriptions WHERE user_id=$1 FOR UPDATE
SQL
update_query = <<SQL
UPDATE subscriptions SET arg=$1 WHERE user_id=$2
SQL
db.transaction do |conn|
arg_json = conn.exec_params(select_query, [user[:id]]).values.first[0]
arg = JSON.parse(arg_json)
arg[service] ||= {}
arg[service]['token'] = token if token
arg[service]['keys'] = keys if keys
if param_name && param_value
arg[service]['params'] ||= {}
arg[service]['params'][param_name] = param_value
end
conn.exec_params(update_query, [arg.to_json, user[:id]])
end
redirect '/modify'
end
def fetch_api(method, uri, headers, params)
client = HTTPClient.new
if uri.start_with? "https://"
client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE
end
fetcher = case method
when 'GET' then client.method(:get_content)
when 'POST' then client.method(:post_content)
else
raise "unknown method #{method}"
end
res = fetcher.call(uri, params, headers)
JSON.parse(res)
end
get '/data' do
unless user = current_user
halt 403
end
arg_json = db.exec_params("SELECT arg FROM subscriptions WHERE user_id=$1", [user[:id]]).values.first[0]
arg = JSON.parse(arg_json)
data = []
arg.each_pair do |service, conf|
row = db.exec_params("SELECT meth, token_type, token_key, uri FROM endpoints WHERE service=$1", [service]).values.first
method, token_type, token_key, uri_template = row
headers = {}
params = (conf['params'] && conf['params'].dup) || {}
case token_type
when 'header' then headers[token_key] = conf['token']
when 'param' then params[token_key] = conf['token']
end
uri = sprintf(uri_template, *conf['keys'])
data << {"service" => service, "data" => fetch_api(method, uri, headers, params)}
end
json data
end
get '/initialize' do
file = File.expand_path("../../sql/initialize.sql", __FILE__)
system("psql", "-f", file, "isucon5f")
end
end
View
@@ -0,0 +1,3 @@
require_relative './app.rb'
run Isucon5f::WebApp
@@ -0,0 +1,4 @@
worker_processes 4
preload_app true
listen 8080
# pid "/home/isucon/webapp/ruby/unicorn.pid"
@@ -0,0 +1,27 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags -->
<title>AirISU Sign in</title>
<link href="/css/bootstrap.min.css" rel="stylesheet">
<link href="/css/signin.css" rel="stylesheet">
</head>
<body>
<div class="container">
<form class="form-signin" method="POST" action="/login">
<h2 class="form-signin-heading">Welcome to AirISU!</h2>
<label for="inputEmail" class="sr-only">Email address</label>
<input name="email" type="email" id="inputEmail" class="form-control" placeholder="Email address" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input name="password" type="password" id="inputPassword" class="form-control" placeholder="Password" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
</body>
</html>
Oops, something went wrong.

0 comments on commit 3d20016

Please sign in to comment.