local git = require "git"
local scan = require "scan"
local hash = require "hash"
local lud = require "ludweb"
local function time_fmt(sig)
local s = os.date("%Y-%m-%d %H:%M:%S", sig.time_)
local offset = sig.offset
local sign
if offset < 0 then
offset = -offset
sign = "-"
else
sign = "+"
end
local hours = math.floor(offset / 60)
local mins = offset % 60
s = s .. (" (UTC%s%02d:%02d)"):format(sign, hours, mins)
return s
end
local function diff_cb(line_type, line)
line = lud.template.escape(line:sub(1, #line-1))
if line_type == " " then
line = ' <span class="diff_ctx">' .. line .. '</span>'
elseif line_type == "+" then
line = '+<span class="diff_add">' .. line .. '</span>'
elseif line_type == "-" then
line = '-<span class="diff_del">' .. line .. '</span>'
elseif line_type == "=" then
line = ' <span class="diff_nonl diff_ctx_nonl">' .. line .. '</span>'
elseif line_type == ">" then
line = ' <span class="diff_nonl diff_old_nonl">' .. line .. '</span>'
elseif line_type == "<" then
line = ' <span class="diff_nonl diff_new_nonl">' .. line .. '</span>'
elseif line_type == "F" then
line = '\n<span class="diff_file_hdr">' .. line .. '</span>'
elseif line_type == "H" then
line = '<span class="diff_hunk_hdr">' .. line .. '</span>'
elseif line_type == "B" then
line = '<span class="diff_bin">' .. line .. '</span>'
else
line = line_type .. line
end
return line .. "\n"
end
local function allowed(user, gname)
if gname == "public" then
return true
elseif user == nil then
return false
end
for _, ok in ipairs(user.groups) do
if gname == ok then
return true
end
end
return false
end
local LOG_ERROR, LOG_WARN, LOG_INFO, LOG_DEBUG = 0, 1, 2, 3
local path = arg[1]
local conf = dofile(path.."/conf.lua")
git.init()
local groups = scan.scanrepos(path)
local sessions = {}
local function get_user(cookies)
local session_id = cookies["sid"]
if session_id == nil then
return nil
end
local uname = sessions[session_id]
if uname == nil then
return nil
end
return conf.users[uname]
end
local routes = {
{"GET", "/?",
function (req)
local user = get_user(req.cookies)
local gnames = {}
for gname in pairs(groups) do
if allowed(user, gname) then
table.insert(gnames, gname)
end
end
return lud.template.render_file("view/home.html", {user=user, gnames=gnames})
end},
{"GET", "/login",
function (req)
return lud.template.render_file("view/login.html", {})
end},
{"POST", "/login",
function (req)
local uname = req.form.username
local pass = req.form.password
local user = conf.users[uname]
local salt, h
if user == nil then
-- hash something as if we're trying to login anyway
salt = hash.get_salt()
h = hash.hash_pass(pass, salt)
conf.log(LOG_INFO, "invalid username")
else
salt = lud.crypt.b64_dec(user.salt)
h = hash.hash_pass(pass, salt)
if h == lud.crypt.b64_dec(user.hash) then
local session_id = lud.crypt.b64_enc(lud.crypt.uuid4())
sessions[session_id] = uname
conf.log(LOG_INFO, "logged in as "..uname)
local cookie = {key="sid", val=session_id, path="/", age=2*60*60}
return "/", 303, "See Other", {cookie}
else
conf.log(LOG_INFO, "invalid password")
end
end
return "/login", 303
end},
{"GET", "/logout",
function (req)
local session_id = req.cookies["sid"]
if session_id ~= nil then
sessions[session_id] = nil
end
return "/", 303
end},
{"GET", "/group/([%w_-]+)",
function (req, gname)
local user = get_user(req.cookies)
if not allowed(user, gname) then
return "/login", 303
end
local rnames = {}
for rname in pairs(groups[gname]) do
table.insert(rnames, rname)
end
local env = {user=user, gname=gname, rnames=rnames}
return lud.template.render_file("view/group.html", env)
end},
{"GET", "/group/([%w_-]+)/repo/([%w_-]+)",
function (req, gname, rname)
local user = get_user(req.cookies)
if not allowed(user, gname) then
return "/login", 303
end
local repo = groups[gname][rname]
local bnames = repo:branches()
local tnames = repo:tags()
local env = {
user=user, repo=repo, gname=gname,
rname=rname, bnames=bnames, tnames=tnames,
}
return lud.template.render_file("view/repo.html", env)
end},
{"GET", "/group/([%w_-]+)/repo/([%w_-]+)/history/([%w_-]+)",
function (req, gname, rname, first)
local user = get_user(req.cookies)
if not allowed(user, gname) then
return "/login", 303
end
local repo = groups[gname][rname]
local commit = repo:commit(first)
local prev = repo:find_prev(commit:id(), conf.limit)
local env = {
user=user, gname=gname, rname=rname, bname=bname,
commit=commit, limit=conf.limit, prev=prev, first=first,
}
return lud.template.render_file("view/history.html", env)
end},
{"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)",
function (req, gname, rname, cid)
local user = get_user(req.cookies)
if not allowed(user, gname) then
return "/login", 303
end
local repo = groups[gname][rname]
local commit = repo:commit(cid)
local prev = repo:find_prev(commit:id(), 1)
local sig = commit:signature()
local time_str = time_fmt(sig)
local diff = repo:diff(commit, diff_cb)
local env = {
user=user, gname=gname, rname=rname, bname=bname, commit=commit,
time_str=time_str, sig=sig, cid=cid, prev=prev, diff=diff,
}
return lud.template.render_file("view/commit.html", env)
end},
{"GET", "/group/([%w_-]+)/repo/([%w_-]+)/commit/([%w_-]+)/tree/(.*)",
function (req, gname, rname, cid, path)
local user = get_user(req.cookies)
if not allowed(user, gname) then
return "/login", 303
end
local repo = groups[gname][rname]
local commit = repo:commit(cid)
local node = commit:tree_entry(path)
if node == nil then
return "File not found", 404, "Not found"
end
local parts = {}
for part in path:gmatch("[^/]+") do
table.insert(parts, part)
end
local base = req.path
if base:sub(#base) ~= "/" then
base = base .. "/"
end
local env = {
user=user, gname=gname, rname=rname, cid=cid,
path=path, base=base, parts=parts, node=node,
}
if node.type_ == "dir" then
return lud.template.render_file("view/dir.html", env)
elseif node.type_ == "file" then
return lud.template.render_file("view/file.html", env)
end
end},
}
local app = lud.app.new_app(routes)
app:run(8080)
git.shutdown()