import asyncdispatch, db_sqlite, httpclient, jester, json, moustachu, os, shorturl, strutils, tables, templates/all, times, tweet/parse, twtxt, typetraits let db = open("data/twtxt.db", nil, nil, nil) try: db.exec sql"""create table if not exists users ( id INTEGER PRIMARY KEY, username TEXT UNIQUE, url TEXT UNIQUE );""" db.exec sql"""create table if not exists tweets ( id INTEGER PRIMARY KEY, username TEXT, time REAL, tweet TEXT, UNIQUE (username, time, tweet) );""" except: quit getCurrentExceptionMsg() 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 pagination(ctx: Context, isnt1: bool, prev, next: string): expr = ctx["paging"] = true ctx["isnt1"] = isnt1 ctx["prev"] = prev ctx["next"] = next settings: port = getEnv("PORT").parseInt().Port bindAddr = "0.0.0.0" routes: get "/": var ctx = newContext() let users = db.getValue sql"select count(*) from users" tweets = db.getValue sql"select count(*) from tweets" ctx["users"] = users ctx["tweets"] = tweets renderMustache("home", indexTemplate, ctx) get "/changelog": const log = staticExec "git log -10" var ctx = newContext() ctx["changelog"] = log renderMustache("changelog", changelogTemplate, ctx) get "/content/system": let myHeaders = { "Content-Type": "text/plain", } tweets = db.getAllRows(sql"select * from tweets where username='system'") var res = "" for tweet in tweets.items(): res &= $(tweet.fromDBRow()) & "\r\n" resp Http200, myHeaders, res get "/timeline": redirect "/timeline/0" get "/submit": renderMustache "submit", submitTemplate, newContext() post "/submit": try: var username = $(request.formData.mget "username").body url = $(request.formData.mget "url").body db.updateTweetsByUser(username, url) db.exec(sql"insert into users values (null, ?, ?)", username, url) db.exec(sql"insert into tweets values (null, 'twtxtlist', ?, ?)", $(getTime().getGMTime().timeinfoToTime().toSeconds()), "Added user @<" & username & " " & url & ">") redirect "/users/" & username & "/0" except: fail() get "/users": var users = db.getAllRows(sql"select username, url from users order by username") ctx = newContext() userList = newSeq[Context]() for user in users.items(): var c = newContext() c["username"] = user[0] c["url"] = user[1] userList.add c ctx["users"] = userList renderMustache "users", usersTemplate, ctx get "/users.txt": let headers = { "Content-Type": "text/plain" } users = db.getAllRows(sql"select username, url from users order by username") var outBuf = "" for user in users.items(): outBuf &= "twtxt follow " & user[0] & " " & user[1] & "\r\n" resp Http200, headers, outBuf get "/users/@name/@page": try: var tweets = db.getAllRows(sql"select * from tweets where username=? order by time desc limit 20 offset ?", @"name", (@"page").parseInt() * 20) ctx = newContext() tweetList = newSeq[Context]() if len(tweets) == 0: raise newException(ValueError, "no such page") var userctx = newContext() userurl = db.getValue(sql"select url from users where username=?", @"name") userctx["url"] = userurl userctx["username"] = @"name" ctx["user"] = userctx for tweet in tweets.items(): var c = newContext() let time = tweet[2].split(".")[0].parseInt() c["id"] = tweet[0] c["permalink"] = tweet[0].parseInt().encodeURLSimple() c["username"] = tweet[1] c["time"] = time c["tweet"] = $tweet[3].prettyfyTweet() tweetList.add c ctx["tweets"] = tweetList let id = (@"page").parseInt() if @"page" == "0": ctx.pagination(false, "", "/users/" & @"name" & "/" & $(id + 1)) else: ctx.pagination(true, "/users/" & @"name" & "/" & $(id - 1), "/users/" & @"name" & "/" & $(id + 1)) renderMustache(@"name" & " page " & @"page", tweetsTemplate, ctx) except: fail() get "/timeline/@page": try: var tweets = db.getAllRows(sql"select * from tweets where username <> 'twtxtlist' and username <> 'directory' order by time desc limit 20 offset ?", (@"page").parseInt() * 20) ctx = newContext() tweetList = newSeq[Context]() if tweets.len == 0: raise newException(ValueError, "no such page") for tweet in tweets.items(): var c = newContext() let time = tweet[2].split(".")[0].parseInt() c["id"] = tweet[0] c["permalink"] = tweet[0].parseInt().encodeURLSimple() c["username"] = tweet[1] c["time"] = time c["tweet"] = $tweet[3].prettyfyTweet() tweetList.add c ctx["tweets"] = tweetList let id = (@"page").parseInt() if @"page" == "0": ctx.pagination(false, "", "/timeline/" & $(id + 1)) else: ctx.pagination(true, "/timeline/" & $(id - 1), "/timeline/" & $(id + 1)) renderMustache("timeline page " & @"page", tweetsTemplate, ctx) except: fail() get "/tweet/@tid": try: var tweets = db.getAllRows(sql"select * from tweets where id=? limit 1", (@"tid").decodeURLSimple()) ctx = newContext() tweetList = newSeq[Context]() if tweets.len == 0: raise newException(ValueError, "no such tweet") for tweet in tweets.items(): var c = newContext() let time = tweet[2].split(".")[0].parseInt() c["id"] = tweet[0] c["permalink"] = tweet[0].parseInt().encodeURLSimple() c["username"] = tweet[1] c["time"] = time c["tweet"] = $tweet[3].prettyfyTweet() tweetList.add c ctx["tweets"] = tweetList renderMustache("tweet " & @"tid", tweetsTemplate, ctx) except: fail() runForever()