├── .gitignore ├── README.md ├── app ├── config │ └── config.lua ├── main.lua ├── middleware │ ├── README.md │ └── check_login.lua ├── model │ └── todo.lua ├── router.lua ├── routes │ ├── auth.lua │ ├── error.lua │ └── todo.lua ├── static │ ├── css │ │ ├── app.css │ │ ├── base.css │ │ └── index.css │ └── js │ │ ├── app.js │ │ ├── base.js │ │ ├── jquery.js │ │ └── juicer-min.js └── views │ ├── error.html │ ├── login.html │ ├── todo.html │ └── welcome.html ├── conf ├── mime.types └── nginx-dev.conf ├── reload.sh ├── start.sh └── stop.sh /.gitignore: -------------------------------------------------------------------------------- 1 | # lor 2 | client_body_temp 3 | fastcgi_temp 4 | logs 5 | proxy_temp 6 | tmp 7 | uwsgi_temp 8 | 9 | # Compiled Lua sources 10 | luac.out 11 | 12 | # luarocks build files 13 | *.src.rock 14 | *.zip 15 | *.tar.gz 16 | 17 | # Object files 18 | *.o 19 | *.os 20 | *.ko 21 | *.obj 22 | *.elf 23 | 24 | # Precompiled Headers 25 | *.gch 26 | *.pch 27 | 28 | # Libraries 29 | *.lib 30 | *.a 31 | *.la 32 | *.lo 33 | *.def 34 | *.exp 35 | 36 | # Shared objects (inc. Windows DLLs) 37 | *.dll 38 | *.so 39 | *.so.* 40 | *.dylib 41 | 42 | # Executables 43 | *.exe 44 | *.out 45 | *.app 46 | *.i*86 47 | *.x86_64 48 | *.hex 49 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Lor Example 2 | 3 | 这是[lor](https://github.com/sumory/lor)框架的一个示例项目,用于展示如何使用lor框架编写CURD API。 4 | 5 | 6 | ## 快速开始 7 | 8 | ### 配置更改 9 | 10 | 下载代码 11 | 12 | ``` 13 | git clone https://github.com/lorlabs/lor-example 14 | ``` 15 | 16 | 特别注意以下几点, 否则示例无法正常运行: 17 | 18 | - lua_code_cache值要设为on, 才能使得示例中的数据更改在刷新页面后继续生效 19 | - lua_package_path, 请修改该值为你机器上的lua和lor对应配置 20 | 21 | 22 | ### 启动 23 | 24 | - sh start.sh 25 | 26 | ### 访问 27 | 28 | 启动成功后,访问http://localhost:9999 29 | 30 | ### License 31 | 32 | MIT 33 | -------------------------------------------------------------------------------- /app/config/config.lua: -------------------------------------------------------------------------------- 1 | return { 2 | users = { 3 | { 4 | username = "test", 5 | password = "test" 6 | }, 7 | { 8 | username = "sumory", 9 | password = "1" 10 | } 11 | }, 12 | 13 | whitelist = { 14 | "/", 15 | "/view", 16 | "/auth/login", -- login page 17 | "/error/" -- error page 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/main.lua: -------------------------------------------------------------------------------- 1 | local lor = require("lor.index") 2 | local session_middleware = require("lor.lib.middleware.session") 3 | local check_login_middleware = require("app.middleware.check_login") 4 | local whitelist = require("app.config.config").whitelist 5 | local router = require("app.router") 6 | local app = lor() 7 | 8 | app:conf("view enable", true) 9 | app:conf("view engine", "tmpl") 10 | app:conf("view ext", "html") 11 | app:conf("views", "./app/views") 12 | 13 | app:use(session_middleware()) 14 | 15 | -- filter: add response header 16 | app:use(function(req, res, next) 17 | res:set_header('X-Powered-By', 'Lor Framework') 18 | next() 19 | end) 20 | 21 | -- intercepter: login or not 22 | app:use(check_login_middleware(whitelist)) 23 | 24 | router(app) -- business routers and routes 25 | 26 | -- 404 error 27 | -- app:use(function(req, res, next) 28 | -- if req:is_found() ~= true then 29 | -- res:status(404):send("404! sorry, page not found.") 30 | -- end 31 | -- end) 32 | 33 | -- error handle middleware 34 | app:erroruse(function(err, req, res, next) 35 | ngx.log(ngx.ERR, err) 36 | if req:is_found() ~= true then 37 | res:status(404):send("404! sorry, page not found. uri:" .. req.path) 38 | else 39 | res:status(500):send("unknown error") 40 | end 41 | end) 42 | 43 | 44 | --ngx.say(app.router.trie:gen_graph()) 45 | 46 | app:run() 47 | -------------------------------------------------------------------------------- /app/middleware/README.md: -------------------------------------------------------------------------------- 1 | 2 | ### 自定义插件目录(define your own middleware) 3 | 4 | 5 | You are recommended to define your own middlewares and keep them in one place to manage. 6 | 7 | 建议用户将自定义插件存放在此目录下统一管理,然后在其他地方引用,插件的格式如下: 8 | 9 | ``` 10 | local middleware = function(params) 11 | return function(req, res, next) 12 | -- do something with req/res 13 | next() 14 | end 15 | end 16 | 17 | return middleware 18 | ``` 19 | 20 | -------------------------------------------------------------------------------- /app/middleware/check_login.lua: -------------------------------------------------------------------------------- 1 | local function isLogin(req) 2 | local login = false 3 | if req.session then 4 | local session_username = req.session.get("username") 5 | if session_username and session_username ~= "" then 6 | login = true 7 | end 8 | end 9 | 10 | return login 11 | end 12 | 13 | local function checkLogin(whitelist) 14 | return function(req, res, next) 15 | local requestPath = req.path 16 | 17 | local in_white_list = false 18 | for i, v in ipairs(whitelist) do 19 | if requestPath == v then 20 | in_white_list = true 21 | end 22 | end 23 | 24 | ngx.log(ngx.ERR, "-------->", requestPath, " ", in_white_list) 25 | 26 | if in_white_list then 27 | next() 28 | else 29 | if isLogin(req) then 30 | next() 31 | else 32 | res:redirect("/auth/login") 33 | end 34 | end 35 | end 36 | end 37 | 38 | return checkLogin 39 | 40 | -------------------------------------------------------------------------------- /app/model/todo.lua: -------------------------------------------------------------------------------- 1 | local todos = { 2 | { 3 | id = "1", 4 | title = "第1个todo", 5 | completed = false 6 | }, 7 | { 8 | id = "2", 9 | title = "第2个todo", 10 | completed = false 11 | }, 12 | } 13 | 14 | 15 | return todos -------------------------------------------------------------------------------- /app/router.lua: -------------------------------------------------------------------------------- 1 | local authRouter = require("app.routes.auth") 2 | local todoRouter = require("app.routes.todo") 3 | local errorRouter = require("app.routes.error") 4 | 5 | return function(app) 6 | app:use("/auth", authRouter()) 7 | app:use("/todo", todoRouter()) 8 | app:use("/error", errorRouter()) 9 | 10 | app:get("/", function(req, res, next) 11 | local data = { 12 | name = req.query.name or "lor", 13 | desc = req.query.desc or 'a framework of lua based on OpenResty' 14 | } 15 | res:render("welcome", data) 16 | end) 17 | end 18 | 19 | -------------------------------------------------------------------------------- /app/routes/auth.lua: -------------------------------------------------------------------------------- 1 | local pairs = pairs 2 | local ipairs = ipairs 3 | local lor = require("lor.index") 4 | local users = require("app.config.config").users 5 | local authRouter = lor:Router() 6 | 7 | authRouter:get("/login", function(req, res, next) 8 | res:render("login") 9 | end) 10 | 11 | authRouter:post("/login", function(req, res, next) 12 | local username = req.body.username 13 | local password = req.body.password 14 | 15 | local isExist = false 16 | for i, v in ipairs(users) do 17 | if v.username == username and v.password == password then 18 | req.session.set("username", username) 19 | isExist = true 20 | return res:redirect("/todo/index") 21 | end 22 | end 23 | 24 | if not isExist then 25 | res:redirect("/error/",{ 26 | errMsg = "Wrong username or password! Please check." 27 | }) 28 | end 29 | end) 30 | 31 | authRouter:get("/logout", function(req, res, next) 32 | req.session.destroy() 33 | res:redirect("/auth/login") 34 | end) 35 | 36 | return authRouter 37 | 38 | -------------------------------------------------------------------------------- /app/routes/error.lua: -------------------------------------------------------------------------------- 1 | local pairs = pairs 2 | local ipairs = ipairs 3 | local lor = require("lor.index") 4 | local errorRouter = lor:Router() 5 | 6 | errorRouter:get("/", function(req, res, next) 7 | res:render("error", { 8 | msg = req.query.errMsg -- injected by the invoke request 9 | }) 10 | end) 11 | 12 | return errorRouter 13 | -------------------------------------------------------------------------------- /app/routes/todo.lua: -------------------------------------------------------------------------------- 1 | local pairs = pairs 2 | local ipairs = ipairs 3 | local tremove = table.remove 4 | local tinsert = table.insert 5 | local lor = require("lor.index") 6 | local todos = require("app.model.todo") 7 | local todoRuter = lor:Router() 8 | 9 | todoRuter:post("/complete", function(req, res, next) 10 | local id = req.body.id 11 | local completed = req.body.completed 12 | if completed == "true" then 13 | completed = true 14 | else 15 | completed = false 16 | end 17 | 18 | for i, v in ipairs(todos) do 19 | if v.id == id then 20 | v.completed = completed 21 | end 22 | end 23 | 24 | res:json({ 25 | success = true, 26 | msg = "mark successful" 27 | }) 28 | end) 29 | 30 | todoRuter:put("/add", function(req, res, next) 31 | local id = req.body.id 32 | local title = req.body.title 33 | local completed = req.body.completed 34 | if completed == "true" then 35 | completed = true 36 | else 37 | completed = false 38 | end 39 | 40 | tinsert(todos, { 41 | id = id, 42 | title = title, 43 | completed = completed 44 | }) 45 | 46 | res:json({ 47 | success = true, 48 | msg = "add successful" 49 | }) 50 | end) 51 | 52 | todoRuter:post("/update", function(req, res, next) 53 | local id = req.body.id 54 | local title = req.body.title 55 | 56 | for i, v in ipairs(todos) do 57 | if v.id == id then 58 | v.title = title 59 | end 60 | end 61 | 62 | res:json({ 63 | success = true, 64 | msg = "update successful" 65 | }) 66 | end) 67 | 68 | todoRuter:delete("/delete", function(req, res, next) 69 | local id = req.body.todoId 70 | 71 | for i=#todos, 1, -1 do 72 | if todos[i].id == id then 73 | table.remove(todos,i) 74 | end 75 | end 76 | 77 | res:json({ 78 | success = true, 79 | msg = "delete successful", 80 | data = { 81 | deleteId = id, 82 | todosLength = #todos, 83 | todos = todos 84 | } 85 | }) 86 | end) 87 | 88 | todoRuter:get("/find/:filter", function(req, res, next) 89 | local todo_type = req.params.filter 90 | local return_todos = {} 91 | 92 | if todo_type ~= "all" and todo_type ~= "active" and todo_type ~= "completed" then 93 | res:json({ 94 | success = false, 95 | msg = "wrong todo type, type must be one of 'all', 'active' or 'completed'" 96 | }) 97 | else 98 | for i, v in ipairs(todos) do 99 | local completed = v.completed 100 | if todo_type == "all" then 101 | tinsert(return_todos, v) 102 | elseif todo_type == "active" and not completed then 103 | tinsert(return_todos, v) 104 | elseif todo_type == "completed" and completed then 105 | tinsert(return_todos, v) 106 | end 107 | end 108 | 109 | res:json({ 110 | success = true, 111 | msg = "", 112 | data = return_todos 113 | }) 114 | end 115 | end) 116 | 117 | todoRuter:get("/index", function(req, res, next) 118 | local data = { 119 | username = req.session.get("username") 120 | } 121 | res:render("todo", data) 122 | end) 123 | 124 | return todoRuter 125 | -------------------------------------------------------------------------------- /app/static/css/app.css: -------------------------------------------------------------------------------- 1 | #main, #footer { 2 | display: none; 3 | } 4 | 5 | .lor { 6 | margin: 100px auto; 7 | width: 800px; 8 | } 9 | 10 | .name { 11 | display: block; 12 | font: 100 4.5em "Helvetica Neue","Open Sans",sans-serif; 13 | margin-bottom: 0.25em; 14 | } 15 | 16 | a { 17 | color: #259DFF; 18 | text-decoration: none; 19 | } 20 | 21 | .description { 22 | position: relative; 23 | top: -5px; 24 | font: 100 3em "Helvetica Neue","Open Sans",sans-serif; 25 | color: #AEAEAE; 26 | } 27 | 28 | .todo_link { 29 | margin: 30px 0 0 0; 30 | width: 100px; 31 | text-align: center; 32 | display: block; 33 | color: inherit; 34 | font-size: 16px; 35 | padding: 10px 18px; 36 | text-decoration: none; 37 | border: 1px solid transparent; 38 | border-radius: 5px; 39 | border-color: rgba(175, 47, 47, 0.2); 40 | } 41 | 42 | .todo_link:hover { 43 | border-color: rgba(175, 47, 47, 0.44); 44 | } 45 | 46 | h3 { 47 | font-size: 24px; 48 | margin: 150px 0 30px 0; 49 | font-weight: 500; 50 | line-height: 1.2; 51 | color: #000; 52 | } 53 | form input { 54 | padding: 16px 16px 16px 60px; 55 | border: 1px #eee solid; 56 | background: rgba(0, 0, 0, 0.003); 57 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 58 | position: relative; 59 | margin: 5px 0 20px 0; 60 | width: 400px; 61 | font-size: 24px; 62 | font-family: inherit; 63 | font-weight: inherit; 64 | line-height: 1.4em; 65 | outline: none; 66 | color: inherit; 67 | padding: 6px; 68 | box-sizing: border-box; 69 | -webkit-font-smoothing: antialiased; 70 | -moz-font-smoothing: antialiased; 71 | font-smoothing: antialiased; 72 | } 73 | button { 74 | color: inherit; 75 | margin: 3px; 76 | padding: 5px 7px; 77 | text-decoration: none; 78 | border: 1px solid transparent; 79 | border-radius: 3px; 80 | border-color: rgba(175, 47, 47, 0.2); 81 | } 82 | -------------------------------------------------------------------------------- /app/static/css/base.css: -------------------------------------------------------------------------------- 1 | hr { 2 | margin: 20px 0; 3 | border: 0; 4 | border-top: 1px dashed #c5c5c5; 5 | border-bottom: 1px dashed #f7f7f7; 6 | } 7 | 8 | .learn a { 9 | font-weight: normal; 10 | text-decoration: none; 11 | color: #b83f45; 12 | } 13 | 14 | .learn a:hover { 15 | text-decoration: underline; 16 | color: #787e7e; 17 | } 18 | 19 | .learn h3, 20 | .learn h4, 21 | .learn h5 { 22 | margin: 10px 0; 23 | font-weight: 500; 24 | line-height: 1.2; 25 | color: #000; 26 | } 27 | 28 | .learn h3 { 29 | font-size: 24px; 30 | } 31 | 32 | .learn h4 { 33 | font-size: 18px; 34 | } 35 | 36 | .learn h5 { 37 | margin-bottom: 0; 38 | font-size: 14px; 39 | } 40 | 41 | .learn ul { 42 | padding: 0; 43 | margin: 0 0 30px 25px; 44 | } 45 | 46 | .learn li { 47 | line-height: 20px; 48 | } 49 | 50 | .learn p { 51 | font-size: 15px; 52 | font-weight: 300; 53 | line-height: 1.3; 54 | margin-top: 0; 55 | margin-bottom: 0; 56 | } 57 | 58 | #issue-count { 59 | display: none; 60 | } 61 | 62 | .quote { 63 | border: none; 64 | margin: 20px 0 60px 0; 65 | } 66 | 67 | .quote p { 68 | font-style: italic; 69 | } 70 | 71 | .quote p:before { 72 | content: '“'; 73 | font-size: 50px; 74 | opacity: .15; 75 | position: absolute; 76 | top: -20px; 77 | left: 3px; 78 | } 79 | 80 | .quote p:after { 81 | content: '”'; 82 | font-size: 50px; 83 | opacity: .15; 84 | position: absolute; 85 | bottom: -42px; 86 | right: 3px; 87 | } 88 | 89 | .quote footer { 90 | position: absolute; 91 | bottom: -40px; 92 | right: 0; 93 | } 94 | 95 | .quote footer img { 96 | border-radius: 3px; 97 | } 98 | 99 | .quote footer a { 100 | margin-left: 5px; 101 | vertical-align: middle; 102 | } 103 | 104 | .speech-bubble { 105 | position: relative; 106 | padding: 10px; 107 | background: rgba(0, 0, 0, .04); 108 | border-radius: 5px; 109 | } 110 | 111 | .speech-bubble:after { 112 | content: ''; 113 | position: absolute; 114 | top: 100%; 115 | right: 30px; 116 | border: 13px solid transparent; 117 | border-top-color: rgba(0, 0, 0, .04); 118 | } 119 | 120 | .learn-bar > .learn { 121 | position: absolute; 122 | width: 272px; 123 | top: 8px; 124 | left: -300px; 125 | padding: 10px; 126 | border-radius: 5px; 127 | background-color: rgba(255, 255, 255, .6); 128 | transition-property: left; 129 | transition-duration: 500ms; 130 | } 131 | 132 | @media (min-width: 899px) { 133 | .learn-bar { 134 | width: auto; 135 | padding-left: 300px; 136 | } 137 | 138 | .learn-bar > .learn { 139 | left: 8px; 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/static/css/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | margin: 0; 4 | padding: 0; 5 | } 6 | 7 | button { 8 | margin: 0; 9 | padding: 0; 10 | border: 0; 11 | background: none; 12 | font-size: 100%; 13 | vertical-align: baseline; 14 | font-family: inherit; 15 | font-weight: inherit; 16 | color: inherit; 17 | -webkit-appearance: none; 18 | appearance: none; 19 | -webkit-font-smoothing: antialiased; 20 | -moz-font-smoothing: antialiased; 21 | font-smoothing: antialiased; 22 | } 23 | 24 | body { 25 | font: 14px 'Helvetica Neue', Helvetica, Arial, sans-serif; 26 | line-height: 1.4em; 27 | background: #f5f5f5; 28 | color: #4d4d4d; 29 | min-width: 230px; 30 | margin: 0 auto; 31 | -webkit-font-smoothing: antialiased; 32 | -moz-font-smoothing: antialiased; 33 | font-smoothing: antialiased; 34 | font-weight: 300; 35 | } 36 | 37 | button, input[type="checkbox"] { 38 | outline: none; 39 | } 40 | 41 | .hidden { 42 | display: none; 43 | } 44 | 45 | #todoapp { 46 | background: #fff; 47 | margin: 130px 0 40px 0; 48 | position: relative; 49 | box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 50 | 0 25px 50px 0 rgba(0, 0, 0, 0.1); 51 | } 52 | 53 | #todoapp input::-webkit-input-placeholder { 54 | font-style: italic; 55 | font-weight: 300; 56 | color: #e6e6e6; 57 | } 58 | 59 | #todoapp input::-moz-placeholder { 60 | font-style: italic; 61 | font-weight: 300; 62 | color: #e6e6e6; 63 | } 64 | 65 | #todoapp input::input-placeholder { 66 | font-style: italic; 67 | font-weight: 300; 68 | color: #e6e6e6; 69 | } 70 | 71 | #todoapp h1 { 72 | position: absolute; 73 | top: -155px; 74 | width: 100%; 75 | font-size: 100px; 76 | font-weight: 100; 77 | text-align: center; 78 | color: rgba(175, 47, 47, 0.15); 79 | -webkit-text-rendering: optimizeLegibility; 80 | -moz-text-rendering: optimizeLegibility; 81 | text-rendering: optimizeLegibility; 82 | } 83 | 84 | #new-todo, 85 | .edit { 86 | position: relative; 87 | margin: 0; 88 | width: 100%; 89 | font-size: 24px; 90 | font-family: inherit; 91 | font-weight: inherit; 92 | line-height: 1.4em; 93 | border: 0; 94 | outline: none; 95 | color: inherit; 96 | padding: 6px; 97 | border: 1px solid #999; 98 | box-shadow: inset 0 -1px 5px 0 rgba(0, 0, 0, 0.2); 99 | box-sizing: border-box; 100 | -webkit-font-smoothing: antialiased; 101 | -moz-font-smoothing: antialiased; 102 | font-smoothing: antialiased; 103 | } 104 | 105 | #new-todo { 106 | padding: 16px 16px 16px 60px; 107 | border: none; 108 | background: rgba(0, 0, 0, 0.003); 109 | box-shadow: inset 0 -2px 1px rgba(0,0,0,0.03); 110 | } 111 | 112 | #main { 113 | position: relative; 114 | z-index: 2; 115 | border-top: 1px solid #e6e6e6; 116 | } 117 | 118 | label[for='toggle-all'] { 119 | display: none; 120 | } 121 | 122 | #toggle-all { 123 | position: absolute; 124 | top: -55px; 125 | left: -12px; 126 | width: 60px; 127 | height: 34px; 128 | text-align: center; 129 | border: none; /* Mobile Safari */ 130 | } 131 | 132 | #toggle-all:before { 133 | content: '❯'; 134 | font-size: 22px; 135 | color: #e6e6e6; 136 | padding: 10px 27px 10px 27px; 137 | } 138 | 139 | #toggle-all:checked:before { 140 | color: #737373; 141 | } 142 | 143 | #todo-list { 144 | margin: 0; 145 | padding: 0; 146 | list-style: none; 147 | } 148 | 149 | #todo-list li { 150 | position: relative; 151 | font-size: 24px; 152 | border-bottom: 1px solid #ededed; 153 | } 154 | 155 | #todo-list li:last-child { 156 | border-bottom: none; 157 | } 158 | 159 | #todo-list li.editing { 160 | border-bottom: none; 161 | padding: 0; 162 | } 163 | 164 | #todo-list li.editing .edit { 165 | display: block; 166 | width: 506px; 167 | padding: 13px 17px 12px 17px; 168 | margin: 0 0 0 43px; 169 | } 170 | 171 | #todo-list li.editing .view { 172 | display: none; 173 | } 174 | 175 | #todo-list li .toggle { 176 | text-align: center; 177 | width: 40px; 178 | /* auto, since non-WebKit browsers doesn't support input styling */ 179 | height: auto; 180 | position: absolute; 181 | top: 0; 182 | bottom: 0; 183 | margin: auto 0; 184 | border: none; /* Mobile Safari */ 185 | -webkit-appearance: none; 186 | appearance: none; 187 | } 188 | 189 | #todo-list li .toggle:after { 190 | content: url('data:image/svg+xml;utf8,'); 191 | } 192 | 193 | #todo-list li .toggle:checked:after { 194 | content: url('data:image/svg+xml;utf8,'); 195 | } 196 | 197 | #todo-list li label { 198 | white-space: pre-line; 199 | word-break: break-all; 200 | padding: 15px 60px 15px 15px; 201 | margin-left: 45px; 202 | display: block; 203 | line-height: 1.2; 204 | transition: color 0.4s; 205 | } 206 | 207 | #todo-list li.completed label { 208 | color: #d9d9d9; 209 | text-decoration: line-through; 210 | } 211 | 212 | #todo-list li .destroy { 213 | padding-top:0px; 214 | display: none; 215 | position: absolute; 216 | top: 0; 217 | right: 10px; 218 | bottom: 0; 219 | width: 40px; 220 | height: 40px; 221 | margin: auto 0; 222 | font-size: 30px; 223 | color: #cc9a9a; 224 | margin-bottom: 11px; 225 | transition: color 0.2s ease-out; 226 | } 227 | 228 | #todo-list li .destroy:hover { 229 | color: #af5b5e; 230 | } 231 | 232 | #todo-list li .destroy:after { 233 | content: '×'; 234 | } 235 | 236 | #todo-list li:hover .destroy { 237 | display: block; 238 | } 239 | 240 | #todo-list li .edit { 241 | display: none; 242 | } 243 | 244 | #todo-list li.editing:last-child { 245 | margin-bottom: -1px; 246 | } 247 | 248 | #footer { 249 | color: #777; 250 | padding: 10px 15px; 251 | height: 20px; 252 | text-align: center; 253 | border-top: 1px solid #e6e6e6; 254 | } 255 | 256 | #footer:before { 257 | content: ''; 258 | position: absolute; 259 | right: 0; 260 | bottom: 0; 261 | left: 0; 262 | height: 50px; 263 | overflow: hidden; 264 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2), 265 | 0 8px 0 -3px #f6f6f6, 266 | 0 9px 1px -3px rgba(0, 0, 0, 0.2), 267 | 0 16px 0 -6px #f6f6f6, 268 | 0 17px 2px -6px rgba(0, 0, 0, 0.2); 269 | } 270 | 271 | #todo-count { 272 | float: left; 273 | text-align: left; 274 | } 275 | 276 | #todo-count strong { 277 | font-weight: 300; 278 | } 279 | 280 | #filters { 281 | margin: 0; 282 | padding: 0; 283 | list-style: none; 284 | position: absolute; 285 | right: 0; 286 | left: 0; 287 | } 288 | 289 | #filters li { 290 | display: inline; 291 | } 292 | 293 | #filters li a { 294 | color: inherit; 295 | margin: 3px; 296 | padding: 3px 7px; 297 | text-decoration: none; 298 | border: 1px solid transparent; 299 | border-radius: 3px; 300 | } 301 | 302 | #filters li a.selected, 303 | #filters li a:hover { 304 | border-color: rgba(175, 47, 47, 0.1); 305 | } 306 | 307 | #filters li a.selected { 308 | border-color: rgba(175, 47, 47, 0.2); 309 | } 310 | 311 | #clear-completed, 312 | html #clear-completed:active { 313 | float: right; 314 | position: relative; 315 | line-height: 20px; 316 | text-decoration: none; 317 | cursor: pointer; 318 | position: relative; 319 | } 320 | 321 | #clear-completed:hover { 322 | text-decoration: underline; 323 | } 324 | 325 | #info { 326 | margin: 65px auto 0; 327 | color: #bfbfbf; 328 | font-size: 10px; 329 | text-shadow: 0 1px 0 rgba(255, 255, 255, 0.5); 330 | text-align: center; 331 | } 332 | 333 | #info p { 334 | line-height: 1; 335 | } 336 | 337 | #info a { 338 | color: inherit; 339 | text-decoration: none; 340 | font-weight: 400; 341 | } 342 | 343 | #info a:hover { 344 | text-decoration: underline; 345 | } 346 | 347 | /* 348 | Hack to remove background from Mobile Safari. 349 | Can't use it globally since it destroys checkboxes in Firefox 350 | */ 351 | @media screen and (-webkit-min-device-pixel-ratio:0) { 352 | #toggle-all, 353 | #todo-list li .toggle { 354 | background: none; 355 | } 356 | 357 | #todo-list li .toggle { 358 | height: 40px; 359 | } 360 | 361 | #toggle-all { 362 | -webkit-transform: rotate(90deg); 363 | transform: rotate(90deg); 364 | -webkit-appearance: none; 365 | appearance: none; 366 | } 367 | } 368 | 369 | @media (max-width: 430px) { 370 | #footer { 371 | height: 50px; 372 | } 373 | 374 | #filters { 375 | bottom: 10px; 376 | } 377 | } 378 | -------------------------------------------------------------------------------- /app/static/js/app.js: -------------------------------------------------------------------------------- 1 | var ENTER_KEY = 13; 2 | var ESCAPE_KEY = 27; 3 | 4 | var util = { 5 | uuid: function () { 6 | /*jshint bitwise:false */ 7 | var i, random; 8 | var uuid = ''; 9 | 10 | for (i = 0; i < 32; i++) { 11 | random = Math.random() * 16 | 0; 12 | if (i === 8 || i === 12 || i === 16 || i === 20) { 13 | uuid += '-'; 14 | } 15 | uuid += (i === 12 ? 4 : (i === 16 ? (random & 3 | 8) : random)).toString(16); 16 | } 17 | 18 | return uuid; 19 | }, 20 | pluralize: function (count, word) { 21 | return count === 1 ? word : word + 's'; 22 | } 23 | }; 24 | 25 | 26 | (function (L) { 27 | var _this = null; 28 | L.Todo = L.Todo || {}; 29 | _this = L.Todo = { 30 | todo: [], 31 | filter: "all", 32 | 33 | init: function () { 34 | _this.todos = []; 35 | _this.todoTemplate = function(todos){ 36 | console.dir(todos) 37 | var data = { 38 | list: todos 39 | }; 40 | var tpl = $('#todo-template').html(); 41 | return juicer(tpl, data); 42 | } 43 | _this.footerTemplate = function(data){ 44 | var tpl = $('#footer-template').html(); 45 | return juicer(tpl, data) 46 | } 47 | 48 | _this.bindEvents(); 49 | _this.getByType("all"); 50 | 51 | }, 52 | 53 | getByType: function(filter){ 54 | var self = _this; 55 | $.ajax({ 56 | url : '/todo/find/'+filter, 57 | type : 'get', 58 | data : {}, 59 | dataType : 'json', 60 | success : function(result) { 61 | if(result.success ){ 62 | self.todos = result.data; 63 | self.filter = filter 64 | self.render(); 65 | }else{ 66 | self.tip(result.msg); 67 | } 68 | }, 69 | error : function() { 70 | self.tip("error to find todos"); 71 | } 72 | }); 73 | }, 74 | 75 | tip: function(msg){ 76 | $('#tipMsg').text("Tip: " + msg).show(); 77 | }, 78 | 79 | bindEvents: function () { 80 | $('#new-todo').on('keyup', _this.create.bind(_this)); 81 | $('#toggle-all').on('change', _this.toggleAll.bind(_this)); 82 | $('#footer').on('click', '#clear-completed', _this.destroyCompleted.bind(_this)); 83 | $('#todo-list') 84 | .on('change', '.toggle', _this.toggle.bind(_this)) 85 | .on('dblclick', 'label', _this.edit.bind(_this)) 86 | .on('keyup', '.edit', _this.editKeyup.bind(_this)) 87 | .on('focusout', '.edit', _this.update) 88 | .on('click', '.destroy', _this.destroy.bind(_this)); 89 | 90 | $(document).on('click', '#filters li a',function(){ 91 | var filter = $(this).attr("data-filter") 92 | _this.getByType(filter); 93 | }); 94 | }, 95 | render: function () { 96 | var todos = _this.getFilteredTodos(); 97 | $('#todo-list').html(_this.todoTemplate(todos)); 98 | $('#main').toggle(todos.length > 0); 99 | $('#toggle-all').prop('checked', _this.getActiveTodos().length === 0); 100 | _this.renderFooter(); 101 | $('#new-todo').focus(); 102 | }, 103 | renderFooter: function () { 104 | var todoCount = _this.todos.length; 105 | var activeTodoCount = $("#todo-list").children().length; 106 | console.log("todoCount", todoCount, "activeTodoCount", activeTodoCount) 107 | 108 | var template = _this.footerTemplate({ 109 | activeTodoCount: activeTodoCount, 110 | activeTodoWord: util.pluralize(activeTodoCount, 'item'), 111 | filter: _this.filter 112 | }); 113 | 114 | $('#footer').html(template); 115 | }, 116 | toggleAll: function (e) { 117 | var isChecked = $(e.target).prop('checked'); 118 | 119 | _this.todos.forEach(function (todo) { 120 | todo.completed = isChecked; 121 | }); 122 | 123 | _this.render(); 124 | }, 125 | getActiveTodos: function () { 126 | return _this.todos.filter(function (todo) { 127 | return !todo.completed; 128 | }); 129 | }, 130 | getCompletedTodos: function () { 131 | return _this.todos.filter(function (todo) { 132 | return todo.completed; 133 | }); 134 | }, 135 | getFilteredTodos: function () { 136 | if (_this.filter === 'active') { 137 | return _this.getActiveTodos(); 138 | } 139 | 140 | if (_this.filter === 'completed') { 141 | return _this.getCompletedTodos(); 142 | } 143 | 144 | return _this.todos; 145 | }, 146 | destroyCompleted: function () { 147 | console.log("destroy") 148 | _this.todos = _this.getActiveTodos(); 149 | _this.filter = 'all'; 150 | _this.render(); 151 | }, 152 | // accepts an element from inside the `.item` div and 153 | // returns the corresponding index in the `todos` array 154 | indexFromEl: function (el) { 155 | var id = $(el).closest('li').attr('data-id'); 156 | var todos = _this.todos; 157 | var i = todos.length; 158 | 159 | while (i--) { 160 | if (todos[i].id === id) { 161 | return i; 162 | } 163 | } 164 | }, 165 | create: function (e) { 166 | var $input = $(e.target); 167 | var val = $input.val().trim(); 168 | 169 | if (e.which !== ENTER_KEY || !val) { 170 | return; 171 | } 172 | 173 | var new_todo = { 174 | id: util.uuid(), 175 | title: val, 176 | completed: false 177 | }; 178 | 179 | $.ajax({ 180 | url : '/todo/add', 181 | type : 'put', 182 | data : new_todo, 183 | dataType : 'json', 184 | success : function(result) { 185 | if(result.success ){ 186 | _this.todos.push(new_todo); 187 | $input.val(''); 188 | _this.render(); 189 | }else{ 190 | self.tip(result.msg); 191 | } 192 | }, 193 | error : function() { 194 | self.tip("error to create todo"); 195 | } 196 | }); 197 | }, 198 | toggle: function (e) { 199 | var i = _this.indexFromEl(e.target); 200 | _this.todos[i].completed = !_this.todos[i].completed; 201 | var completed = _this.todos[i].completed 202 | var id = _this.todos[i].id 203 | 204 | $.ajax({ 205 | url : '/todo/complete', 206 | type : 'post', 207 | data : { 208 | id: id, 209 | completed: completed 210 | }, 211 | dataType : 'json', 212 | success : function(result) { 213 | if(result.success ){ 214 | _this.render(); 215 | }else{ 216 | self.tip(result.msg); 217 | } 218 | }, 219 | error : function() { 220 | self.tip("error to mark todo completed status"); 221 | } 222 | }); 223 | }, 224 | edit: function (e) { 225 | var $input = $(e.target).closest('li').addClass('editing').find('.edit'); 226 | $input.val($input.val()).focus(); 227 | }, 228 | editKeyup: function (e) { 229 | if (e.which === ENTER_KEY) { 230 | e.target.blur(); 231 | } 232 | 233 | if (e.which === ESCAPE_KEY) { 234 | $(e.target).data('abort', true).blur(); 235 | } 236 | }, 237 | update: function (e) { 238 | var el = e.target; 239 | var $el = $(el); 240 | var val = $el.val().trim(); 241 | 242 | if (!val) { 243 | _this.destroy(e); 244 | return; 245 | } 246 | 247 | if ($el.data('abort')) { 248 | $el.data('abort', false); 249 | } else { 250 | console.log(_this.indexFromEl(el)) 251 | 252 | var index = _this.indexFromEl(el); 253 | 254 | 255 | var todo = _this.todos[index]; 256 | var id = todo.id; 257 | 258 | $.ajax({ 259 | url : '/todo/update', 260 | type : 'post', 261 | data : { 262 | id: id, 263 | title: val 264 | }, 265 | dataType : 'json', 266 | success : function(result) { 267 | if(result.success ){ 268 | _this.todos[index].title = val; 269 | _this.render(); 270 | }else{ 271 | self.tip(result.msg); 272 | } 273 | }, 274 | error : function() { 275 | self.tip("error to update todo"); 276 | } 277 | }); 278 | } 279 | }, 280 | 281 | destroy: function (e) { 282 | var index = _this.indexFromEl(e.target); 283 | var todo = _this.todos[index]; 284 | var id = todo.id; 285 | 286 | $.ajax({ 287 | url : '/todo/delete', 288 | type : 'delete', 289 | data : { 290 | todoId: id 291 | }, 292 | dataType : 'json', 293 | success : function(result) { 294 | if(result.success ){ 295 | _this.todos.splice(index, 1); 296 | _this.render(); 297 | }else{ 298 | self.tip(result.msg); 299 | } 300 | }, 301 | error : function() { 302 | self.tip("error to delete todo"); 303 | } 304 | }); 305 | }, 306 | 307 | formatDate: function (now) { 308 | var year = now.getFullYear(); 309 | var month = now.getMonth() + 1; 310 | var date = now.getDate(); 311 | var hour = now.getHours(); 312 | var minute = now.getMinutes(); 313 | var second = now.getSeconds(); 314 | if (second < 10) second = "0" + second; 315 | return year + "-" + month + "-" + date + " " + hour + ":" + minute + ":" + second; 316 | } 317 | }; 318 | }(Lor)); 319 | -------------------------------------------------------------------------------- /app/static/js/base.js: -------------------------------------------------------------------------------- 1 | var Lor = {} -------------------------------------------------------------------------------- /app/static/js/juicer-min.js: -------------------------------------------------------------------------------- 1 | (function(){var c=function(){var e=[].slice.call(arguments);e.push(c.options);if(e[0].match(/^\s*#([\w:\-\.]+)\s*$/igm)){e[0].replace(/^\s*#([\w:\-\.]+)\s*$/igm,function(h,i){var f=document;var g=f&&f.getElementById(i);e[0]=g?(g.value||g.innerHTML):h;});}if(arguments.length==1){return c.compile.apply(c,e);}if(arguments.length>=2){return c.to_html.apply(c,e);}};var d={escapehash:{"<":"<",">":">","&":"&",'"':""","'":"'","/":"/"},escapereplace:function(e){return d.escapehash[e];},escaping:function(e){return typeof(e)!=="string"?e:e.replace(/[&<>"]/igm,this.escapereplace);},detection:function(e){return typeof(e)==="undefined"?"":e;}};var b=function(e){if(typeof(console)!=="undefined"){if(console.warn){console.warn(e);return;}if(console.log){console.log(e);return;}}throw (e);};var a=function(h,f){h=h!==Object(h)?{}:h;if(h.__proto__){h.__proto__=f;return h;}var g=function(){};var j=Object.create?Object.create(f):new (g.prototype=f,g);for(var e in h){if(h.hasOwnProperty(e)){j[e]=h[e];}}return j;};c.__cache={};c.version="0.6.5-stable";c.settings={};c.tags={operationOpen:"{@",operationClose:"}",interpolateOpen:"\\${",interpolateClose:"}",noneencodeOpen:"\\$\\${",noneencodeClose:"}",commentOpen:"\\{#",commentClose:"\\}"};c.options={cache:true,strip:true,errorhandling:true,detection:true,_method:a({__escapehtml:d,__throw:b,__juicer:c},{})};c.tagInit=function(){var f=c.tags.operationOpen+"each\\s*([^}]*?)\\s*as\\s*(\\w*?)\\s*(,\\s*\\w*?)?"+c.tags.operationClose;var h=c.tags.operationOpen+"\\/each"+c.tags.operationClose;var i=c.tags.operationOpen+"if\\s*([^}]*?)"+c.tags.operationClose;var j=c.tags.operationOpen+"\\/if"+c.tags.operationClose;var n=c.tags.operationOpen+"else"+c.tags.operationClose;var o=c.tags.operationOpen+"else if\\s*([^}]*?)"+c.tags.operationClose;var k=c.tags.interpolateOpen+"([\\s\\S]+?)"+c.tags.interpolateClose;var l=c.tags.noneencodeOpen+"([\\s\\S]+?)"+c.tags.noneencodeClose;var m=c.tags.commentOpen+"[^}]*?"+c.tags.commentClose;var g=c.tags.operationOpen+"each\\s*(\\w*?)\\s*in\\s*range\\(([^}]+?)\\s*,\\s*([^}]+?)\\)"+c.tags.operationClose;var e=c.tags.operationOpen+"include\\s*([^}]*?)\\s*,\\s*([^}]*?)"+c.tags.operationClose;c.settings.forstart=new RegExp(f,"igm");c.settings.forend=new RegExp(h,"igm");c.settings.ifstart=new RegExp(i,"igm");c.settings.ifend=new RegExp(j,"igm");c.settings.elsestart=new RegExp(n,"igm");c.settings.elseifstart=new RegExp(o,"igm");c.settings.interpolate=new RegExp(k,"igm");c.settings.noneencode=new RegExp(l,"igm");c.settings.inlinecomment=new RegExp(m,"igm");c.settings.rangestart=new RegExp(g,"igm");c.settings.include=new RegExp(e,"igm");};c.tagInit();c.set=function(f,j){var h=this;var e=function(i){return i.replace(/[\$\(\)\[\]\+\^\{\}\?\*\|\.]/igm,function(l){return"\\"+l;});};var k=function(l,m){var i=l.match(/^tag::(.*)$/i);if(i){h.tags[i[1]]=e(m);h.tagInit();return;}h.options[l]=m;};if(arguments.length===2){k(f,j);return;}if(f===Object(f)){for(var g in f){if(f.hasOwnProperty(g)){k(g,f[g]);}}}};c.register=function(g,f){var e=this.options._method;if(e.hasOwnProperty(g)){return false;}return e[g]=f;};c.unregister=function(f){var e=this.options._method;if(e.hasOwnProperty(f)){return delete e[f];}};c.template=function(e){var f=this;this.options=e;this.__interpolate=function(g,l,i){var h=g.split("|"),k=h[0]||"",j;if(h.length>1){g=h.shift();j=h.shift().split(",");k="_method."+j.shift()+".call({}, "+[g].concat(j)+")";}return"<%= "+(l?"_method.__escapehtml.escaping":"")+"("+(!i||i.detection!==false?"_method.__escapehtml.detection":"")+"("+k+")) %>";};this.__removeShell=function(h,g){var i=0;h=h.replace(c.settings.forstart,function(n,k,m,l){var m=m||"value",l=l&&l.substr(1);var j="i"+i++;return"<% ~function() {for(var "+j+" in "+k+") {if("+k+".hasOwnProperty("+j+")) {var "+m+"="+k+"["+j+"];"+(l?("var "+l+"="+j+";"):"")+" %>";}).replace(c.settings.forend,"<% }}}(); %>").replace(c.settings.ifstart,function(j,k){return"<% if("+k+") { %>";}).replace(c.settings.ifend,"<% } %>").replace(c.settings.elsestart,function(j){return"<% } else { %>";}).replace(c.settings.elseifstart,function(j,k){return"<% } else if("+k+") { %>";}).replace(c.settings.noneencode,function(k,j){return f.__interpolate(j,false,g);}).replace(c.settings.interpolate,function(k,j){return f.__interpolate(j,true,g);}).replace(c.settings.inlinecomment,"").replace(c.settings.rangestart,function(m,l,n,k){var j="j"+i++;return"<% ~function() {for(var "+j+"="+n+";"+j+"<"+k+";"+j+"++) {{var "+l+"="+j+"; %>";}).replace(c.settings.include,function(l,j,k){return"<%= _method.__juicer("+j+", "+k+"); %>";});if(!g||g.errorhandling!==false){h="<% try { %>"+h;h+='<% } catch(e) {_method.__throw("Juicer Render Exception: "+e.message);} %>';}return h;};this.__toNative=function(h,g){return this.__convert(h,!g||g.strip);};this.__lexicalAnalyze=function(k){var j=[];var o=[];var n="";var g=["if","each","_","_method","console","break","case","catch","continue","debugger","default","delete","do","finally","for","function","in","instanceof","new","return","switch","this","throw","try","typeof","var","void","while","with","null","typeof","class","enum","export","extends","import","super","implements","interface","let","package","private","protected","public","static","yield","const","arguments","true","false","undefined","NaN"];var m=function(r,q){if(Array.prototype.indexOf&&r.indexOf===Array.prototype.indexOf){return r.indexOf(q);}for(var p=0;p=,\(\)\[\]]\s*([A-Za-z_]+)/igm,h);for(var l=0;l";};this.__convert=function(h,i){var g=[].join("");g+="'use strict';";g+="var _=_||{};";g+="var _out='';_out+='";if(i!==false){g+=h.replace(/\\/g,"\\\\").replace(/[\r\t\n]/g," ").replace(/'(?=[^%]*%>)/g,"\t").split("'").join("\\'").split("\t").join("'").replace(/<%=(.+?)%>/g,"';_out+=$1;_out+='").split("<%").join("';").split("%>").join("_out+='")+"';return _out;";return g;}g+=h.replace(/\\/g,"\\\\").replace(/[\r]/g,"\\r").replace(/[\t]/g,"\\t").replace(/[\n]/g,"\\n").replace(/'(?=[^%]*%>)/g,"\t").split("'").join("\\'").split("\t").join("'").replace(/<%=(.+?)%>/g,"';_out+=$1;_out+='").split("<%").join("';").split("%>").join("_out+='")+"';return _out.replace(/[\\r\\n]\\s+[\\r\\n]/g, '\\r\\n');";return g;};this.parse=function(h,g){var i=this;if(!g||g.loose!==false){h=this.__lexicalAnalyze(h)+h;}h=this.__removeShell(h,g);h=this.__toNative(h,g);this._render=new Function("_, _method",h);this.render=function(k,j){if(!j||j!==f.options._method){j=a(j,f.options._method);}return i._render.call(this,k,j);};return this;};};c.compile=function(g,f){if(!f||f!==this.options){f=a(f,this.options);}try{var h=this.__cache[g]?this.__cache[g]:new this.template(this.options).parse(g,f);if(!f||f.cache!==false){this.__cache[g]=h;}return h;}catch(i){b("Juicer Compile Exception: "+i.message);return{render:function(){}};}};c.to_html=function(f,g,e){if(!e||e!==this.options){e=a(e,this.options);}return this.compile(f,e).render(g,e._method);};typeof(module)!=="undefined"&&module.exports?module.exports=c:this.juicer=c;})(); -------------------------------------------------------------------------------- /app/views/error.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lor Framework - Error page 8 | 9 | 10 | 11 | 12 | 13 |

Error

14 | 15 | {{msg}} 16 | 17 | 18 | -------------------------------------------------------------------------------- /app/views/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Lor Framework - Login 8 | 9 | 10 | 11 | 12 | 13 |

Login

14 | 15 |
16 | Username: 17 | Password: 18 | 19 |
20 | account: test/test or sumory/1 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/views/todo.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Lor Framework - Todo Example 7 | 8 | 9 | 10 | 11 | 12 | 13 | 36 |
37 | 42 |
43 | 44 | 45 |
    46 | 47 |
48 |
49 |
50 | 0 items left 51 | 56 | 57 |
58 |
59 |
60 |

61 |

Double-click to edit a todo

62 |

Modified from TodoMVC example

63 |
64 | 65 | 66 | 78 | 79 | 94 | 95 | 96 | 97 | 98 | 99 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /app/views/welcome.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | Lor Framework - Todo Example 4 | 5 | 6 | 7 | 8 |
9 | Powered by {{name}} 10 | {{desc}} 11 | Login to Todo 12 |
13 | 14 | 15 | -------------------------------------------------------------------------------- /conf/mime.types: -------------------------------------------------------------------------------- 1 | 2 | types { 3 | text/html html htm shtml; 4 | text/css css; 5 | text/xml xml; 6 | image/gif gif; 7 | image/jpeg jpeg jpg; 8 | application/javascript js; 9 | application/atom+xml atom; 10 | application/rss+xml rss; 11 | 12 | text/mathml mml; 13 | text/plain txt; 14 | text/vnd.sun.j2me.app-descriptor jad; 15 | text/vnd.wap.wml wml; 16 | text/x-component htc; 17 | 18 | image/png png; 19 | image/tiff tif tiff; 20 | image/vnd.wap.wbmp wbmp; 21 | image/x-icon ico; 22 | image/x-jng jng; 23 | image/x-ms-bmp bmp; 24 | image/svg+xml svg svgz; 25 | image/webp webp; 26 | 27 | application/font-woff woff; 28 | application/java-archive jar war ear; 29 | application/json json; 30 | application/mac-binhex40 hqx; 31 | application/msword doc; 32 | application/pdf pdf; 33 | application/postscript ps eps ai; 34 | application/rtf rtf; 35 | application/vnd.apple.mpegurl m3u8; 36 | application/vnd.ms-excel xls; 37 | application/vnd.ms-fontobject eot; 38 | application/vnd.ms-powerpoint ppt; 39 | application/vnd.wap.wmlc wmlc; 40 | application/vnd.google-earth.kml+xml kml; 41 | application/vnd.google-earth.kmz kmz; 42 | application/x-7z-compressed 7z; 43 | application/x-cocoa cco; 44 | application/x-java-archive-diff jardiff; 45 | application/x-java-jnlp-file jnlp; 46 | application/x-makeself run; 47 | application/x-perl pl pm; 48 | application/x-pilot prc pdb; 49 | application/x-rar-compressed rar; 50 | application/x-redhat-package-manager rpm; 51 | application/x-sea sea; 52 | application/x-shockwave-flash swf; 53 | application/x-stuffit sit; 54 | application/x-tcl tcl tk; 55 | application/x-x509-ca-cert der pem crt; 56 | application/x-xpinstall xpi; 57 | application/xhtml+xml xhtml; 58 | application/xspf+xml xspf; 59 | application/zip zip; 60 | 61 | application/octet-stream bin exe dll; 62 | application/octet-stream deb; 63 | application/octet-stream dmg; 64 | application/octet-stream iso img; 65 | application/octet-stream msi msp msm; 66 | 67 | application/vnd.openxmlformats-officedocument.wordprocessingml.document docx; 68 | application/vnd.openxmlformats-officedocument.spreadsheetml.sheet xlsx; 69 | application/vnd.openxmlformats-officedocument.presentationml.presentation pptx; 70 | 71 | audio/midi mid midi kar; 72 | audio/mpeg mp3; 73 | audio/ogg ogg; 74 | audio/x-m4a m4a; 75 | audio/x-realaudio ra; 76 | 77 | video/3gpp 3gpp 3gp; 78 | video/mp2t ts; 79 | video/mp4 mp4; 80 | video/mpeg mpeg mpg; 81 | video/quicktime mov; 82 | video/webm webm; 83 | video/x-flv flv; 84 | video/x-m4v m4v; 85 | video/x-mng mng; 86 | video/x-ms-asf asx asf; 87 | video/x-ms-wmv wmv; 88 | video/x-msvideo avi; 89 | } 90 | -------------------------------------------------------------------------------- /conf/nginx-dev.conf: -------------------------------------------------------------------------------- 1 | pid tmp/nginx.pid; 2 | 3 | worker_processes 4; 4 | 5 | events { 6 | worker_connections 4096; 7 | } 8 | 9 | http { 10 | sendfile on; 11 | include ./mime.types; 12 | 13 | # lor initialization 14 | lua_package_path "./app/?.lua;./?.lua;/usr/local/lor/?.lua;;"; 15 | lua_code_cache on; 16 | 17 | server { 18 | listen 9999; 19 | set $template_root ''; 20 | 21 | location /s { 22 | content_by_lua_file ./app/test.lua; 23 | } 24 | 25 | location /static { 26 | alias ./app/static; #app/static; 27 | } 28 | 29 | access_log logs/dev-access.log; 30 | error_log logs/dev-error.log; 31 | 32 | location / { 33 | content_by_lua_file ./app/main.lua; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /reload.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ##################################################################### 4 | # usage: 5 | # sh reload.sh -- reload application @dev 6 | # sh reload.sh ${env} -- reload application @${env} 7 | 8 | # examples: 9 | # sh reload.sh prod -- use conf/nginx-prod.conf to reload OpenResty 10 | # sh reload.sh -- use conf/nginx-dev.conf to reload OpenRest 11 | ##################################################################### 12 | 13 | if [ -n "$1" ];then 14 | PROFILE="$1" 15 | else 16 | PROFILE=dev 17 | fi 18 | 19 | mkdir -p logs & mkdir -p tmp 20 | echo "Use profile: "${PROFILE} 21 | nginx -s reload -p `pwd`/ -c conf/nginx-${PROFILE}.conf 22 | -------------------------------------------------------------------------------- /start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ##################################################################### 4 | # usage: 5 | # sh start.sh -- start application @dev 6 | # sh start.sh ${env} -- start application @${env} 7 | 8 | # examples: 9 | # sh start.sh prod -- use conf/nginx-prod.conf to start OpenResty 10 | # sh start.sh -- use conf/nginx-dev.conf to start OpenRest 11 | ##################################################################### 12 | 13 | if [ -n "$1" ];then 14 | PROFILE="$1" 15 | else 16 | PROFILE=dev 17 | fi 18 | 19 | mkdir -p logs & mkdir -p tmp 20 | echo "Use profile: "${PROFILE} 21 | nginx -p `pwd`/ -c conf/nginx-${PROFILE}.conf 22 | -------------------------------------------------------------------------------- /stop.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | ##################################################################### 4 | # usage: 5 | # sh stop.sh -- stop application @dev 6 | # sh stop.sh ${env} -- stop application @${env} 7 | 8 | # examples: 9 | # sh stop.sh prod -- use conf/nginx-prod.conf to stop OpenResty 10 | # sh stop.sh -- use conf/nginx-dev.conf to stop OpenRest 11 | ##################################################################### 12 | 13 | if [ -n "$1" ];then 14 | PROFILE="$1" 15 | else 16 | PROFILE=dev 17 | fi 18 | 19 | mkdir -p logs & mkdir -p tmp 20 | echo "Use profile: "${PROFILE} 21 | nginx -s stop -p `pwd`/ -c conf/nginx-${PROFILE}.conf 22 | --------------------------------------------------------------------------------