Index

resty-gitweb / 113602d

A git web interface for Lua/OpenResty (you're on it right now!)

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
1123 Jan 2021 10:52113602dMore code updatesJosh Stockin12914G

Blob @ resty-gitweb / app.lua

text/plain9097 bytesdownload raw
1-- resty-gitweb@app.lua
2-- Entry point for git HTTP site implementation
3
4-- Copyright (c) 2020 Joshua 'joshuas3' Stockin
5-- <https://git.joshstock.in/resty-gitweb>
6-- This software is licensed under the MIT License.
7
8local puremagic = require("puremagic")
9
10local utils = require("utils/utils")
11local git = require("git/git")
12local parse_uri = require("utils/parse_uri")
13
14local parsed_uri = parse_uri()
15local view
16local content
17
18-- TODO: Rewrite app script completely
19
20if parsed_uri.repo == nil then
21 content = require("pages/index")(CONFIG)
22else -- repo found
23 local repo
24 for _,r in pairs(CONFIG) do
25 if parsed_uri.repo == r.name then
26 repo = r
27 break
28 end
29 end
30 if repo then
31 repo.loc = repo.location.dev
32 local success, repo_libgit2_object = git.repo.open(repo.loc)
33 if not success then
34 error("Failed to open repository at "..repo.loc)
35 end
36 repo.obj = repo_libgit2_object
37 view = parsed_uri.parts[2] or "tree"
38 local branch
39
40 local res, status = pcall(function() -- if branch is real
41 branch = git.find_rev(repo.obj, parsed_uri.parts[3]) -- if parts[3] is nil, defaults to "HEAD"
42 end)
43 if res then
44 local res, status = pcall(function() -- effectively catch any errors, 404 if any
45 if view == "tree" then -- directory display (with automatic README rendering)
46 local path = parsed_uri.parts
47 table.remove(path, 3) -- branch
48 table.remove(path, 2) -- "tree"
49 table.remove(path, 1) -- repo
50 if #path > 0 then
51 path = table.concat(path, "/").."/"
52 else
53 path = ""
54 end
55
56 content = require("pages/tree")(repo, repo.loc, branch, path)
57 elseif view == "blob" then
58 local path = parsed_uri.parts
59 table.remove(path, 3) -- branch
60 table.remove(path, 2) -- "tree"
61 table.remove(path, 1) -- repo
62 if #path > 0 then
63 path = table.concat(path, "/")
64 else
65 path = ""
66 end
67
68 content = require("pages/blob")(repo, repo.loc, branch, path)
69 elseif view == "raw" then
70 local path = parsed_uri.parts
71 table.remove(path, 3) -- branch
72 table.remove(path, 2) -- "tree"
73 table.remove(path, 1) -- repo
74 if #path > 0 then
75 path = table.concat(path, "/")
76 else
77 path = ""
78 end
79
80 content, is_binary = require("pages/raw")(repo, repo.loc, branch, path)
81 if content then
82 if is_binary then
83 mimetype = puremagic.via_content(content.body, path)
84 content.type = mimetype
85 else
86 content.type = "text/plain"
87 end
88 end
89
90 elseif view == "log" then
91 content = require("pages/log")(repo, repo.loc, branch, ngx.var.arg_n, ngx.var.arg_skip)
92 elseif view == "refs" then
93 content = require("pages/refs")(repo, repo.loc, branch)
94 elseif view == "download" then
95 content = require("pages/download")(repo, repo.loc, branch)
96 elseif view == "commit" then
97 -- /repo/commit/[COMMIT HASH]
98 else
99 error("bad view "..view)
100 end
101 end) -- pcall
102
103 if res ~= true then
104 if not PRODUCTION then
105 ngx.say(res)
106 ngx.say(status)
107 end
108 ngx.exit(ngx.HTTP_NOT_FOUND)
109 return
110 end
111 elseif not PRODUCTION then -- branch doesn't exist, show an error in non-prod environments
112 ngx.say(res)
113 ngx.say(status)
114 ngx.exit(ngx.HTTP_NOT_FOUND)
115 end
116 git.repo.free(repo.obj)
117 end
118end
119
120if content ~= nil then -- TODO: HTML templates from files, static serving
121 if view ~= "raw" then
122 ngx.header.content_type = "text/html"
123 ngx.say([[<style>
124 @import url('https://fonts.googleapis.com/css?family=Fira+Sans:400,400i,700,700i&display=swap');
125 *{
126 box-sizing:border-box;
127 }
128 body{
129 color: #212121;
130 font-family:'Fira Sans', sans-serif;
131 padding-bottom:200px;
132 line-height:1.4;
133 max-width:1000px;
134 margin:20px auto;
135 }
136 body>h2{
137 margin-top:5px;
138 margin-bottom:0;
139 }
140 h3{
141 margin-bottom:4px;
142 }
143 td,th{
144 padding:2px 5px;
145 border:1px solid #858585;
146 text-align:left;
147 vertical-align:top;
148 }
149 th{
150 border:1px solid #000;
151 }
152 table.files,table.log,table.blob{
153 width:100%;
154 max-width:100%;
155 }
156 table{
157 border-collapse:collapse;
158 overflow:auto;
159 font-family: monospace;
160 font-size:14px;
161 }
162 table.files td:first-child{
163 padding-right:calc(5px + 1em);
164 }
165 table.files td:not(:nth-child(2)), table.log td:not(:nth-child(4)){
166 width:1%;
167 white-space:nowrap;
168 }
169 span.q{
170 text-decoration:underline;
171 text-decoration-style:dotted;
172 }
173 .q:hover{
174 cursor:help;
175 }
176 th, tr:hover{ /*darker color for table head, hovered-over rows*/
177 background-color:#dedede;
178 }
179 div.markdown{
180 width:100%;
181 padding:20px 50px;
182 border:1px solid #858585;
183 border-radius:6px;
184 }
185 img{
186 max-width:100%;
187 }
188 pre{
189 background-color:#eee;
190 padding:15px;
191 overflow-x:auto;
192 border-radius:8px;
193 }
194 :not(pre)>code{
195 background-color:#eee;
196 padding:2.5px;
197 border-radius:4px;
198 }
199
200 div.blob.table {
201 overflow-x: auto;
202 border:1px solid #858585;
203 border-top: none;
204 }
205 div.blob.header {
206 font-family: monospace;
207 font-size:14px;
208 font-weight: bold;
209 border:1px solid #000;
210 background-color:#dedede;
211 }
212 div.blob.header span{
213 margin:0 4px;
214 }
215 table.blob {
216 font-size:1em;
217 width:100%;
218 max-width:100%;
219 line-height:1;
220 }
221 table.blob tr:hover {
222 background-color: inherit;
223 }
224 table.blob td{
225 border:none;
226 padding:1px 5px;
227 }
228 table.blob.binary td{
229 text-align:center;
230 padding: 0;
231 }
232 table.blob.binary td>img, table.blob.binary td>video{
233 max-width:100%;
234 max-height:600px;
235 }
236 table.blob.lines td:first-child{
237 text-align: right;
238 padding-left:20px;
239 user-select: none;
240 color:#858585;
241 max-width:1%;
242 white-space:nowrap;
243 }
244 table.blob.lines td:first-child:hover{
245 color: #454545;
246 }
247 table.blob.lines td:nth-child(2){
248 width:100%;
249 white-space:pre;
250 }
251
252 a{
253 text-decoration:none;
254 color: #0077aa;
255 display: inline-block;
256 }
257 a:hover{
258 text-decoration:underline;
259 }
260 .home-banner h1 {
261 margin-top:40px;
262 margin-bottom:0;
263 }
264 .home-banner p {
265 margin-top:8px;
266 margin-bottom:30px;
267 }
268 .repo-section .name {
269 margin-bottom:0;
270 }
271 .repo-section h3 {
272 margin-top:10px;
273 }
274 .repo-section .description {
275 margin-top:8px;
276 }
277 .repo-section .nav {
278 margin-top:10px;
279 }
280 hr {
281 margin: 20px 0;
282 }
283 </style>]])
284
285 if parsed_uri.repo then
286 local arrow_left_circle = [[<img style="width:1.2em;height:1.2em;vertical-align:middle;margin-right:0.2em" src="https://joshuas3.s3.amazonaws.com/svg/arrow-left.svg"/>]]
287 ngx.say("<a style=\"margin-left:-1.35em\" href=\"/\">"..arrow_left_circle.."<span style=\"vertical-align:middle\">Index</span></a>")
288 end
289 ngx.print(content:build())
290 else
291 ngx.header.content_type = content.type
292 ngx.print(content.body)
293 end
294 ngx.exit(ngx.HTTP_OK)
295 return
296else
297 ngx.exit(ngx.HTTP_NOT_FOUND) -- default behavior
298 return
299end
300