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 +1 -1
- data/ass.yml +2 -0
- data/cron +17 -4
- data/lib/ass.rb +158 -17
- data/public/favicon.ico +0 -0
- data/views/about.erb +9 -0
- data/views/error.erb +6 -0
- data/views/index.erb +11 -20
- data/views/layout.erb +45 -25
- data/views/not_found.erb +6 -0
- data/views/push.erb +88 -0
- data/views/token.erb +87 -0
- metadata +340 -283
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.17
|
data/ass.yml
CHANGED
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
|
-
|
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
|
-
|
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:
|
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 <<
|
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
|
-
|
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
|
-
|
126
|
-
|
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
|
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)
|
data/public/favicon.ico
ADDED
Binary file
|
data/views/about.erb
ADDED
@@ -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 »</a></p>
|
data/views/error.erb
ADDED
data/views/index.erb
CHANGED
@@ -1,26 +1,17 @@
|
|
1
|
-
|
2
|
-
|
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
|
-
|
16
|
-
|
17
|
-
|
6
|
+
|
7
|
+
http://serverIP:4567/v1/apps/{yourapp}/DeviceToken
|
8
|
+
|
18
9
|
2: How to send push notification? (Server Side)
|
19
|
-
|
20
|
-
$ curl http://localhost
|
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
|
-
|
15
|
+
param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest, db table ID
|
16
|
+
</pre>
|
26
17
|
|
data/views/layout.erb
CHANGED
@@ -1,29 +1,49 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
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
|
|