X-Git-Url: http://nitlanguage.org diff --git a/contrib/benitlux/src/benitlux_controller.nit b/contrib/benitlux/src/benitlux_controller.nit index e20eab7..118b01c 100644 --- a/contrib/benitlux/src/benitlux_controller.nit +++ b/contrib/benitlux/src/benitlux_controller.nit @@ -30,8 +30,8 @@ import benitlux_social abstract class BenitluxAction super Action - # Path to the database - var db_path = "benitlux_sherbrooke.db" + # Database used for both the mailing list and the social network + var db: BenitluxDB # Path to the storage of the last email sent var sample_email_path = "benitlux_sherbrooke.email" @@ -59,16 +59,12 @@ class BenitluxSubscriptionAction template.message_level = "success" template.message_content = "Subscription successful!" - var db = new DB.open(db_path) db.subscribe email - db.close else if unsub then template.message_level = "warning" template.message_content = "You've been unsubscribed." - var db = new DB.open(db_path) db.unsubscribe email - db.close end end @@ -96,6 +92,7 @@ class BenitluxRESTAction # signup?name=a&pass=b&email=c -> LoginResult | BenitluxError fun signup(name, pass, email: String): HttpResponse is restful do + # Validate input if not name.name_is_ok then var error = new BenitluxError("Invalid username") return new HttpResponse.ok(error) @@ -107,7 +104,6 @@ class BenitluxRESTAction end # Query DB - var db = new DB.open(db_path) var error_message = db.signup(name, pass, email) var object: nullable Serializable @@ -116,7 +112,6 @@ class BenitluxRESTAction else object = new BenitluxError(error_message) end - db.close if object == null then # There was an error in the call to login @@ -132,25 +127,29 @@ class BenitluxRESTAction # login?name=a&pass=b -> LoginResult | BenitluxError fun login(name, pass: String): HttpResponse is restful do - var db = new DB.open(db_path) var log: nullable Serializable = db.login(name, pass) - db.close - if log == null then log = new BenitluxError("Login Failed", "Invalid username and password combination.") return new HttpResponse.ok(log) end + # Is `token` valid? + # + # check_token?token=a -> true | BenitluxError + fun check_token(token: String): HttpResponse + is restful do + var user_id = db.token_to_id(token) + if user_id == null then return new HttpResponse.invalid_token + return new HttpResponse.ok(true) + end + # Search a user # # search?token=b&query=a&offset=0 -> Array[UserAndFollowing] | BenitluxError fun search(token: nullable String, query: String): HttpResponse is restful do - var db = new DB.open(db_path) var user_id = db.token_to_id(token) var users = db.search_users(query, user_id) - db.close - if users == null then return new HttpResponse.server_error return new HttpResponse.ok(users) @@ -161,13 +160,8 @@ class BenitluxRESTAction # list?token=a[&offset=0&count=1] -> Array[BeerAndRatings] | BenitluxError fun list(token: nullable String): HttpResponse is restful do - - # Query DB - var db = new DB.open(db_path) var user_id = db.token_to_id(token) var list = db.list_beers_and_rating(user_id) - db.close - if list == null then return new HttpResponse.server_error return new HttpResponse.ok(list) @@ -178,17 +172,10 @@ class BenitluxRESTAction # review?token=a&beer=b&rating=0 -> true | BenitluxError fun review(token: String, rating, beer: Int): HttpResponse is restful do - # Query DB - var db = new DB.open(db_path) var user_id = db.token_to_id(token) - - if user_id == null then - db.close - return new HttpResponse.invalid_token - end + if user_id == null then return new HttpResponse.invalid_token db.post_review(user_id, beer, rating, "") - db.close return new HttpResponse.ok(true) end @@ -198,22 +185,13 @@ class BenitluxRESTAction # follow?token=a&user_to=0 -> true | BenitluxError fun follow(token: String, user_to: Int, follow: nullable Bool): HttpResponse is restful do - - # Query DB - var db = new DB.open(db_path) var user = db.token_to_id(token) - - if user == null then - db.close - return new HttpResponse.invalid_token - end + if user == null then return new HttpResponse.invalid_token if follow or else true then db.add_followed(user, user_to) else db.remove_followed(user, user_to) - db.close - return new HttpResponse.ok(true) end @@ -222,16 +200,10 @@ class BenitluxRESTAction # followers?token=a -> Array[UserAndFollowing] | BenitluxError | BenitluxError fun followers(token: String): HttpResponse is restful do - # Query DB - var db = new DB.open(db_path) var user = db.token_to_id(token) - if user == null then - db.close - return new HttpResponse.invalid_token - end - var users = db.followers(user) - db.close + if user == null then return new HttpResponse.invalid_token + var users = db.followers(user) if users == null then return new HttpResponse.server_error return new HttpResponse.ok(users) @@ -242,16 +214,10 @@ class BenitluxRESTAction # followed?token=a -> Array[UserAndFollowing] | BenitluxError fun followed(token: String): HttpResponse is restful do - # Query DB - var db = new DB.open(db_path) var user = db.token_to_id(token) - if user == null then - db.close - return new HttpResponse.invalid_token - end - var users = db.followed(user) - db.close + if user == null then return new HttpResponse.invalid_token + var users = db.followed(user) if users == null then return new HttpResponse.server_error return new HttpResponse.ok(users) @@ -262,12 +228,8 @@ class BenitluxRESTAction # friends?token=a -> Array[UserAndFollowing] | BenitluxError fun friends(token: String, n: nullable Int): HttpResponse is restful do - # Query DB - var db = new DB.open(db_path) var user = db.token_to_id(token) var users = db.friends(user, n) - db.close - if users == null then return new HttpResponse.server_error return new HttpResponse.ok(users) @@ -278,19 +240,32 @@ class BenitluxRESTAction # checkin?token=a -> true | BenitluxError fun checkin(token: String, is_in: nullable Bool): HttpResponse is restful do - var db = new DB.open(db_path) var id = db.token_to_id(token) - if id == null then - db.close - return new HttpResponse.invalid_token - end + if id == null then return new HttpResponse.invalid_token # Register in DB db.checkin(id, is_in or else true) # Update followed_followers var common_followers = db.followed_followers(id) - db.close + + # Sent push notifications to connected reciprocal friends + if common_followers != null then + for friend in common_followers do + var conn = push_connections.get_or_null(friend.id) + if conn != null then + push_connections.keys.remove friend.id + if not conn.closed then + var report = db.checkedin_followed_followers(friend.id) + var response = if report == null then + new HttpResponse.server_error + else new HttpResponse.ok(report) + conn.respond response + conn.close + end + end + end + end return new HttpResponse.ok(true) end @@ -300,15 +275,10 @@ class BenitluxRESTAction # checkedin?token=a -> Array[UserAndFollowing] fun checkedin(token: String): HttpResponse is restful do - var db = new DB.open(db_path) var user_id = db.token_to_id(token) - if user_id == null then - db.close - return new HttpResponse.invalid_token - end - var report = db.checkedin_followed_followers(user_id) - db.close + if user_id == null then return new HttpResponse.invalid_token + var report = db.checkedin_followed_followers(user_id) if report == null then return new HttpResponse.server_error return new HttpResponse.ok(report) end @@ -319,11 +289,8 @@ class BenitluxRESTAction fun since(token, date: nullable String): HttpResponse is restful do # Query DB - var db = new DB.open(db_path) var user_id = db.token_to_id(token) var list = db.list_beers_and_rating(user_id, date) - db.close - if list == null then return new HttpResponse.server_error return new HttpResponse.ok(list) @@ -334,6 +301,88 @@ class BenitluxRESTAction end # --- +# Push notification + +# Benitlux push notification interface +class BenitluxPushAction + super BenitluxAction + + # Intercept the full answer to set aside the connection and complete it later + redef fun prepare_respond_and_close(request, turi, connection) + do + var token = request.string_arg("token") + + var user = db.token_to_id(token) + if user == null then + # Report errors right away + var response = new HttpResponse.invalid_token + connection.respond response + connection.close + return + end + + # Set aside the connection + push_connections[user] = connection + end +end + +redef class Sys + # Connections left open for a push notification, organized per user id + private var push_connections = new Map[Int, HttpServer] +end + +# --- +# Administration + +# Path to the secret used to authenticate admin requests +fun secret_path: String do return "benitlux.secret" + +# Services reserved to administrators +class BenitluxAdminAction + super BenitluxAction + super RestfulAction + + private fun server_secret: String do return secret_path.to_path.read_all + + # Trigger sending daily menu to connected clients + # + # This should usually be called by an external cron program. + # send_daily_updates?secret=shared_secret -> true | BenitluxError + fun send_daily_updates(secret: nullable String): HttpResponse + is restful do + # Check secrets + var server_secret = server_secret + if server_secret.is_empty then + print_error "The admin interface needs a secret at '{secret_path}'" + return new HttpResponse.server_error + end + + if server_secret != secret then + return new HttpResponse.invalid_token + end + + # Load beer menu + var list = db.list_beers_and_rating + if list == null then return new HttpResponse.server_error + + var msg = new DailyNotification(list) + + # Broadcast updates + for conn in push_connections.values.to_a do + if not conn.closed then + conn.respond new HttpResponse.ok(msg) + conn.close + end + end + push_connections.clear + + return new HttpResponse.ok(true) + end + + redef fun answer(request, turi) do return new HttpResponse.bad_request +end + +# --- # Misc services redef class Text