ass 0.0.15 → 0.0.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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