-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.rb
301 lines (240 loc) · 7.13 KB
/
app.rb
1
2
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
require 'bundler'
Bundler.require
require 'rss'
require 'logger'
require 'time'
require 'yaml'
require 'net/http'
require 'securerandom'
require './config/environments'
require './models/chapter'
require './models/entry'
secrets = if File.file?('config/secrets.yml')
YAML.load_file('config/secrets.yml')
else
{}
end
enable :sessions
set :server => :puma
set :public_folder => 'public'
set :sessions, :expire_after => 3500
set :session_secret, secrets['session'] || SecureRandom.hex(64)
ENVIRONMENT = secrets['app_env'] || 'development'
databases = YAML.load(ERB.new(File.read('config/database.yml')).result)
ActiveRecord::Base.establish_connection(databases[ENVIRONMENT])
require 'pry' if ENVIRONMENT == 'development'
Logger.class_eval { alias :write :'<<' }
app_log = File.join(File.dirname(File.expand_path(__FILE__)), 'var', 'log', 'app.log')
app_logger = Logger.new(app_log)
error_logger = File.new(File.join(File.dirname(File.expand_path(__FILE__)), 'var', 'log', 'error.log'), "a+")
error_logger.sync = true
configure { use Rack::CommonLogger, app_logger }
before { env["rack.errors"] = error_logger }
error 501..510 do
if ENVIRONMENT == 'production'
subject = "Production Error Occurred"
message = "#{Time.now}\nsinatra.error: #{env["sinatra.error"]}"
send_error_email(subject, message)
end
end
get '/' do
if released_content.empty?
send_file File.join(settings.public_folder, 'coming_soon.html')
else
send_file File.join(settings.public_folder, 'reader.html')
end
end
get '/read.html' do
redirect '/'
end
get '/api/chapters' do # public chapters
content_type :json
success_response
json all_chapters_with_entries('released')
end
get '/api/next' do # next release's release date
content_type :json
success_response
json next_release_date
end
get '/rss' do # rss (public chapters)
@releases = rss_feed
success_response
builder :rss
end
post '/api/chapters' do # all chapters
content_type :json
payload = JSON.parse(request.body.read)
if authorized?
success_response
json all_chapters_with_entries('all')
else
failure_response
end
end
post '/api/chapters/crupdate' do
content_type :json
payload = JSON.parse(request.body.read)
data = payload["data"]
log(payload)
if authorized?
if data["id"].nil? # Create chapter
chapter = Chapter.new(data)
chapter.save
success_response
else # Update chapter
entries_to_be_deleted = diff_entry_ids(data["id"], data["entries_attributes"])
Entry.destroy(entries_to_be_deleted)
chapter = Chapter.update(data["id"], data)
chapter.save
success_response
end
else
failure_response
end
end
post '/api/chapters/delete' do
content_type :json
payload = JSON.parse(request.body.read)
log(payload)
if authorized?
Chapter.destroy(payload["data"])
success_response
else
failure_response
end
end
post '/api/auth' do
return success_response if authorized?
valid_emails = ['[email protected]', '[email protected]']
valid_aud = '361874213844-33mf5b41pp4p0q38q26u8go81cod0h7f.apps.googleusercontent.com'
valid_exp = Time.now.to_i
token = request.body.read.gsub(/"/, '')
uri = URI("https://www.googleapis.com/oauth2/v3/tokeninfo?id_token=#{token}")
payload = JSON.parse(Net::HTTP.get(uri))
payload_email = payload['email']
payload_aud = payload['aud']
payload_exp = payload['exp'].to_i
if valid_emails.include?(payload_email) && payload_aud == valid_aud && payload_exp > valid_exp
session[:authorized] = true
success_response
else
failure_response
end
end
def success_response
status 200
body '1'
end
def failure_response
status 418
body '0'
end
def log(payload)
File.open("var/log/app.log", "a+") do |f|
f.puts(payload)
end
end
def authorized?
session[:authorized] || ENVIRONMENT == 'development'
end
def all_chapters_with_entries(type = 'all')
chapters_with_entries = []
if type == 'released'
chapters = Chapter.select { |chapter| chapter.releaseDate && chapter.releaseDate <= DateTime.now }
else
chapters = Chapter.all
end
chapters.each do |chapter|
chapters_with_entries.push(chapter.with_entries(type))
end
chapters_with_entries
end
def next_release_date
all_content = []
Chapter.all.each do |chapter|
entries = chapter.entries
chapter = chapter.as_json.deep_symbolize_keys
chapter[:level] = 0
all_content.push(chapter)
entries.each {|entry| all_content.push(entry.as_json.deep_symbolize_keys)}
end
next_release = all_content.find { |data| data[:releaseDate] && data[:releaseDate] > DateTime.now }
next_release.class == Hash ? next_release[:releaseDate] : ''
end
def released_content
released_content = []
Chapter.select { |chapter| chapter.releaseDate && chapter.releaseDate <= DateTime.now }.each do |chapter|
entries = chapter.entries.select { |entry| entry.releaseDate && entry.releaseDate <= DateTime.now }
chapter = chapter.as_json.deep_symbolize_keys
chapter[:level] = 0
released_content.push(chapter)
entries.each {|entry| released_content.push(entry.as_json.deep_symbolize_keys)}
end
released_content
end
def rss_feed
return [] if released_content.empty?
feed = []
release_stack = []
released_content.each do |sub_release|
# if level value is greater || stack is empty
## if current stack is valid release
### push stack in feed
## push on the stack
# elsif level value is not greater
## push stack into feed
## then pop items from stack until it is greater or stack empty
## push current element onto stack
# push remaining stack to feed
if release_stack.empty? || sub_release[:level] > release_stack.last[:level]
feed.push(release_stack.clone) if !release_stack.empty? && valid_release?(release_stack.last)
release_stack.push(sub_release)
elsif sub_release[:level] <= release_stack.last[:level]
feed.push(release_stack.clone)
release_stack.reverse.each do |element|
if sub_release[:level] <= element[:level]
release_stack.pop
end
end
release_stack.push(sub_release)
end
end
feed.push(release_stack.clone)
feed.each do |release|
use_chapter_link = release.last[:order] - release.size == -2
release.each do |element|
next if element == release.last
use_chapter_link = use_chapter_link && !valid_release?(element)
end
if use_chapter_link
release.last[:use_chapter_link] = true
end
end
feed
end
def valid_release?(sub_release)
!sub_release[:content].empty? || sub_release[:isInteractive]
end
def diff_entry_ids(chapter_id, entries)
all_entry_ids = []
all_entries = Chapter.find(chapter_id).entries
all_entries.each do |entry|
all_entry_ids.push(entry[:id])
end
given_entry_ids = []
entries.each do |entry|
given_entry_ids.push(entry["id"])
end
all_entry_ids - given_entry_ids
end
def send_error_email(subject, message)
Pony.mail({
:to => '[email protected]',
:from => '[email protected]',
:subject => subject,
:body => message,
:via => :sendmail,
:via_options => { :location => '/usr/sbin/sendmail' }
})
end