ass 0.0.2 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +83 -15
- data/VERSION +1 -1
- data/ass.yml +1 -6
- data/lib/ass.rb +71 -42
- metadata +53 -5
data/README.md
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
ass
|
2
2
|
=======
|
3
|
-
Apple Service Server written with Sinatra and Sequel(Sqlite3)
|
3
|
+
####Apple Service Server was written with Ruby/Sinatra and Sequel(Sqlite3), it provides push notification and in app purchase service.####
|
4
4
|
|
5
5
|
Feature:
|
6
6
|
=======
|
7
7
|
|
8
|
-
1. rubygem, simple to install.
|
9
|
-
2. provide pem file
|
10
|
-
3. no need to setup database
|
8
|
+
1. 'ass' is a rubygem, simple to install.
|
9
|
+
2. only need to provide pem file.
|
10
|
+
3. no need to setup database(using sqlite3).
|
11
11
|
4. provide default config file(ass.yml) with default value.
|
12
12
|
|
13
|
-
|
13
|
+
Sqlite3 Installation
|
14
14
|
=======
|
15
15
|
|
16
|
-
Sqlite3
|
17
|
-
-------
|
18
|
-
|
19
16
|
CentOS/Redhat:
|
20
17
|
|
21
18
|
$ yum install sqlite3
|
@@ -31,8 +28,8 @@ FreeBSD:
|
|
31
28
|
|
32
29
|
Mac:
|
33
30
|
|
34
|
-
$ brew install
|
35
|
-
$ port install
|
31
|
+
$ brew install sqlite # Homebrew
|
32
|
+
$ port install sqlite # Macport
|
36
33
|
|
37
34
|
Installation
|
38
35
|
=======
|
@@ -41,16 +38,87 @@ Installation
|
|
41
38
|
|
42
39
|
Usage
|
43
40
|
=======
|
44
|
-
1. under the current directory, provide single pem file combined with certificate and key, HOWTO ([Check this link](http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12))
|
45
41
|
|
46
|
-
|
42
|
+
Prepare pem file:
|
43
|
+
|
44
|
+
under the current directory, provide single pem file combined with certificate and key(name pattern: appid_mode.pem), HOWTO ([Check this link](http://www.raywenderlich.com/3443/apple-push-notification-services-tutorial-part-12))
|
45
|
+
|
46
|
+
cert.pem:
|
47
|
+
openssl x509 -in aps_development.cer -inform der -out cert.pem
|
48
|
+
|
49
|
+
key.pem:
|
50
|
+
$ openssl pkcs12 -nocerts -in key.p12 -out key.pem
|
51
|
+
$ Enter Import Password:
|
52
|
+
$ MAC verified OK
|
53
|
+
$ Enter PEM pass phrase:
|
54
|
+
$ Verifying - Enter PEM pass phrase:
|
55
|
+
|
56
|
+
Development Profile:
|
57
|
+
cat cert.pem key.pem > appid_development.pem
|
58
|
+
|
59
|
+
Distribution Profile:
|
60
|
+
cat cert.pem key.pem > appid_production.pem
|
47
61
|
|
48
|
-
3. provide a cron script under current directory, default named "cron" according to ass.yml
|
49
62
|
|
50
|
-
|
63
|
+
* start ass server, default port is 4567 (sinatra's default port)
|
51
64
|
|
52
65
|
![ass usage](https://raw.github.com/eiffelqiu/ass/master/doc/capture1.png)
|
53
66
|
|
67
|
+
Configuration (ass.yml)
|
68
|
+
=======
|
69
|
+
|
70
|
+
when you run 'ass' first time, it will generate 'ass.yml' config file under current directory. ([Check this link](https://raw.github.com/eiffelqiu/ass/master/ass.yml))
|
71
|
+
|
72
|
+
port: 4567 ## ASS server port, default is sinatra's port number: 4567
|
73
|
+
mode: development ## 'development' or 'production' mode, you should provide pem file ({appid}_{mode}.pem) accordingly(such as, app1_development.pem, app1_production.pem).
|
74
|
+
cron: cron ## cron job file name, ASS server will generate a demo 'cron' file for demostration only under current directory.
|
75
|
+
timer: 0 # how often you run the cron job, unit: minute. when set with 0, means no cron job execute.
|
76
|
+
apps:
|
77
|
+
- app1 ## appid you want to supprt APNS, ASS Server can give push notification support for many iOS apps, just list the appid here.
|
78
|
+
|
79
|
+
|
80
|
+
FAQ:
|
81
|
+
=======
|
82
|
+
|
83
|
+
1. How to register notification? (Client Side)
|
84
|
+
-------
|
85
|
+
|
86
|
+
In AppDelegate file, add methods below to register device token
|
87
|
+
|
88
|
+
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
|
89
|
+
{
|
90
|
+
NSString * tokenAsString = [[[deviceToken description]
|
91
|
+
stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@"<>"]]
|
92
|
+
stringByReplacingOccurrencesOfString:@" " withString:@""];
|
93
|
+
NSString *urlAsString = [NSString stringWithFormat:@"http://serverIP:4567/v1/apps/app1/%@", token];
|
94
|
+
NSURL *url = [NSURL URLWithString:urlAsString];
|
95
|
+
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url];
|
96
|
+
[urlRequest setTimeoutInterval:30.0f];
|
97
|
+
[urlRequest setHTTPMethod:@"GET"];
|
98
|
+
NSOperationQueue *queue = [[[NSOperationQueue alloc] init] autorelease];
|
99
|
+
[NSURLConnection sendAsynchronousRequest:urlRequest queue:queue completionHandler:nil];
|
100
|
+
}
|
101
|
+
|
102
|
+
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error
|
103
|
+
{
|
104
|
+
NSLog(@"Failed to get token, error: %@", error);
|
105
|
+
}
|
106
|
+
|
107
|
+
2. How to send push notification? (Server Side)
|
108
|
+
-------
|
109
|
+
|
110
|
+
run **curl** command to send push notification message, whatever shell.
|
111
|
+
|
112
|
+
$ curl http://localhost:4567/v1/apps/app1/push/{message}/{pid}
|
113
|
+
|
114
|
+
Note:
|
115
|
+
|
116
|
+
param1 (message): push notification message you want to send, remember the message should be html escaped
|
117
|
+
|
118
|
+
param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest
|
119
|
+
|
120
|
+
|
121
|
+
|
54
122
|
Contributing to ass
|
55
123
|
=======
|
56
124
|
|
@@ -65,5 +133,5 @@ Contributing to ass
|
|
65
133
|
Copyright
|
66
134
|
=======
|
67
135
|
|
68
|
-
Copyright (c) 2012 Eiffel Qiu. See LICENSE.txt for further details
|
136
|
+
#####Copyright (c) 2012 Eiffel Qiu. See LICENSE.txt for further details.#####
|
69
137
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.9
|
data/ass.yml
CHANGED
data/lib/ass.rb
CHANGED
@@ -15,20 +15,20 @@ require 'yaml'
|
|
15
15
|
############################################################
|
16
16
|
## Initilization Setup
|
17
17
|
############################################################
|
18
|
-
|
19
|
-
|
20
|
-
unless $LOAD_PATH.include?(
|
21
|
-
$LOAD_PATH <<
|
18
|
+
LIBDIR = File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
|
19
|
+
ROOTDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
20
|
+
unless $LOAD_PATH.include?(LIBDIR)
|
21
|
+
$LOAD_PATH << LIBDIR
|
22
22
|
end
|
23
23
|
|
24
24
|
unless File.exist?("#{Dir.pwd}/ass.yml") then
|
25
25
|
puts 'create config file: ass.yml'
|
26
|
-
system "cp #{
|
26
|
+
system "cp #{ROOTDIR}/ass.yml #{Dir.pwd}/ass.yml"
|
27
27
|
end
|
28
28
|
|
29
29
|
unless File.exist?("#{Dir.pwd}/cron") then
|
30
30
|
puts "create a demo 'cron' script"
|
31
|
-
system "cp #{
|
31
|
+
system "cp #{ROOTDIR}/cron #{Dir.pwd}/cron"
|
32
32
|
end
|
33
33
|
|
34
34
|
############################################################
|
@@ -36,21 +36,37 @@ end
|
|
36
36
|
############################################################
|
37
37
|
env = ENV['SINATRA_ENV'] || "development"
|
38
38
|
config = YAML.load_file("#{Dir.pwd}/ass.yml")
|
39
|
-
$timer = config['timer']
|
39
|
+
$timer = "#{config['timer']}".to_i
|
40
40
|
$cron = config['cron'] || 'cron'
|
41
41
|
$port = config['port'] || 4567
|
42
42
|
$mode = config['mode'] || env
|
43
|
-
$
|
44
|
-
ROOTDIR = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
45
|
-
VERSION = File.open("#{ROOTDIR}/VERSION", "rb").read
|
43
|
+
$VERSION = File.open("#{ROOTDIR}/VERSION", "rb").read
|
46
44
|
$apps = config['apps'] || []
|
47
45
|
|
48
46
|
############################################################
|
49
47
|
## Certificate Key Setup
|
50
48
|
############################################################
|
51
49
|
|
52
|
-
|
53
|
-
|
50
|
+
$certkey = {}
|
51
|
+
def check_cert
|
52
|
+
$apps.each { |app|
|
53
|
+
unless File.exist?("#{Dir.pwd}/#{app}_#{$mode}.pem") then
|
54
|
+
puts "Please provide #{app}_#{$mode}.pem under '#{Dir.pwd}/' directory"
|
55
|
+
return false;
|
56
|
+
else
|
57
|
+
puts "'#{app}'s #{$mode} PEM: (#{app}_#{$mode}.pem)"
|
58
|
+
certfile = File.read("#{Dir.pwd}/#{app}_#{$mode}.pem")
|
59
|
+
openSSLContext = OpenSSL::SSL::SSLContext.new
|
60
|
+
openSSLContext.cert = OpenSSL::X509::Certificate.new(certfile)
|
61
|
+
openSSLContext.key = OpenSSL::PKey::RSA.new(certfile)
|
62
|
+
$certkey["#{app}"] = openSSLContext
|
63
|
+
end
|
64
|
+
}
|
65
|
+
return true
|
66
|
+
end
|
67
|
+
|
68
|
+
unless check_cert then
|
69
|
+
puts "1: please provide certificate key pem file under current directory, name should be: appid_dev.pem for development and appid_prod.pem for production"
|
54
70
|
puts "2: edit your ass.yml under current directory"
|
55
71
|
puts "3: run ass"
|
56
72
|
puts "4: iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below:"
|
@@ -61,91 +77,103 @@ unless File.exist?("#{Dir.pwd}/#{$certificate}") then
|
|
61
77
|
exit
|
62
78
|
else
|
63
79
|
puts "*"*80
|
64
|
-
puts "
|
80
|
+
puts "Apple Service Server(#{$VERSION}) is Running ..."
|
81
|
+
puts "Push Notification Service: Enabled"
|
65
82
|
puts "Mode: #{$mode}"
|
66
83
|
puts "Port: #{$port}"
|
67
|
-
puts "
|
68
|
-
puts "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".squeeze.strip == "0"
|
84
|
+
puts "Cron Job: '#{Dir.pwd}/#{$cron}' script is running every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'} " unless "#{$timer}".to_i == 0
|
69
85
|
puts "*"*80
|
70
86
|
end
|
71
87
|
|
72
|
-
$cert = File.read("#{Dir.pwd}/#{$certificate}")
|
73
|
-
$openSSLContext = OpenSSL::SSL::SSLContext.new
|
74
|
-
$openSSLContext.cert = OpenSSL::X509::Certificate.new($cert)
|
75
|
-
$openSSLContext.key = OpenSSL::PKey::RSA.new($cert)
|
76
|
-
|
77
88
|
############################################################
|
78
89
|
## Sequel Database Setup
|
79
90
|
############################################################
|
80
91
|
|
81
|
-
|
82
|
-
|
83
|
-
unless File.exist?("#{Dir.pwd}/push.db") then
|
84
|
-
DB = Sequel.connect("sqlite://#{Dir.pwd}/push.db")
|
92
|
+
unless File.exist?("#{Dir.pwd}/ass.db") then
|
93
|
+
$DB = Sequel.connect("sqlite://#{Dir.pwd}/ass.db")
|
85
94
|
|
86
|
-
DB.create_table :tokens do
|
95
|
+
$DB.create_table :tokens do
|
87
96
|
primary_key :id
|
88
|
-
String :app, :unique =>
|
97
|
+
String :app, :unique => false, :null => false
|
89
98
|
String :token, :unique => true, :null => false, :size => 100
|
90
99
|
index [:app, :token]
|
91
100
|
end
|
92
101
|
|
93
|
-
DB.create_table :pushes do
|
102
|
+
$DB.create_table :pushes do
|
94
103
|
primary_key :id
|
95
104
|
String :pid, :unique => true, :null => false, :size => 100
|
96
105
|
index :pid
|
97
106
|
end
|
98
107
|
else
|
99
|
-
DB = Sequel.connect("sqlite://#{Dir.pwd}/
|
108
|
+
$DB = Sequel.connect("sqlite://#{Dir.pwd}/ass.db")
|
100
109
|
end
|
101
110
|
|
102
|
-
Token = DB[:tokens]
|
103
|
-
Push = DB[:pushes]
|
111
|
+
Token = $DB[:tokens]
|
112
|
+
Push = $DB[:pushes]
|
104
113
|
|
105
114
|
############################################################
|
106
115
|
## Timer Job Setup
|
107
116
|
############################################################
|
108
117
|
scheduler = Rufus::Scheduler.start_new
|
109
118
|
|
110
|
-
unless
|
119
|
+
unless $timer == 0 then
|
111
120
|
scheduler.every "#{$timer}m" do
|
112
121
|
puts "running job: '#{Dir.pwd}/#{$cron}' every #{$timer} #{($timer == 1) ? 'minute' : 'minutes'}"
|
113
122
|
system "./#{$cron}"
|
114
123
|
end
|
115
124
|
else
|
125
|
+
puts "1: How to register notification? (Client Side)"
|
116
126
|
puts
|
117
|
-
puts "
|
118
|
-
puts "How to register notification?"
|
119
|
-
puts
|
120
|
-
puts "iOS Client: in AppDelegate file, didRegisterForRemoteNotificationsWithDeviceToken method should access url below:"
|
127
|
+
puts "In AppDelegate file, inside didRegisterForRemoteNotificationsWithDeviceToken method access url below to register device token:"
|
121
128
|
$apps.each { |app|
|
122
129
|
puts "'#{app}'s registration url: http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken"
|
123
130
|
}
|
124
131
|
puts
|
125
|
-
puts "How to send push notification?"
|
132
|
+
puts "2: How to send push notification? (Server Side)"
|
126
133
|
puts
|
127
134
|
$apps.each { |app|
|
128
135
|
puts "curl http://localhost:#{$port}/v1/apps/#{app}/push/{message}/{pid}"
|
129
136
|
}
|
130
137
|
puts
|
131
138
|
puts "Note:"
|
132
|
-
puts "message: notification message you want to send, remember the message should be html escaped"
|
133
|
-
puts "pid: unique
|
139
|
+
puts "param1 (message): push notification message you want to send, remember the message should be html escaped"
|
140
|
+
puts "param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest"
|
134
141
|
puts
|
135
142
|
puts "*"*80
|
136
|
-
puts
|
137
143
|
end
|
138
144
|
|
139
145
|
############################################################
|
140
|
-
##
|
146
|
+
## Apple Service Server based on Sinatra
|
141
147
|
############################################################
|
142
148
|
|
143
149
|
class App < Sinatra::Base
|
144
150
|
|
145
151
|
set :port, "#{$port}".to_i
|
152
|
+
|
153
|
+
if "#{$mode}".strip == 'development' then
|
154
|
+
set :show_exceptions, true
|
155
|
+
set :dump_errors, true
|
156
|
+
else
|
157
|
+
set :show_exceptions, false
|
158
|
+
set :dump_errors, false
|
159
|
+
end
|
146
160
|
|
147
161
|
get '/' do
|
148
|
-
|
162
|
+
o = "Apple Service Server #{$VERSION} <br/><br/>" +
|
163
|
+
"author: Eiffel(Q) <br/>email: eiffelqiu@gmail.com<br/><br/>"
|
164
|
+
o += "1: How to register notification? (Client Side)<br/><br/>"
|
165
|
+
o += "In AppDelegate file, inside didRegisterForRemoteNotificationsWithDeviceToken method access url below to register device token:<br/><br/>"
|
166
|
+
$apps.each { |app|
|
167
|
+
o += "'#{app}': http://serverIP:#{$port}/v1/apps/#{app}/DeviceToken<br/>"
|
168
|
+
}
|
169
|
+
o += "<br/>2: How to send push notification? (Server Side)<br/><br/>"
|
170
|
+
$apps.each { |app|
|
171
|
+
o += "curl http://localhost:#{$port}/v1/apps/#{app}/push/{message}/{pid}<br/>"
|
172
|
+
}
|
173
|
+
o += "<br/>Note:<br/>"
|
174
|
+
o += "param1 (message): push notification message you want to send, remember the message should be html escaped<br/>"
|
175
|
+
o += "param2 (pid ): unique string to mark the message, for example current timestamp or md5/sha1 digest<br/>"
|
176
|
+
o
|
149
177
|
end
|
150
178
|
|
151
179
|
$apps.each { |app|
|
@@ -170,6 +198,7 @@ class App < Sinatra::Base
|
|
170
198
|
@exist = Push.first(:pid => pid)
|
171
199
|
|
172
200
|
unless @exist
|
201
|
+
openSSLContext = $certkey["#{app}"]
|
173
202
|
# Connect to port 2195 on the server.
|
174
203
|
sock = nil
|
175
204
|
if $mode == 'production' then
|
@@ -178,7 +207,7 @@ class App < Sinatra::Base
|
|
178
207
|
sock = TCPSocket.new('gateway.sandbox.push.apple.com', 2195)
|
179
208
|
end
|
180
209
|
# do our SSL handshaking
|
181
|
-
sslSocket = OpenSSL::SSL::SSLSocket.new(sock,
|
210
|
+
sslSocket = OpenSSL::SSL::SSLSocket.new(sock, openSSLContext)
|
182
211
|
sslSocket.connect
|
183
212
|
#Push.create( :pid => pid )
|
184
213
|
Push.insert(:pid => pid)
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ass
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.9
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-09-
|
12
|
+
date: 2012-09-18 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: sqlite3
|
@@ -27,6 +27,22 @@ dependencies:
|
|
27
27
|
- - ! '>='
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: thin
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :runtime
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
30
46
|
- !ruby/object:Gem::Dependency
|
31
47
|
name: sinatra
|
32
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -171,6 +187,38 @@ dependencies:
|
|
171
187
|
- - ~>
|
172
188
|
- !ruby/object:Gem::Version
|
173
189
|
version: 1.8.4
|
190
|
+
- !ruby/object:Gem::Dependency
|
191
|
+
name: sqlite3
|
192
|
+
requirement: !ruby/object:Gem::Requirement
|
193
|
+
none: false
|
194
|
+
requirements:
|
195
|
+
- - ! '>='
|
196
|
+
- !ruby/object:Gem::Version
|
197
|
+
version: '0'
|
198
|
+
type: :runtime
|
199
|
+
prerelease: false
|
200
|
+
version_requirements: !ruby/object:Gem::Requirement
|
201
|
+
none: false
|
202
|
+
requirements:
|
203
|
+
- - ! '>='
|
204
|
+
- !ruby/object:Gem::Version
|
205
|
+
version: '0'
|
206
|
+
- !ruby/object:Gem::Dependency
|
207
|
+
name: thin
|
208
|
+
requirement: !ruby/object:Gem::Requirement
|
209
|
+
none: false
|
210
|
+
requirements:
|
211
|
+
- - ! '>='
|
212
|
+
- !ruby/object:Gem::Version
|
213
|
+
version: '0'
|
214
|
+
type: :runtime
|
215
|
+
prerelease: false
|
216
|
+
version_requirements: !ruby/object:Gem::Requirement
|
217
|
+
none: false
|
218
|
+
requirements:
|
219
|
+
- - ! '>='
|
220
|
+
- !ruby/object:Gem::Version
|
221
|
+
version: '0'
|
174
222
|
- !ruby/object:Gem::Dependency
|
175
223
|
name: sinatra
|
176
224
|
requirement: !ruby/object:Gem::Requirement
|
@@ -251,7 +299,7 @@ dependencies:
|
|
251
299
|
- - ! '>='
|
252
300
|
- !ruby/object:Gem::Version
|
253
301
|
version: 3.2.8
|
254
|
-
description:
|
302
|
+
description: Apple Service Server written with Sinatra and Sequel(Sqlite3)
|
255
303
|
email: eiffelqiu@gmail.com
|
256
304
|
executables:
|
257
305
|
- ass
|
@@ -283,7 +331,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
283
331
|
version: '0'
|
284
332
|
segments:
|
285
333
|
- 0
|
286
|
-
hash: -
|
334
|
+
hash: -54990434379922484
|
287
335
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
288
336
|
none: false
|
289
337
|
requirements:
|
@@ -295,5 +343,5 @@ rubyforge_project: ass
|
|
295
343
|
rubygems_version: 1.8.21
|
296
344
|
signing_key:
|
297
345
|
specification_version: 3
|
298
|
-
summary:
|
346
|
+
summary: Apple Service Server
|
299
347
|
test_files: []
|