259 lines
6.6 KiB
Nim
259 lines
6.6 KiB
Nim
import asyncdispatch, db_sqlite, jester, moustachu, os,
|
|
shorturl, strutils, tables, times, typetraits
|
|
|
|
const
|
|
baseTemplate*: string = staticRead "./templates/layout.mustache"
|
|
browseTemplate*: string = staticRead "./templates/browse.mustache"
|
|
errorTemplate*: string = staticRead "./templates/error.mustache"
|
|
channelTemplate*: string = staticRead "./templates/channel.mustache"
|
|
submitTemplate*: string = staticRead "./templates/submit.mustache"
|
|
|
|
testTemplate*: string = """<p>hi!</p>"""
|
|
|
|
let
|
|
db = open("data/quotes.db", nil, nil, nil)
|
|
|
|
try:
|
|
db.exec(sql"""create table if not exists quotes (
|
|
id INTEGER PRIMARY KEY,
|
|
channel TEXT,
|
|
adder TEXT,
|
|
nick TEXT,
|
|
message TEXT,
|
|
time REAL,
|
|
deleted INTEGER DEFAULT 0);""")
|
|
|
|
db.exec(sql"""create table if not exists votes (
|
|
id integer primary key,
|
|
quoteid integer,
|
|
ip TEXT,
|
|
UNIQUE (quoteid, ip));""")
|
|
|
|
try:
|
|
db.exec(sql"""ALTER TABLE quotes
|
|
ADD votes INTEGER DEFAULT 0 NOT NULL""")
|
|
except: discard
|
|
|
|
except:
|
|
echo getCurrentExceptionMsg()
|
|
raise
|
|
|
|
proc validItem(s: string, channel: bool = false) =
|
|
if channel:
|
|
if s.startsWith "#":
|
|
echo "fuck"
|
|
else:
|
|
raise newException(ValueError, "invalid layout of channel name")
|
|
else: discard
|
|
|
|
if s.contains " ":
|
|
raise newException(ValueError, "invalid layout of quote")
|
|
else: discard
|
|
|
|
template renderMustache*(title: string, templ: string, ctx: Context): expr =
|
|
var
|
|
layoutCtx = moustachu.newContext()
|
|
|
|
layoutCtx["title"] = title
|
|
layoutCtx["body"] = render(templ, ctx)
|
|
|
|
resp render(baseTemplate, layoutCtx)
|
|
|
|
template fail*(): expr =
|
|
var ctx = newContext()
|
|
ctx["exception"] = getCurrentExceptionMsg()
|
|
|
|
var
|
|
layoutCtx = moustachu.newContext()
|
|
|
|
layoutCtx["title"] = "fail"
|
|
layoutCtx["body"] = render(errorTemplate, ctx)
|
|
|
|
halt render(baseTemplate, layoutCtx)
|
|
|
|
template contextQuote(ctx: Context, qid: int, quote: Row): expr =
|
|
ctx["listid"] = qid
|
|
ctx["id"] = quote[0].parseInt().encodeURLSimple()
|
|
ctx["channel"] = quote[1]
|
|
ctx["channelsafe"] = quote[1].replace("#", "hashtag-")
|
|
ctx["nick"] = quote[2]
|
|
ctx["adder"] = quote[3]
|
|
ctx["message"] = quote[4]
|
|
ctx["time"] = parseInt(split(quote[5], '.')[0])
|
|
ctx["score"] = quote[7]
|
|
|
|
template pagination(ctx: Context, qid: int, kind: string): expr =
|
|
ctx["paging"] = true
|
|
if qid != 0:
|
|
ctx["isnt1"] = true
|
|
ctx["prev"] = qid - 1
|
|
ctx["next"] = qid + 1
|
|
ctx["kind"] = kind
|
|
|
|
settings:
|
|
port = getEnv("PORT").parseInt().Port
|
|
bindAddr = "0.0.0.0"
|
|
|
|
routes:
|
|
get "/test":
|
|
renderMustache("test", testTemplate, newContext())
|
|
|
|
get "/":
|
|
redirect "/browse/0"
|
|
|
|
get "/channel":
|
|
try:
|
|
var
|
|
channels = db.getAllRows(sql"select channel from quotes group by channel")
|
|
ctx: Context = newContext()
|
|
channelList = newSeq[Context]()
|
|
|
|
for row in channels.items():
|
|
var c = newContext()
|
|
c["name"] = row[0]
|
|
c["safe"] = row[0].replace("#", "hashtag-")
|
|
channelList.add c
|
|
|
|
ctx["channels"] = channelList
|
|
|
|
renderMustache("channel list", channelTemplate, ctx)
|
|
except:
|
|
fail()
|
|
|
|
post "/seek":
|
|
try:
|
|
var
|
|
query = $(request.formData.mget "query").body
|
|
myquery = sql("select * from quotes where message like ?")
|
|
rows = db.getAllRows(myquery, query)
|
|
|
|
resp $rows
|
|
except: fail()
|
|
|
|
get "/submit":
|
|
renderMustache "submit", submitTemplate, newContext()
|
|
|
|
post "/submit":
|
|
try:
|
|
var
|
|
channel = $(request.formData.mget "channel").body
|
|
nick = $(request.formData.mget "nick").body
|
|
adder = $(request.formData.mget "adder").body
|
|
quote = $(request.formData.mget "quote").body
|
|
|
|
channel.validItem()
|
|
nick.validItem()
|
|
adder.validItem()
|
|
|
|
let
|
|
now = $(getTime().toSeconds())
|
|
data = now.split(".")[0].parseInt()
|
|
|
|
db.exec(sql"INSERT INTO quotes VALUES(null, ?, ?, ?, ?, ?, 0);", channel, nick, adder, quote, data)
|
|
|
|
redirect (channel.replace("#", "hashtag-") & "/0")
|
|
except:
|
|
fail()
|
|
|
|
get "/quotes/@id/vote":
|
|
try:
|
|
let
|
|
row = db.getRow(sql"SELECT * FROM quotes WHERE id=?", @"id")
|
|
|
|
if row[0] == "":
|
|
raise newException(ValueError, "no such quote")
|
|
|
|
let
|
|
qid = row[0].parseInt()
|
|
|
|
db.exec(sql"insert into votes values(null, ?, ?);", qid, request.headers.getOrDefault "X-Forwarded-For")
|
|
db.exec(sql"update quotes set votes = votes + 1 where id = ?", qid)
|
|
|
|
redirect "/quotes/" & row[0]
|
|
except: fail()
|
|
|
|
get "/random":
|
|
try:
|
|
var
|
|
rows = db.getAllRows(sql"SELECT * from quotes where deleted=0 order by random() LIMIT 20")
|
|
ctx = newContext()
|
|
quoteSeq = newSeq[Context]()
|
|
|
|
for row in items(rows):
|
|
var c = newContext()
|
|
c.contextQuote row[0].parseInt(), row
|
|
quoteSeq.add c
|
|
|
|
ctx["quotes"] = quoteSeq
|
|
|
|
renderMustache("random 20 quotes", browseTemplate, ctx)
|
|
except: fail()
|
|
|
|
get "/top":
|
|
try:
|
|
var
|
|
rows = db.getAllRows(sql"SELECT * from quotes where deleted=0 and votes > 0 order by votes desc LIMIT 20")
|
|
ctx = newContext()
|
|
quoteSeq = newSeq[Context]()
|
|
|
|
for row in items(rows):
|
|
var c = newContext()
|
|
c.contextQuote row[0].parseInt(), row
|
|
quoteSeq.add c
|
|
|
|
ctx["quotes"] = quoteSeq
|
|
|
|
renderMustache("top 20 quotes", browseTemplate, ctx)
|
|
|
|
except:
|
|
fail()
|
|
|
|
get "/@kind/@id":
|
|
try:
|
|
var
|
|
title: string = ""
|
|
ctx: Context
|
|
rows: seq[Row]
|
|
qid: int = 0
|
|
|
|
try:
|
|
qid = (@"id").parseInt
|
|
except:
|
|
qid = (@"id").decodeURLSimple()
|
|
redirect "/" & @"kind" & "/" & $qid
|
|
|
|
case @"kind":
|
|
of "browse":
|
|
rows = db.getAllRows(sql"SELECT * FROM quotes where deleted=0 ORDER BY time desc LIMIT 20 OFFSET ?", (qid * 20))
|
|
|
|
title = "page " & @"id"
|
|
of "quotes":
|
|
rows = db.getAllRows(sql"SELECT * FROM quotes WHERE id = ?", qid)
|
|
|
|
title = "#" & $qid & " by " & rows[0][2]
|
|
else:
|
|
if (@"kind").startsWith "hashtag-":
|
|
let channel = (@"kind").replace("hashtag-", "#")
|
|
|
|
rows = db.getAllRows(sql"SELECT * FROM quotes WHERE channel=? and deleted=0 ORDER BY time desc LIMIT 20 OFFSET ?", channel, qid * 20)
|
|
|
|
title = channel & " page " & @"id"
|
|
else:
|
|
halt Http404, "not found"
|
|
|
|
ctx = newContext()
|
|
var quoteSeq = newSeq[Context]()
|
|
|
|
for row in items(rows):
|
|
var c = newContext()
|
|
c.contextQuote row[0].parseInt(), row
|
|
quoteSeq.add c
|
|
|
|
ctx["quotes"] = quoteSeq
|
|
ctx.pagination(qid, @"kind")
|
|
|
|
renderMustache(title, browseTemplate, ctx)
|
|
except: fail()
|
|
|
|
runForever()
|