| 
0
 | 
     1 local Luan = require "luan:Luan.luan"
 | 
| 
 | 
     2 local error = Luan.error
 | 
| 
 | 
     3 local ipairs = Luan.ipairs or error()
 | 
| 
 | 
     4 local pairs = Luan.pairs or error()
 | 
| 
 | 
     5 local type = Luan.type or error()
 | 
| 
 | 
     6 local set_metatable = Luan.set_metatable or error()
 | 
| 
 | 
     7 local stringify = Luan.stringify or error()
 | 
| 
 | 
     8 local parse = Luan.parse or error()
 | 
| 
 | 
     9 local range = Luan.range or error()
 | 
| 
 | 
    10 local String = require "luan:String.luan"
 | 
| 
 | 
    11 local split = String.split or error()
 | 
| 
 | 
    12 local trim = String.trim or error()
 | 
| 
 | 
    13 local sub_string = String.sub or error()
 | 
| 
 | 
    14 local Table = require "luan:Table.luan"
 | 
| 
 | 
    15 local concat = Table.concat or error()
 | 
| 
 | 
    16 local Math = require "luan:Math.luan"
 | 
| 
 | 
    17 local random = Math.random or error()
 | 
| 
 | 
    18 local Binary = require "luan:Binary.luan"
 | 
| 
 | 
    19 local binary_base64_decode = Binary.base64_decode or error()
 | 
| 
 | 
    20 local binary_to_string = Binary.to_string or error()
 | 
| 
 | 
    21 local Io = require "luan:Io.luan"
 | 
| 
 | 
    22 local uri = Io.uri or error()
 | 
| 
 | 
    23 local Http = require "luan:http/Http.luan"
 | 
| 
 | 
    24 local Logging = require "luan:logging/Logging.luan"
 | 
| 
 | 
    25 local logger = Logging.logger "Shared"
 | 
| 
 | 
    26 
 | 
| 
 | 
    27 
 | 
| 
 | 
    28 local Shared = {}
 | 
| 
 | 
    29 
 | 
| 
 | 
    30 function Shared.head()
 | 
| 
 | 
    31 %>
 | 
| 
 | 
    32 		<meta name="viewport" content="width=device-width, initial-scale=1">
 | 
| 
 | 
    33 		<style>
 | 
| 
 | 
    34 			@import "/site.css";
 | 
| 
 | 
    35 		</style>
 | 
| 
 | 
    36 <%
 | 
| 
 | 
    37 end
 | 
| 
 | 
    38 
 | 
| 
 | 
    39 local function header(crumbs)
 | 
| 
 | 
    40 %>
 | 
| 
 | 
    41 		<div header>
 | 
| 
 | 
    42 			<a href="/">Mercurial Hosting</a>
 | 
| 
 | 
    43 <%	for _, crumb in ipairs(crumbs or {}) do %>
 | 
| 
 | 
    44 			/ <%=crumb%>
 | 
| 
 | 
    45 <%	end %>
 | 
| 
 | 
    46 		</div>
 | 
| 
 | 
    47 <%
 | 
| 
 | 
    48 end
 | 
| 
 | 
    49 Shared.header = header
 | 
| 
 | 
    50 
 | 
| 
 | 
    51 function Shared.admin_header()
 | 
| 
 | 
    52 	header{
 | 
| 
 | 
    53 		[[<a href="/admin/">Your Repositories</a>]]
 | 
| 
 | 
    54 	}
 | 
| 
 | 
    55 end
 | 
| 
 | 
    56 
 | 
| 
 | 
    57 function Shared.private_header()
 | 
| 
 | 
    58 	header{
 | 
| 
 | 
    59 		[[<a href="/private/">private</a>]]
 | 
| 
 | 
    60 		[[<a href="/private/tools/">tools</a>]]
 | 
| 
 | 
    61 	}
 | 
| 
 | 
    62 end
 | 
| 
 | 
    63 
 | 
| 
 | 
    64 Shared.admin_return = [[<p>Return to <a href="/admin/">Your Repositories</a></p>]]
 | 
| 
 | 
    65 
 | 
| 
 | 
    66 local function base64_decode(s)
 | 
| 
 | 
    67 	return binary_to_string(binary_base64_decode(s))
 | 
| 
 | 
    68 end
 | 
| 
 | 
    69 
 | 
| 
 | 
    70 function Shared.get_user()
 | 
| 
 | 
    71 	local s = Http.request.headers["Authorization"] or error "not authorized"
 | 
| 
 | 
    72 	local tp, auth = split(s," ")
 | 
| 
 | 
    73 	tp=="Basic" or error("invalid auth type: "..tp)
 | 
| 
 | 
    74 	s = base64_decode(auth)
 | 
| 
 | 
    75 	s = split(s,":")
 | 
| 
 | 
    76 	return s
 | 
| 
 | 
    77 end
 | 
| 
 | 
    78 
 | 
| 
 | 
    79 local function deep_copy(tbl)
 | 
| 
 | 
    80 	local t = {}
 | 
| 
 | 
    81 	for k,v in pairs(tbl) do
 | 
| 
 | 
    82 		if type(v)=="table" then
 | 
| 
 | 
    83 			v = deep_copy(v)
 | 
| 
 | 
    84 		end
 | 
| 
 | 
    85 		t[k] = v
 | 
| 
 | 
    86 	end
 | 
| 
 | 
    87 	return t
 | 
| 
 | 
    88 end
 | 
| 
 | 
    89 
 | 
| 
 | 
    90 local set_mt = {}
 | 
| 
 | 
    91 function set_mt.__index(table,key)
 | 
| 
 | 
    92 	return false
 | 
| 
 | 
    93 end
 | 
| 
 | 
    94 
 | 
| 
 | 
    95 local function list_to_set(list)
 | 
| 
 | 
    96 	local set = {}
 | 
| 
 | 
    97 	for _, v in ipairs(list) do
 | 
| 
 | 
    98 		set[v] = true
 | 
| 
 | 
    99 	end
 | 
| 
 | 
   100 	set_metatable(set,set_mt)
 | 
| 
 | 
   101 	return set
 | 
| 
 | 
   102 end
 | 
| 
 | 
   103 Shared.list_to_set = list_to_set
 | 
| 
 | 
   104 
 | 
| 
 | 
   105 function Shared.text_to_list(text)
 | 
| 
 | 
   106 	local list = {}
 | 
| 
 | 
   107 	for line in Io.schemes.string(text).read_lines() do
 | 
| 
 | 
   108 		line = trim(line)
 | 
| 
 | 
   109 		if line ~= "" then
 | 
| 
 | 
   110 			list[#list+1] = line
 | 
| 
 | 
   111 		end
 | 
| 
 | 
   112 	end
 | 
| 
 | 
   113 	return list
 | 
| 
 | 
   114 end
 | 
| 
 | 
   115 
 | 
| 
 | 
   116 local password_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
 | 
| 
 | 
   117 do
 | 
| 
 | 
   118 	local t = {}
 | 
| 
 | 
   119 	for i in range(1,#password_chars) do
 | 
| 
 | 
   120 		t[#t+1] = sub_string(password_chars,i,i)
 | 
| 
 | 
   121 	end
 | 
| 
 | 
   122 	password_chars = t
 | 
| 
 | 
   123 end	
 | 
| 
 | 
   124 
 | 
| 
 | 
   125 function Shared.new_password()
 | 
| 
 | 
   126 	local n = #password_chars
 | 
| 
 | 
   127 	local t = {}
 | 
| 
 | 
   128 	for _ in range(1,10) do
 | 
| 
 | 
   129 		t[#t+1] = password_chars[random(n)]
 | 
| 
 | 
   130 	end
 | 
| 
 | 
   131 	return concat(t)
 | 
| 
 | 
   132 end
 | 
| 
 | 
   133 
 | 
| 
 | 
   134 local raw_config = parse( uri("file:config/config.luano").read_text() )
 | 
| 
 | 
   135 local config = deep_copy(raw_config)
 | 
| 
 | 
   136 for name, repo in pairs(config.repos) do
 | 
| 
 | 
   137 	repo.name = name
 | 
| 
 | 
   138 	repo.users = list_to_set(repo.users)
 | 
| 
 | 
   139 	repo.admins = list_to_set(repo.admins)
 | 
| 
 | 
   140 end
 | 
| 
 | 
   141 config.private = config.private and list_to_set(config.private)
 | 
| 
 | 
   142 Shared.config = config
 | 
| 
 | 
   143 
 | 
| 
4
 | 
   144 local user_repos = {}
 | 
| 
 | 
   145 for _, repo in pairs(config.repos) do
 | 
| 
 | 
   146 	for user in pairs(repo.users) do
 | 
| 
 | 
   147 		local repos = user_repos[user]
 | 
| 
 | 
   148 		if repos == nil then
 | 
| 
 | 
   149 			repos = {}
 | 
| 
 | 
   150 			user_repos[user] = repos
 | 
| 
 | 
   151 		end
 | 
| 
 | 
   152 		repos[#repos+1] = repo
 | 
| 
 | 
   153 	end
 | 
| 
 | 
   154 end
 | 
| 
 | 
   155 Shared.user_repos = user_repos
 | 
| 
 | 
   156 
 | 
| 
0
 | 
   157 function Shared.get_raw_config()
 | 
| 
 | 
   158 	return deep_copy(raw_config)
 | 
| 
 | 
   159 end
 | 
| 
 | 
   160 
 | 
| 
 | 
   161 local function reload_nginx()
 | 
| 
 | 
   162 	local cmd = [[sudo $(which nginx) -s reload]]
 | 
| 
 | 
   163 	local s = uri("bash:"..cmd).read_text()
 | 
| 
 | 
   164 	logger.info("reload_nginx "..s)
 | 
| 
 | 
   165 end
 | 
| 
 | 
   166 
 | 
| 
 | 
   167 function Shared.save_raw_config(raw_config)
 | 
| 
 | 
   168 	local config_file = uri("file:config/config.luano")
 | 
| 
 | 
   169 	local config_old = uri("file:config/config.luano.old")
 | 
| 
 | 
   170 	config_old.delete()
 | 
| 
 | 
   171 	config_file.move_to(config_old)
 | 
| 
 | 
   172 	config_file.write_text(stringify(raw_config).."\n")
 | 
| 
 | 
   173 	Http.reset_luan()
 | 
| 
 | 
   174 	Luan.do_file "update_repositories.luan"
 | 
| 
 | 
   175 	reload_nginx()
 | 
| 
 | 
   176 end
 | 
| 
 | 
   177 
 | 
| 
 | 
   178 return Shared
 |