quotesite/src/quotesite.nim

259 lines
6.6 KiB
Nim
Raw Normal View History

2016-02-06 02:07:11 +00:00
import asyncdispatch, db_sqlite, jester, moustachu, os,
2016-02-06 02:43:30 +00:00
shorturl, strutils, tables, times, typetraits
2016-02-05 18:40:26 +00:00
const
baseTemplate*: string = staticRead "./templates/layout.mustache"
2016-02-06 05:02:12 +00:00
browseTemplate*: string = staticRead "./templates/browse.mustache"
2016-02-06 02:07:11 +00:00
errorTemplate*: string = staticRead "./templates/error.mustache"
2016-02-06 05:12:20 +00:00
channelTemplate*: string = staticRead "./templates/channel.mustache"
2016-02-06 08:01:23 +00:00
submitTemplate*: string = staticRead "./templates/submit.mustache"
testTemplate*: string = """<p>hi!</p>"""
2016-02-06 02:07:11 +00:00
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,
2016-02-06 08:30:37 +00:00
deleted INTEGER DEFAULT 0);""")
db.exec(sql"""create table if not exists votes (
id integer primary key,
quoteid integer,
2016-02-06 08:38:31 +00:00
ip TEXT,
UNIQUE (quoteid, ip));""")
2016-02-06 08:30:37 +00:00
try:
db.exec(sql"""ALTER TABLE quotes
ADD votes INTEGER DEFAULT 0 NOT NULL""")
except: discard
2016-02-06 02:07:11 +00:00
except:
echo getCurrentExceptionMsg()
raise
2016-02-06 08:01:23 +00:00
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)
2016-02-05 18:40:26 +00:00
2016-02-06 02:07:11 +00:00
template fail*(): expr =
var ctx = newContext()
ctx["exception"] = getCurrentExceptionMsg()
2016-02-06 02:43:30 +00:00
var
layoutCtx = moustachu.newContext()
2016-02-06 05:02:12 +00:00
layoutCtx["title"] = "fail"
layoutCtx["body"] = render(errorTemplate, ctx)
2016-02-06 02:43:30 +00:00
halt render(baseTemplate, layoutCtx)
2016-02-06 02:07:11 +00:00
2016-02-06 05:02:12 +00:00
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])
2016-02-06 08:30:37 +00:00
ctx["score"] = quote[7]
2016-02-06 05:02:12 +00:00
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
2016-02-05 18:40:26 +00:00
settings:
2016-02-06 05:33:47 +00:00
port = getEnv("PORT").parseInt().Port
2016-02-05 18:40:26 +00:00
bindAddr = "0.0.0.0"
routes:
2016-02-06 02:07:11 +00:00
get "/test":
renderMustache("test", testTemplate, newContext())
2016-02-05 18:40:26 +00:00
2016-02-06 02:07:11 +00:00
get "/":
2016-02-06 05:02:12 +00:00
redirect "/browse/0"
2016-02-06 02:07:11 +00:00
2016-02-06 05:02:12 +00:00
get "/channel":
try:
var
channels = db.getAllRows(sql"select channel from quotes group by channel")
ctx: Context = newContext()
2016-02-06 05:12:20 +00:00
channelList = newSeq[Context]()
2016-02-06 02:07:11 +00:00
2016-02-06 05:12:20 +00:00
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)
2016-02-06 05:02:12 +00:00
except:
fail()
2016-02-06 02:07:11 +00:00
2016-02-06 09:37:47 +00:00
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()
2016-02-06 08:01:23 +00:00
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()
2016-02-06 08:30:37 +00:00
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()
2016-02-06 08:50:30 +00:00
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()
2016-02-06 08:45:37 +00:00
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()
2016-02-06 05:02:12 +00:00
get "/@kind/@id":
2016-02-06 02:07:11 +00:00
try:
2016-02-06 09:01:15 +00:00
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-", "#")
2016-02-06 02:43:30 +00:00
2016-02-06 22:46:41 +00:00
rows = db.getAllRows(sql"SELECT * FROM quotes WHERE channel=? and deleted=0 ORDER BY time desc LIMIT 20 OFFSET ?", channel, qid * 20)
2016-02-06 05:02:12 +00:00
2016-02-06 09:01:15 +00:00
title = channel & " page " & @"id"
else:
halt Http404, "not found"
2016-02-06 02:43:30 +00:00
2016-02-06 09:01:15 +00:00
ctx = newContext()
var quoteSeq = newSeq[Context]()
2016-02-06 02:43:30 +00:00
2016-02-06 09:01:15 +00:00
for row in items(rows):
var c = newContext()
c.contextQuote row[0].parseInt(), row
quoteSeq.add c
2016-02-06 05:02:12 +00:00
2016-02-06 09:01:15 +00:00
ctx["quotes"] = quoteSeq
ctx.pagination(qid, @"kind")
2016-02-06 02:07:11 +00:00
2016-02-06 09:01:15 +00:00
renderMustache(title, browseTemplate, ctx)
except: fail()
2016-02-06 02:07:11 +00:00
2016-02-05 18:40:26 +00:00
runForever()