ass 0.0.2 → 0.0.9
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/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
|

|
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: []
|