ass 0.0.15 → 0.0.17

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.15
1
+ 0.0.17
data/ass.yml CHANGED
@@ -3,5 +3,7 @@ mode: development
3
3
  log: off
4
4
  cron: cron
5
5
  timer: 0
6
+ user: admin
7
+ pass: pass
6
8
  apps:
7
9
  - app1
data/cron CHANGED
@@ -1,11 +1,12 @@
1
+ #encoding: utf-8
1
2
  #!/usr/bin/env ruby
2
- # -*- coding: utf-8 -*-
3
3
 
4
4
  require 'rubygems'
5
5
  require 'yaml'
6
6
  require 'cgi'
7
7
  require 'digest/sha2'
8
8
  require 'uri-handler'
9
+ require 'net/http'
9
10
 
10
11
  ###########################################################################
11
12
  ####
@@ -26,13 +27,25 @@ $apps = config['apps'] || []
26
27
  ## Using curl command to send push notification message
27
28
  ############################################################
28
29
 
29
- @message = CGI::escape("This is an push notification message sent by ASS".to_uri)
30
+ @message = "This is an push notification message sent by ASS" # CGI::escape("This is an push notification message sent by ASS".to_uri)
30
31
 
31
32
  #@pid = Digest::SHA2.hexdigest("#{Time.now.to_i}")
32
33
  @pid = "#{Time.now.to_i}"
33
34
 
35
+
36
+ ## get api
34
37
  $apps.each { |app|
35
38
  sleep 1
36
- puts "curl http://localhost:#{$port}/v1/apps/#{app}/push/#{@message}/#{@pid}"
37
- system "curl http://localhost:#{$port}/v1/apps/#{app}/push/#{@message}/#{@pid}"
39
+ # system "curl http://localhost:#{$port}/v1/apps/#{app}/push/#{@message}/#{@pid}"
38
40
  }
41
+
42
+ ## post api
43
+ begin
44
+ url = URI.parse("http://localhost:#{$port}/v1/apps/#{app}/push")
45
+ post_args1 = { :alert => "#{@message}", :pid => "#{@pid}" }
46
+ puts url
47
+ Net::HTTP.post_form(url, post_args1)
48
+ rescue =>err
49
+ puts "#{err.class} ##{err}"
50
+ end
51
+
data/lib/ass.rb CHANGED
@@ -1,5 +1,4 @@
1
- #!/usr/bin/env ruby
2
- # -*- coding: utf-8 -*-
1
+ #encoding: utf-8
3
2
 
4
3
  require 'rubygems'
5
4
  require 'sinatra'
@@ -12,7 +11,14 @@ require 'eventmachine'
12
11
  require 'sinatra/base'
13
12
  require 'yaml'
14
13
  require 'uri-handler'
14
+ require 'net/http'
15
15
  require 'active_support'
16
+ require 'json'
17
+ require 'digest/sha2'
18
+ require 'will_paginate'
19
+ require 'will_paginate/sequel' # or data_mapper/sequel
20
+ require 'uri'
21
+ require 'sinatra/reloader' if development?
16
22
 
17
23
  ############################################################
18
24
  ## Initilization Setup
@@ -45,6 +51,8 @@ $mode = config['mode'] || env
45
51
  $VERSION = File.open("#{ROOTDIR}/VERSION", "rb").read
46
52
  $apps = config['apps'] || []
47
53
  $log = config['log'] || 'off'
54
+ $user = config['user'] || 'admin'
55
+ $pass = config['pass'] || 'pass'
48
56
 
49
57
  ############################################################
50
58
  ## Certificate Key Setup
@@ -71,15 +79,15 @@ end
71
79
 
72
80
  unless check_cert then
73
81
  html = <<-END
74
- 1: please provide certificate key pem file under current directory, name should be: appid_dev.pem for development and appid_prod.pem for production
82
+ 1: please provide certificate key pem file under current directory, name should be: appid_development.pem for development and appid_production.pem for production
75
83
  2: edit your ass.yml under current directory
76
84
  3: run ass
77
85
  4: iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below:
78
- END
86
+ END
79
87
  $apps.each { |app|
80
88
  html << "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken"
81
89
  }
82
- html << "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message"
90
+ html << "5: Server: cron should access 'curl http://localhost:#{$port}/v1/app/push/{messages}/{pid}' to send push message"
83
91
  puts html
84
92
  exit
85
93
  else
@@ -89,7 +97,7 @@ Apple Service Server(#{$VERSION}) is Running ...
89
97
  Push Notification Service: Enabled
90
98
  Mode: #{$mode}
91
99
  Port: #{$port}
92
- END
100
+ END
93
101
  html << "#{'*'*80}"
94
102
  html << "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".to_i == 0
95
103
  html << "\n"
@@ -110,20 +118,38 @@ unless File.exist?("#{Dir.pwd}/ass.db") then
110
118
  primary_key :id
111
119
  String :app, :unique => false, :null => false
112
120
  String :token, :unique => false, :null => false, :size => 100
121
+ Time :created_at
113
122
  index [:app, :token]
114
123
  end
115
124
 
116
125
  $DB.create_table :pushes do
117
126
  primary_key :id
118
127
  String :pid, :unique => true, :null => false, :size => 100
119
- index :pid
128
+ String :app, :unique => true, :null => false, :size => 30
129
+ String :message, :unique => false, :null => false, :size => 107
130
+ Time :created_at
131
+ index [:pid, :message]
120
132
  end
121
133
  else
122
134
  $DB = Sequel.connect("sqlite://#{Dir.pwd}/ass.db")
123
135
  end
124
136
 
125
- Token = $DB[:tokens]
126
- Push = $DB[:pushes]
137
+ WillPaginate.per_page = 10
138
+
139
+ # Token = $DB[:tokens]
140
+ # Push = $DB[:pushes]
141
+
142
+ class Token < Sequel::Model
143
+ Sequel.extension :pagination
144
+
145
+ # here is your code
146
+ end
147
+
148
+ class Push < Sequel::Model
149
+ Sequel.extension :pagination
150
+
151
+ # here is your code
152
+ end
127
153
 
128
154
  ############################################################
129
155
  ## Timer Job Setup
@@ -142,17 +168,29 @@ end
142
168
  ############################################################
143
169
 
144
170
  class App < Sinatra::Base
145
-
171
+
146
172
  set :root, File.expand_path('../../', __FILE__)
147
173
  set :port, "#{$port}".to_i
148
174
  set :public_folder, File.dirname(__FILE__) + '/../public'
149
- set :views, File.dirname(__FILE__) + '/../views'
175
+ set :views, File.dirname(__FILE__) + '/../views'
176
+
177
+ def authorized?
178
+ @auth ||= Rack::Auth::Basic::Request.new(request.env)
179
+ @auth.provided? && @auth.basic? && @auth.credentials && @auth.credentials == ["#{$user}", "#{$pass}"]
180
+ end
181
+
182
+ def protected!
183
+ unless authorized?
184
+ response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
185
+ throw(:halt, [401, "Oops... we need your login name & password\n"])
186
+ end
187
+ end
150
188
 
151
189
  configure :production, :development do
152
190
  if "#{$log}".strip == 'on' then
153
- enable :logging
191
+ enable :logging
154
192
  end
155
- end
193
+ end
156
194
 
157
195
  if "#{$mode}".strip == 'development' then
158
196
  set :show_exceptions, true
@@ -166,10 +204,60 @@ class App < Sinatra::Base
166
204
  erb :index
167
205
  end
168
206
 
207
+ get '/about' do
208
+ erb :about
209
+ end
210
+
211
+ not_found do
212
+ erb :not_found
213
+ end
214
+
215
+ error do
216
+ @error = params['captures'].first.inspect
217
+ end
218
+
219
+ post '/v1/send' do
220
+ app = params[:app]
221
+ message = CGI::escape(params[:message] || "")
222
+ pid = "#{Time.now.to_i}"
223
+ # begin
224
+ # url = URI.parse("http://localhost:#{$port}/v1/apps/#{app}/push")
225
+ # post_args1 = { :alert => "#{message}".encode('UTF-8'), :pid => "#{pid}" }
226
+ # Net::HTTP.post_form(url, post_args1)
227
+ # rescue =>err
228
+ # puts "#{err.class} ##{err}"
229
+ # end
230
+ system "curl http://localhost:#{$port}/v1/apps/#{app}/push/#{message}/#{pid}"
231
+ redirect '/' if (params[:app] and params[:message])
232
+ end
233
+
234
+ get "/v1/admin/:db" do
235
+ protected!
236
+ db = params[:db] || 'token'
237
+ page = 1
238
+ page = params[:page].to_i if params[:page]
239
+ if (db == 'token') then
240
+ @o = []
241
+ $apps.each_with_index { |app, index|
242
+ @o << Token.where(:app => app).order(:id).reverse.paginate(page, 20)
243
+ }
244
+ erb :token
245
+ end
246
+ if (db == 'push') then
247
+ @p = []
248
+ $apps.each_with_index { |app, index|
249
+ @p << Push.where(:app => app).order(:id).reverse.paginate(page, 20)
250
+ }
251
+ erb :push
252
+ end
253
+ end
254
+
169
255
  $apps.each { |app|
256
+
257
+ ## register token api
170
258
  get "/v1/apps/#{app}/:token" do
171
- puts "[#{params[:token]}] was added to '#{app}'" if "#{$mode}".strip == 'development'
172
- o = Token.first(:app => app , :token => params[:token])
259
+ puts "[#{params[:token]}] was added to '#{app}'" if "#{$mode}".strip == 'development'
260
+ o = Token.first(:app => app, :token => params[:token])
173
261
  unless o
174
262
  Token.insert(
175
263
  :app => app,
@@ -178,12 +266,65 @@ class App < Sinatra::Base
178
266
  end
179
267
  end
180
268
 
269
+ ## http POST method push api
270
+ post "/v1/apps/#{app}/push" do
271
+ protected! unless request.host == 'localhost'
272
+ message = CGI::unescape(params[:alert] || "")[0..107]
273
+ badge = 1
274
+ puts "params[:badge] = [#{params[:badge]}]"
275
+ badge = params[:badge].to_i if params[:badge] and params[:badge] != ''
276
+ sound = CGI::unescape(params[:sound] || "")
277
+ extra = CGI::unescape(params[:extra] || "")
278
+
279
+ puts "#{badge} : #{message} extra: #{extra}" if "#{$mode}".strip == 'development'
280
+ pid = params[:pid]
281
+
282
+ puts "'#{message}' was sent to (#{app}) with pid: [#{pid}], badge:#{badge} , sound: #{sound}, extra:#{extra}" if "#{$mode}".strip == 'development'
283
+
284
+ @push = Token.where(:app => app)
285
+ @exist = Push.first(:pid => pid)
286
+
287
+ unless @exist
288
+ openSSLContext = $certkey["#{app}"]
289
+ # Connect to port 2195 on the server.
290
+ sock = nil
291
+ if $mode == 'production' then
292
+ sock = TCPSocket.new('gateway.push.apple.com', 2195)
293
+ else
294
+ sock = TCPSocket.new('gateway.sandbox.push.apple.com', 2195)
295
+ end
296
+ # do our SSL handshaking
297
+ sslSocket = OpenSSL::SSL::SSLSocket.new(sock, openSSLContext)
298
+ sslSocket.connect
299
+ #Push.create( :pid => pid )
300
+ Push.insert(:pid => pid, :message => message, :created_at => Time.now, :app => "#{app}" )
301
+ # write our packet to the stream
302
+ @push.each do |o|
303
+ tokenText = o[:token]
304
+ # pack the token to convert the ascii representation back to binary
305
+ tokenData = [tokenText].pack('H*')
306
+ # construct the payload
307
+ po = {:aps => {:alert => "#{message}", :badge => badge, :sound => "#{sound}"}, :extra => "#{extra}"}
308
+ payload = ActiveSupport::JSON.encode(po)
309
+ # construct the packet
310
+ packet = [0, 0, 32, tokenData, 0, payload.length, payload].pack("ccca*cca*")
311
+ # read our certificate and set up our SSL context
312
+ sslSocket.write(packet)
313
+ end
314
+ # cleanup
315
+ sslSocket.close
316
+ sock.close
317
+ end
318
+ end
319
+
320
+ ## http GET method push api
181
321
  get "/v1/apps/#{app}/push/:message/:pid" do
322
+ protected! unless request.host == 'localhost'
182
323
  message = CGI::unescape(params[:message])
183
- puts message if "#{$mode}".strip == 'development'
324
+ puts message if "#{$mode}".strip == 'development'
184
325
  pid = params[:pid]
185
326
 
186
- puts "'#{message}' was sent to (#{app}) with pid: [#{pid}]" if "#{$mode}".strip == 'development'
327
+ puts "'#{message}' was sent to (#{app}) with pid: [#{pid}]" if "#{$mode}".strip == 'development'
187
328
 
188
329
  @push = Token.where(:app => app)
189
330
  @exist = Push.first(:pid => pid)
Binary file
@@ -0,0 +1,9 @@
1
+ <h3>Apple Service Server (<%= $VERSION %>) </h3>
2
+ <p/>
3
+ <p>
4
+ Author: Eiffel(Q) <br/>
5
+ Email: <a href="mailto:eiffelqiu@gmail.com"> eiffelqiu@gmail.com </a> <br/>
6
+ QQ: 1053568 <br/>
7
+ Github: <a href="https://github.com/eiffelqiu/"> https://github.com/eiffelqiu/ </a>
8
+ <p/>
9
+ <p><a href="https://github.com/eiffelqiu/ass" class="btn btn-primary btn-large">Learn more ASS &raquo;</a></p>
@@ -0,0 +1,6 @@
1
+ <h3>Apple Service Server (<%= $VERSION %>) </h3>
2
+ <p/>
3
+ <p>
4
+ Oops , you got an error : <%= @error %>
5
+ <p/>
6
+ <p><a href="https://github.com/eiffelqiu/ass" class="btn btn-primary btn-large">Learn more ASS &raquo;</a></p>
@@ -1,26 +1,17 @@
1
-
2
- <h1>Apple Service Server (<%= $VERSION %>) </h1>
3
- <hr>
4
- <p/>
5
- <p><b>
6
- author: Eiffel(Q) <br/>
7
- email: eiffelqiu@gmail.com <br/>
8
- github: https://github.com/eiffelqiu/
9
- </b><p/>
10
- <hr>
11
- <pre class="prettyprint linenums">
1
+ <h3>Apple Service Server Usage: </h3>
2
+ <pre class="prettyprint linenums">
12
3
  1: How to register notification? (Client Side)
13
-
4
+
14
5
  In AppDelegate file, inside didRegisterForRemoteNotificationsWithDeviceToken method access url below to register device token:
15
- <% $apps.each { |app| %>
16
- '#{app}': http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken
17
- <% } %>
6
+
7
+ http://serverIP:4567/v1/apps/{yourapp}/DeviceToken
8
+
18
9
  2: How to send push notification? (Server Side)
19
- <% $apps.each { |app| %>
20
- $ curl http://localhost:#{$port}/v1/apps/#{app}/push/{message}/{pid}
21
- <% } %>
10
+
11
+ $ curl http://localhost:4567/v1/apps/{yourapp}/push/{message}/{pid}
12
+
22
13
  Note:
23
14
  param1 (message): push notification message you want to send, remember the message should be html escaped
24
- param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest
25
- </pre>
15
+ param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest, db table ID
16
+ </pre>
26
17
 
@@ -1,29 +1,49 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
- <head>
4
- <title>Apple Service Server</title>
5
- <!-- Bootstrap -->
6
- <link href="css/bootstrap.min.css" rel="stylesheet">
7
- </head>
8
- <body>
9
- <div class="container">
10
-
11
- <!-- Main hero unit for a primary marketing message or call to action -->
12
- <div class="hero-unit">
13
- <%= yield %>
14
- <p><a href="https://github.com/eiffelqiu/ass" class="btn btn-primary btn-large">Learn more &raquo;</a></p>
15
- </div>
16
-
17
-
18
- <hr>
19
-
20
- <footer>
21
- <p>&copy; likenote.com 2012</p>
22
- </footer>
23
-
24
- </div> <!-- /container -->
25
- <script src="http://code.jquery.com/jquery-latest.js"></script>
26
- <script src="js/bootstrap.min.js"></script>
27
- </body>
3
+ <head>
4
+ <title>Apple Service Server</title>
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <meta name="description" content="Apple Service Server">
7
+ <meta name="author" content="eiffel.q">
8
+ <!-- Bootstrap -->
9
+ <link href="/css/bootstrap.min.css" rel="stylesheet">
10
+ <style type="text/css">
11
+ footer {
12
+ height: 60px;
13
+ background-color: #f5f5f5;
14
+ }
15
+ </style>
16
+ <link href="/css/bootstrap-responsive.min.css" rel="stylesheet">
17
+ <link rel="icon" href="/favicon.ico" mce_href="/favicon.ico" type="image/x-icon">
18
+ </head>
19
+ <body>
20
+ <div class="container">
21
+ <div class="navbar navbar-inverse navbar-fixed-top">
22
+ <div class="navbar-inner">
23
+ <a class="brand" href="#">Apple Service Server (<%= $VERSION %>)</a>
24
+ <ul class="nav">
25
+ <li><a href="/">Home</a></li>
26
+ <li><a href="/about">About</a></li>
27
+ <li><a href="/v1/admin/token">Admin</a></li>
28
+ </ul>
29
+ <ul class="nav pull-right">
30
+ <li><a href="mailto:eiffelqiu@gmail.com">Eiffel Qiu</a></li>
31
+ </ul>
32
+ </div>
33
+ </div>
34
+ <!-- Main hero unit for a primary marketing message or call to action -->
35
+ <div class="hero-unit">
36
+ <%= yield %>
37
+ </div>
38
+ </div>
39
+ <footer id="footer">
40
+ <div class="container">
41
+ <p><a href="https://github.com/eiffelqiu/">github.com/eiffelqiu</a> 2013</p>
42
+ </div>
43
+ </footer>
44
+ <!-- /container -->
45
+ <script src="http://code.jquery.com/jquery-latest.js"></script>
46
+ <script src="/js/bootstrap.min.js"></script>
47
+ </body>
28
48
  </html>
29
49