From e430d9b512d7dde140f2e70725ab6fb3a06d7187 Mon Sep 17 00:00:00 2001 From: Robin Malley Date: Wed, 17 May 2023 21:18:24 +0000 Subject: [PATCH] Work on ripping out spp --- Makefile | 79 ++++++++--------- spec/author_bio_spec.lua | 2 +- spec/cacheing_spec.lua | 14 ++- spec/env_mock.lua | 18 ++-- spec/login_spec.lua | 9 +- src/libcrypto.c | 14 ++- src/lua/addon.lua | 18 ++-- src/lua/cache.lua | 11 ++- src/lua/config.lua.in | 13 --- src/lua/db.lua | 143 +++++++++++++++++++++--------- src/lua/endpoints/claim_post.lua | 1 - src/lua/endpoints/index_get.lua | 2 +- src/lua/endpoints/login_post.lua | 67 +++++++------- src/lua/endpoints/paste_post.lua | 2 +- src/lua/endpoints/read_get.lua | 6 +- src/lua/pages.lua | 18 ++++ src/lua/session.lua | 11 +-- src/lua/tags.lua | 11 +++ src/lua/util.lua | 35 +++++++- src/pages/author_edit.etlua.in | 27 ------ src/pages/author_index.etlua.in | 39 -------- src/pages/author_paste.etlua.in | 34 ------- src/pages/cantedit.etlua.in | 11 --- src/pages/claim.etlua.in | 20 ----- src/pages/edit.etlua.in | 48 ---------- src/pages/edit_bio.etlua.in | 18 ---- src/pages/error.etlua.in | 14 --- src/pages/index.etlua.in | 36 -------- src/pages/login.etlua.in | 15 ---- src/pages/noauthor.etlua.in | 11 --- src/pages/nostory.etlua.in | 10 --- src/pages/paste.etlua.in | 27 ------ src/pages/read.etlua.in | 84 ------------------ src/pages/search.etlua.in | 19 ---- src/pages/search_sql.etlua.in | 56 ------------ src/smr.c | 21 +++-- src/sql/create_index_tags.sql | 3 + src/sql/create_table_authors.sql | 8 ++ src/sql/create_table_comments.sql | 6 ++ src/sql/create_table_images.sql | 3 + src/sql/create_table_session.sql | 6 +- src/sql/create_table_tags.sql | 4 + 42 files changed, 347 insertions(+), 647 deletions(-) delete mode 100644 src/lua/config.lua.in delete mode 100644 src/pages/author_edit.etlua.in delete mode 100644 src/pages/author_index.etlua.in delete mode 100644 src/pages/author_paste.etlua.in delete mode 100644 src/pages/cantedit.etlua.in delete mode 100644 src/pages/claim.etlua.in delete mode 100644 src/pages/edit.etlua.in delete mode 100644 src/pages/edit_bio.etlua.in delete mode 100644 src/pages/error.etlua.in delete mode 100644 src/pages/index.etlua.in delete mode 100644 src/pages/login.etlua.in delete mode 100644 src/pages/noauthor.etlua.in delete mode 100644 src/pages/nostory.etlua.in delete mode 100644 src/pages/paste.etlua.in delete mode 100644 src/pages/read.etlua.in delete mode 100644 src/pages/search.etlua.in delete mode 100644 src/pages/search_sql.etlua.in diff --git a/Makefile b/Makefile index e584758..4d2a811 100644 --- a/Makefile +++ b/Makefile @@ -16,12 +16,14 @@ kmgr_chroot = $(smr_var)/kore_kmgr parent_chroot = $(smr_var)/kore_parent conf_path = /etc/smr smr_bin_path = /usr/local/lib +app_root=$(worker_chroot)/var/smr ifeq ($(DEV),true) worker_chroot = ./kore_chroot kmgr_chroot = ./kore_chroot parent_chroot = ./kore_chroot conf_path = ./kore_chroot/conf smr_bin_path = ./kore_chroot + app_root=./kore_chroot/app endif mirror=http://dl-cdn.alpinelinux.org/alpine/ @@ -35,11 +37,11 @@ domain=test.monster:$(port) server_cert=/root/cert/server.pem server_key=/root/cert/key.pem -SPPFLAGS=-D port=$(port) -D kore_chroot=$(build_dir) -D chuser=$(user) -D domain=$(domain) -D bin_path="$(bin_path)" -D server_cert="$(server_cert)" -D server_key="$(server_key)" -D worker_chroot="$(worker_chroot)" -D kmgr_chroot="$(kmgr_chroot)" +SPPFLAGS=-D port=$(port) -D kore_chroot=$(worker_chroot) -D chuser=$(user) -D domain=$(domain) -D bin_path="$(bin_path)" -D server_cert="$(server_cert)" -D server_key="$(server_key)" -D worker_chroot="$(worker_chroot)" -D kmgr_chroot="$(kmgr_chroot)" # squelch prints, flip to print verbose information #Q=@ Q= -LUAROCKS_FLAGS=--tree $(build_dir)/usr/lib/luarocks --lua-version 5.1 +LUAROCKS_FLAGS=--tree $(worker_chroot)/usr/lib/luarocks --lua-version 5.1 chroot_packages=\ -p luarocks5.1 \ -p "build-base" \ @@ -62,21 +64,18 @@ lua_packages = \ zlib # Probably don't change stuff past here if you're just using smr -lua_in_files=$(shell find src/lua/*.in -type f) -lua_files=$(shell find src/lua/*.lua -type f) $(shell find src/lua/endpoints -type f) $(lua_in_files:%.in=%) +lua_files=$(shell find src/lua/*.lua -type f) $(shell find src/lua/endpoints -type f) src_files=$(shell find src -type f) $(shell find conf -type f) sql_files=$(shell find src/sql -type f) -test_files=$(shell find spec -type f) -built_tests=$(test_files:%=$(build_dir)%) -built_files=$(lua_files:src/lua/%.lua=$(build_dir)%.lua) -in_page_files=$(shell find src/pages/*.in -type f) -page_files=$(in_page_files:%.in=%) +test_files=$(shell find spec -type f) $(shell find spec/parser_tests -type f) +page_files=$(shell find src/pages -type f) +built_tests=$(test_files:%=$(app_root)/%) +built_files=$(lua_files:src/lua/%.lua=$(app_root)/%.lua) part_files=$(shell find src/pages/parts/*.etlua -type f) -built_pages=$(page_files:src/pages/%.etlua=$(build_dir)pages/%.etlua) -built_sql=$(sql_files:src/sql/%.sql=$(build_dir)sql/%.sql) +built_parts=$(part_files:src/%=$(app_root)/%) +built_pages=$(page_files:src/pages/%.etlua=$(app_root)/pages/%.etlua) +built_sql=$(sql_files:src/sql/%.sql=$(app_root)/sql/%.sql) built=$(built_files) $(built_sql) $(built_pages) $(built_tests) -asset_in_files=$(wildcard assets/*.in -type f) -asset_files=$(asset_in_files:%.in=%) initscript=/lib/systemd/system/smr.service config=$(conf_path)/smr.conf built_bin=$(smr_bin_path)/smr.so @@ -88,7 +87,7 @@ apk_hash := $(APK_$(arch)_HASH) help: ## Print this help $(Q)$(GREP) -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | $(SORT) | $(AWK) 'BEGIN {FS = ":.*?## "}; {printf "%-10s %s\n", $$1, $$2}' -all: $(build_dir) smr.so $(built_files) $(built_pages) $(built_sql) ## Build and run smr in a chroot +all: $(app_root) smr.so $(built_files) $(built_pages) $(built_sql) ## Build and run smr in a chroot apk-tools-static-$(version).apk: wget -q $(mirror)latest-stable/main/$(arch)/apk-tools-static-$(version).apk @@ -100,7 +99,7 @@ clean: ## clean up all the files generated by this makefile $(Q)$(RM) $(asset_files) $(Q)$(RM) smr.so -install: $(worker_chroot) $(kmgr_chroot) $(parent_chroot) $(initscript) $(config) smr.so $(built_files) $(built_pages) $(built_sql) ## Install smr into a new host system +install: $(app_root) $(kmgr_chroot) $(parent_chroot) $(initscript) $(config) smr.so $(built_files) $(built_pages) $(built_sql) ## Install smr into a new host system $(Q)$(COPY) smr.so $(built_bin) $(config) : conf/smr.conf @@ -111,15 +110,19 @@ $(initscript) : packaging/systemd/smr.service $(Q)$(COPY) $< $@ cloc: ## calculate source lines of code in smr - cloc --force-lang="html",etlua.in --force-lang="html",etlua --force-lang="lua",lua.in src assets Makefile + cloc --force-lang="html",etlua --force-lang="lua",luasrc assets Makefile -$(build_dir): - $(Q)$(MKDIR) $(build_dir) - $(Q)$(MKDIR) $(build_dir)/pages - $(Q)$(MKDIR) $(build_dir)/sql - $(Q)$(MKDIR) $(build_dir)/data - $(Q)$(MKDIR) $(build_dir)/data/archive - $(Q)$(MKDIR) $(build_dir)/endpoints +$(app_root): + $(Q)$(MKDIR) $(app_root) + +$(app_root): $(worker_chroot) + $(Q)$(MKDIR) $(app_root) + $(Q)$(MKDIR) $(app_root)/pages + $(Q)$(MKDIR) $(app_root)/pages/parts + $(Q)$(MKDIR) $(app_root)/sql + $(Q)$(MKDIR) $(app_root)/data + $(Q)$(MKDIR) $(app_root)/data/archive + $(Q)$(MKDIR) $(app_root)/endpoints alpine-chroot-install: $(Q)wget https://raw.githubusercontent.com/alpinelinux/alpine-chroot-install/v0.14.0/alpine-chroot-install \ @@ -134,43 +137,39 @@ $(worker_chroot) $(kmgr_chroot) $(parent_chroot): alpine-chroot-install code : $(built_files) -$(built_files): $(build_dir)%.lua : src/lua/%.lua $(build_dir) +$(built_files): $(app_root)/%.lua : src/lua/%.lua $(app_root) $(Q)$(ECHO) "[copy] $@" $(Q)$(COPY) $< $@ -$(built_pages): $(build_dir)pages/%.etlua : src/pages/%.etlua $(build_dir) +$(built_pages): $(app_root)/pages/%.etlua : src/pages/%.etlua $(app_root) $(Q)$(ECHO) "[copy] $@" $(Q)$(COPY) $< $@ -src/lua/config.lua : src/lua/config.lua.in Makefile - $(Q)$(ECHO) "[preprocess] $@" - $(Q)$(SPP) $(SPPFLAGS) -o $@ $< +$(built_parts): $(app_root)/% : src/% + $(Q)$(ECHO) "[copy] $@" + $(Q)$(COPY) $< $@ -$(page_files) : % : %.in $(part_files) - $(Q)$(ECHO) "[preprocess] $@" - $(Q)$(SPP) $(SPPFLAGS) -o $@ $< - -$(built_sql): $(build_dir)sql/%.sql : src/sql/%.sql +$(built_sql): $(app_root)/sql/%.sql : src/sql/%.sql $(Q)$(ECHO) "[copy] $@" $(Q)$(COPY) $^ $@ -$(built_tests) : $(build_dir)% : src/spec/% +$(built_tests) : $(app_root)/spec/% : spec/% $(app_root)/spec $(Q)$(ECHO) "[copy] $@" - $(Q)$(COPY) $^ $@ + $(Q)$(COPY) $< $@ -$(asset_files) : % : %.in - $(Q)$(ECHO) "[preprocess] $@" - $(Q)$(SPP) $(SPPFLAGS) -o $@ $< +$(app_root)/spec: $(app_root) + $(Q)$(MKDIR) $@ + $(Q)$(MKDIR) $@/parser_tests smr.so : $(src_files) conf/build.conf $(asset_files) $(Q)$(ECHO) "[build] $@" $(Q)$(KODEV) build test : $(built) ## run the unit tests - $(Q)$(CD) kore_chroot && busted -v --no-keep-going #--exclude-tags slow + $(Q)$(CD) $(app_root) && busted -v --no-keep-going --exclude-tags "slow,todo,working" cov : $(built) ## code coverage (based on unit tests) $(Q)$(RM) $(kore_chroot)/luacov.stats.out - $(Q)$(CD) $(kore_chroot) && busted -v -c --no-keep-going #--exclude-tags slow + $(Q)$(CD) $(kore_chroot) && busted -v -c --no-keep-going --exclude-tags slow $(Q)$(CD) $(kore_chroot) && luacov endpoints/ $(Q)$(ECHO) "open kore_chroot/luacov.report.out to view coverage results." diff --git a/spec/author_bio_spec.lua b/spec/author_bio_spec.lua index edb67d7..70a18a4 100644 --- a/spec/author_bio_spec.lua +++ b/spec/author_bio_spec.lua @@ -2,7 +2,7 @@ _G.spy = spy local mock_env = require("spec.env_mock") local rng = require("spec.fuzzgen") -describe("smr biography",function() +describe("smr biography #todo",function() setup(mock_env.setup) teardown(mock_env.teardown) it("should allow users to set their biography",function() diff --git a/spec/cacheing_spec.lua b/spec/cacheing_spec.lua index c4fbc6f..15a9085 100644 --- a/spec/cacheing_spec.lua +++ b/spec/cacheing_spec.lua @@ -1,10 +1,10 @@ _G.spy = spy -local mock_env = require("spec.env_mock") +local env_mock = require("spec.env_mock") describe("smr cacheing",function() - setup(mock_env.setup) - teardown(mock_env.teardown) + setup(env_mock.setup) + teardown(env_mock.teardown) it("caches a page if the page is requested twice #working",function() local read_get = require("endpoints.read_get") local cache = require("cache") @@ -29,9 +29,15 @@ describe("smr cacheing",function() )) end end) + it("should expose the database connection", function() + local cache = require("cache") + configure() + assert(cache.cache, "Not exposed under .cache") + end) it("does not cache the page if the user is logged in", function() local read_get = require("endpoints.read_get") local cache = require("cache") + env_mock.mockdb() renderspy = spy.on(cache,"render") configure() for row in cache.cache:rows("SELECT COUNT(*) FROM cache") do @@ -40,7 +46,7 @@ describe("smr cacheing",function() "request have been made." )) end - local req = mock_env.session() + local req = env_mock.session() req.method = "GET" req.path = "/a" req.args = {} diff --git a/spec/env_mock.lua b/spec/env_mock.lua index 30d8d07..726d61e 100644 --- a/spec/env_mock.lua +++ b/spec/env_mock.lua @@ -12,6 +12,7 @@ local ntostring local login_post local fuzzy local claim_post +local session print_table= function(...) print("Print called") local args = {...} @@ -69,7 +70,10 @@ local smr_mock_env = { http_request_get_path = spy.new(function(req) return req.path or "/" end), http_request_populate_qs = spy.new(function(req) req.qs_populated = true end), http_request_populate_post = spy.new(function(req) req.post_populated = true end), - http_populate_multipart_form = spy.new(function(req) req.post_populated = true end), + http_populate_multipart_form = spy.new(function(req) + req.post_populated = true + req.multipart_form_populated = true + end), http_argument_get_string = spy.new(function(req,str) assert(req.args,"requests should have a .args table") assert( @@ -83,7 +87,7 @@ local smr_mock_env = { return req.args[str] end), http_file_get = spy.new(function(req,filename) - assert(req.multipart_forum_populated,[[ + assert(req.multipart_form_populated,[[ http_file_get() can only be called after the approriate populate method has been called. (http_populate_multipart_form()) ]]) @@ -156,9 +160,9 @@ local string_fmt_override = { setmetatable(string_fmt_override,{__index = string}) local smr_override_env = { --Detour assert so we don't actually perform any checks - assert = spy.new(function(bool,msg,level) return bool end), + --assert = spy.new(function(bool,msg,level) return bool end), --Allow string.format to accept nil as arguments - string = string_fmt_override + --string = string_fmt_override } mock.olds = {} @@ -173,10 +177,12 @@ end function mock.mockdb() local config = require("config") - config.db = "data/unittest.db" + --config.db = "data/unittest.db" + config.db = ":memory:" assert(os.execute("rm " .. config.db)) package.loaded.db = nil local db = require("db") + configure() end function mock.teardown() @@ -207,7 +213,7 @@ local session_m = {__index = { }} function mock.session(tbl) - if post_login == nil then + if login_post == nil then login_post = require("endpoints.login_post") fuzzy = require("spec.fuzzgen") claim_post = require("endpoints.claim_post") diff --git a/spec/login_spec.lua b/spec/login_spec.lua index ab7479b..deefd78 100644 --- a/spec/login_spec.lua +++ b/spec/login_spec.lua @@ -1,13 +1,13 @@ _G.spy = spy -local mock_env = require("spec.env_mock") +local env_mock = require("spec.env_mock") local rng = require("spec.fuzzgen") describe("smr login",function() - setup(mock_env.setup) - teardown(mock_env.teardown) + setup(env_mock.setup) + teardown(env_mock.teardown) it("should allow someone to claim an account",function() - mock_env.mockdb() + env_mock.mockdb() local claim_post = require("endpoints.claim_post") configure() claim_req = { @@ -186,6 +186,7 @@ describe("smr login",function() text = "post text", markup = "plain", tags = "", + pasteas = username } } paste_post(paste_req_post) diff --git a/src/libcrypto.c b/src/libcrypto.c index 207b2d7..e318178 100644 --- a/src/libcrypto.c +++ b/src/libcrypto.c @@ -10,8 +10,18 @@ borrowed sha3 implementation from https://keccak.team #include "libcrypto.h" #include "keccak.h" -/* -sha3(data::string)::string +/* rst +@name lua/sha3 + +Provides a sha3 implementation. + +::signature + sha3(data :: {{lua/string}}) :: {{lua/string}} +::params + {{lua/string}} data - The data to hash +::returns + {{lua/string}} - The hash of the input string + */ int lsha3(lua_State *L){ diff --git a/src/lua/addon.lua b/src/lua/addon.lua index 58d6cce..da2bb7b 100644 --- a/src/lua/addon.lua +++ b/src/lua/addon.lua @@ -1,7 +1,9 @@ ---[[ +--[[ md +@name lua/addon + Addon loader - Addons are either: * A folder with at least two files: - - meta.lua - contains load order information + - meta.lua - contains addon information - init.lua - entrypoint that gets run to load the addon * A zip file with the same * A sqlite3 database with a table "files" that has at least the columns @@ -15,9 +17,15 @@ each worker), and should return a table with at least the following information { name :: string - A name for the addon (all addons must have unique names) desc :: string - A description for the addon. - entry :: table[number -> string] - Describes the load order for this - addon. Each addon's meta.lua is run, and sorted to get a load - order for entrypoints (the strings are the names files) + order :: number - When should we run init.lua relative to other addons? + Each addon's meta.lua is run (in any order), addons are sorted + according to their order, and finally each addon's init.lua is + called according to this order. +} + +meta.lua may include additional information that can be read and used by other +addons. meta.lua is run in a restricted environment with almost no functions +available. ]] local oldconfigure = configure diff --git a/src/lua/cache.lua b/src/lua/cache.lua index 9c40e32..2f79e0d 100644 --- a/src/lua/cache.lua +++ b/src/lua/cache.lua @@ -17,12 +17,11 @@ local stmnt_cache, stmnt_insert_cache, stmnt_dirty_cache local oldconfigure = configure function configure(...) - local cache = db.sqlassert(sql.open_memory()) - ret.cache = cache -- Expose db for testing + ret.cache = db.sqlassert(sql.open_memory())-- Expose db for testing --A cache table to store rendered pages that do not need to be --rerendered. In theory this could OOM the program eventually and start --swapping to disk. TODO - assert(cache:exec([[ + assert(ret.cache:exec([[ CREATE TABLE IF NOT EXISTS cache ( path TEXT PRIMARY KEY, data BLOB, @@ -30,7 +29,7 @@ function configure(...) dirty INTEGER ); ]])) - stmnt_cache = assert(cache:prepare([[ + stmnt_cache = assert(ret.cache:prepare([[ SELECT data FROM cache WHERE @@ -38,14 +37,14 @@ function configure(...) ((dirty = 0) OR (strftime('%s','now') - updated) > 20) ; ]])) - stmnt_insert_cache = assert(cache:prepare([[ + stmnt_insert_cache = assert(ret.cache:prepare([[ INSERT OR REPLACE INTO cache ( path, data, updated, dirty ) VALUES ( :path, :data, strftime('%s','now'), 0 ); ]])) - stmnt_dirty_cache = assert(cache:prepare([[ + stmnt_dirty_cache = assert(ret.cache:prepare([[ UPDATE OR IGNORE cache SET dirty = 1 WHERE path = :path; diff --git a/src/lua/config.lua.in b/src/lua/config.lua.in deleted file mode 100644 index 17fe0ea..0000000 --- a/src/lua/config.lua.in +++ /dev/null @@ -1,13 +0,0 @@ ---[[ -Holds configuration. -A one-stop-shop for runtime configuration -]] -local config = { - domain = "<{get domain}>", - production = false, - legacy_url_cutoff = 144, - approot = "<{get approot}>" -} -config.db = config.approot .. "data/posts.db" - -return config diff --git a/src/lua/db.lua b/src/lua/db.lua index 5c1df09..9592997 100644 --- a/src/lua/db.lua +++ b/src/lua/db.lua @@ -1,4 +1,5 @@ ---[[ +--[[ md +@name lua/db Does most of the database interaction. Notably, holds a connection to the open sqlite3 database in .conn ]] @@ -9,9 +10,34 @@ local config = require("config") local db = {} ---[[ +--[[ md +@name lua/db/sqlassert + Runs an sql query and receives the 3 arguments back, prints a nice error message on fail, and returns true on success. + +Parameters: + +0. r - {{lsqlite/stmnt}} | {{lua/nil}} - The userdata returned from +{{lsqlite/db/prepare}} +0. errcode - {{lua/nil}} | {{lua/number}} - If the first argument back from +{{lsqlite/db/prepare}} is nil, this second argument is a numeric errorcode, +see {{lsqlite/errcodes}} +0. err - {{lua/nil}} | {{lua/string}} - The string error returned from +{{lsqlite/db/prepare}}. Only non-nil if the first return value was nil. A +string message describing what went wrong in the statment. If this argument is +also {{lua/nil}}, this function retrives the error mssage from +{{lsqlite/db/errmsg}}. + +Returns: + +0. r - {{lua/userdata}} | {{lua/nil}} - The first argument passed in. Used so +that error checking and assignment can all be done on a single line. + +Example: + +> db = require("db") +> query = db.sqlassert(db.conn:parepare("SELECT 'Hello, world!'")) ]] function db.sqlassert(r, errcode, err) if not r then @@ -24,8 +50,27 @@ function db.sqlassert(r, errcode, err) return r end ---[[ -Continuously tries to perform an sql statement until it goes through +--[[ md +@name lua/db/do_sql + +Continuously tries to perform an sql statement until it goes through. This function may call {{lua/coroutine/yield}} + +Parameters: + +0. stmnt - {{lsqlite/stmnt}} - The userdata returned form {{lsqlite/db/prepare}} + +Returns: + +0. err - {{lua/number}} - The error code returned from running the statement. Will be `lsqlite.OK` on success, see {{lsqlite/errcodes}} + +Example: + +> sql = require("lsqlite3") +> configure = function(...) end -- Mock smr environment +> db = require("db") +> configure() +> query = db.conn:prepare("SELECT 'Hello, world!';") +> assert(db.do_sql(query)) ]] function db.do_sql(stmnt) if not stmnt then error("No statement",2) end @@ -42,11 +87,33 @@ function db.do_sql(stmnt) return err end ---[[ -Provides an iterator that loops over results in an sql statement -or throws an error, then resets the statement after the loop is done. +--[[ md +@name lua/db/sql_rows + +Provides an iterator that loops over results in an sql statement or throws an +error, then resets the statement after the loop is done. Returned iterator returns varargs, so the values can be unpacked in-line in the -for loop. +for loop. This statement is approximately the same as {{sqlite/stmt/rows}}, but +may yield when the db connection is busy, and continue execution when the +connection is free again. + +Parameters: + +0. stmnt - {{lsqlite/stmnt}} - The userdata returned from {{sqlite/db/prepare}} + +Returns: + +0. iterator - {{lua/iterator}} - The iterator function that returns varargs of the returns from the sql statement. + +Example: + +> db = require("db") +> query = db.conn:prepare("SELECT 'Hello, world!';") +> for row in db.sql_rows(query) do +> print(row) +> end +Hello, world! + ]] function db.sql_rows(stmnt) if not stmnt then error("No statement",2) end @@ -75,7 +142,9 @@ function db.sql_rows(stmnt) end --[[ -Binds an argument to as statement with nice error reporting on failure +@name lua/db/sqlbind + +Binds an argument to as statement with nice error reporting on failure. Approximatly the same as {{lsqlite/stmt/bind_names}}, but with better error reporting. stmnt :: sql.stmnt - the prepared sql statemnet call :: string - a string "bind" or "bind_blob" position :: number - the argument position to bind to @@ -85,7 +154,15 @@ function db.sqlbind(stmnt,call,position,data) assert(call == "bind" or call == "bind_blob","Bad bind call, call was:" .. call) local f = stmnt[call](stmnt,position,data) if f ~= sql.OK then - error(string.format("Failed call %s(%d,%q): %s", call, position, data, db.conn:errmsg()),2) + local errs = string.format( + "Failed call %s(%d,%q): %s", + call, + position, + data, + db.conn:errmsg() + ) + log(LOG_ERR,errs) + error(errs,2) end end @@ -94,39 +171,25 @@ end local oldconfigure = configure db.conn = db.sqlassert(sql.open(config.db)) function configure(...) - - --Create sql tables - assert(db.conn:exec(queries.create_table_authors)) - --Create a fake "anonymous" user, so we don't run into trouble - --so that no one runs into trouble being able to paste under this account. - assert(db.conn:exec(queries.insert_anon_author)) - --If/when an author deletes their account, all posts - --and comments by that author are also deleted (on - --delete cascade) this is intentional. This also - --means that all comments by other users on a post - --an author makes will also be deleted. - -- - --Post text uses zlib compression - assert(db.conn:exec(queries.create_table_posts)) - --Store the raw text so people can download it later, maybe - --we can use it for "download as image" or "download as pdf" - --in the future too. Stil stored zlib compressed - assert(db.conn:exec(queries.create_table_raw_text)) - --Maybe we want to store images one day? - assert(db.conn:exec(queries.create_table_images)) - --Comments on a post - assert(db.conn:exec(queries.create_table_comments)) - --Tags for a post - assert(db.conn:exec(queries.create_table_tags)) - --Index for tags - assert(db.conn:exec(queries.create_index_tags)) - --Store a cookie for logged in users. Logged in users can edit - --their own posts, and edit their biographies. - assert(db.conn:exec(queries.create_table_session)) + local statements = { + "create_table_authors", + "insert_anon_author", + "create_table_posts", + "create_table_raw_text", + "create_table_images", + "create_table_comments", + "create_table_tags", + "create_index_tags", + "create_table_session" + } + -- ipairs() needed, "create table authors" must be executed before + -- "insert anon author" + for _, statement in ipairs(statements) do + db.sqlassert(db.conn:exec(queries[statement])) + end return oldconfigure(...) end -configure() function db.close() db.conn:close() diff --git a/src/lua/endpoints/claim_post.lua b/src/lua/endpoints/claim_post.lua index b83f524..537348a 100644 --- a/src/lua/endpoints/claim_post.lua +++ b/src/lua/endpoints/claim_post.lua @@ -15,7 +15,6 @@ local stmnt_author_create --a while, but whatever. local oldconfigure = configure function configure(...) - stmnt_author_create = db.sqlassert(db.conn:prepare(queries.insert_author)) return oldconfigure(...) end diff --git a/src/lua/endpoints/index_get.lua b/src/lua/endpoints/index_get.lua index 7197a87..d2b3d97 100644 --- a/src/lua/endpoints/index_get.lua +++ b/src/lua/endpoints/index_get.lua @@ -35,7 +35,7 @@ local function get_site_home(req, loggedin) isanon = tonumber(iar) == 1, posted = os.date("%B %d %Y",tonumber(dater)), author = author, - tags = libtags.get(idr), + taglist = libtags.get(idr), hits = hits, ncomments = cmts } diff --git a/src/lua/endpoints/login_post.lua b/src/lua/endpoints/login_post.lua index ea97864..9868c59 100644 --- a/src/lua/endpoints/login_post.lua +++ b/src/lua/endpoints/login_post.lua @@ -5,6 +5,7 @@ local util = require("util") local session = require("session") local config = require("config") local pages = require("pages") +local api = require("hooks") local stmnt_author_acct @@ -18,47 +19,43 @@ function configure(...) return oldconfigure(...) end +local old_authenticate = api.authenticate +function api.authenticate(data) + stmnt_author_acct:bind_names{name=data.user} + local err = db.do_sql(stmnt_author_acct) + if err ~= sql.ROW then + stmnt_author_acct:reset() + log(LOG_NOTICE,string.format("User %q failed to log in",data.user)) + end + local id, salt, passhash = unpack(stmnt_author_acct:get_values()) + stmnt_author_acct:reset() + local hash = sha3(salt .. data.pass) + if hash == passhash then + return id + end +end + local function login_post(req) --Try to log in http_populate_multipart_form(req) local name = assert(http_argument_get_string(req,"user")) local pass = assert(http_file_get(req,"pass")) - stmnt_author_acct:bind_names{ - name = name - } - local text - local err = db.do_sql(stmnt_author_acct) - if err == sql.ROW then - local id, salt, passhash = unpack(stmnt_author_acct:get_values()) - stmnt_author_acct:reset() - local todigest = salt .. pass - local hash = sha3(todigest) - if hash == passhash then - local mysession = session.start(id) - local domain_no_port = config.domain:match("(.*):.*") or config.domain - http_response_header(req,"set-cookie",string.format( - [[session=%s; SameSite=Lax; Path=/; Domain=%s; HttpOnly; Secure]],mysession,domain_no_port - )) - local loc = string.format("https://%s.%s",name,config.domain) - http_response_header(req,"Location",loc) - http_response(req,303,"") - return - else - text = pages.login{ - err = "Incorrect username or password" - } - end - elseif err == sql.DONE then --Allows user enumeration, do we want this? - --Probably not a problem since all passwords are forced to be "good" - stmnt_author_acct:reset() - text = pages.login{ - err = "Failed to find user:" .. name - } - else - stmnt_author_acct:reset() - error("Other sql error during login") + local uid, err = api.authenticate({user=name,pass=pass}) + if not uid then + http_response(req,200,pages.login{err=err}) + return end - http_response(req,200,text) + local user_session = session.start(uid) + local domain_no_port = config.domain:match("(.*):.*") or config.domain + local cookie_string = string.format( + [[session=%s; SameSite=Lax; Path=/; Domain=%s; HttpOnly; Secure]], + user_session, + domain_no_port + ) + http_response_header(req,"set-cookie",cookie_string) + local loc = string.format("https://%s.%s",name,config.domain) + http_response_header(req,"Location",loc) + http_response(req,303,"") end return login_post diff --git a/src/lua/endpoints/paste_post.lua b/src/lua/endpoints/paste_post.lua index d9698c6..c2e3b16 100644 --- a/src/lua/endpoints/paste_post.lua +++ b/src/lua/endpoints/paste_post.lua @@ -102,7 +102,7 @@ local function author_paste(req,ps) text = ps.text } end - local asanon = assert(http_argument_get_string(req,"pasteas")) + local asanon = assert(http_argument_get_string(req,"pasteas") or "anonymous") local textsha3 = sha3(ps.text .. get_random_bytes(32)) --No need to check if the author is posting to the --"right" sudomain, just post it to the one they have diff --git a/src/lua/endpoints/read_get.lua b/src/lua/endpoints/read_get.lua index bbb0d25..3019b6e 100644 --- a/src/lua/endpoints/read_get.lua +++ b/src/lua/endpoints/read_get.lua @@ -14,9 +14,9 @@ local stmnt_read, stmnt_update_views, stmnt_comments local oldconfigure = configure function configure(...) - stmnt_read = assert(db.conn:prepare(queries.select_post)) - stmnt_update_views = assert(db.conn:prepare(queries.update_views)) - stmnt_comments = assert(db.conn:prepare(queries.select_comments)) + stmnt_read = db.sqlassert(db.conn:prepare(queries.select_post)) + stmnt_update_views = db.sqlassert(db.conn:prepare(queries.update_views)) + stmnt_comments = db.sqlassert(db.conn:prepare(queries.select_comments)) return oldconfigure(...) end diff --git a/src/lua/pages.lua b/src/lua/pages.lua index cbdbe87..e276b80 100644 --- a/src/lua/pages.lua +++ b/src/lua/pages.lua @@ -28,6 +28,18 @@ local pagenames = { "parts/story_breif", "parts/taglist" } +--Functions available to all templates +local global_env = { + include = function(filename) + local fp = assert(io.open(filename,"r")) + local data = assert(fp:read("*a")) + fp:close() + return data + end, +} +local global_env_m = { + __index=global_env +} local pages = {} for k,v in pairs(pagenames) do local path = string.format(config.approot .. "pages/%s.etlua",v) @@ -46,6 +58,12 @@ for k,v in pairs(pagenames) do assert(func, "Failed to load " .. path) pages[v] = function(env) assert(type(env) == "table","env must be a table") + -- Add our global metatable functions at the bottom metatable's __index + local cursor = env + while cursor ~= nil and getmetatable(cursor) and getmetatable(cursor).__index do + cursor = getmetatable(cursor) + end + setmetatable(cursor,global_env_m) local buff, err = parser:run(func,env) if not buff then errorf("Failed to render %s : %s", path, err) diff --git a/src/lua/session.lua b/src/lua/session.lua index e97834d..b21d930 100644 --- a/src/lua/session.lua +++ b/src/lua/session.lua @@ -4,12 +4,13 @@ local db = require("db") local util = require("util") local queries = require("queries") -local oldconfigure = configure local stmnt_get_session, stmnt_insert_session, stmnt_delete_session + +local oldconfigure = configure function configure(...) - stmnt_get_session = assert(db.conn:prepare(queries.select_valid_sessions)) - stmnt_insert_session = assert(db.conn:prepare(queries.insert_session)) - stmnt_delete_session = assert(db.conn:prepare(queries.delete_session)) + stmnt_get_session = db.sqlassert(db.conn:prepare(queries.select_valid_sessions)) + stmnt_insert_session = db.sqlassert(db.conn:prepare(queries.insert_session)) + stmnt_delete_session = db.sqlassert(db.conn:prepare(queries.delete_session)) return oldconfigure(...) end @@ -59,7 +60,7 @@ function session.start(who) } local err = db.do_sql(stmnt_insert_session) stmnt_insert_session:reset() - assert(err == sql.DONE) + assert(err == sql.DONE, "Error should have been 'DONE', was: " .. tostring(err)) return session end diff --git a/src/lua/tags.lua b/src/lua/tags.lua index ee8e54e..e20feb0 100644 --- a/src/lua/tags.lua +++ b/src/lua/tags.lua @@ -1,3 +1,14 @@ +--[[ md +@name lua/tags + +Helper methods for cleaning story tags. +Tags are the main way to search smr, a simple `+` or `-` will show all +stories that include (+) or do not include (-) a particular tag. + +Tags are stored in the {{table_tags}} and are deleted if the story they are +attached to is deleted. If an author is deleted, all their stories are deleted, +and this will cascade to deleting tags on their stories too. +]] local sql = require("lsqlite3") local db = require("db") diff --git a/src/lua/util.lua b/src/lua/util.lua index cc367f1..ddfd6dc 100644 --- a/src/lua/util.lua +++ b/src/lua/util.lua @@ -1,3 +1,9 @@ +--[[ md +@name lua/util +Various utilities that aren't big enough for their own module, but are still +used in more than one place. +]] + local sql = require("lsqlite3") local config = require("config") @@ -15,8 +21,26 @@ function configure(...) return oldconfigure(...) end ---see https://perishablepress.com/stop-using-unsafe-characters-in-urls/ ---no underscore because we use that for our operative pages +--[[ md +@name doc/url_spec + +URLs generated from smr use letters and numbers to encode a monotonically +increasing post id into a url that can easily be shared (and ends up +considerably shorter). The characters used in url generation are: +[a-z][A-Z][0-9], and numbers are encoded to use the second available 1-character +permuation, then the first available 2-character permutation, and so on. + +For example, the first post is encoded as 'b', the second as 'c', the thrid +as 'd', and so on. The off-by-one nature is to simplify implementation of +2-character and 3-character combinations with Lua's 1-indexed arrays. + +see https://perishablepress.com/stop-using-unsafe-characters-in-urls/ +no underscore because we use that for our operative pages + +A set of legacy characters that are no longer in use (because they were invalid +to use in URL's) is also defined, but unused as long as +{{config/legacy_url_cutoff}} is set to 0. +]] local url_characters = [[abcdefghijklmnopqrstuvwxyz]].. [[ABCDEFGHIJKLMNOPQRSTUVWXYZ]].. @@ -34,8 +58,11 @@ local url_characters_rev_legacy = {} for i = 1,string.len(url_characters_legacy) do url_characters_rev_legacy[string.sub(url_characters_legacy,i,i)] = i end ---[[ -Encode a number to a shorter HTML-safe url path + +--[[ md +@name lua/util/encode_id +Encode a number to a shorter HTML-safe url path. Url paths are generated +according to the {{doc/url_spec} ]] function util.encode_id(number) local result = {} diff --git a/src/pages/author_edit.etlua.in b/src/pages/author_edit.etlua.in deleted file mode 100644 index 9dd423a..0000000 --- a/src/pages/author_edit.etlua.in +++ /dev/null @@ -1,27 +0,0 @@ -<% assert(author,"No author specified") %> -<% assert(bio,"No bio included") %> -<{system cat src/pages/parts/header.etlua}> -

- <%= author %>.<%= domain %> -

- -
-
-
- -
-
-
- <% if #stories == 0 then %> - This author has not made any pastes yet. - <% else %> - - <% for k,story in pairs(stories) do %> - <{system cat src/pages/parts/story_breif.etlua}> - <% end %> -
- <% end %> -
-<{system cat src/pages/parts/footer.etlua}> diff --git a/src/pages/author_index.etlua.in b/src/pages/author_index.etlua.in deleted file mode 100644 index acfc7b5..0000000 --- a/src/pages/author_index.etlua.in +++ /dev/null @@ -1,39 +0,0 @@ -<% assert(author,"No author specified") %> -<% assert(bio,"No bio included") %> -<{system cat src/pages/parts/header.etlua}> -

- <%= author %>.<%= domain %> -

-
-
- New paste - <% if not loggedin then %> - Log in - <% else %> - Log out - Edit bio - <% end %> - - <{system cat src/pages/parts/search.etlua}> -
-
-<% if bio ~= "" then %> -
-
- <%- bio %> -
-
-<% end %> -
- <% if #stories == 0 then %> - This author has not made any pastes yet. - <% else %> - - <% for k,story in pairs(stories) do %> - <{system cat src/pages/parts/story_breif.etlua}> - <% end %> -
- <% end %> -
-<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/author_paste.etlua.in b/src/pages/author_paste.etlua.in deleted file mode 100644 index 2068110..0000000 --- a/src/pages/author_paste.etlua.in +++ /dev/null @@ -1,34 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- Paste -

-<% if err then %><%= err %><% end %> -
-
-
- - - -
- - -
-
-
- -
-
-
-
- - -
-
-<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/cantedit.etlua.in b/src/pages/cantedit.etlua.in deleted file mode 100644 index ceb215f..0000000 --- a/src/pages/cantedit.etlua.in +++ /dev/null @@ -1,11 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- 🙁 -

-
-

- You don't have permission to edit: <%= path %> -

-
-<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/claim.etlua.in b/src/pages/claim.etlua.in deleted file mode 100644 index 08eaf7d..0000000 --- a/src/pages/claim.etlua.in +++ /dev/null @@ -1,20 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- Register -

-Once you press submit, you will be prompted to download a file.
-slash.monster uses this file in place of a password, keep it safe.
-Consider keeping a copy on a USB in case your hard drive fails.
-The admin cannot recover your passfile, and will not reset accounts.
-Names may be up to 30 characters, alphanumeric, no symbols, all lower case.
-<% if err then %><%= err %><% end %> -
-
- - - -
-
-Once you have your file, you can log in -<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/edit.etlua.in b/src/pages/edit.etlua.in deleted file mode 100644 index 03693e4..0000000 --- a/src/pages/edit.etlua.in +++ /dev/null @@ -1,48 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- Paste -

-<% if err then %><%= err %><% end %> -
-
-
- - - - -
- - - checked - <% end %> - > - -
-
-
- -
-
-
-
- - -
-
-<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/edit_bio.etlua.in b/src/pages/edit_bio.etlua.in deleted file mode 100644 index b3a0121..0000000 --- a/src/pages/edit_bio.etlua.in +++ /dev/null @@ -1,18 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- Edit Biography for <%= user %> -

-<% if err then %><%= err %><% end %> -
-
- -
-
-
-
- -
-
-
-<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/error.etlua.in b/src/pages/error.etlua.in deleted file mode 100644 index df3f9ce..0000000 --- a/src/pages/error.etlua.in +++ /dev/null @@ -1,14 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- <% if errcode then -%><%= errcode -%><% end -%>: - <% if errcodemsg then -%><%= errcodemsg -%><% end %> -

- -<% if explanation then -%><%= explanation -%><% end -%> - -<% if should_traceback then %> -
-	<%- debug.traceback() %>
-	
-<% end %> -<{system cat src/pages/parts/footer.etlua}> diff --git a/src/pages/index.etlua.in b/src/pages/index.etlua.in deleted file mode 100644 index 12f0aad..0000000 --- a/src/pages/index.etlua.in +++ /dev/null @@ -1,36 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- - <%= domain %> - -

- -
-
- New paste - <% if not loggedin then %> - Log in - Register - <% else %> - Log out - - <% end %> - <{system cat src/pages/parts/search.etlua}> -
-

- <{ system cat src/pages/parts/motd.etlua }> -

-
-
- <% if #stories == 0 then %> - No stories available. - <% else %> - - <% for k,story in pairs(stories) do %> - <{system cat src/pages/parts/story_breif.etlua}> - <% end %> -
- <% end %> -
-<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/login.etlua.in b/src/pages/login.etlua.in deleted file mode 100644 index 72b132a..0000000 --- a/src/pages/login.etlua.in +++ /dev/null @@ -1,15 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- Login -

-<% if err then %><%= err %><% end %> -
-
- - - - - -
-
-<{system cat src/pages/parts/footer.etlua}> diff --git a/src/pages/noauthor.etlua.in b/src/pages/noauthor.etlua.in deleted file mode 100644 index a2869e2..0000000 --- a/src/pages/noauthor.etlua.in +++ /dev/null @@ -1,11 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- 🙁 -

-
-

- No author found: <%= author %> -

-
-<{system cat src/pages/parts/footer.etlua}> - diff --git a/src/pages/nostory.etlua.in b/src/pages/nostory.etlua.in deleted file mode 100644 index 2bb1747..0000000 --- a/src/pages/nostory.etlua.in +++ /dev/null @@ -1,10 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- 🙁 -

-
-

- No story found: <%= path %> -

-
-<{system cat src/pages/parts/footer.etlua}> diff --git a/src/pages/paste.etlua.in b/src/pages/paste.etlua.in deleted file mode 100644 index 686329f..0000000 --- a/src/pages/paste.etlua.in +++ /dev/null @@ -1,27 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- Paste -

-<% if err then %><%= err %><% end %> -
-
- - -
- - -
-
-
- -
-
-
-
- - -
-<{system cat src/pages/parts/footer.etlua}> diff --git a/src/pages/read.etlua.in b/src/pages/read.etlua.in deleted file mode 100644 index 1121d61..0000000 --- a/src/pages/read.etlua.in +++ /dev/null @@ -1,84 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -<% local api = require("hooks") %> - -<% if owner then -%> -
- <% for _, spec in ipairs(api.get.page_owner(getfenv(1))) do %> -
- <% for key, value in pairs(spec.fields) do %> - - <% end %> - -
- <% end %> -
-<% end -%> -
-

<%- title %>

-

- <% if isanon or author == nil then -%> - By Anonymous - <% else -%> - By <%= author %> - <% end -%> -

-
    - <% for _,tag in pairs(tags) do -%> - <{system cat src/pages/parts/taglist.etlua}> - <% end -%> -
- - <%- text %> -
- -
- -

<%= views %> Hits, <%= #comments %> Comments

- -
-<% for _, spec in ipairs(api.get.page_reader(getfenv(1))) do %> -
- <% for key, value in pairs(spec.fields) do %> - - <% end %> - - -
-<% end %> -
- -
- <% if unlisted then %> - - <% end %> - - <% if iam then %> - - - <% else %> - - - <% end %> -
-<% if comments and #comments == 0 then %> -

No comments yet

-<% else %> -
- <% for _,comment in pairs(comments) do %> -
- <% if comment.isanon then %> -

Anonymous

- <% else %> -

<%= comment.author %>

- <% end %> -

<%= comment.text %>

-
- <% end %> -
-<% end %> -<{system cat src/pages/parts/footer.etlua}> diff --git a/src/pages/search.etlua.in b/src/pages/search.etlua.in deleted file mode 100644 index 6677f5f..0000000 --- a/src/pages/search.etlua.in +++ /dev/null @@ -1,19 +0,0 @@ -<{system cat src/pages/parts/header.etlua}> -

- <%= domain %>/ -

-
- <{system cat src/pages/parts/search.etlua}> -
-
- <% if #results == 0 then %> - No stories matched your search. - <% else %> - - <% for k,story in pairs(results) do %> - <{system cat src/pages/parts/story_breif.etlua}> - <% end %> -
- <% end %> -
-<{system cat src/pages/parts/footer.etlua}> diff --git a/src/pages/search_sql.etlua.in b/src/pages/search_sql.etlua.in deleted file mode 100644 index 9297186..0000000 --- a/src/pages/search_sql.etlua.in +++ /dev/null @@ -1,56 +0,0 @@ -SELECT - posts.id, - posts.post_title, - posts.isanon, - authors.name, - posts.post_time, - posts.views, - COUNT(comments.id) -FROM - posts,authors -LEFT JOIN comments ON comments.postid = posts.id -WHERE - authors.id = posts.authorid - AND posts.unlisted = 0 -<% for field, values in pairs(result) do -%> - <% for _,value in pairs(values) do -%> - <% local pn,expr,value = unpack(value) -%> - <% local n = (pn == "+" and "" or "NOT") -%> - <% if field == "title" then -%> - AND <%= n %> posts.post_title LIKE ? - <% elseif field == "author" then -%> - AND <%= n %> authors.name LIKE ? - <% elseif field == "date" then -%> - AND <%= n %> posts.post_time <%- expr %> ? - <% elseif field == "hits" then -%> - AND posts.views <%- expr -%> ? - <% end -%> - <% end -%> -<% end -%> -GROUP BY - posts.id -<% for _,tag in pairs(result.tags) do -%> -INTERSECT -SELECT - posts.id, - posts.post_title, - posts.isanon, - authors.name, - posts.post_time, - posts.views, - COUNT(comments.id) -FROM - posts,authors,tags -LEFT JOIN comments ON comments.postid = posts.id -WHERE - posts.authorid = authors.id - AND tags.postid = posts.id - <% local n,v,t = unpack(tag) -%> - <% n = (n == "-" and "NOT" or "") -%> - AND <%= n %> tags.tag = ? -GROUP BY - posts.id -<% end -%> -ORDER BY - posts.post_time DESC -; diff --git a/src/smr.c b/src/smr.c index 26c046a..824049e 100644 --- a/src/smr.c +++ b/src/smr.c @@ -33,7 +33,9 @@ int delete(struct http_request *); int do_lua(struct http_request *req, const char *name); int errhandeler(lua_State *); lua_State *L; -/* These should be defined in in kore somewhere and included here */ +/* +These should be defined in in kore somewhere and included here +*/ void kore_worker_configure(void); void kore_worker_teardown(void); /* @@ -111,7 +113,8 @@ do_lua(struct http_request *req, const char *name){ return do_lua(req,#lua_method);\ } -/*** +/* md +@name http/_paste Called at the endpoint /_paste. This method doesn't need any parameters for GET requests. This method expects the following for POST requests: @@ -121,10 +124,14 @@ This method expects the following for POST requests: In addition to the normal assets, this page includes suggest_tags.js, which suggests tags that have been submitted to the site before. -@function _G.paste @custom http_method GET POST +*/ +/* md +@name lua/paste +This function is called automatically with the request submitted at +/_paste @param http_request req The request to service -***/ +*/ route(post_story,"paste"); /*** @@ -180,10 +187,14 @@ route(read_story, "read"); /*** Called at the endpoint /_login -This method does not requirei any parameters for GET requests. +This method does not require any parameters for GET requests. This method requiries the following for POST requests: * user :: [a-z0-9]{1,30} - The username to log in as * pass :: any - The passfile for this user +To overload login functionality in an addon, see @{api.authenticate} +@function _G.login +@custom http_method GET POST +@param http_request req The request to service. ***/ int login(struct http_request *req){ diff --git a/src/sql/create_index_tags.sql b/src/sql/create_index_tags.sql index 04a71bd..ebdd999 100644 --- a/src/sql/create_index_tags.sql +++ b/src/sql/create_index_tags.sql @@ -1 +1,4 @@ +/* +The tags table is indexed on tag, so that search is fast +*/ CREATE INDEX tag_index ON tags(tag); diff --git a/src/sql/create_table_authors.sql b/src/sql/create_table_authors.sql index 3663b03..24fe2eb 100644 --- a/src/sql/create_table_authors.sql +++ b/src/sql/create_table_authors.sql @@ -1,3 +1,11 @@ +/* md +@name sql/table/authors +If/when an author deletes their account, all posts +and comments by that author are also deleted (on +delete cascade) this is intentional. This also +means that all comments by other users on a post +an author makes will also be deleted. +*/ CREATE TABLE IF NOT EXISTS authors ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT UNIQUE ON CONFLICT FAIL, diff --git a/src/sql/create_table_comments.sql b/src/sql/create_table_comments.sql index 0a906a9..565bbdd 100644 --- a/src/sql/create_table_comments.sql +++ b/src/sql/create_table_comments.sql @@ -1,3 +1,9 @@ +/* +Comments on a post. + +When an author deletes their account or the posts this comment +is posted on is deleted, this comment will also be deleted. +*/ CREATE TABLE IF NOT EXISTS comments ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, postid REFERENCES posts(id) ON DELETE CASCADE, diff --git a/src/sql/create_table_images.sql b/src/sql/create_table_images.sql index fa06a8f..c72b5ba 100644 --- a/src/sql/create_table_images.sql +++ b/src/sql/create_table_images.sql @@ -1,3 +1,6 @@ +/* +We may want to store images one day. This is unused for now +*/ CREATE TABLE IF NOT EXISTS images ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name TEXT, diff --git a/src/sql/create_table_session.sql b/src/sql/create_table_session.sql index e4f4c8c..d956250 100644 --- a/src/sql/create_table_session.sql +++ b/src/sql/create_table_session.sql @@ -1,8 +1,10 @@ /* Store a cookie for logged in users. Logged in users can edit -their own posts. +their own posts, edit their biographies, and post stories and comment under their own name. +TODO: WE can hash the "key" so that even if the database gets +dumped, a hacker can't cookie-steal with only read access +to the db. */ - CREATE TABLE IF NOT EXISTS sessions ( key TEXT PRIMARY KEY, author REFERENCES authors(id) ON DELETE CASCADE, diff --git a/src/sql/create_table_tags.sql b/src/sql/create_table_tags.sql index bf35a67..5066343 100644 --- a/src/sql/create_table_tags.sql +++ b/src/sql/create_table_tags.sql @@ -1,3 +1,7 @@ +/* +Tags on a post +A post's tags are deleted if the post is deleted. +*/ CREATE TABLE IF NOT EXISTS tags ( id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, postid REFERENCES posts(id) ON DELETE CASCADE,