Index

resty-gitweb / 72a1883

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
1713 Oct 2021 20:5472a1883Update S3 image source endpointsJosh Stockin111G

Blob @ resty-gitweb / app.lua

text/plain9348 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 = PRODUCTION and repo.location.prod or repo.location.dev
32 repo.obj = git.repo.open(repo.loc)
33 view = parsed_uri.parts[2] or "tree"
34 local branch
35
36 local res, status = pcall(function() -- if branch is real
37 branch = git.find_rev(repo.obj, parsed_uri.parts[3]) -- if parts[3] is nil, defaults to "HEAD"
38 end)
39 if res then
40 local res, status = pcall(function() -- effectively catch any errors, 404 if any
41 if view == "tree" then -- directory display (with automatic README rendering)
42 local path = parsed_uri.parts
43 table.remove(path, 3) -- branch
44 table.remove(path, 2) -- "tree"
45 table.remove(path, 1) -- repo
46 if #path > 0 then
47 path = table.concat(path, "/").."/"
48 else
49 path = ""
50 end
51
52 content = require("pages/tree")(repo, repo.loc, branch, path)
53 elseif view == "blob" then
54 local path = parsed_uri.parts
55 table.remove(path, 3) -- branch
56 table.remove(path, 2) -- "tree"
57 table.remove(path, 1) -- repo
58 if #path > 0 then
59 path = table.concat(path, "/")
60 else
61 path = ""
62 end
63
64 content = require("pages/blob")(repo, repo.loc, branch, path)
65 elseif view == "raw" then
66 local path = parsed_uri.parts
67 table.remove(path, 3) -- branch
68 table.remove(path, 2) -- "tree"
69 table.remove(path, 1) -- repo
70 if #path > 0 then
71 path = table.concat(path, "/")
72 else
73 path = ""
74 end
75
76 content, is_binary = require("pages/raw")(repo, repo.loc, branch, path)
77 if content then
78 if is_binary then
79 mimetype = puremagic.via_content(content.body, path)
80 content.type = mimetype
81 else
82 content.type = "text/plain"
83 end
84 end
85
86 elseif view == "log" then
87 content = require("pages/log")(repo, repo.loc, branch, ngx.var.arg_n, ngx.var.arg_skip)
88 elseif view == "refs" then
89 content = require("pages/refs")(repo, repo.loc, branch)
90 elseif view == "download" then
91 content = require("pages/download")(repo, repo.loc, branch)
92 elseif view == "commit" then
93 content = require("pages/commit")(repo, repo.loc, parsed_uri.parts[3])
94 else
95 error("bad view "..view)
96 end
97 end) -- pcall
98
99 if res ~= true then
100 if not PRODUCTION then
101 ngx.say(res)
102 ngx.say(status)
103 end
104 ngx.exit(ngx.HTTP_NOT_FOUND)
105 return
106 end
107 elseif not PRODUCTION then -- branch doesn't exist, show an error in non-prod environments
108 ngx.say(res)
109 ngx.say(status)
110 ngx.exit(ngx.HTTP_NOT_FOUND)
111 end
112 git.repo.free(repo.obj)
113 end
114end
115
116if content ~= nil then -- TODO: HTML templates from files, static serving
117 if view ~= "raw" then
118 ngx.header.content_type = "text/html"
119 ngx.say([[<!DOCTYPE html><html lang="en"><head>]])
120 ngx.say([[<link rel="stylesheet" href="https://raw.githubusercontent.com/necolas/normalize.css/master/normalize.css">]])
121
122 ngx.say(
123 [[<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 line-height:1.2;
162 }
163 table.files td:first-child{
164 padding-right:calc(5px + 1em);
165 }
166 table.files td:not(:nth-child(2)), table.log td:not(:nth-child(4)){
167 width:1%;
168 white-space:nowrap;
169 }
170 span.q{
171 text-decoration:underline;
172 text-decoration-style:dotted;
173 }
174 .q:hover{
175 cursor:help;
176 }
177 th, tr:hover{ /*darker color for table head, hovered-over rows*/
178 background-color:#dedede;
179 }
180 div.markdown{
181 width:100%;
182 padding:20px 50px;
183 border:1px solid #858585;
184 border-radius:6px;
185 }
186 img{
187 max-width:100%;
188 }
189 pre{
190 background-color:#eee;
191 padding:15px;
192 overflow-x:auto;
193 border-radius:8px;
194 }
195 :not(pre)>code{
196 background-color:#eee;
197 padding:2.5px;
198 border-radius:4px;
199 }
200
201 div.blob.table {
202 overflow-x: auto;
203 border:1px solid #858585;
204 border-top: none;
205 }
206 div.blob.header {
207 font-family: monospace;
208 font-size:14px;
209 font-weight: bold;
210 border:1px solid #000;
211 background-color:#dedede;
212 }
213 div.blob.header span{
214 margin:0 4px;
215 }
216 table.blob {
217 font-size:1em;
218 width:100%;
219 max-width:100%;
220 line-height:1;
221 }
222 table.blob tr:hover {
223 background-color: inherit;
224 }
225 table.blob td{
226 border:none;
227 padding:1px 5px;
228 }
229 table.blob.binary td{
230 text-align:center;
231 padding: 0;
232 }
233 table.blob.binary td>img, table.blob.binary td>video{
234 max-width:100%;
235 max-height:600px;
236 }
237 table.blob.lines td:first-child{
238 text-align: right;
239 padding-left:20px;
240 user-select: none;
241 color:#858585;
242 max-width:1%;
243 white-space:nowrap;
244 }
245 table.blob.lines td:first-child:hover{
246 color: #454545;
247 }
248 table.blob.lines td:nth-child(2){
249 width:100%;
250 white-space:pre;
251 }
252
253 a{
254 text-decoration:none;
255 color: #0077aa;
256 display: inline-block;
257 }
258 a:hover{
259 text-decoration:underline;
260 }
261 center.index-banner h1.title {
262 margin-top:40px;
263 margin-bottom:0;
264 }
265 center.index-banner p.description {
266 margin-top:8px;
267 margin-bottom:30px;
268 }
269 div.repo-section .name {
270 margin-bottom:0;
271 }
272 div.repo-section h3 {
273 margin-top:10px;
274 }
275 div.repo-section .description {
276 margin-top:8px;
277 }
278 div.repo-section .nav {
279 margin-top:10px;
280 }
281 hr {
282 margin: 20px 0;
283 }
284 </style>]])
285 ngx.say("</head><body>")
286
287 if parsed_uri.repo then
288 local arrow_left_circle = [[<img style="width:1.2em;height:1.2em;vertical-align:middle;margin-right:0.2em" src="https://joshstock.in/static/svg/arrow-left.svg"/>]]
289 ngx.say("<a style=\"margin-left:-1.35em\" href=\"/\">"..arrow_left_circle.."<span style=\"vertical-align:middle\">Index</span></a>")
290 end
291 ngx.print(content:build())
292 ngx.say("</body></html>")
293 else
294 ngx.header.content_type = content.type
295 ngx.print(content:build())
296 end
297 ngx.exit(ngx.HTTP_OK)
298 return
299else
300 ngx.exit(ngx.HTTP_NOT_FOUND) -- default behavior
301 return
302end
303