@@ 4,8 4,14 @@ local json = require('json')
local router = require('router').new()
local ft = require('filetree')
local docstore = require('docstore')
+local form = require('form')
+local http = require('http')
+
+local client = http.new()
+client:headers():set('Accept', 'application/json')
local col = docstore.col('notes')
+local published_notes = docstore.col('published_notes')
-- File upload endpoint used when creating a new note
router:post('/attachment', function(params)
@@ 82,20 88,134 @@ router:get('/', function(params)
app.response:write(template.render('index.html', 'layout.html', { app_id = blobstash.app_id, stats = stats, qs = qs, collections = {}, docs = jdocs }))
end)
+function get_micropub_endpoints(endpoint)
+ if blobstash.app_cache["micropub:cached:" .. endpoint] then
+ return {
+ authorization_endpoint = blobstash.app_cache['micropub:authorization_endpoint:' .. endpoint],
+ token_endpoint = blobstash.app_cache['micropub:token_endpoint:' .. endpoint],
+ micropub = blobstash.app_cache['micropub:micropub:' .. endpoint],
+ cached = true
+ }
+ end
+ local rels = extra.parse_microformats(endpoint).rels
+ local endpoints = { cached = false, endpoint = endpoint }
+ if #rels.authorization_endpoint > 0 then
+ endpoints.authorization_endpoint = rels.authorization_endpoint[1]
+ blobstash.app_cache['micropub:authorization_endpoint:' .. endpoint] = endpoints.authorization_endpoint
+ end
+ if #rels.token_endpoint > 0 then
+ endpoints.token_endpoint = rels.token_endpoint[1]
+ blobstash.app_cache['micropub:token_endpoint:' .. endpoint] = endpoints.token_endpoint
+ end
+ if #rels.micropub > 0 then
+ endpoints.micropub = rels.micropub[1]
+ blobstash.app_cache['micropub:micropub:' .. endpoint] = endpoints.micropub
+ end
+ blobstash.app_cache["micropub:cached:" .. endpoint] = true
+ return endpoints
+end
+
+local micropub_config = {}
+if blobstash.app_config.micropub ~= nil and #blobstash.app_config.micropub.endpoints > 0 then
+ micropub_config = get_micropub_endpoints(blobstash.app_config.micropub.endpoints[1])
+end
+
router:get('/view', function(params)
local cdoc = {}
local args = app.request:args()
local cid = args:get('_id')
+
+ if cid == nil then
+ app.response:set_status(404)
+ app.response:write('no found')
+ return
+ end
+
+ -- XXX v is a string cause a timestamp nano is too big for Lua numbers
+ local v = args:get('v')
+ if v == "" then
+ v = "-1"
+ end
if cid ~= "" then
- cdoc, pointers = col:get(cid)
+ cdoc, pointers = col:get(cid, v)
_expand_note(cdoc, pointers, 800, false)
cdoc._id = cid
end
- app.response:write(template.render('view.html', 'layout.html', { app_id = blobstash.app_id, cid = cid, cdoc = cdoc, js = json.encode(cdoc), col = notes_col }))
+ versions, vpointers = col:versions(cid)
+ for _, v in ipairs(versions) do
+ v.updated = extra.format_datetime(v._updated, '2006-01-02T15:04:05Z07:00', 'Jan 02, 2006 @ 15:04 MST')
+ end
+ app.response:write(template.render('view.html', 'layout.html', { app_id = blobstash.app_id, cid = cid, cdoc = cdoc, js = json.encode(cdoc), col = notes_col,
+ versions = versions, micropub_config = micropub_config }))
+end)
+
+router:get('/conf', function(params)
+ -- app.response:write(json.encode(url_for("/lol")))
+ -- app.response:write(extra.parse_microformats(
+ -- app.response:write(json.encode(extra.parse_microformats(blobstash.app_config.micropub.endpoints[1])))
+ -- app.response:write(blobstash.app_base_url .. "\n" .. url_for("/lol"))
+ -- app.response:write(json.encode(micropub_config))
+ app.response:write(json.encode(blobstash.app_config))
end)
+router:get('/ia', function(params)
+ -- Generate a CSRF token
+ local state = extra.random(12)
+ blobstash.app_cache[state] = true
+
+ -- Build the authorization request to the IndieAuth endpoint
+ local f = form.new()
+ f:set('me', blobstash.app_config.micropub.me)
+ f:set('response_type', 'code')
+ f:set('state', state)
+ f:set('redirect_uri', url_for('/ia-redirect'))
+ f:set('scope', 'create update')
+ f:set('client_id', 'https://a4.io') -- FIXME(tsileo): client ID for dev?
+ app.response:redirect(micropub_config.authorization_endpoint .. "?" .. f:encode())
+end)
+
+router:get('/ia-redirect', function(params)
+ local f = form.new()
+ local args = app.request:args()
+
+ -- Check the forwarded state to prevent CSRF attacks (i.e. someone faking an initiated request)
+ if blobstash.app_cache[args:get('state')] ~= true then
+ app.response:set_status(403)
+ app.response:write("CSRF failed")
+ end
+
+ -- Exchange the code for a token (by calling the token endpoint)
+ f:set('grant_type', 'authorization_code')
+ f:set('code', args:get('code'))
+ f:set('client_id', 'https://a4.io')
+ f:set('redirect_uri', url_for('/ia-redirect'))
+ f:set('me', blobstash.app_config.micropub.me)
+ local resp, err = client:post(micropub_config.token_endpoint, {}, f)
+ if err ~= nil then
+ app.response:set_status(500)
+ app.response:write("failed to retrieve token")
+ return
+ end
+
+ -- Extract the access token
+ local access_token = resp.body:json()["access_token"]
+
+ -- Do a micropub request
+ local c = http.new()
+ c:headers():set("Authorization", "Bearer " .. access_token)
+ c:headers():set("Accept", "application/json")
+ c:headers():set("Content-Type", "application/json")
+ local payload = { ["type"] = {"h-entry"}, properties = { content = {"Hello"}, name = {"Test from BlobStash"} } }
+ local resp, err = c:post(micropub_config.micropub_endpoint, {}, json.encode(payload))
+ if err ~= nil then
+ app.response:write("failed to post")
+ return
+ end
+
+ app.response:write(resp.status_code .. resp.body:text() .. resp.headers:get("Location"))
+end)
router:get('/new', function(params)
local cdoc = {}