Skip to content

Commit 014d800

Browse files
committed
resty-app
1 parent fe55f55 commit 014d800

17 files changed

+599
-87
lines changed

README.md

+16-6
Original file line numberDiff line numberDiff line change
@@ -7,20 +7,20 @@ Elegant, performant and productive router for Openresty.
77
It's recommended to use npm to scaffold a lua-resty-router project.
88

99
```bash
10-
npm create lua-resty-router@latest
10+
npm create resty-app@latest
1111
```
12+
Then follow the instructions to complete the project.
1213

1314
# Api
1415

15-
## create
16+
## new
1617

1718
```lua
18-
---@alias Route {[1]:string, [2]:function|string, [3]?:string|string[]}
19-
(method) Router:create(routes: Route[])
19+
(method) Router:new()
2020
-> Router
2121
```
2222

23-
init a router with routes
23+
create a router.
2424

2525
## insert
2626

@@ -29,7 +29,17 @@ init a router with routes
2929
-> Router
3030
```
3131

32-
insert a route
32+
insert a route.
33+
34+
## extend
35+
36+
```lua
37+
---@alias Route {[1]:string, [2]:function|string, [3]?:string|string[]}
38+
(method) Router:extend(routes: Route[])
39+
-> Router
40+
```
41+
42+
insert routes to a router.
3343

3444
## match
3545

api/account/users.lua

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
local function get_users(ctx)
2+
return {
3+
{ id = 1, name = "Alice" },
4+
{ id = 2, name = "Bob" },
5+
}
6+
end
7+
8+
return get_users

api/array_group.lua

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
return {
2+
-- 数组形式的多路由定义
3+
{ "bloggers", function()
4+
return {
5+
{ id = 1, name = "Alice" },
6+
{ id = 2, name = "Bob" }
7+
}
8+
end },
9+
10+
{ "bloggers/:name", function(ctx)
11+
return {
12+
name = ctx.params.name
13+
}
14+
end },
15+
16+
{ "bloggers/#id/posts", function(ctx)
17+
return {
18+
user_id = ctx.params.id,
19+
posts = {
20+
{ id = 1, title = "Post 1" },
21+
{ id = 2, title = "Post 2" }
22+
}
23+
}
24+
end }
25+
}

api/hash_group.lua

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
return {
2+
-- 映射形式的路由定义
3+
list = function()
4+
return {
5+
{ id = 1, type = "item1" },
6+
{ id = 2, type = "item2" }
7+
}
8+
end,
9+
10+
["detail/#id"] = function(ctx)
11+
return {
12+
id = ctx.params.id,
13+
type = "detail",
14+
description = "Item details"
15+
}
16+
end,
17+
18+
["search/:keyword"] = function(ctx)
19+
return {
20+
keyword = ctx.params.keyword,
21+
results = {
22+
{ id = 1, match = true },
23+
{ id = 2, match = false }
24+
}
25+
}
26+
end
27+
}

app.lua

+3
Original file line numberDiff line numberDiff line change
@@ -132,4 +132,7 @@ router:get("/response-body", function(ctx)
132132
ctx.response.body = "response body"
133133
end)
134134

135+
router:fs("./api")
136+
137+
assert(io.open("logs/router.log", "w")):write(require("resty.repr")(router)):close()
135138
return router

conf/nginx.conf

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
worker_processes auto;
22
pid logs/nginx.pid;
3-
3+
user root;
44
error_log logs/error.log;
55
events {
66
worker_connections 1024;

lib/resty/router.lua

+122-33
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,35 @@
1-
local json = require "cjson.safe"
2-
local bit = bit
3-
local gmatch = string.gmatch
4-
local ipairs = ipairs
5-
local tonumber = tonumber
6-
local byte = string.byte
7-
local ngx_re_match = ngx.re.match
8-
local coroutine = coroutine
9-
local resume = coroutine.resume
10-
local trace_back = debug.traceback
11-
local ngx = ngx
12-
local ngx_header = ngx.header
13-
local ngx_print = ngx.print
14-
local ngx_var = ngx.var
15-
local string_format = string.format
16-
local xpcall = xpcall
17-
local spawn = ngx.thread.spawn
18-
local ngx_req = ngx.req
19-
local decode = json.decode
20-
local encode = json.encode
21-
local get_post_args = ngx.req.get_post_args
22-
local read_body = ngx.req.read_body
23-
local get_body_data = ngx.req.get_body_data
24-
local assert = assert
25-
local rawget = rawget
26-
local setmetatable = setmetatable
1+
local json = require "cjson.safe"
2+
local bit = bit
3+
local gmatch = string.gmatch
4+
local ipairs = ipairs
5+
local tonumber = tonumber
6+
local byte = string.byte
7+
local ngx_re_match = ngx.re.match
8+
local coroutine = coroutine
9+
local resume = coroutine.resume
10+
local trace_back = debug.traceback
11+
local ngx = ngx
12+
local ngx_header = ngx.header
13+
local ngx_print = ngx.print
14+
local ngx_var = ngx.var
15+
local string_format = string.format
16+
local xpcall = xpcall
17+
local spawn = ngx.thread.spawn
18+
local ngx_req = ngx.req
19+
local decode = json.decode
20+
local encode = json.encode
21+
local get_post_args = ngx.req.get_post_args
22+
local read_body = ngx.req.read_body
23+
local get_body_data = ngx.req.get_body_data
24+
local assert = assert
25+
local rawget = rawget
26+
local setmetatable = setmetatable
27+
local lfs
28+
do
29+
local o, l = pcall(require, "syscall.lfs")
30+
if not o then o, l = pcall(require, "lfs") end
31+
if o then lfs = l end
32+
end
2733

2834
local method_bitmask = {
2935
GET = 1, -- 2^0
@@ -201,24 +207,23 @@ function Router:is_route(view)
201207
return self:is_handler(view[2])
202208
end
203209

204-
---init a router with routes
210+
---insert routes to a router
205211
---@param routes Route[]
206212
---@return Router
207-
function Router:create(routes)
208-
local tree = Router:new()
213+
function Router:extend(routes)
209214
for _, route in ipairs(routes) do
210215
assert(self:is_route(route))
211216
local path = route[1]
212217
if type(path) == 'string' then
213-
tree:_insert(path, route[2], route[3])
218+
self:_insert(path, route[2], route[3])
214219
else
215220
for _, p in ipairs(path) do
216-
tree:_insert(p, route[2], route[3])
221+
self:_insert(p, route[2], route[3])
217222
end
218223
end
219224
end
220-
_set_find_method(tree)
221-
return tree
225+
_set_find_method(self)
226+
return self
222227
end
223228

224229
---@param path string|table
@@ -574,4 +579,88 @@ function Router:run()
574579
return self:dispatch(ngx_var.document_uri, ngx_var.request_method)
575580
end
576581

582+
local function callable(handler)
583+
return type(handler) == "function" or
584+
(type(handler) == "table" and getmetatable(handler) and getmetatable(handler).__call)
585+
end
586+
587+
local function is_handler(handler)
588+
return type(handler) == "string" or callable(handler)
589+
end
590+
591+
---@param dir string Directory path to scan
592+
---@param base_path? string Base path (used for recursion)
593+
---@return table[] Route configuration array
594+
function Router:collect_routes(dir, base_path)
595+
local routes = {}
596+
base_path = base_path or ""
597+
598+
-- Recursively scan directory
599+
for file in lfs.dir(dir) do
600+
if file ~= "." and file ~= ".." then
601+
local path = dir .. "/" .. file
602+
local attr = lfs.attributes(path)
603+
604+
if attr.mode == "directory" then
605+
-- Process subdirectories recursively
606+
local sub_routes = self:collect_routes(path, base_path .. "/" .. file)
607+
for _, route in ipairs(sub_routes) do
608+
routes[#routes + 1] = route
609+
end
610+
elseif attr.mode == "file" and file:match("%.lua$") then
611+
-- Process .lua files
612+
local module_path = path:gsub("%.lua$", ""):gsub("/", ".")
613+
local ok, route = pcall(require, module_path)
614+
if ok then
615+
-- Get relative path (remove .lua extension)
616+
local relative_path = base_path .. "/" .. file:gsub("%.lua$", "")
617+
618+
-- Handle different types of route definitions
619+
if is_handler(route) then
620+
-- Type 1: Function or callable table
621+
routes[#routes + 1] = { relative_path, route }
622+
elseif type(route) == "table" then
623+
if #route > 0 then
624+
if type(route[1]) == "string" and is_handler(route[2]) then
625+
-- Type 2: Single route definition
626+
local path = route[1]
627+
if path:sub(1, 1) == "/" then
628+
routes[#routes + 1] = { path, route[2], route[3] }
629+
else
630+
routes[#routes + 1] = { relative_path .. "/" .. path, route[2], route[3] }
631+
end
632+
else
633+
-- Type 3: Array of multiple route definitions
634+
for _, view in ipairs(route) do
635+
if type(view[1]) == "string" and is_handler(view[2]) then
636+
if view[1]:sub(1, 1) == "/" then
637+
routes[#routes + 1] = { view[1], view[2], view[3] }
638+
else
639+
routes[#routes + 1] = { relative_path .. "/" .. view[1], view[2], view[3] }
640+
end
641+
end
642+
end
643+
end
644+
else
645+
-- Type 4: Map-style route definitions
646+
for key, handler in pairs(route) do
647+
if type(key) == "string" and key:sub(1, 1) ~= "/" and is_handler(handler) then
648+
routes[#routes + 1] = { relative_path .. "/" .. key, handler }
649+
end
650+
end
651+
end
652+
end
653+
end
654+
end
655+
end
656+
end
657+
658+
return routes
659+
end
660+
661+
function Router:fs(dir)
662+
local routes = self:collect_routes(dir)
663+
self:extend(routes)
664+
end
665+
577666
return Router

package.json

+6-6
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
{
2-
"name": "create-lua-resty-router-tmp",
3-
"version": "0.4.0",
2+
"name": "create-resty-app",
3+
"version": "0.5.0",
44
"description": "high performance router",
55
"type": "module",
66
"bin": {
7-
"create-lua-resty-router-tmp": "outfile.cjs"
7+
"create-resty-app": "outfile.cjs"
88
},
99
"files": [
1010
"outfile.cjs",
@@ -29,11 +29,11 @@
2929
"format": "prettier --write .",
3030
"build": "zx ./scripts/build.mjs",
3131
"snapshot": "zx ./scripts/snapshot.mjs",
32-
"pretest2": "run-s build snapshot",
32+
"pretest": "rm -f logs/error.log; lsof -t -i:8080 | xargs kill -9 || true",
3333
"prepublishOnly": "npm run build",
3434
"prepublishOnly2": "zx ./scripts/prepublish.mjs",
3535
"nginx": "nginx -p . -c conf/nginx.conf",
36-
"test": "mv logs/error.log; kill -9 $(lsof -t -i:8080);yarn nginx; ./test.py ; yarn nginx -s stop"
36+
"test": "yarn nginx; ./test.py ; yarn nginx -s stop"
3737
},
3838
"author": "",
3939
"license": "ISC",
@@ -56,4 +56,4 @@
5656
"prompts": "^2.4.2",
5757
"zx": "^7.2.3"
5858
}
59-
}
59+
}

0 commit comments

Comments
 (0)