Index

resty-gitweb / 0bfe00a

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

Latest Commit

{#}TimeHashSubjectAuthor#(+)(-)GPG?
1005 Jan 2021 21:560bfe00aReformat blob renders with header rowJosh Stockin1169G

Blob @ resty-gitweb / app.lua

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