local sgmatch = string.gmatch local utils = require 'bin.scaffold.utils' local gitignore = [[ # lor client_body_temp fastcgi_temp logs proxy_temp tmp uwsgi_temp # Compiled Lua sources luac.out # luarocks build files *.src.rock *.zip *.tar.gz # Object files *.o *.os *.ko *.obj *.elf # Precompiled Headers *.gch *.pch # Libraries *.lib *.a *.la *.lo *.def *.exp # Shared objects (inc. Windows DLLs) *.dll *.so *.so.* *.dylib # Executables *.exe *.out *.app *.i*86 *.x86_64 *.hex ]] local mime_types = [[ types { text/html html htm shtml; text/css css; text/xml xml; image/gif gif; image/jpeg jpeg jpg; application/javascript js; application/atom+xml atom; application/rss+xml rss; text/mathml mml; text/plain txt; text/vnd.sun.j2me.app-descriptor jad; text/vnd.wap.wml wml; text/x-component htc; image/png png; image/tiff tif tiff; image/vnd.wap.wbmp wbmp; image/x-icon ico; image/x-jng jng; image/x-ms-bmp bmp; image/svg+xml svg svgz; image/webp webp; application/font-woff woff; application/java-archive jar war ear; application/json json; application/mac-binhex40 hqx; application/msword doc; application/pdf pdf; application/postscript ps eps ai; application/rtf rtf; application/vnd.apple.mpegurl m3u8; application/vnd.ms-excel xls; application/vnd.ms-fontobject eot; application/vnd.ms-powerpoint ppt; application/vnd.wap.wmlc wmlc; application/vnd.google-earth.kml+xml kml; application/vnd.google-earth.kmz kmz; application/x-7z-compressed 7z; application/x-cocoa cco; application/x-java-archive-diff jardiff; application/x-java-jnlp-file jnlp; application/x-makeself run; application/x-perl pl pm; application/x-pilot prc pdb; application/x-rar-compressed rar; application/x-redhat-package-manager rpm; application/x-sea sea; application/x-shockwave-flash swf; application/x-stuffit sit; application/x-tcl tcl tk; application/x-x509-ca-cert der pem crt; application/x-xpinstall xpi; application/xhtml+xml xhtml; application/xspf+xml xspf; application/zip zip; application/octet-stream bin exe dll; application/octet-stream deb; application/octet-stream dmg; application/octet-stream iso img; application/octet-stream msi msp msm; application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; audio/midi mid midi kar; audio/mpeg mp3; audio/ogg ogg; audio/x-m4a m4a; audio/x-realaudio ra; video/3gpp 3gpp 3gp; video/mp2t ts; video/mp4 mp4; video/mpeg mpeg mpg; video/quicktime mov; video/webm webm; video/x-flv flv; video/x-m4v m4v; video/x-mng mng; video/x-ms-asf asx asf; video/x-ms-wmv wmv; video/x-msvideo avi; } ]] local index_view_tpl = [[
{{name}} {% if locals.app_version then %} {{locals.app_version}} {% end %} {{desc}}
]] local user_info_view_tpl = [[
{{desc}}
{{id}} {{name}}
]] local main_tpl = [[ local app = require("app.server") app:run() ]] local server_tpl = [[ local string_find = string.find local lor = require("lor.index") local router = require("app.router") local app = lor() -- 模板配置 app:conf("view enable", true) app:conf("view engine", "tmpl") app:conf("view ext", "html") app:conf("view layout", "") app:conf("views", "./app/views") -- session和cookie支持,如果不需要可注释以下配置 local mw_cookie = require("lor.lib.middleware.cookie") local mw_session = require("lor.lib.middleware.session") app:use(mw_cookie()) app:use(mw_session({ session_key = "__app__", -- the key injected in cookie session_aes_key = "aes_key_for_session", -- should set by yourself timeout = 3600 -- default session timeout is 3600 seconds })) -- 自定义中间件1: 注入一些全局变量供模板渲染使用 local mw_inject_version = require("app.middleware.inject_app_info") app:use(mw_inject_version()) -- 自定义中间件2: 设置响应头 app:use(function(req, res, next) res:set_header("X-Powered-By", "Lor framework") next() end) router(app) -- 业务路由处理 -- 错误处理插件,可根据需要定义多个 app:erroruse(function(err, req, res, next) ngx.log(ngx.ERR, err) if req:is_found() ~= true then if string_find(req.headers["Accept"], "application/json") then res:status(404):json({ success = false, msg = "404! sorry, not found." }) else res:status(404):send("404! sorry, not found. " .. (req.path or "")) end else if string_find(req.headers["Accept"], "application/json") then res:status(500):json({ success = false, msg = "500! internal error, please check the log." }) else res:status(500):send("internal error, please check the log.") end end end) return app ]] local router_tpl = [[ -- 业务路由管理 local userRouter = require("app.routes.user") return function(app) -- simple router: hello world! app:get("/hello", function(req, res, next) res:send("hi! welcome to lor framework.") end) -- simple router: render html, visit "/" or "/?name=foo&desc=bar app:get("/", function(req, res, next) local data = { name = req.query.name or "lor", desc = req.query.desc or 'a framework of lua based on OpenResty' } res:render("index", data) end) -- group router: 对以`/user`开始的请求做过滤处理 app:use("/user", userRouter()) end ]] local user_router_tpl = [[ local lor = require("lor.index") local userRouter = lor:Router() -- 生成一个group router对象 -- 按id查找用户 -- e.g. /query/123 userRouter:get("/query/:id", function(req, res, next) local query_id = tonumber(req.params.id) -- 从req.params取参数 if not query_id then return res:render("user/info", { desc = "Error to find user, path variable `id` should be a number. e.g. /user/query/123" }) end -- 渲染页面 res:render("user/info", { id = query_id, name = "user" .. query_id, desc = "User Information" }) end) -- 删除用户 -- e.g. /delete?id=123 userRouter:delete("/delete", function(req, res, next) local id = req.query.id -- 从req.query取参数 if not id then return res:html("

Error: query param id is required.

") end -- 返回html res:html("succeed to delete user
user id is:" .. id .. "") end) -- 修改用户 -- e.g. /put/123?name=sumory userRouter:put("/put/:id", function(req, res, next) local id = req.params.id -- 从req.params取参数 local name = req.query.name -- 从req.query取参数 if not id or not name then return res:send("error params: id and name are required.") end -- 返回文本格式的响应结果 res:send("succeed to modify user[" .. id .. "] with new name:" .. name) end) -- 创建用户 userRouter:post("/post", function(req, res, next) local content_type = req.headers['Content-Type'] -- 如果请求类型为form表单或json请求体 if string.find(content_type, "application/x-www-form-urlencoded",1, true) or string.find(content_type, "application/json",1, true) then local id = req.body.id -- 从请求体取参数 local name = req.body.name -- 从请求体取参数 if not id or not name then return res:json({ success = false, msg = "error params: id and name are required." }) end res:json({-- 返回json格式的响应体 success = true, data = { id = id, name = name, desc = "succeed to create new user" .. id } }) else -- 不支持其他请求体 res:status(500):send("not supported request Content-Type[" .. content_type .. "]") end end) return userRouter ]] local middleware_tpl = [[ ### 自定义插件目录(define your own middleware) You are recommended to define your own middlewares and keep them in one place to manage. 建议用户将自定义插件存放在此目录下统一管理,然后在其他地方引用,插件的格式如下: ``` local middleware = function(params) return function(req, res, next) -- do something with req/res next() end end return middleware ``` ]] local middleware_example_tpl = [[ --- 中间件示例: 为每个请求注入一些通用的变量 local lor = require("lor.index") return function() return function(req, res, next) -- res.locals是一个table, 可以在这里注入一些“全局”变量 -- 这个示例里注入app的名称和版本号, 在渲染页面时即可使用 res.locals.app_name = "lor application" res.locals.app_version = lor.version or "" next() end end ]] local static_tpl = [[ ### 静态文件目录(static files directory) nginx对应配置为 ``` location /static { alias app/static; } ``` ]] local ngx_conf_directory = [[ ### nginx configuration directory ]] local ngx_config = require 'bin.scaffold.nginx.config' local ngx_conf_template = require 'bin.scaffold.nginx.conf_template' local function nginx_conf_content() -- read nginx.conf file local nginx_conf_template = ngx_conf_template.get_ngx_conf_template() -- append notice nginx_conf_template = [[ #generated by `lor framework` ]] .. nginx_conf_template local match = {} local tmp = 1 for v in sgmatch(nginx_conf_template , '{{(.-)}}') do match[tmp] = v tmp = tmp + 1 end for _, directive in ipairs(match) do if ngx_config[directive] ~= nil then nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', ngx_config[directive]) else nginx_conf_template = string.gsub(nginx_conf_template, '{{' .. directive .. '}}', '#' .. directive) end end return nginx_conf_template end local ngx_conf_tpl = nginx_conf_content() local start_sh = [[ #!/bin/sh ##################################################################### # usage: # sh start.sh -- start application @dev # sh start.sh ${env} -- start application @${env} # examples: # sh start.sh prod -- use conf/nginx-prod.conf to start OpenResty # sh start.sh -- use conf/nginx-dev.conf to start OpenResty ##################################################################### if [ -n "$1" ];then PROFILE="$1" else PROFILE=dev fi mkdir -p logs & mkdir -p tmp echo "start lor application with profile: "${PROFILE} nginx -p `pwd`/ -c conf/nginx-${PROFILE}.conf ]] local stop_sh = [[ #!/bin/sh ##################################################################### # usage: # sh stop.sh -- stop application @dev # sh stop.sh ${env} -- stop application @${env} # examples: # sh stop.sh prod -- use conf/nginx-prod.conf to stop OpenResty # sh stop.sh -- use conf/nginx-dev.conf to stop OpenResty ##################################################################### if [ -n "$1" ];then PROFILE="$1" else PROFILE=dev fi mkdir -p logs & mkdir -p tmp echo "stop lor application with profile: "${PROFILE} nginx -s stop -p `pwd`/ -c conf/nginx-${PROFILE}.conf ]] local reload_sh = [[ #!/bin/sh ##################################################################### # usage: # sh reload.sh -- reload application @dev # sh reload.sh ${env} -- reload application @${env} # examples: # sh reload.sh prod -- use conf/nginx-prod.conf to reload OpenResty # sh reload.sh -- use conf/nginx-dev.conf to reload OpenResty ##################################################################### if [ -n "$1" ];then PROFILE="$1" else PROFILE=dev fi mkdir -p logs & mkdir -p tmp echo "reload lor application with profile: "${PROFILE} nginx -s reload -p `pwd`/ -c conf/nginx-${PROFILE}.conf ]] local Generator = {} Generator.files = { ['.gitignore'] = gitignore, ['app/main.lua'] = main_tpl, ['app/server.lua'] = server_tpl, ['app/router.lua'] = router_tpl, ['app/routes/user.lua'] = user_router_tpl, ['app/views/index.html'] = index_view_tpl, ['app/views/user/info.html'] = user_info_view_tpl, ['app/middleware/README.md'] = middleware_tpl, ['app/middleware/inject_app_info.lua'] = middleware_example_tpl, ['app/static/README.md'] = static_tpl, -- static files directory,e.g. js/css/img ['conf/README.md'] = ngx_conf_directory, -- nginx config directory ['conf/nginx-dev.conf'] = ngx_conf_tpl, -- nginx config file ['conf/mime.types'] = mime_types, -- nginx mime ['start.sh'] = start_sh, ['stop.sh'] = stop_sh, ['reload.sh'] = reload_sh } function Generator.new(name) print('Creating app: ' .. name .. '...') Generator.create_files(name) end function Generator.create_files(parent) for file_path, file_content in pairs(Generator.files) do local full_file_path = parent .. '/' .. file_path local full_file_dirname = utils.dirname(full_file_path) os.execute('mkdir -p ' .. full_file_dirname .. ' > /dev/null') local fw = io.open(full_file_path, 'w') fw:write(file_content) fw:close() print(' created file ' .. full_file_path) end end return Generator