started working on unlisted posts
This commit is contained in:
parent
7d71b119c1
commit
85a730ebcb
|
@ -29,7 +29,8 @@ p,.tag-list{margin-bottom:0px}
|
|||
flex:10 10 auto;
|
||||
translate: -100%;
|
||||
}
|
||||
.column-0{margin-right:5px;}
|
||||
.column-0{margin-right:5px}
|
||||
.label-inline{margin:0.5rem}
|
||||
|
||||
@media (prefers-color-scheme: dark){
|
||||
body, input, select, textarea, pre, code{
|
||||
|
|
|
@ -25,6 +25,8 @@ validator v_storyid regex [a-zA-Z0-9$+!*'(),-]+
|
|||
validator v_subdomain regex [a-z0-9]{1,30}
|
||||
validator v_markup regex (plain|imageboard)
|
||||
validator v_bool regex (0|1)
|
||||
validator v_checkbox regex (|on)
|
||||
validator v_hex_128 regex [0-9a-f]{128}
|
||||
|
||||
domain * {
|
||||
attach tls
|
||||
|
@ -65,6 +67,7 @@ domain * {
|
|||
validate pasteas v_subdomain
|
||||
validate markup v_markup
|
||||
validate tags v_any
|
||||
validate unlisted v_checkbox
|
||||
}
|
||||
params post /_paste {
|
||||
validate title v_any
|
||||
|
@ -72,6 +75,7 @@ domain * {
|
|||
validate pasteas v_subdomain
|
||||
validate markup v_markup
|
||||
validate tags v_any
|
||||
validate unlisted v_checkbox
|
||||
}
|
||||
params post /_preview {
|
||||
validate title v_any
|
||||
|
@ -79,12 +83,14 @@ domain * {
|
|||
validate pasteas v_subdomain
|
||||
validate markup v_markup
|
||||
validate tags v_any
|
||||
validate unlisted v_checkbox
|
||||
}
|
||||
params get /_search {
|
||||
validate q v_any
|
||||
}
|
||||
params get ^/[^_].* {
|
||||
validate comments v_bool
|
||||
validate pwd v_hex_128
|
||||
}
|
||||
params post ^/[^_].* {
|
||||
validate text v_any
|
||||
|
|
|
@ -32,6 +32,7 @@ local function edit_post(req)
|
|||
local text = assert(http_argument_get_string(req,"text"))
|
||||
local pasteas = assert(http_argument_get_string(req,"pasteas"))
|
||||
local markup = assert(http_argument_get_string(req,"markup"))
|
||||
local unlisted = http_argument_get_string(req,"unlisted") == "on"
|
||||
local tags_str = http_argument_get_string(req,"tags")
|
||||
stmnt_author_of:bind_names{
|
||||
id = storyid
|
||||
|
@ -61,11 +62,14 @@ local function edit_post(req)
|
|||
assert(stmnt_update:bind_blob(2,compr) == sql.OK)
|
||||
assert(stmnt_update:bind(3,pasteas == "anonymous" and 1 or 0) == sql.OK)
|
||||
assert(stmnt_update:bind(4,storyid) == sql.OK)
|
||||
assert(stmnt_update:bind(5,unlisted) == sql.OK)
|
||||
assert(util.do_sql(stmnt_update) == sql.DONE, "Failed to update text")
|
||||
stmnt_update:reset()
|
||||
tagslib.set(storyid,tags)
|
||||
local id_enc = util.encode_id(storyid)
|
||||
local loc = string.format("https://%s/%s",config.domain,id_enc)
|
||||
--Turning something from not unlisted to unlisted should dirty all these
|
||||
--places anyway, so the post can now be hidden.
|
||||
cache.dirty(string.format("%s/%s",config.domain,id_enc)) -- This place to read this post
|
||||
cache.dirty(string.format("%s",config.domain)) -- The site index (ex, if the author changed the paste from their's to "Anonymous", the cache should reflect that).
|
||||
cache.dirty(string.format("%s.%s",author,config.domain)) -- The author's index, same reasoning as above.
|
||||
|
|
|
@ -14,11 +14,18 @@ local stmnt_raw,stmnt_paste
|
|||
|
||||
local oldconfigure = configure
|
||||
function configure(...)
|
||||
stmnt_paste = assert(db.conn:prepare(queries.insert_post))
|
||||
stmnt_raw = assert(db.conn:prepare(queries.insert_raw))
|
||||
stmnt_paste = assert(db.conn:prepare(queries.insert_post),db.conn:errmsg())
|
||||
stmnt_raw = assert(db.conn:prepare(queries.insert_raw),db.conn:errmsg())
|
||||
return oldconfigure(...)
|
||||
end
|
||||
|
||||
local function get_random_bytes(n)
|
||||
local f = assert(io.open("/dev/urandom","r"))
|
||||
local ret = assert(f:read(n))
|
||||
assert(f:close())
|
||||
return ret
|
||||
end
|
||||
|
||||
local function anon_paste(req,ps)
|
||||
--Public paste
|
||||
--[[
|
||||
|
@ -34,16 +41,24 @@ local function anon_paste(req,ps)
|
|||
--Don't store this information for now, until I come up
|
||||
--with a more elegent solution.
|
||||
|
||||
log(LOG_DEBUG,string.format("new story: %q, length: %d",ps.title,string.len(ps.text)))
|
||||
print("Unlisted:",ps.unlisted)
|
||||
local textsha3 = sha3(ps.text .. get_random_bytes(32))
|
||||
util.sqlbind(stmnt_paste,"bind_blob",1,ps.text)
|
||||
util.sqlbind(stmnt_paste,"bind",2,ps.title)
|
||||
util.sqlbind(stmnt_paste,"bind",3,-1)
|
||||
util.sqlbind(stmnt_paste,"bind",4,true)
|
||||
util.sqlbind(stmnt_paste,"bind_blob",5,"")
|
||||
util.sqlbind(stmnt_paste,"bind",6,ps.unlisted)
|
||||
util.sqlbind(stmnt_paste,"bind_blob",7,textsha3)
|
||||
err = util.do_sql(stmnt_paste)
|
||||
stmnt_paste:reset()
|
||||
if err == sql.DONE then
|
||||
local rowid = stmnt_paste:last_insert_rowid()
|
||||
local url = util.encode_id(rowid)
|
||||
if ps.unlisted then
|
||||
url = url .. "?pwd=" .. util.encode_unlisted(textsha3)
|
||||
end
|
||||
assert(stmnt_raw:bind(1,rowid) == sql.OK)
|
||||
assert(stmnt_raw:bind_blob(2,ps.raw) == sql.OK)
|
||||
assert(stmnt_raw:bind(3,ps.markup) == sql.OK)
|
||||
|
@ -61,10 +76,12 @@ local function anon_paste(req,ps)
|
|||
end
|
||||
tags.set(rowid,ps.tags)
|
||||
local loc = string.format("https://%s/%s",config.domain,url)
|
||||
if not ps.unlisted then
|
||||
cache.dirty(string.format("%s/%s",config.domain,url))
|
||||
cache.dirty(string.format("%s",config.domain))
|
||||
end
|
||||
http_response_header(req,"Location",loc)
|
||||
http_response(req,303,"")
|
||||
cache.dirty(string.format("%s/%s",config.domain,url))
|
||||
cache.dirty(string.format("%s",config.domain))
|
||||
return
|
||||
elseif err == sql.ERROR or err == sql.MISUSE then
|
||||
error("Failed to paste:" .. tostring(err))
|
||||
|
@ -93,6 +110,8 @@ local function author_paste(req,ps)
|
|||
assert(stmnt_paste:bind(3,authorid) == sql.OK)
|
||||
assert(stmnt_paste:bind(4,asanon == "anonymous") == sql.OK)
|
||||
assert(stmnt_paste:bind_blob(5,"") == sql.OK)
|
||||
util.sqlbind(stmnt_paste,"bind",6,ps.unlisted)
|
||||
util.sqlbind(stmnt_paste,"bind_blob",7,sha3(ps.text))
|
||||
err = util.do_sql(stmnt_paste)
|
||||
stmnt_paste:reset()
|
||||
if err == sql.DONE then
|
||||
|
@ -120,11 +139,13 @@ local function author_paste(req,ps)
|
|||
else
|
||||
loc = string.format("https://%s.%s/%s",author,config.domain,url)
|
||||
end
|
||||
if not ps.unlisted then
|
||||
cache.dirty(string.format("%s.%s",author,config.domain))
|
||||
cache.dirty(string.format("%s/%s",config.domain,url))
|
||||
cache.dirty(string.format("%s",config.domain))
|
||||
end
|
||||
http_response_header(req,"Location",loc)
|
||||
http_response(req,303,"")
|
||||
cache.dirty(string.format("%s.%s",author,config.domain))
|
||||
cache.dirty(string.format("%s/%s",config.domain,url))
|
||||
cache.dirty(string.format("%s",config.domain))
|
||||
return
|
||||
elseif err == sql.ERROR or err == sql.MISUSE then
|
||||
error("Failed to paste: " .. tostring(err) .. " : " .. db.conn:errmsg())
|
||||
|
@ -171,6 +192,8 @@ local function paste_post(req)
|
|||
--Always sanatize the title with the plain parser. no markup
|
||||
--in the title.
|
||||
ps.title = parsers.plain(title)
|
||||
local unlisted = http_argument_get_string(req,"unlisted")
|
||||
ps.unlisted = unlisted == "on" --might be nil
|
||||
if host == config.domain then
|
||||
anon_paste(req,ps)
|
||||
else
|
||||
|
|
|
@ -39,8 +39,10 @@ or nil if it wasn't
|
|||
local function populate_ps_story(req,ps)
|
||||
--Make sure our story exists
|
||||
stmnt_read:bind_names{
|
||||
id = ps.storyid
|
||||
id = ps.storyid,
|
||||
}
|
||||
print("populating, hash was:",ps.hash)
|
||||
stmnt_read:bind(2,ps.hash or "")
|
||||
local err = util.do_sql(stmnt_read)
|
||||
if err == sql.DONE then
|
||||
--We got no story
|
||||
|
@ -99,6 +101,7 @@ local function read_get(req)
|
|||
ps.storyid = util.decode_id(ps.idp)
|
||||
add_view(ps.storyid)
|
||||
|
||||
|
||||
--If we're logged in, set author and authorid
|
||||
local author, authorid = session.get(req)
|
||||
if author and authorid then
|
||||
|
@ -113,15 +116,34 @@ local function read_get(req)
|
|||
if ps.show_comments then
|
||||
ps.comments = get_comments(req,ps)
|
||||
end
|
||||
|
||||
--If this post is unlisted, get the hash
|
||||
local hashstr = http_argument_get_string(req,"pwd")
|
||||
print("hashstr was:",hashstr)
|
||||
if hashstr then
|
||||
ps.hash = util.decode_unlisted(hashstr)
|
||||
end
|
||||
|
||||
|
||||
local text
|
||||
--normal story display
|
||||
if (not ps.loggedauthor) then
|
||||
local cachestr = string.format("%s%s%s",
|
||||
local params = {}
|
||||
if ps.show_comments then
|
||||
table.insert(params,"comments=1")
|
||||
end
|
||||
if ps.hash then
|
||||
table.insert(params,"pwd=" .. hashstr)
|
||||
end
|
||||
local cachestrparts = {
|
||||
ps.host,
|
||||
ps.path,
|
||||
ps.show_comments and "?comments=1" or ""
|
||||
)
|
||||
}
|
||||
if #params > 0 then
|
||||
table.insert(cachestrparts,"?")
|
||||
table.insert(cachestrparts,table.concat(params,"&"))
|
||||
end
|
||||
local cachestr = table.concat(cachestrparts)
|
||||
text = cache.render(cachestr,function()
|
||||
log(LOG_DEBUG,"Cache miss, rendering story " .. cachestr)
|
||||
if not populate_ps_story(req,ps) then
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
|
||||
local sql = require("lsqlite3")
|
||||
|
||||
local util = {}
|
||||
|
||||
--[[
|
||||
|
@ -124,6 +123,26 @@ function util.decode_id(s)
|
|||
end
|
||||
end
|
||||
|
||||
--arbitary data to hex encoded string
|
||||
function util.encode_unlisted(str)
|
||||
local safe = {}
|
||||
for i = 1,#str do
|
||||
local byte = str:byte(i)
|
||||
table.insert(safe,string.format("%02x",byte))
|
||||
end
|
||||
return table.concat(safe)
|
||||
end
|
||||
|
||||
--hex encoded string to arbitrary data
|
||||
function util.decode_unlisted(str)
|
||||
print("str was:",str)
|
||||
local output = {}
|
||||
for byte in str:gmatch("%x%x") do
|
||||
table.insert(output, string.char(tonumber(byte,16)))
|
||||
end
|
||||
return table.concat(output)
|
||||
end
|
||||
|
||||
--[[
|
||||
Parses a semicolon seperated string into it's parts:
|
||||
1. seperates by semicolon
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
author_edit.etlua
|
||||
author_index.etlua
|
||||
author_paste.etlua
|
||||
cantedit.etlua
|
||||
claim.etlua
|
||||
edit.etlua
|
||||
index.etlua
|
||||
login.etlua
|
||||
noauthor.etlua
|
||||
nostory.etlua
|
||||
paste.etlua
|
||||
read.etlua
|
||||
search.etlua
|
||||
search_sql.etlua
|
|
@ -6,7 +6,7 @@
|
|||
<form action="https://<%= user %>.<%= domain %>/_paste" method="post" class="container">
|
||||
<fieldset>
|
||||
<div class="row">
|
||||
<input type="text" name="title" placeholder="Title" class="column column-80"></input>
|
||||
<input type="text" name="title" placeholder="Title" class="column column-70"></input>
|
||||
<select id="pasteas" name="pasteas" class="column column-10">
|
||||
<option value="<%= user %>"><%= user %></option>
|
||||
<option value="anonymous">Anonymous</option>
|
||||
|
@ -15,6 +15,10 @@
|
|||
<option value="plain">Plain</option>
|
||||
<option value="imageboard">Imageboard</option>
|
||||
</select>
|
||||
<div class="column column-10">
|
||||
<label for="unlisted" class="label-inline">Unlisted</label>
|
||||
<input type="checkbox" name="unlisted" id="unlisted"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="text" name="tags" placeholder="Tags (semicolon;seperated)" class="column"></input>
|
||||
|
|
|
@ -5,11 +5,15 @@
|
|||
<% if err then %><em class="error"><%= err %></em><% end %>
|
||||
<form action="https://<%= domain %>/_paste" method="post" class="container"><fieldset>
|
||||
<div class="row">
|
||||
<input type="text" name="title" placeholder="Title" class="column column-80"></input>
|
||||
<input type="text" name="title" placeholder="Title" class="column column-70"></input>
|
||||
<select id="markup" name="markup" class="column column-20">
|
||||
<option value="plain">Plain</option>
|
||||
<option value="imageboard">Imageboard</option>
|
||||
</select>
|
||||
<div class="column column-10">
|
||||
<label for="unlisted" class="label-inline">Unlisted</label>
|
||||
<input type="checkbox" name="unlisted" id="unlisted"></input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<input type="text" name="tags" placeholder="Tags (semicolon;seperated)" class="column"></input>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
CREATE INDEX unlisted_index ON posts(hash);
|
|
@ -6,6 +6,8 @@ means that all comments by other users on a post
|
|||
an author makes will also be deleted.
|
||||
|
||||
Post text uses zlib compression
|
||||
|
||||
Unlisted hashes are SHAv3 521
|
||||
*/
|
||||
CREATE TABLE IF NOT EXISTS posts (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
|
||||
|
@ -15,5 +17,7 @@ CREATE TABLE IF NOT EXISTS posts (
|
|||
isanon INTEGER,
|
||||
hashedip BLOB,
|
||||
post_time INTEGER,
|
||||
views INTEGER DEFAULT 0
|
||||
views INTEGER DEFAULT 0,
|
||||
unlisted INTEGER,
|
||||
hash BLOB
|
||||
);
|
||||
|
|
|
@ -4,6 +4,8 @@ INSERT INTO posts (
|
|||
authorid,
|
||||
isanon,
|
||||
hashedip,
|
||||
unlisted,
|
||||
hash,
|
||||
post_time
|
||||
) VALUES (
|
||||
?,
|
||||
|
@ -11,5 +13,7 @@ INSERT INTO posts (
|
|||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
?,
|
||||
strftime('%s','now')
|
||||
);
|
||||
|
|
|
@ -11,7 +11,7 @@ FROM
|
|||
WHERE
|
||||
posts.isanon = 0 AND
|
||||
posts.authorid = authors.id AND
|
||||
authors.name = :author
|
||||
authors.name = :author AND
|
||||
posts.unlisted = 0
|
||||
ORDER BY
|
||||
posts.post_time DESC
|
||||
LIMIT 10;
|
||||
|
|
|
@ -4,4 +4,5 @@ FROM
|
|||
raw_text, posts
|
||||
WHERE
|
||||
raw_text.id = posts.id AND
|
||||
raw_text.id = :postid
|
||||
raw_text.id = :postid AND
|
||||
posts.unlisted = 0
|
||||
|
|
|
@ -13,4 +13,9 @@ FROM
|
|||
posts,authors
|
||||
WHERE
|
||||
posts.authorid = authors.id AND
|
||||
posts.id = :id;
|
||||
posts.id = :id AND
|
||||
(
|
||||
posts.unlisted = 1 AND
|
||||
posts.hash = :hash
|
||||
) OR
|
||||
posts.unlisted = 0;
|
||||
|
|
|
@ -10,7 +10,8 @@ FROM
|
|||
posts,
|
||||
authors
|
||||
WHERE
|
||||
posts.authorid = authors.id
|
||||
posts.authorid = authors.id AND
|
||||
posts.unlisted = 0
|
||||
ORDER BY
|
||||
posts.post_time DESC
|
||||
LIMIT 10;
|
||||
LIMIT 50;
|
||||
|
|
|
@ -4,6 +4,7 @@ SET
|
|||
post_title = ?,
|
||||
post_text = ?,
|
||||
isanon = ?,
|
||||
unlisted = ?,
|
||||
post_time = strftime('%s','now')
|
||||
WHERE
|
||||
posts.id = ?;
|
||||
|
|
Loading…
Reference in New Issue