lib/ass.rb in ass-0.0.15 vs lib/ass.rb in ass-0.0.17

- old
+ new

@@ -1,7 +1,6 @@ -#!/usr/bin/env ruby -# -*- coding: utf-8 -*- +#encoding: utf-8 require 'rubygems' require 'sinatra' require 'sequel' require 'socket' @@ -10,11 +9,18 @@ require 'rufus/scheduler' require 'eventmachine' require 'sinatra/base' require 'yaml' require 'uri-handler' +require 'net/http' require 'active_support' +require 'json' +require 'digest/sha2' +require 'will_paginate' +require 'will_paginate/sequel' # or data_mapper/sequel +require 'uri' +require 'sinatra/reloader' if development? ############################################################ ## Initilization Setup ############################################################ LIBDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')) @@ -43,10 +49,12 @@ $port = config['port'] || 4567 $mode = config['mode'] || env $VERSION = File.open("#{ROOTDIR}/VERSION", "rb").read $apps = config['apps'] || [] $log = config['log'] || 'off' +$user = config['user'] || 'admin' +$pass = config['pass'] || 'pass' ############################################################ ## Certificate Key Setup ############################################################ @@ -69,29 +77,29 @@ return true end unless check_cert then html = <<-END -1: please provide certificate key pem file under current directory, name should be: appid_dev.pem for development and appid_prod.pem for production +1: please provide certificate key pem file under current directory, name should be: appid_development.pem for development and appid_production.pem for production 2: edit your ass.yml under current directory 3: run ass 4: iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below: -END + END $apps.each { |app| html << "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken" } - html << "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message" + html << "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message" puts html exit else html = <<-END #{'*'*80} Apple Service Server(#{$VERSION}) is Running ... Push Notification Service: Enabled Mode: #{$mode} Port: #{$port} -END + END html << "#{'*'*80}" html << "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".to_i == 0 html << "\n" html << "access http://localhost:#{$port}/ for more information" html << "\n" @@ -108,25 +116,43 @@ $DB.create_table :tokens do primary_key :id String :app, :unique => false, :null => false String :token, :unique => false, :null => false, :size => 100 + Time :created_at index [:app, :token] end $DB.create_table :pushes do primary_key :id String :pid, :unique => true, :null => false, :size => 100 - index :pid + String :app, :unique => true, :null => false, :size => 30 + String :message, :unique => false, :null => false, :size => 107 + Time :created_at + index [:pid, :message] end else $DB = Sequel.connect("sqlite://#{Dir.pwd}/ass.db") end -Token = $DB[:tokens] -Push = $DB[:pushes] +WillPaginate.per_page = 10 +# Token = $DB[:tokens] +# Push = $DB[:pushes] + +class Token < Sequel::Model + Sequel.extension :pagination + + # here is your code +end + +class Push < Sequel::Model + Sequel.extension :pagination + + # here is your code +end + ############################################################ ## Timer Job Setup ############################################################ scheduler = Rufus::Scheduler.start_new @@ -140,21 +166,33 @@ ############################################################ ## Apple Service Server based on Sinatra ############################################################ class App < Sinatra::Base - + set :root, File.expand_path('../../', __FILE__) set :port, "#{$port}".to_i set :public_folder, File.dirname(__FILE__) + '/../public' - set :views, File.dirname(__FILE__) + '/../views' + set :views, File.dirname(__FILE__) + '/../views' + def authorized? + @auth ||= Rack::Auth::Basic::Request.new(request.env) + @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ["#{$user}", "#{$pass}"] + end + + def protected! + unless authorized? + response['WWW-Authenticate'] = %(Basic realm="Restricted Area") + throw(:halt, [401, "Oops... we need your login name & password\n"]) + end + end + configure :production, :development do if "#{$log}".strip == 'on' then - enable :logging + enable :logging end - end + end if "#{$mode}".strip == 'development' then set :show_exceptions, true set :dump_errors, true else @@ -164,27 +202,130 @@ get '/' do erb :index end + get '/about' do + erb :about + end + + not_found do + erb :not_found + end + + error do + @error = params['captures'].first.inspect + end + + post '/v1/send' do + app = params[:app] + message = CGI::escape(params[:message] || "") + pid = "#{Time.now.to_i}" + # begin + # url = URI.parse("http://localhost:#{$port}/v1/apps/#{app}/push") + # post_args1 = { :alert => "#{message}".encode('UTF-8'), :pid => "#{pid}" } + # Net::HTTP.post_form(url, post_args1) + # rescue =>err + # puts "#{err.class} ##{err}" + # end + system "curl http://localhost:#{$port}/v1/apps/#{app}/push/#{message}/#{pid}" + redirect '/' if (params[:app] and params[:message]) + end + + get "/v1/admin/:db" do + protected! + db = params[:db] || 'token' + page = 1 + page = params[:page].to_i if params[:page] + if (db == 'token') then + @o = [] + $apps.each_with_index { |app, index| + @o << Token.where(:app => app).order(:id).reverse.paginate(page, 20) + } + erb :token + end + if (db == 'push') then + @p = [] + $apps.each_with_index { |app, index| + @p << Push.where(:app => app).order(:id).reverse.paginate(page, 20) + } + erb :push + end + end + $apps.each { |app| + + ## register token api get "/v1/apps/#{app}/:token" do - puts "[#{params[:token]}] was added to '#{app}'" if "#{$mode}".strip == 'development' - o = Token.first(:app => app , :token => params[:token]) + puts "[#{params[:token]}] was added to '#{app}'" if "#{$mode}".strip == 'development' + o = Token.first(:app => app, :token => params[:token]) unless o Token.insert( :app => app, :token => params[:token] ) end end + ## http POST method push api + post "/v1/apps/#{app}/push" do + protected! unless request.host == 'localhost' + message = CGI::unescape(params[:alert] || "")[0..107] + badge = 1 + puts "params[:badge] = [#{params[:badge]}]" + badge = params[:badge].to_i if params[:badge] and params[:badge] != '' + sound = CGI::unescape(params[:sound] || "") + extra = CGI::unescape(params[:extra] || "") + + puts "#{badge} : #{message} extra: #{extra}" if "#{$mode}".strip == 'development' + pid = params[:pid] + + puts "'#{message}' was sent to (#{app}) with pid: [#{pid}], badge:#{badge} , sound: #{sound}, extra:#{extra}" if "#{$mode}".strip == 'development' + + @push = Token.where(:app => app) + @exist = Push.first(:pid => pid) + + unless @exist + openSSLContext = $certkey["#{app}"] + # Connect to port 2195 on the server. + sock = nil + if $mode == 'production' then + sock = TCPSocket.new('gateway.push.apple.com', 2195) + else + sock = TCPSocket.new('gateway.sandbox.push.apple.com', 2195) + end + # do our SSL handshaking + sslSocket = OpenSSL::SSL::SSLSocket.new(sock, openSSLContext) + sslSocket.connect + #Push.create( :pid => pid ) + Push.insert(:pid => pid, :message => message, :created_at => Time.now, :app => "#{app}" ) + # write our packet to the stream + @push.each do |o| + tokenText = o[:token] + # pack the token to convert the ascii representation back to binary + tokenData = [tokenText].pack('H*') + # construct the payload + po = {:aps => {:alert => "#{message}", :badge => badge, :sound => "#{sound}"}, :extra => "#{extra}"} + payload = ActiveSupport::JSON.encode(po) + # construct the packet + packet = [0, 0, 32, tokenData, 0, payload.length, payload].pack("ccca*cca*") + # read our certificate and set up our SSL context + sslSocket.write(packet) + end + # cleanup + sslSocket.close + sock.close + end + end + + ## http GET method push api get "/v1/apps/#{app}/push/:message/:pid" do + protected! unless request.host == 'localhost' message = CGI::unescape(params[:message]) - puts message if "#{$mode}".strip == 'development' + puts message if "#{$mode}".strip == 'development' pid = params[:pid] - puts "'#{message}' was sent to (#{app}) with pid: [#{pid}]" if "#{$mode}".strip == 'development' + puts "'#{message}' was sent to (#{app}) with pid: [#{pid}]" if "#{$mode}".strip == 'development' @push = Token.where(:app => app) @exist = Push.first(:pid => pid) unless @exist \ No newline at end of file