mirror of
https://github.com/lxsang/antd-web-apps
synced 2025-07-17 06:09:50 +02:00
using silk as base framework for all webapp
This commit is contained in:
95
silk/BaseController.lua
Normal file
95
silk/BaseController.lua
Normal file
@ -0,0 +1,95 @@
|
||||
-- create class
|
||||
BaseController = BaseObject:extends{
|
||||
class="BaseController",
|
||||
registry = {},
|
||||
models = {},
|
||||
main = false }
|
||||
-- set the name here in each subclasses
|
||||
function BaseController:initialize()
|
||||
for k, v in pairs(self.models) do
|
||||
--- infer the class here
|
||||
local modelname = firstToUpper(v).."Model"
|
||||
local path = MODEL_ROOT.."."..modelname
|
||||
-- require it
|
||||
pcall(require, path)
|
||||
--require(controller_path)
|
||||
if not _G[modelname] then
|
||||
self:modelnotfound(v)
|
||||
else
|
||||
-- create new model object
|
||||
self[v] = _G[modelname]:new{registry = self.registry}
|
||||
end
|
||||
end
|
||||
-- create template
|
||||
self.template = Template:new{registry = self.registry}
|
||||
end
|
||||
|
||||
function BaseController:redirect(url)
|
||||
std.status(301, "Moved Permanently")
|
||||
std.custom_header("Content-Type","text/html")
|
||||
std.custom_header("Location", url)
|
||||
std.header_flush()
|
||||
end
|
||||
|
||||
function BaseController:switchLayout(name)
|
||||
if self.main then
|
||||
self.registry.layout = name
|
||||
else
|
||||
self:log("Cannot switch layout since the controller "..self.class.." is not the main controller")
|
||||
end
|
||||
end
|
||||
|
||||
function BaseController:setSession(key, value)
|
||||
SESSION[key] = value
|
||||
end
|
||||
function BaseController:getSession(key)
|
||||
return SESSION[key]
|
||||
end
|
||||
function BaseController:removeSession(key)
|
||||
self:setSession(key, nil)
|
||||
end
|
||||
function BaseController:index(...)
|
||||
self:error("#index: subclasses responsibility")
|
||||
end
|
||||
|
||||
-- not found action
|
||||
function BaseController:actionnotfound(...)
|
||||
local args = {...}
|
||||
self:error("#action "..args[1].." is not found in controller "..self.class)
|
||||
end
|
||||
-- not found model
|
||||
function BaseController:modelnotfound(...)
|
||||
local args = {...}
|
||||
self:error("Model "..firstToUpper(args[1]).."Model is not found in controller "..self.class)
|
||||
end
|
||||
-- The not found controller
|
||||
|
||||
NotfoundController = BaseController:extends{ registry = {}, models = {} }
|
||||
|
||||
function NotfoundController:index(...)
|
||||
local args = {...}
|
||||
self:error("404: Controller "..args[1].." not found")
|
||||
return false
|
||||
end
|
||||
|
||||
-- The asset controller for the static file
|
||||
AssetController = BaseController:extends{name= "AssetController",registry={}, models={}}
|
||||
function AssetController:index(...)
|
||||
local args = {...}
|
||||
return self:get(table.unpack(args))
|
||||
end
|
||||
|
||||
function AssetController:get(...)
|
||||
local path = WWW_ROOT..DIR_SEP..implode({...}, DIR_SEP)
|
||||
local mime = std.mimeOf(path)
|
||||
|
||||
if POLICY.mimes[mime] then
|
||||
std.header(mime)
|
||||
if std.isBinary(path) then
|
||||
std.f(path)
|
||||
else
|
||||
std.fb(path)
|
||||
end
|
||||
end
|
||||
return false
|
||||
end
|
46
silk/BaseModel.lua
Normal file
46
silk/BaseModel.lua
Normal file
@ -0,0 +1,46 @@
|
||||
|
||||
-- create class
|
||||
BaseModel = BaseObject:extends{class="BaseModel",registry = {}}
|
||||
|
||||
function BaseModel:initialize()
|
||||
self.db = self.registry.db
|
||||
if self.db and self.name and self.name ~= "" and self.fields and not self.db:available(self.name) then
|
||||
self.db:createTable(self.name, self.fields)
|
||||
end
|
||||
end
|
||||
|
||||
function BaseModel:create(m)
|
||||
if self.db and m then
|
||||
return self.db:insert(self.name,m)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function BaseModel:update(m)
|
||||
if self.db and m then
|
||||
return self.db:update(self.name,m)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function BaseModel:delete(cond)
|
||||
if self.db and cond then
|
||||
return self.db:delete(self.name,cond)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function BaseModel:find(cond)
|
||||
if self.db and cond then
|
||||
return self.db:find(self.name, cond)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function BaseModel:findAll()
|
||||
if self.db then
|
||||
return self.db:getAll(self.name)
|
||||
end
|
||||
return false
|
||||
end
|
25
silk/BaseObject.lua
Normal file
25
silk/BaseObject.lua
Normal file
@ -0,0 +1,25 @@
|
||||
BaseObject = Object:extends{registry = {}, class="BaseObject"}
|
||||
|
||||
|
||||
function BaseObject:log(msg, level)
|
||||
level = level or "INFO"
|
||||
if self.registry.logger then
|
||||
self.registry.logger:log(msg,level)
|
||||
end
|
||||
end
|
||||
|
||||
function BaseObject:debug(msg)
|
||||
self:log(msg, "DEBUG")
|
||||
end
|
||||
|
||||
function BaseObject:error(msg, trace)
|
||||
html()
|
||||
echo(msg)
|
||||
self:log(msg,"ERROR")
|
||||
if trace then
|
||||
debug.traceback=nil
|
||||
error(msg)
|
||||
else
|
||||
error(msg)
|
||||
end
|
||||
end
|
150
silk/DBHelper.lua
Normal file
150
silk/DBHelper.lua
Normal file
@ -0,0 +1,150 @@
|
||||
sqlite = modules.sqlite()
|
||||
|
||||
if sqlite == nil then return 0 end
|
||||
-- create class
|
||||
DBHelper = BaseObject:extends{db=nil, class='DBHelper'}
|
||||
|
||||
function DBHelper:createTable(tbl, m)
|
||||
if self:available(tbl) then return true end
|
||||
local sql = "CREATE TABLE "..tbl.."(id INTEGER PRIMARY KEY"
|
||||
for k, v in pairs(m) do
|
||||
if k ~= "id" then
|
||||
sql = sql..","..k.." "..v
|
||||
end
|
||||
end
|
||||
sql = sql..");"
|
||||
return sqlite.query(self.db,sql) == 1
|
||||
end
|
||||
|
||||
function DBHelper:insert(tbl, m)
|
||||
local keys = {}
|
||||
local values = {}
|
||||
for k,v in pairs(m) do
|
||||
if k ~= "id" then
|
||||
table.insert(keys,k)
|
||||
if type(v) == "number" then
|
||||
table.insert(values, v)
|
||||
else
|
||||
local t = "\""..v:gsub('"', '""').."\""
|
||||
table.insert(values,t)
|
||||
end
|
||||
end
|
||||
end
|
||||
local sql = "INSERT INTO "..tbl.." ("..table.concat(keys,',')..') VALUES ('
|
||||
sql = sql..table.concat(values,',')..');'
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
|
||||
function DBHelper:get(tbl, id)
|
||||
return sqlite.select(self.db, tbl, "*","id="..id)[1]
|
||||
end
|
||||
|
||||
function DBHelper:getAll(tbl)
|
||||
local data = sqlite.select(self.db, tbl, "*", "1=1")
|
||||
if data == nil then return nil end
|
||||
local a = {}
|
||||
for n in pairs(data) do table.insert(a, n) end
|
||||
table.sort(a)
|
||||
return data, a
|
||||
end
|
||||
|
||||
function DBHelper:find(tbl, cond)
|
||||
local cnd = "1=1"
|
||||
local sel = "*"
|
||||
if cond.exp then
|
||||
cnd = self:gencond(cond.exp)
|
||||
end
|
||||
if cond.order then
|
||||
cnd = cnd.." ORDER BY "
|
||||
local l = {}
|
||||
local i = 1
|
||||
for k,v in pairs(cond.order) do
|
||||
l[i] = k.." "..v
|
||||
i = i+1
|
||||
end
|
||||
cnd = cnd..table.concat(l, ",")
|
||||
end
|
||||
if cond.limit then
|
||||
cnd = cnd.." LIMIT "..cond.limit
|
||||
end
|
||||
if cond.fields then
|
||||
sel = table.concat(cond.fields, ",")
|
||||
--print(sel)
|
||||
end
|
||||
--print(cnd)
|
||||
local data = sqlite.select(self.db, tbl, sel, cnd)
|
||||
if data == nil then return nil end
|
||||
local a = {}
|
||||
for n in pairs(data) do table.insert(a, n) end
|
||||
table.sort(a)
|
||||
return data, a
|
||||
end
|
||||
|
||||
function DBHelper:query(sql)
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
|
||||
function DBHelper:update(tbl, m)
|
||||
local id = m['id']
|
||||
if id ~= nil then
|
||||
local lst = {}
|
||||
for k,v in pairs(m) do
|
||||
if(type(v)== "number") then
|
||||
table.insert(lst,k.."="..v)
|
||||
else
|
||||
table.insert(lst,k.."=\""..v:gsub('"', '""').."\"")
|
||||
end
|
||||
end
|
||||
local sql = "UPDATE "..tbl.." SET "..table.concat(lst,",").." WHERE id="..id..";"
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
function DBHelper:available(tbl)
|
||||
return sqlite.hasTable(self.db, tbl) == 1
|
||||
end
|
||||
function DBHelper:deleteByID(tbl,id)
|
||||
local sql = "DELETE FROM "..tbl.." WHERE id="..id..";"
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
function DBHelper:gencond(o)
|
||||
for k,v in pairs(o) do
|
||||
if k == "and" or k == "or" then
|
||||
local cnd = {}
|
||||
local i = 1
|
||||
for k1,v1 in pairs(v) do
|
||||
cnd[i] = self:gencond(v1)
|
||||
i = i + 1
|
||||
end
|
||||
return " ("..table.concat(cnd, " "..k.." ")..") "
|
||||
else
|
||||
for k1,v1 in pairs(v) do
|
||||
local t = type(v1)
|
||||
if(t == "string") then
|
||||
return " ("..k1.." "..k..' "'..v1:gsub('"','""')..'") '
|
||||
end
|
||||
return " ("..k1.." "..k.." "..v1..") "
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
function DBHelper:delete(tbl, cond)
|
||||
local sql = "DELETE FROM "..tbl.." WHERE "..self:gencond(cond)..";"
|
||||
return sqlite.query(self.db, sql) == 1
|
||||
end
|
||||
|
||||
function DBHelper:lastInsertID()
|
||||
return sqlite.lastInsertID(self.db)
|
||||
end
|
||||
|
||||
function DBHelper:close()
|
||||
if self.db then
|
||||
sqlite.dbclose(self.db)
|
||||
end
|
||||
end
|
||||
function DBHelper:open()
|
||||
if self.db ~= nil then
|
||||
self.db = sqlite.getdb(self.db)
|
||||
end
|
||||
end
|
30
silk/Logger.lua
Normal file
30
silk/Logger.lua
Normal file
@ -0,0 +1,30 @@
|
||||
Logger = Object:extends{levels = {}}
|
||||
|
||||
function Logger:initialize()
|
||||
end
|
||||
|
||||
function Logger:log(msg,level)
|
||||
if self.levels[level] and ulib.exists(LOG_ROOT) then
|
||||
local path = LOG_ROOT..DIR_SEP..level..'.txt'
|
||||
local f = io.open(path, 'a')
|
||||
local text = '['..level.."]: "..msg
|
||||
if f then
|
||||
f:write(text..'\n')
|
||||
f:close()
|
||||
end
|
||||
print(text)
|
||||
end
|
||||
end
|
||||
|
||||
function Logger:info(msg)
|
||||
self:log(msg, "INFO")
|
||||
end
|
||||
|
||||
function Logger:debug(msg)
|
||||
self:log(msg, "DEBUG")
|
||||
end
|
||||
|
||||
|
||||
function Logger:error(msg)
|
||||
self:log(msg, "ERROR")
|
||||
end
|
127
silk/Router.lua
Normal file
127
silk/Router.lua
Normal file
@ -0,0 +1,127 @@
|
||||
|
||||
--define the class
|
||||
Router = BaseObject:extends{class="Router",registry = {}}
|
||||
function Router:setPath(path)
|
||||
self.path = path
|
||||
end
|
||||
|
||||
function Router:initialize()
|
||||
self.routes = {}
|
||||
end
|
||||
|
||||
--function Router:setArgs(args)
|
||||
-- self.args = args
|
||||
--end
|
||||
|
||||
--function Router:arg(name)
|
||||
-- return self.args[name]
|
||||
--end
|
||||
|
||||
function Router:infer(url)
|
||||
-- a controller is like this /a/b/c/d/e
|
||||
-- a is controller name
|
||||
-- b is action
|
||||
-- c,d,e is parameters
|
||||
-- if user dont provice the url, try to infer it
|
||||
-- from the REQUEST
|
||||
url = url or REQUEST.query.r
|
||||
url = std.trim(url,"/")
|
||||
local args = explode(url,"/")
|
||||
local data = {
|
||||
name = 'index',
|
||||
action = 'index',
|
||||
args = {}
|
||||
}
|
||||
if args and #args > 0 and args[1] ~= "" then
|
||||
data.name = args[1]
|
||||
if args[2] then data.action = args[2] end
|
||||
for i = 3, #args do table.insert( data.args, args[i] ) end
|
||||
end
|
||||
|
||||
self:log('Controller: '..JSON.encode(data))
|
||||
-- find the controller class and init it
|
||||
local controller_name = firstToUpper(data.name).."Controller"
|
||||
local controller_path = self.path.."."..controller_name
|
||||
-- require the controller module
|
||||
-- ignore the error
|
||||
pcall(require, controller_path)
|
||||
--require(controller_path)
|
||||
if not _G[controller_name] then
|
||||
data.controller = NotfoundController:new{ registry = self.registry }
|
||||
data.args = {controller_name}
|
||||
data.action = "index"
|
||||
data.name = "notfound"
|
||||
else
|
||||
data.controller = _G[controller_name]:new{ registry = self.registry }
|
||||
if not data.controller[data.action] then
|
||||
data.args = {data.action}
|
||||
data.action = "actionnotfound"
|
||||
end
|
||||
end
|
||||
return data
|
||||
end
|
||||
|
||||
|
||||
function Router:delegate()
|
||||
local views = {}
|
||||
local data = self:infer()
|
||||
-- set the controller to the main controller
|
||||
data.controller.main = true
|
||||
views.__main__ = self:call(data)
|
||||
if not views.__main__ then return end
|
||||
-- get all visible routes
|
||||
local routes = self:dependencies(data.name.."/"..data.action)
|
||||
for k, v in pairs(routes) do
|
||||
data = self:infer(v)
|
||||
views[k] = self:call(data)
|
||||
end
|
||||
-- now require the main page to put the view
|
||||
|
||||
local fn, e = loadscript(VIEW_ROOT..DIR_SEP..self.registry.layout..DIR_SEP.."index.ls")
|
||||
html()
|
||||
if fn then
|
||||
local r,o = pcall(fn, views)
|
||||
if not r then
|
||||
self:error(o)
|
||||
end
|
||||
else
|
||||
self:error("The index page is not found for layout: "..self.registry.layout)
|
||||
end
|
||||
end
|
||||
|
||||
function Router:dependencies(url)
|
||||
if not self.routes[self.registry.layout] then return {} end
|
||||
local list = {}
|
||||
--self:log("comparing "..url)
|
||||
for k,v in pairs(self.routes[self.registry.layout]) do
|
||||
v.url = std.trim(v.url,"/")
|
||||
if v.visibility == "ALL" then
|
||||
list[k] = v.url
|
||||
elseif v.visibility.routes then
|
||||
if v.visibility.shown == true or v.visibility.shown == nil then
|
||||
if v.visibility.routes[url] then
|
||||
list[k] = v.url
|
||||
end
|
||||
else
|
||||
if not v.visibility.routes[url] then
|
||||
list[k] = v.url
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return list
|
||||
end
|
||||
|
||||
function Router:call(data)
|
||||
local obj = data.controller[data.action](data.controller,table.unpack(data.args))
|
||||
if obj then
|
||||
data.controller.template:setView(data.action, data.name)
|
||||
return data.controller.template
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
function Router:route(layout, dependencies)
|
||||
self.routes[layout] = dependencies
|
||||
end
|
37
silk/Template.lua
Normal file
37
silk/Template.lua
Normal file
@ -0,0 +1,37 @@
|
||||
-- create class
|
||||
Template = BaseObject:extends{class="Template",registry = {}}
|
||||
|
||||
function Template:initialize()
|
||||
self.vars = {}
|
||||
end
|
||||
|
||||
function Template:set(k, v, ow)
|
||||
if not self.vars[k] or (self.vars[k] and ow) then
|
||||
self.vars[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
function Template:remove(k)
|
||||
self.vars[k] = nil
|
||||
end
|
||||
|
||||
-- infer view path
|
||||
function Template:setView(name, controller)
|
||||
self.path = VIEW_ROOT..DIR_SEP..self.registry.layout..DIR_SEP..controller..DIR_SEP..name..".ls"
|
||||
if ulib.exists(self.path) then
|
||||
else
|
||||
self:error("View not found: "..self.path)
|
||||
end
|
||||
end
|
||||
-- render the page
|
||||
function Template:render()
|
||||
local fn, e = loadscript(self.path)
|
||||
if fn then
|
||||
local r,o = pcall(fn, self.vars)
|
||||
if not r then
|
||||
self:error(o)
|
||||
end
|
||||
else
|
||||
self:error(e)
|
||||
end
|
||||
end
|
52
silk/api.lua
Normal file
52
silk/api.lua
Normal file
@ -0,0 +1,52 @@
|
||||
require("OOP")
|
||||
ulib = require("ulib")
|
||||
require(BASE_FRW.."silk.BaseObject")
|
||||
require(BASE_FRW.."silk.DBHelper")
|
||||
require(BASE_FRW.."silk.Router")
|
||||
require(BASE_FRW.."silk.BaseController")
|
||||
require(BASE_FRW.."silk.BaseModel")
|
||||
require(BASE_FRW.."silk.Logger")
|
||||
require(BASE_FRW.."silk.Template")
|
||||
|
||||
-- mime type allows
|
||||
-- this will bypass the default server security
|
||||
-- the default list is from the server setting
|
||||
POLICY = {}
|
||||
POLICY.mimes = {
|
||||
["application/javascript"] = true,
|
||||
["image/bmp"] = true,
|
||||
["image/jpeg"] = true,
|
||||
["text/css"] = true,
|
||||
["text/markdown"] = true,
|
||||
["text/csv"] = true,
|
||||
["application/pdf"] = true,
|
||||
["image/gif"] = true,
|
||||
["text/html"] = true,
|
||||
["application/json"] = true,
|
||||
["application/javascript"] = true,
|
||||
["image/x-portable-pixmap"] = true,
|
||||
["application/x-rar-compressed"] = true,
|
||||
["image/tiff"] = true,
|
||||
["application/x-tar"] = true,
|
||||
["text/plain"] = true,
|
||||
["application/x-font-ttf"] = true,
|
||||
["application/xhtml+xml"] = true,
|
||||
["application/xml"] = true,
|
||||
["application/zip"] = true,
|
||||
["image/svg+xml"] = true,
|
||||
["application/vnd.ms-fontobject"] = true,
|
||||
["application/x-font-woff"] = true,
|
||||
["application/x-font-otf"] = true,
|
||||
["audio/mpeg"] = true,
|
||||
|
||||
}
|
||||
|
||||
|
||||
HEADER_FLAG = false
|
||||
|
||||
function html()
|
||||
if not HEADER_FLAG then
|
||||
std.chtml(SESSION)
|
||||
HEADER_FLAG = true
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user