1
0
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:
Xuan Sang LE
2018-08-23 11:15:08 +02:00
parent dd778713d3
commit b07da22b52
13 changed files with 110 additions and 81 deletions

95
silk/BaseController.lua Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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