Repository navigation
Expand file tree
/
Copy pathsoakbean.lua
More file actions
345 lines (320 loc) · 8.82 KB
/
Copy pathsoakbean.lua
File metadata and controls
345 lines (320 loc) · 8.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
local json = require "json"
function error(str) print("[error] " .. str) end
function keys(table) local i = 0 ; for k, v in pairs(table) do i = i + 1 end ; return i end
sb = {}
sb = {
middleware={},
handler={},
data={},
charset="utf-8",
__index=function(self,k,v)
if sb.data[k] then return sb.data[k] end
if sb[k] then return sb[k] end
return rawget(self,k)
end,
__newindex=function(self,k,v)
if type(v) == "function" then
sb.data[k] = (function(k,v)
return function(a,b,c,d,e,f)
local ret = v(a,b,c,d,e,f)
sb.pub(k,v,a,b,c,d,e,f)
return ret
end
end)(k,v)
else
sb.data[k] = v
sb.pub(k,v)
end
end,
on = function(k,f)
sb.handler[k] = sb.handler[k] or {}
table.insert( sb.handler[k], f )
return sb.app
end,
pub = function(k,v,a,b,c,d,e,f)
if sb.handler[k] ~= nil then
for i,handler in pairs(sb.handler[k]) do
if a then --
coroutine.wrap(handler)(a,b,c,d,e,f) -- forward function args
else
coroutine.wrap(handler)(v,k) -- forward assignment
end
end
end
end,
-- usage:
--
-- tpl([[
-- Hello ${world}
-- foo() = ${foo(1)}
--
-- {if true then
-- print(world)
-- print("hello universe")
-- end}
--
-- {for i in pairs(numbers) do
-- print(i)
-- end}
--
-- {for k,v in pairs(items) do
-- print(k .. " " .. v.x)
-- end}
-- ]],{
-- world="WORLD",
-- foo = function(i) return i+1 end,
-- numbers = {2,3,4},
-- items = {{x=2},{x=3}}
-- })
-- -- Hello WORLD
-- -- foo() = 2
-- -- WORLD
-- -- hello universe
-- -- 1
-- -- 2
-- -- 3
-- -- 1 2
-- -- 2 3
tpl = function(s, __data)
local __env = {}
local fallback = function(key)
if os.getenv('DEBUG') then
print("⚠️ template '" .. key .. "' does not exist")
end
return setmetatable({}, {
__tostring = function() return "" end,
__concat = function(a, b) return tostring(a) .. tostring(b) end,
__call = function(...) return "" end
})
end
setmetatable(__env, {
__index = function(t, key) return __data[key] or _G[key] or fallback(key) end,
__newindex = function(t, key, value) __data[key] = value end
})
-- lets evaluate inline lua linearly
local luastr = 'local str=""\n print = function(s) str = str .. s .. "\\n" end\n'
luastr = luastr .. 'defined = function(v) return tostring(v or "") ~= "" end\n'
local islua = false
for str in string.gmatch(s, "([^\n]+)") do
if str:match("{if ") or str:match("{for ") then
luastr = luastr .. str:gsub("{","") .. "\n"
islua = true
elseif str:match("end}") then
luastr = luastr .. "end\n"
islua = false
else
if islua then
luastr = luastr .. str .. "\n"
else
luastr = luastr .. "str = str .. \"" .. str:gsub('"','\\"') .. "\\n\"\n"
end
end
end
local s1 = load( luastr .. "\n return str", "chunkname", "t", __env )()
-- evaluate template vars
local s2 = (s1:gsub('($%b{})', function(w)
local inner = w:sub(3, -2):match("^%s*(.-)%s*$")
-- Execute as raw Lua code if it contains spaces or equals "end"
if inner:find("%s") or inner == "end" then
local fn = (loadstring or load)("return function(__env) " .. inner .. " end")
if fn then
setfenv(fn(), setmetatable(__env, { __index = _G }))()
end
return ""
end
-- Allow dots in variable names along with alphanumeric characters and underscores
local path, arg = w:match("^%${%s*([%w_%.]+)" .. "%s*%(?(.-)%)?%s*}")
-- Traverse the nested table path (e.g. "foo.bar" -> __env["foo"]["bar"])
local v = __env
if path then
for key in path:gmatch("[%w_]+") do
if type(v) == "table" then
v = v[key]
else
v = nil
break
end
end
else
v = nil
end
if type(v) == "function" then
return tostring(v(load("return " .. arg)()) or '')
end
return v ~= nil and tostring(v) or w
end))
return s2
end,
write = function(str)
Write( sb.app.tpl(str,sb.app.data) )
end,
job = function(app)
return function(cfg)
-- app.
return app
end
end,
request = function(method, app)
return function(path,f)
app.use( function(req,res,next)
if req.url:match(path) and req.method == method then
f(req,res,next)
else next() end
end)
end
end,
use = function(f)
table.insert(sb.middleware,f)
return sb.app
end,
useDefaults = function(app)
app.use( function(req,res,next)
app.host = GetHost()
app.scheme = GetScheme()
app.pub(req.url,{req=req,res=res})
next()
end)
end,
run = function(req)
local next = function() end
local k = 0
local internal = req
req = req or {
param={},
method=GetMethod(),
host=GetHost(),
header=GetHeaders(),
url=GetPath(),
protocol=GetScheme(),
body=GetBody(),
}
local res={
_status=nil,
_header={},
_body="",
send = function(req,res,next)
sb.response()(req,res,next)
end
}
local params = internal and {} or GetParams()
if params ~= nil then
for i,p in pairs(params) do req.param[ p[1] ] = p[2] end
end
res.status = function(status) res._status = status ; sb.pub('res.status',status) end
res.body = function(v) res._body = v ; sb.pub('req.body' ,body) end
res.header = function(k,v)
if v == nil then return res._header[k] end
res._header[k] = v
sb.pub('res.header',{key=k,value=v})
end
--res.header("content-type","text/html")
-- run middleware
next = function()
k = k+1
if type(sb.middleware[k]) == "function" then sb.middleware[k](req,res,next) end
end
next()
end,
json = function()
local json_response = function(response)
return function()
return function(req,res,next)
if type(res._body) == "table" then
res.header('content-type',"application/json")
res.body( json.encode(res._body) )
end
response(req,res,next)
end
end
end
sb.response = json_response(sb.response())
return function(req,res,next)
if req.method ~= "GET" and req.header['Content-Type']:match("application/json") and GetPayload():sub(0,1) == "{" then
req.body = json.decode( GetPayload() )
end
next()
end
end,
response = function()
return function(req,res,next)
sb.pub('response', {req=req, res=res, next=next})
if res._status ~= nil then
SetStatus(res._status)
for k,v in pairs(res._header) do
if k == "content-type" then v = v .. "; charset=" .. sb.charset end
SetHeader(k,v)
end
if type(res._body) == "string" then
Write( res._body )
else print("[ERROR] res.body is not a string (HINT: use json middleware)") end
else
if next then next() end
end
end
end,
router = function(router)
return function(req,res,next)
for p1, p2 in pairs(router) do
if GetPath():match(p1) then
if type(p2) == "string" then
print("router: " .. p1 .. " => " .. p2)
RoutePath(p2)
end
if type(p2) == "function" then p2(req,res,next) end
sb.pub(p1,req)
sb.pub(p2,req)
return true
end
end
next()
end
end,
template = function(file)
return function(req,res,next)
res.status(200)
res.header('content-type','text/html')
res.body( sb.app.tpl( LoadAsset(file), sb.app ) )
next()
end
end,
init = function(app)
end,
destroy = function(app)
end,
runcmd = function(app)
for k,v in pairs(argv) do
app.opts[ v:gsub("=.*","") ] = v:gsub(".*=","")
end
for k,v in pairs(argv) do
if app.cmd[v] then
local file = app.cmd[v].file
return require( file:sub(0,-5) )(app,argv)
end
end
print("\nUsage: " .. app.bin .. " <cmd> [opts]\n\n")
for k,v in pairs(app.cmd) do
print( string.format("\t%s %-15s %s", app.bin, k, v.info ) )
end
print("")
os.exit()
end
}
return function(data)
local app = {}
setmetatable(app,sb)
sb.arg = arg
sb.bin = arg[-1]:gsub(".*/","")
sb.bindir = arg[-1]:gsub("/[a-zA-Z0-9-_ ]+.com$","/")
sb.app = app
sb.data = data
sb.data.url = {}
if data.url == nil then sb.data.url = {} end
sb.get = sb.request('GET', app)
sb.post = sb.request('POST', app)
sb.put = sb.request('PUT', app)
sb.options = sb.request('OPTIONS', app)
sb.delete = sb.request('DELETE', app)
sb.init(app)
sb.useDefaults(app)
return app
end