Newer
Older
IntroQuiz2019 / app.rb
require 'bundler/setup'
Bundler.require
require 'sinatra/reloader' if development?
require 'sinatra-websocket'
require 'active_support'
require 'active_support/core_ext'
require 'sqlite3'
require 'active_record'
require 'json'
require 'openssl'
require 'logger'

enable :sessions

configure do #どの環境でも起動時に1回だけ実行される
  set :flags => (1..6).to_a.map{|i| [i, 0]}.to_h #どのチームが押したか管理するためのハッシュ
end

set :server, 'thin'
set :sockets, []

logger = Logger.new(STDOUT)

ActiveRecord::Base.establish_connection(
  adapter: 'sqlite3',
  database: './db/accounts.db'
)

after do
  ActiveRecord::Base.connection.close
end

class User < ActiveRecord::Base
end

get '/' do
  if session[:team].blank? #チームがsetされていないときに選択画面に遷移する
    erb :select
  else
    pushed=Array.new
    settings.flags.sort_by{|key,value| value}.each do |key, value|
      if value==0
        next
      else
        pushed.push("<li>チーム#{key}</li>")
      end
    end
    @pushed=pushed.join("\n")
    erb :index
  end
end

get '/websocket' do
  if request.websocket? #WebSocket通信かどうかを確認
    request.websocket do |ws|
      ws.onopen do #最初に開いたときの処理
        settings.sockets << ws
      end
      ws.onmessage do |btn| #ボタン or resetが押された時の処理
        logger.debug(btn)
        if session[:team].present? && btn=="Stop!"
          if session[:team]>=1 && session[:team]<=6 && settings.flags[session[:team]]==0
            settings.flags[session[:team]] = settings.flags.values.inject(:+) + 1
            t_name="チーム#{session[:team]}"
            logger.debug(settings.flags)
            settings.sockets.each do |s|
              s.send(t_name)
            end
          end
        elsif session[:admin].present? && btn=="Delete!" #controllerでresetを押された時の処理
          if session[:admin]==8804912
            settings.sockets.each do |s|
              settings.flags=(1..6).to_a.map{|i| [i, 0]}.to_h
              logger.debug(settings.flags)
              s.send("Delete!")
            end
          end
        end
      end
      ws.onclose do #ブラウザ閉じたりとかしてWebSocket通信が切れた時の処理
        settings.sockets.delete(ws)
      end
    end
  end
end

post '/setteam' do #チーム決定ボタンを押した時の処理
  t_num=params[:t_num].to_i
  if t_num>=1 && t_num<=6
    session[:team]=t_num
    redirect "/"
  else
    session.clear
    erb :error
  end
end

get '/resetteam' do #チーム再選択ボタンを押した時の処理
  session.clear #チーム番号を保持しているセッションを消す
  redirect "/"
end

get '/viewer' do
  pushed=Array.new
  settings.flags.sort_by{|key,value| value}.each do |key, value|
    if value==0
      next
    else
      pushed.push("<li>チーム#{key}</li>")
    end
  end
  @pushed=pushed.join("\n")
  erb :viewer
end

get '/controller' do #出題者が操作するページ
  if session[:admin].blank?
    erb :login
  elsif session[:admin]==8804912
    pushed=Array.new
    settings.flags.sort_by{|key,value| value}.each do |key, value|
      if value==0
        next
      else
        pushed.push("<li>チーム#{key}</li>")
      end
    end
    @pushed=pushed.join("\n")
    erb :controller
  end
end

post '/check' do
  if params[:user_id]
    user_id = params[:user_id]
    session[:user_id] = user_id
    user=User.find_by(user_id: user_id)
    salt = [OpenSSL::Random.random_bytes(32)].pack("m").chomp!
    session[:salt]=salt
    logger.debug("hogehoge"+salt)
    logger.debug(user)
    if user.blank?
      salt_dummy = [OpenSSL::Random.random_bytes(32)].pack("m").chomp!
      {nounce: salt_dummy, salt: salt}.to_json
    else
      logger.debug("akanwa")
      {nounce: user.salt, salt: salt}.to_json
    end
  elsif params[:password]
    password = params[:password]
    user=User.find_by(user_id: session[:user_id])
    if user.blank?
      return
    end
    payload = user.password_digest
    hmac = OpenSSL::HMAC.hexdigest('sha256', session[:salt], payload)
    logger.debug(hmac)
    payload2 = hmac
    logger.debug(params[:salt])
    hmac2 = OpenSSL::HMAC.hexdigest('sha256', params[:salt], payload2)
    logger.debug(hmac2)
    if password == hmac2
      session[:admin]=8804912
      return
    else
      return
    end
  end
end