1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 2014 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Actions for the Web interface of Benitlux
18 module benitlux_controller
21 import nitcorn
::restful
22 private import json
::serialization
27 import benitlux_social
29 # Server action for REST or Web, for a given location
30 abstract class BenitluxAction
33 # Path to the database
34 var db_path
= "benitlux_sherbrooke.db"
36 # Path to the storage of the last email sent
37 var sample_email_path
= "benitlux_sherbrooke.email"
40 # Web interface to subscribe to the mailing list
41 class BenitluxSubscriptionAction
44 redef fun answer
(request
, turi
)
46 var template
= new BenitluxDocument
48 var sub
= request
.post_args
.keys
.has
("sub")
49 var unsub
= request
.all_args
.keys
.has
("unsub")
52 if request
.all_args
.keys
.has
("email") then email
= request
.all_args
["email"].trim
55 if email
.is_empty
or not email
.chars
.has
('@') or not email
.chars
.has
('.') then
56 template
.message_level
= "danger"
57 template
.message_content
= "Invalid email."
58 else if sub
and request
.post_args
.keys
.has
("email") then
59 template
.message_level
= "success"
60 template
.message_content
= "Subscription successful!"
62 var db
= new DB.open
(db_path
)
66 template
.message_level
= "warning"
67 template
.message_content
= "You've been unsubscribed."
69 var db
= new DB.open
(db_path
)
75 if sample_email_path
.file_exists
then
76 var f
= new FileReader.open
(sample_email_path
)
77 var lines
= new Array[String]
78 for line
in f
.read_all
.split_with
("\n") do if not line
.is_empty
then lines
.add line
80 template
.sample_email_lines
= lines
83 var response
= new HttpResponse(200)
84 response
.body
= template
.write_to_string
89 # RESTful interface for the client app
90 class BenitluxRESTAction
96 # signup?name=a&pass=b&email=c -> LoginResult | BenitluxError
97 fun signup
(name
, pass
, email
: String): HttpResponse
99 if not name
.name_is_ok
then
100 var error
= new BenitluxError("Invalid username")
101 return new HttpResponse.ok
(error
)
104 if not pass
.pass_is_ok
then
105 var error
= new BenitluxError("Invalid password")
106 return new HttpResponse.ok
(error
)
110 var db
= new DB.open
(db_path
)
111 var error_message
= db
.signup
(name
, pass
, email
)
113 var object
: nullable Serializable
114 if error_message
== null then
115 object
= db
.login
(name
, pass
)
117 object
= new BenitluxError(error_message
)
121 if object
== null then
122 # There was an error in the call to login
123 return new HttpResponse.server_error
126 # It went ok, may or may not be signed up
127 return new HttpResponse.ok
(object
)
132 # login?name=a&pass=b -> LoginResult | BenitluxError
133 fun login
(name
, pass
: String): HttpResponse
135 var db
= new DB.open
(db_path
)
136 var log
: nullable Serializable = db
.login
(name
, pass
)
139 if log
== null then log
= new BenitluxError("Login Failed", "Invalid username and password combination.")
141 return new HttpResponse.ok
(log
)
146 # search?token=b&query=a&offset=0 -> Array[UserAndFollowing] | BenitluxError
147 fun search
(token
: nullable String, query
: String): HttpResponse
149 var db
= new DB.open
(db_path
)
150 var user_id
= db
.token_to_id
(token
)
151 var users
= db
.search_users
(query
, user_id
)
154 if users
== null then return new HttpResponse.server_error
156 return new HttpResponse.ok
(users
)
159 # List available beers
161 # list?token=a[&offset=0&count=1] -> Array[BeerAndRatings] | BenitluxError
162 fun list
(token
: nullable String): HttpResponse
166 var db
= new DB.open
(db_path
)
167 var user_id
= db
.token_to_id
(token
)
168 var list
= db
.list_beers_and_rating
(user_id
)
171 if list
== null then return new HttpResponse.server_error
173 return new HttpResponse.ok
(list
)
176 # Post a review of `beer`
178 # review?token=a&beer=b&rating=0 -> true | BenitluxError
179 fun review
(token
: String, rating
, beer
: Int): HttpResponse
182 var db
= new DB.open
(db_path
)
183 var user_id
= db
.token_to_id
(token
)
185 if user_id
== null then
187 return new HttpResponse.invalid_token
190 db
.post_review
(user_id
, beer
, rating
, "")
193 return new HttpResponse.ok
(true)
196 # Set whether user of `token` follows `user_to`, by default set as follow
198 # follow?token=a&user_to=0 -> true | BenitluxError
199 fun follow
(token
: String, user_to
: Int, follow
: nullable Bool): HttpResponse
203 var db
= new DB.open
(db_path
)
204 var user
= db
.token_to_id
(token
)
208 return new HttpResponse.invalid_token
211 if follow
or else true then
212 db
.add_followed
(user
, user_to
)
213 else db
.remove_followed
(user
, user_to
)
217 return new HttpResponse.ok
(true)
220 # List followers of the user of `token`
222 # followers?token=a -> Array[UserAndFollowing] | BenitluxError | BenitluxError
223 fun followers
(token
: String): HttpResponse
226 var db
= new DB.open
(db_path
)
227 var user
= db
.token_to_id
(token
)
230 return new HttpResponse.invalid_token
232 var users
= db
.followers
(user
)
235 if users
== null then return new HttpResponse.server_error
237 return new HttpResponse.ok
(users
)
240 # List users followed by the user of `token`
242 # followed?token=a -> Array[UserAndFollowing] | BenitluxError
243 fun followed
(token
: String): HttpResponse
246 var db
= new DB.open
(db_path
)
247 var user
= db
.token_to_id
(token
)
250 return new HttpResponse.invalid_token
252 var users
= db
.followed
(user
)
255 if users
== null then return new HttpResponse.server_error
257 return new HttpResponse.ok
(users
)
260 # List friends of the user of `token`
262 # friends?token=a -> Array[UserAndFollowing] | BenitluxError
263 fun friends
(token
: String, n
: nullable Int): HttpResponse
266 var db
= new DB.open
(db_path
)
267 var user
= db
.token_to_id
(token
)
268 var users
= db
.friends
(user
, n
)
271 if users
== null then return new HttpResponse.server_error
273 return new HttpResponse.ok
(users
)
276 # Check user in or out
278 # checkin?token=a -> true | BenitluxError
279 fun checkin
(token
: String, is_in
: nullable Bool): HttpResponse
281 var db
= new DB.open
(db_path
)
282 var id
= db
.token_to_id
(token
)
285 return new HttpResponse.invalid_token
289 db
.checkin
(id
, is_in
or else true)
291 # Update followed_followers
292 var common_followers
= db
.followed_followers
(id
)
295 return new HttpResponse.ok
(true)
298 # List users currently checked in among friends of the user of `token`
300 # checkedin?token=a -> Array[UserAndFollowing]
301 fun checkedin
(token
: String): HttpResponse
303 var db
= new DB.open
(db_path
)
304 var user_id
= db
.token_to_id
(token
)
305 if user_id
== null then
307 return new HttpResponse.invalid_token
309 var report
= db
.checkedin_followed_followers
(user_id
)
312 if report
== null then return new HttpResponse.server_error
313 return new HttpResponse.ok
(report
)
316 # List beer changes since `date` with information in relation to the user of `token`
318 # since?token=a&date=date -> BeerEvents
319 fun since
(token
, date
: nullable String): HttpResponse
322 var db
= new DB.open
(db_path
)
323 var user_id
= db
.token_to_id
(token
)
324 var list
= db
.list_beers_and_rating
(user_id
, date
)
327 if list
== null then return new HttpResponse.server_error
329 return new HttpResponse.ok
(list
)
332 # Fallback answer on errors
333 redef fun answer
(request
, turi
) do return new HttpResponse.bad_request
340 # Rewrite the date represented by `self` in the format expected by SQLite
341 private fun std_date
: String
343 var parts
= self.split
("-")
344 if parts
.length
!= 3 then return "1970-01-01"
346 var y
= parts
[0].to_s
347 var m
= parts
[1].to_s
348 var d
= parts
[2].to_s
350 m
= "0"*(2 - m
.length
) + m
351 d
= "0"*(2 - d
.length
) + d
357 redef class HttpResponse
359 # Respond with `data` in Json and a code 200
360 init ok
(data
: Serializable)
363 body
= data
.to_json_string
366 # Respond with a `BenitluxError` in JSON and a code 403
370 var error
= new BenitluxTokenError("Forbidden", "Invalid or outdated token.")
371 body
= error
.to_json_string
374 # Respond with a `BenitluxError` in JSON and a code 400
378 var error
= new BenitluxError("Bad Request", "Application error, or it needs to be updated.")
379 body
= error
.to_json_string
382 # Respond with a `BenitluxError` in JSON and a code 500
386 var error
= new BenitluxError("Internal Server Error", "Server error, try again later.")
387 body
= error
.to_json_string