diff --git a/apps/api.lua b/apps/api.lua index 954f2f0..3ddcefb 100644 --- a/apps/api.lua +++ b/apps/api.lua @@ -1,8 +1,13 @@ local app = require("lapis").Application() +local json_params = require("lapis.application").json_params + local sse = require("lib.sse") local db = require("lapis.db") +local html_escape = require("lapis.html").escape +local babycode = require("lib.babycode") + local util = require("util") app:get("sse_thread_updates", "/thread-updates/:thread_id", function(self) @@ -33,4 +38,20 @@ app:get("sse_thread_updates", "/thread-updates/:thread_id", function(self) return {skip_render = true} end) +app:post("babycode_preview", "/babycode-preview", json_params(function(self) + local user = util.get_logged_in_user(self) + if not user then + return {json = {error = "not authorized"}, status = 401} + end + if not util.rate_limit_allowed(user.id, "babycode_preview", 5) then + return {json = {error = "too many requests"}, status = 429} + end + local markup = self.params.markup + if not markup or type(markup) ~= "string" then + return {json = {error = "markup field missing or invalid type"}, status = 400} + end + local rendered = babycode.to_html(markup, html_escape) + return {json = {html = rendered}} +end)) + return app diff --git a/migrations.lua b/migrations.lua index 908dad0..db7c9c4 100644 --- a/migrations.lua +++ b/migrations.lua @@ -91,4 +91,15 @@ return { db.query("COMMIT") end, + + [12] = function () + schema.create_table("api_rate_limits", { + {"id", types.integer{primary_key = true}}, + {"method", types.text{null = false}}, + {"user_id", "INTEGER REFERENCES users(id) ON DELETE CASCADE"}, + {"logged_at", "INTEGER DEFAULT (unixepoch(CURRENT_TIMESTAMP))"}, + }) + + db.query("CREATE INDEX idx_rate_limit_user_method ON api_rate_limits (user_id, method)") + end, } diff --git a/util.lua b/util.lua index c55b84f..c8ad50d 100644 --- a/util.lua +++ b/util.lua @@ -316,4 +316,25 @@ function util.inject_warn_infobox(req, message) req.session.infobox = ib end +function util.rate_limit_allowed(user_id, method, seconds) + local last_call = db.query([[ + SELECT logged_at FROM api_rate_limits + WHERE user_id = ? AND method = ? + ORDER BY logged_at DESC LIMIT 1 + ]], user_id, method) + if #last_call == 0 or (os.time() - last_call[1].logged_at) >= seconds then + db.query( + "DELETE FROM api_rate_limits WHERE user_id = ? AND method = ?", + user_id, method + ) + db.query( + "INSERT INTO api_rate_limits (user_id, method) VALUES (?, ?)", + user_id, method + ) + return true + else + return false + end +end + return util