all repos — litestore @ 6f28bb8e1fd640ad6b1cfb0572cbae996534c128

A minimalist nosql document store.

Added more HTTP headers (includes basic CORS support).
* Closes #22.
h3rald h3rald@h3rald.com
Sat, 16 May 2015 10:40:56 +0200
commit

6f28bb8e1fd640ad6b1cfb0572cbae996534c128

parent

0fad50ca4490503b87b5ecd0cc9a4929b39d5fb7

5 files changed, 105 insertions(+), 68 deletions(-)

jump to
M admin/js/models.jsadmin/js/models.js

@@ -3,6 +3,9 @@ window.Page = {};

window.Info = {}; window.Doc = {}; var u = window.LS.utils; + var ls_host = 'http://localhost:9500' + + var host = location.origin === ls_host ? "" : ls_host Page.get = function(id) { var content = m.prop("");

@@ -17,7 +20,7 @@ Info.get = function(){

var content = m.prop(""); return m.request({ method: "GET", - url: "/info" + url: host+"/info" }).then(content); };

@@ -27,7 +30,7 @@ limit = limit || 10;

var docs = m.prop(""); return m.request({ method: "GET", - url: "/docs?contents=false&tags="+tag+"&limit="+limit+"&offset="+offset + url: host+"/docs?contents=false&tags="+tag+"&limit="+limit+"&offset="+offset }).then(docs); };

@@ -37,7 +40,7 @@ limit = limit || 10;

var docs = m.prop(""); return m.request({ method: "GET", - url: "/docs?contents=false&search="+search+"&limit="+limit+"&offset="+offset, + url: host+"/docs?contents=false&search="+search+"&limit="+limit+"&offset="+offset, }).then(docs); };

@@ -45,14 +48,14 @@ Doc.get = function(id) {

var doc = m.prop(""); return m.request({ method: "GET", - url: "/docs/"+id+"?raw=true" + url: host+"/docs/"+id+"?raw=true" }).then(doc); }; Doc.delete = function(id){ return m.request({ method: "DELETE", - url: "/docs/"+id + url: host+"/docs/"+id }); };

@@ -61,7 +64,7 @@ xhrcfg = u.setContentType(doc, contentType);

console.log("Doc.put - Saving Document:", doc); return m.request({ method: "PUT", - url: "/docs/"+doc.id, + url: host+"/docs/"+doc.id, data: doc.data, serialize: function(data){return data;}, config: xhrcfg

@@ -72,7 +75,7 @@ Doc.upload = function(doc) {

console.log("Doc.put - Uploading Document:", doc); return m.request({ method: "PUT", - url: "/docs/"+doc.id, + url: host+"/docs/"+doc.id, data: doc.data, serialize: function(data) {return data} });

@@ -104,7 +107,7 @@ }

console.log("Doc.patch - Saving Tags:", ops); return m.request({ method: "PATCH", - url: "/docs/"+id, + url: host+"/docs/"+id, data: ops }); });
M lib/api_v1.nimlib/api_v1.nim

@@ -1,6 +1,7 @@

import asynchttpserver2, asyncdispatch, strutils, cgi, strtabs, pegs, json, os, times import types, core, utils, logger + # Helper procs proc orderByClause(clause: string): string =

@@ -137,7 +138,8 @@ let res = LS.store.destroyDocument(id)

if res == 0: result = resError(Http500, "Unable to delete document '$1'" % id) else: - result.headers = {"Content-Length": "0"}.newStringTable + result.headers = TAB_HEADERS.newStringTable + result.headers["Content-Length"] = "0" result.content = "" result.code = Http204 except:

@@ -279,22 +281,33 @@ return resError(Http404, "Info '$1' not found." % id)

else: result.code = Http200 result.content = "" - result.headers = {"Allow": "GET,OPTIONS"}.newStringTable + result.headers = TAB_HEADERS.newStringTable + result.headers["Allow"] = "GET,OPTIONS" + result.headers["Access-Control-Allow-Methods"] = "GET,OPTIONS" of "docs": if id != "": result.code = Http200 result.content = "" if LS.readonly: - result.headers = {"Allow": "HEAD,GET,OPTIONS"}.newStringTable + result.headers = TAB_HEADERS.newStringTable + result.headers["Allow"] = "HEAD,GET,OPTIONS" + result.headers["Access-Control-Allow-Methods"] = "HEAD,GET,OPTIONS" else: - result.headers = {"Allow": "HEAD,GET,PUT,PATCH,DELETE,OPTIONS"}.newStringTable + result.headers = TAB_HEADERS.newStringTable + result.headers["Allow"] = "HEAD,GET,OPTIONS,PUT,PATCH,DELETE" + result.headers["Allow-Patch"] = "application/json-patch+json" + result.headers["Access-Control-Allow-Methods"] = "HEAD,GET,OPTIONS,PUT,PATCH,DELETE" else: result.code = Http200 result.content = "" if LS.readonly: - result.headers = {"Allow": "HEAD,GET,OPTIONS"}.newStringTable + result.headers = TAB_HEADERS.newStringTable + result.headers["Allow"] = "HEAD,GET,OPTIONS" + result.headers["Access-Control-Allow-Methods"] = "HEAD,GET,OPTIONS" else: - result.headers = {"Allow": "HEAD,GET,POST,OPTIONS"}.newStringTable + result.headers = TAB_HEADERS.newStringTable + result.headers["Allow"] = "HEAD,GET,OPTIONS,POST" + result.headers["Access-Control-Allow-Methods"] = "HEAD,GET,OPTIONS,POST" else: discard # never happens really.
M lib/cli.nimlib/cli.nim

@@ -1,19 +1,15 @@

import parseopt2, - parsecfg, - streams, - strutils + strutils, + strtabs import logger, types, utils -const cfgfile = "litestore.nimble".slurp const favicon = "admin/favicon.ico".slurp var - file*, address*, version*, appname*: string - port*: int operation = opRun directory:string = nil readonly = false

@@ -21,37 +17,6 @@ logLevel = "info"

mount = false reset = false -var f = newStringStream(cfgfile) -if f != nil: - var p: CfgParser - open(p, f, "litestore.nimble") - while true: - var e = next(p) - case e.kind - of cfgEof: - break - of cfgKeyValuePair: - case e.key: - of "version": - version = e.value - of "appame": - appname = e.value - of "port": - port = e.value.parseInt - of "address": - address = e.value - of "file": - file = e.value - else: - discard - of cfgError: - fail(1, "Configuration error.") - else: - discard - close(p) -else: - fail(2, "Cannot process configuration file.") - let usage* = appname & " v" & version & " - Lightweight REST Document Store" & """ (c) 2015 Fabio Cevasco

@@ -150,17 +115,9 @@

if directory == nil and (operation in [opDelete, opImport, opExport] or mount): fail(105, "Directory option not specified.") -# Initialize LiteStore -var LS* {.threadvar.}: LiteStore - -LS.port = port -LS.address = address LS.operation = operation -LS.file = file LS.directory = directory -LS.appversion = version LS.readonly = readonly -LS.appname = appname LS.favicon = favicon LS.loglevel = loglevel LS.mount = mount
M lib/types.nimlib/types.nim

@@ -1,4 +1,11 @@

-import db_sqlite, pegs, asynchttpserver2, strtabs +import + db_sqlite, + pegs, + asynchttpserver2, + strtabs, + parsecfg, + strutils, + streams type EDatastoreExists* = object of Exception

@@ -77,13 +84,61 @@ PEG_USER_TAG = peg"""^[a-zA-Z0-9_\-?~:.@#^!+]+$"""

PEG_DEFAULT_URL = peg"""^\/{(docs / info)} (\/ {(.+)} / \/?)$""" PEG_URL = peg"""^\/({(v\d+)} \/) {([^\/]+)} (\/ {(.+)} / \/?)$""" -const CT_JSON* = {"Content-Type": "application/json"} +const cfgfile = "litestore.nimble".slurp -proc ctHeader*(ct: string): StringTableRef = - return {"Content-Type": ct}.newStringTable +var + file*, address*, version*, appname*: string + port*: int + f = newStringStream(cfgfile) -proc ctJsonHeader*(): StringTableRef = - return CT_JSON.newStringTable +if f != nil: + var p: CfgParser + open(p, f, "litestore.nimble") + while true: + var e = next(p) + case e.kind + of cfgEof: + break + of cfgKeyValuePair: + case e.key: + of "version": + version = e.value + of "appame": + appname = e.value + of "port": + port = e.value.parseInt + of "address": + address = e.value + of "file": + file = e.value + else: + discard + of cfgError: + stderr.writeln("Configuration error.") + quit(1) + else: + discard + close(p) +else: + stderr.writeln("Cannot process configuration file.") + quit(2) + +# Initialize LiteStore +var LS* {.threadvar.}: LiteStore +var TAB_HEADERS* {.threadvar.}: array[0..2, (string, string)] + + +LS.port = port +LS.address = address +LS.file = file +LS.appversion = version +LS.appname = appname + +TAB_HEADERS = { + "Access-Control-Allow-Origin": "*", + "Access-Control-Allow-Headers": "Content-Type", + "Server": LS.appname & "/" & LS.appversion +} proc newQueryOptions*(): QueryOptions = return QueryOptions(select: @["id", "data", "content_type", "binary", "searchable", "created", "modified"], single: false, limit: 0, offset: 0, orderby: "", tags: "", search: "")
M lib/utils.nimlib/utils.nim

@@ -6,7 +6,8 @@ pegs,

asyncdispatch, asynchttpserver2, math, - sqlite3 + sqlite3, + strtabs import types,

@@ -135,12 +136,20 @@ tags.add "$format:text"

for tag in tags: store.db.exec(SQL_INSERT_TAG, tag, docid) -proc destroyDocumentSystemTags*(store: Datastore, docid) = +proc destroyDocumentSystemTags*(store: Datastore, docid: string) = store.db.exec(SQL_DELETE_DOCUMENT_SYSTEM_TAGS, docid) -proc fail*(code, msg) = +proc fail*(code: int, msg: string) = LOG.error(msg) quit(code) + +proc ctHeader*(ct: string): StringTableRef = + var h = TAB_HEADERS.newStringTable + h["Content-Tyoe"] = ct + return h + +proc ctJsonHeader*(): StringTableRef = + return ctHeader("application/json") proc resError*(code: HttpCode, message: string, trace = ""): Response = LOG.warn(message.replace("$", "$$"))