News version of antos backend for luad fastcgi

This commit is contained in:
DanyLE 2023-01-26 19:07:54 +01:00
parent 389067f4d4
commit 116e304e76
14 changed files with 1396 additions and 2 deletions

41
Jenkinsfile vendored Normal file
View File

@ -0,0 +1,41 @@
pipeline{
agent { node{ label'workstation' }}
options {
// Limit build history with buildDiscarder option:
// daysToKeepStr: history is only kept up to this many days.
// numToKeepStr: only this many build logs are kept.
// artifactDaysToKeepStr: artifacts are only kept up to this many days.
// artifactNumToKeepStr: only this many builds have their artifacts kept.
buildDiscarder(logRotator(numToKeepStr: "1"))
// Enable timestamps in build log console
timestamps()
// Maximum time to run the whole pipeline before canceling it
timeout(time: 1, unit: 'HOURS')
// Use Jenkins ANSI Color Plugin for log console
ansiColor('xterm')
// Limit build concurrency to 1 per branch
disableConcurrentBuilds()
}
stages
{
stage('Build') {
steps {
sh'''
cd $WORKSPACE
[ -d build ] && rm -rf build
mkdir -p build/opt/www/htdocs/os
export BUILDDIR="$WORKSPACE/build/opt/www/htdocs/os"
cp router.lua "$BUILDDIR"
cp -rf controllers "$BUILDDIR"
cp -rf libs "$BUILDDIR"
'''
script {
// only useful for any master branch
//if (env.BRANCH_NAME =~ /^master/) {
archiveArtifacts artifacts: 'build/', fingerprint: true
//}
}
}
}
}
}

View File

@ -1,2 +1,4 @@
# antos-backend ## AntOS lua-based backend
Backend implementation for AntOS
This backend is the reference implementation of AntOS server side API.
The backend is implemented for Antd webserver + fcgi plugin + luafcgi FastCGI server

View File

@ -0,0 +1,33 @@
BaseController:subclass(
"IndexController",
{
registry = {},
models = {}
}
)
function IndexController:actionnotfound(...)
return self:index(table.unpack({...}))
end
function IndexController:index(...)
html()
std.f(WWW_ROOT..DIR_SEP.."index.html")
end
function IndexController:doc(...)
local api = {
author = "Xuan Sang LE",
email = "xsang.le@gmail.com",
api_name = "AntOS API",
version = "2.0.0-a",
documents = {
vfs = HTTP_ROOT.."/VFS",
vdb = HTTP_ROOT.."/VDB",
user = HTTP_ROOT.."/user",
system = HTTP_ROOT.."/system"
}
}
result(api)
return false
end

View File

@ -0,0 +1,249 @@
BaseController:subclass(
"SystemController",
{
registry = {},
models = {}
}
)
function SystemController:actionnotfound(...)
return self:index(table.unpack({...}))
end
function SystemController:index(...)
local api = {
description = "This api handle system operations",
actions = {
["/packages"] = "Handle all operation relate to package: list, install, cache, uninstall",
["/settings"] = "Save user setting",
["/application"] = "Call a specific server side application api",
["/apigateway"] = "Gateway for executing custom server side code"
}
}
result(api)
return false
end
function SystemController:packages(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
local packages = require("packages")
packages.init(rq.args.paths)
if rq ~= nil then
-- check user command here
if (rq.command == "install") then
packages.install(rq.args)
elseif rq.command == "cache" then
packages.cache(rq.args)
elseif rq.command == "list" then
packages.list(rq.args.paths)
elseif rq.command == "uninstall" then
packages.uninstall(rq.args.path)
else
fail("Uknown packages command")
end
else
fail("Uknown request")
end
end
function SystemController:settings(...)
auth_or_die("User unauthorized. Please login")
local user = SESSION.user
if user then
local ospath = require("vfs").ospath("home:///", user)
if REQUEST and REQUEST.json then
local file_path = ospath .. "/" .. ".settings.json"
local f = io.open(file_path, "w")
if f then
f:write(REQUEST.json)
f:close()
-- TODO: maybe use ulib
os.execute("chmod o-r "..file_path)
result(true)
else
fail("Cannot save setting")
end
else
fail("No setting founds")
end
else
fail("User not found")
end
end
function SystemController:application(...)
auth_or_die("User unauthorized. Please login")
local rq = nil
if REQUEST.json ~= nil then
rq = (JSON.decodeString(REQUEST.json))
else
rq = REQUEST
end
if rq.path ~= nil then
local pkg = require("vfs").ospath(rq.path)
if pkg == nil then
pkg = WWW_ROOT .. "/packages/" .. rq.path
--die("unkown request path:"..rq.path)
end
pkg = pkg .. "/api.lua"
if ulib.exists(pkg) then
dofile(pkg).exec(rq.method, rq.arguments)
else
fail("Uknown application handler: " .. pkg)
end
else
fail("Uknown request")
end
end
function SystemController:apigateway(...)
local args={...}
local use_ws = false
if REQUEST and REQUEST.ws == "1" then
-- override the global cout command
cout = std.ws.t
echo = std.ws.t
use_ws = true
else
cout = function(e)
std.json()
echo(e)
end
-- std.json()
end
local exec_with_user_priv = function(data)
local uid = ulib.uid(SESSION.user)
if not ulib.setgid(uid.gid) or not ulib.setuid(uid.id) then
cout("Cannot set permission to execute the code")
return
end
local r, e
e = "{'error': 'Unknow function'}"
-- set env var
local home = ulib.home_dir(uid.id)
ulib.setenv("USER", SESSION.user, 1)
ulib.setenv("LOGNAME", SESSION.user, 1)
if home then
ulib.setenv("HOME", home, 1)
ulib.setenv("PWD", home,1)
local paths = ""
if ulib.exists(home.."/bin") then
paths = home.."/bin:"
end
if ulib.exists(home.."/.local/bin") then
paths = paths..home.."/.local/bin:"
end
local envar = ulib.getenv("PATH")
if envar then
paths = paths..envar
end
ulib.setenv("PATH", paths,1)
end
-- run the code as user
if data.code then
r, e = load(data.code)
if r then
local status, result = pcall(r)
if result then
if (status) then
cout(JSON.encode(result))
else
cout(result)
end
end
else
cout(e)
end
elseif data.path then
local ospath = require("vfs").ospath(data.path)
r, e = loadfile(ospath)
if r then
local status, result = pcall(r, data.parameters)
if result then
if (status) then
cout(JSON.encode(result))
else
cout(result)
end
end
else
cout(e)
end
else
cout(e)
end
end
if (is_auth()) then
local pid = ulib.fork()--std.pfork(HTTP_REQUEST.id)
if (pid == -1) then
cout("{'error':'Cannot create process'}")
elseif pid > 0 then -- parent
-- wait for the child exit or websocket exit
ulib.waitpid(pid, 0)
--ulib.kill(pid)
LOG_INFO("Parent exit")
else -- child
if use_ws then
if std.ws.enable() then
-- read header
local header = std.ws.header()
if header then
if header.mask == 0 then
LOG_WARN("Web socket Data is not masked")
std.ws.close(1012)
elseif header.opcode == std.ws.CLOSE then
LOG_DEBUG("Websocket Connection closed")
-- std.ws.close(1000)
elseif header.opcode == std.ws.TEXT then
-- read the file
local data = std.ws.read(header)
if data then
data = (JSON.decodeString(tostring(data)))
exec_with_user_priv(data)
std.ws.close(1011)
else
print("Error: Invalid request")
std.ws.close(1011)
end
end
else
std.ws.close(1011)
end
else
fail("Web socket is not available.")
end
else
if REQUEST.path then
exec_with_user_priv(REQUEST)
elseif REQUEST.json then
data = JSON.decodeString(REQUEST.json)
exec_with_user_priv(data)
elseif args and #args > 0 then
-- data is encoded in url safe base64
local encoded = args[1]:gsub('_', '/'):gsub('-', '+')
if #encoded % 4 == 2 then
encoded = encoded.."=="
elseif #encoded %4 == 3 then
encoded = encoded.."="
end
local decoded = enc.b64decode(encoded)
data = JSON.decodeString(tostring(decoded))
if data and data.path then
exec_with_user_priv(data)
else
fail("Unknown request")
end
else
fail("Unkown request")
end
end
print("Child exit")
ulib.kill(-1)
end
else
cout('{"error":"User unauthorized. Please login"}')
end
end

View File

@ -0,0 +1,102 @@
BaseController:subclass(
"UserController",
{
registry = {},
models = {}
}
)
function UserController:actionnotfound(...)
return self:index(table.unpack({...}))
end
function UserController:index(...)
local api = {
description = "This api handle the user authentification",
actions = {
["/auth"] = "Return user information if a user is alreay logged in",
["/login"] = "Perform a login operation",
["/logout"] = "Perform a logout operation"
}
}
result(api)
return false
end
--[[
request query: none
return:
]]
function UserController:auth(...)
auth_or_die("User unauthorized. Please login")
local user = require("uman").userinfo(SESSION.user)
result(user)
return false
end
--[[ request:
{"username":"mrsang", "password":"pass"}
return:
{} ]]
function UserController:login(...)
if REQUEST.json ~= nil then
local request = JSON.decodeString(REQUEST.json)
local r = ulib.auth(request.username,request.password)
if r == true then
local salt = utils.generate_salt(20)
local cookie = {sessionid=enc.sha1(request.username..request.password..salt)} -- iotos_user = request.username
local db = sysdb();
if db == nil then return fail("Cannot setup session") end
local cond = {exp= {["="] = { sessionid = cookie.sessionid }}}
local data = db:find(cond)
--print(data)
if data == nil or data[1] == nil then
--print("insert new data")
data = {sessionid = cookie.sessionid, username=request.username, stamp=os.time(os.date("!*t"))}
else
data = data[1]
--print("Update old data")
data.stamp = os.time(os.date("!*t"))
end
if data.id == nil then
db:insert(data)
else
db:update(data)
end
db:close()
for k,v in pairs(cookie) do
--- TODO: add expire date to cookie
std.setCookie(k.."="..v, "Path=/")
end
std.json()
SESSION.user = request.username
local user = {
result = require("uman").userinfo(request.username),
error = false
}
std.t(JSON.encode(user))
else
fail("Invalid login")
end
else
fail("Invalid request")
end
return false
end
function UserController:logout(...)
if SESSION.sessionid ~= nil and SESSION.sessionid ~= '0' then
local cookie = {sessionid='0'}
local db = sysdb()
if db ~= nil then
local cond = {["="] = { sessionid = SESSION.sessionid }}
db:delete(cond)
db:close()
end
for k,v in pairs(cookie) do
std.setCookie(k.."="..v, "Path=/")
end
end
std.json()
std.t(JSON.encode({error=false,result=true}))
end

View File

@ -0,0 +1,130 @@
BaseController:subclass(
"VDBController",
{
registry = {},
models = {}
}
)
function VDBController:actionnotfound(...)
return self:index(table.unpack({...}))
end
function VDBController:index(...)
local api = {
description = "This api handle database operation",
actions = {
["/save"] = "Save a record to a table",
["/get"] = "Get all records or Get a record by id",
["/select"] = "Select records by a condition",
["/delete"] = "Delete record(s) by condition or by id"
}
}
result(api)
return false
end
function VDBController:save(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if (rq ~= nil and rq.table ~= nil) then
local model = require("dbmodel").get(SESSION.user, rq.table, rq.data)
local ret
if model == nil then
fail("Cannot get table metadata:" .. rq.table)
else
if (rq.data.id ~= nil) then
rq.data.id = tonumber(rq.data.id)
ret = model:update(rq.data)
else
ret = model:insert(rq.data)
end
model:close()
if ret == true then
result(ret)
else
fail("Cannot modify/update table " .. rq.table)
end
end
else
fail("Unknown database request")
end
return false
end
function VDBController:get(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if (rq ~= nil and rq.table ~= nil) then
local model = require("dbmodel").get(SESSION.user, rq.table, nil)
local ret
if model == nil then
fail("Cannot get table metadata:" .. rq.table)
else
if (rq.id == nil) then
ret = model:getAll()
else
ret = model:get(rq.id)
end
model:close()
result(ret)
end
else
fail("Unknown database request")
end
end
function VDBController:select(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if (rq ~= nil and rq.table ~= nil) then
local model = require("dbmodel").get(SESSION.user, rq.table, nil)
local ret
if model == nil then
fail("Cannot get table metadata:" .. rq.table)
else
if (rq.cond == nil) then
model:close()
return fail("Unknow condition")
else
ret = model:find(rq.cond)
end
model:close()
result(ret)
end
else
fail("Unknown database request")
end
end
function VDBController:delete(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if (rq ~= nil and rq.table ~= nil) then
local model = require("dbmodel").get(SESSION.user, rq.table, nil)
local ret
if model == nil then
fail("Cannot get table metadata:" .. rq.table)
else
if (rq.id == nil) then
if (rq.cond) then
ret = model:delete(rq.cond)
model:close()
else
model:close()
return fail("Unknow element to delete")
end
else
ret = model:deleteByID(rq.id)
model:close()
end
if ret then
result(ret)
else
fail("Querry error or database is locked")
end
end
else
fail("Unknown database request")
end
end

View File

@ -0,0 +1,222 @@
BaseController:subclass(
"VFSController",
{
registry = {},
models = {}
}
)
function VFSController:actionnotfound(...)
return self:index(table.unpack({...}))
end
function VFSController:index(...)
local api = {
description = "This api handle file operations",
actions = {
["/fileinfo"] = "Get file information",
["/exists"] = "Check if file exists",
["/delete"] = "Delete a file",
["/get"] = "Get a file content",
["/mkdir"] = "Create directory",
["/move"] = "Move file to a new destination",
["/publish"] = "Share a file to all users",
["/scandir"] = "List all files and folders",
["/write"] = "Write data to file",
["/shared"] = "Get shared file content"
}
}
result(api)
return false
end
function VFSController:fileinfo(...)
auth_or_die("User unauthorized. Please login")
local vfspath = (JSON.decodeString(REQUEST.json)).path
local r, m = require("vfs").fileinfo(vfspath)
if r then
result(m)
else
fail(m)
end
return false
end
function VFSController:exists(...)
auth_or_die("User unauthorized. Please login")
local vfs = require("vfs")
local rq = (JSON.decodeString(REQUEST.json))
if rq ~= nil then
result(vfs.exists(rq.path))
else
fail("Uknown request")
end
return false
end
function VFSController:delete(...)
auth_or_die("User unauthorized. Please login")
local vfs = require("vfs")
local rq = (JSON.decodeString(REQUEST.json))
if rq ~= nil then
local r, e = vfs.delete(rq.path)
if r then
result(r)
else
fail(e)
end
else
fail("Uknown request")
end
return false
end
function VFSController:get(...)
local args = {...}
local uri = implode(args, "/")
auth_or_die("User unauthorized. Please login")
local vfsfile = utils.decodeURI(uri)
local r, m = require("vfs").checkperm(vfsfile, "read")
if r then
std.sendFile(m)
else
fail(m)
end
return false
end
function VFSController:mkdir(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if rq ~= nil then
local r, m = require("vfs").mkdir(rq.path)
if r then
result(r)
else
fail(m)
end
else
fail("Uknown request")
end
return false
end
function VFSController:move(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if rq ~= nil then
local r, m = require("vfs").move(rq.src, rq.dest)
if r then
result(r)
else
fail(m)
end
else
fail("Uknown request")
end
return false
end
function VFSController:publish(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if rq ~= nil then
local p = nil
if rq.publish then
p = require("vfs").ospath(rq.path)
else
p = require("shared").ospath(rq.path)
end
local user = SESSION.user
local uid = ulib.uid(user)
local st = ulib.file_stat(p)
if uid.id ~= st.uid then
die("Only the owner can share or unshare this file")
end
local entry = {sid = enc.sha1(p), user = SESSION.user, path = p, uid = uid.id}
local db = require("dbmodel").get("sysdb", "shared", entry)
if db == nil then
die("Cannot get system database")
end
local cond = nil
if rq.publish then
cond = {exp = {["="] = {path = p}}}
local data = db:find(cond)
if data == nil or data[0] == nil then
-- insert entry
db:insert(entry)
end
else
cond = {["="] = {sid = rq.path}}
db:delete(cond)
end
db:close()
result(entry.sid)
else
fail("Uknown request")
end
return false
end
function VFSController:scandir(...)
auth_or_die("User unauthorized. Please login")
local rq = JSON.decodeString(REQUEST.json)
local vfspath = rq.path
local r = require("vfs").readDir(vfspath)
if r == nil then
fail("Resource not found: " .. rq.path)
else
--print(JSON.encode(readDir(ospath, vfspath)))
result(r)
end
return false
end
function VFSController:upload(...)
auth_or_die("User unauthorized. Please login")
local vfs = require("vfs")
if REQUEST and REQUEST.path then
local r, m = require("vfs").upload(REQUEST.path)
if r then
result(r)
else
self:error(m)
fail(m)
end
else
fail("Invalid query")
end
return false
end
function VFSController:write(...)
auth_or_die("User unauthorized. Please login")
local rq = (JSON.decodeString(REQUEST.json))
if rq ~= nil then
local r, m = require("vfs").write(rq.path, rq.data)
if r then
result(r)
else
fail(m)
end
else
fail("Uknown request")
end
return false
end
function VFSController:shared(sid)
require("shared").get(sid)
end

80
libs/common.lua Normal file
View File

@ -0,0 +1,80 @@
require("silk.core.sqlite")
local TUNNEL_KEYCHAIN = "/opt/www/tmp/channels/antunnel_keychain"
function fail(msg)
std.custom_header("Connection", "close")
std.json()
std.t(JSON.encode({
error = msg
}))
end
function result(obj)
std.custom_header("Connection", "close")
std.json()
std.t(JSON.encode({
result = obj,
error = false
}))
end
function die(msg)
fail(msg)
debug.traceback = nil
error("Permission denied")
return false
end
-- check if the sysdb is create, otherwise create the table
function sysdb()
local meta = {}
meta.sessionid = ""
meta.username = ""
meta.stamp = 0
return require("dbmodel").get("sysdb", "sessions", meta)
end
function is_auth()
local sessionid = nil
if SESSION.sessionid and SESSION.sessionid ~= '0' then
sessionid = SESSION.sessionid
-- should be used only by API call
elseif REQUEST.sessionid and REQUEST.sessionid ~= '0' then
sessionid = REQUEST.sessionid
elseif REQUEST.access_token and REQUEST.access_token ~= '0' then
sessionid = REQUEST.access_token
end
if sessionid == nil then
return false
end
-- query session id from database
local db = sysdb()
if db == nil then
return false
end
local cond = {
exp = {
["="] = {
sessionid = sessionid
}
}
}
local data = db:find(cond)
db:close()
if data == nil or data[1] == nil then
return false
end
-- TODO check the stamp
SESSION.user = data[1].username
local f = io.open(TUNNEL_KEYCHAIN, "w")
if f then
f:write(sessionid .. SESSION.user)
f:close()
end
return true
end
function auth_or_die(msg)
if (is_auth() == false) then
die(msg)
end
end

57
libs/dbmodel.lua Normal file
View File

@ -0,0 +1,57 @@
local model = {}
model.get = function(name, tbl, data)
local mod = {
name = tbl,
db = DBModel:new{db = name}
}
function mod:close()
self.db:close()
end
function mod:open()
self.db:open()
end
function mod:createTable(m)
return self.db:createTable(self.name,m)
end
function mod:insert(m)
return self.db:insert(self.name,m)
end
function mod:get(id)
return self.db:get(self.name, id)
end
function mod:getAll()
return self.db:getAll(self.name)
end
function mod:find(cond)
return self.db:find(self.name, cond)
end
function mod:update(m)
return self.db:update(self.name, m)
end
function mod:available()
return self.db:available(self.name)
end
function mod:delete(cond)
return self.db:delete(self.name, cond)
end
function mod:deleteByID(id)
return self.db:deleteByID(self.name, id)
end
mod:open()
if mod:available() then return mod end
if data == nil then return nil end
local meta = {}
--print(JSON.encode(data))
for k,v in pairs(data) do
if type(v) == "number" or type(v) == "boolean" then
meta[k] = "NUMERIC"
else
meta[k] = "TEXT"
end
end
mod:createTable(meta)
return mod
end
return model

119
libs/packages.lua Normal file
View File

@ -0,0 +1,119 @@
local packages={}
local vfs = require("vfs")
local uid = ulib.uid(SESSION.user)
packages._cache = function(y)
local p = vfs.ospath(y)
local f = io.open(p.."/packages.json", "w")
local has_cache = false
local i = 1
local meta = {}
if f then
local files = vfs.readDir(y)
for k,v in pairs(files) do
if v.type == "dir" then
local f1 = io.open(vfs.ospath(v.path.."/package.json"))
if f1 then
local name = utils.basename(v.path)
local mt = JSON.decodeString(f1:read("*all"))
mt.path = v.path
meta[i] ='"'..name..'":'..JSON.encode(mt)
i = i+1
f1:close()
has_cache = true;
end
end
end
f:write(table.concat(meta, ","))
f:close()
if has_cache == false then
ulib.delete(p.."/packages.json");
end
end
end
-- we will change this later
packages.list = function(paths)
std.json()
std.t("{\"result\" : { ")
local first = true
--std.f(__ROOT__.."/system/packages.json")
for k,v in pairs(paths) do
local osp = vfs.ospath(v.."/packages.json")
if ulib.exists(osp) == false then
packages._cache(v)
end
if ulib.exists(osp) then
if first == false then
std.t(",")
else
first = false
end
std.f(osp)
end
end
std.t("}, \"error\":false}")
end
-- generate the packages caches
packages.cache = function(args)
-- perform a packages caches
for x,y in pairs(args.paths) do
packages._cache(y)
end
result(true)
end
-- install a function from zip file
packages.install = function(args)
local path = vfs.ospath(args.dest)
local zip = vfs.ospath(args.zip)
if(ulib.exists(path) == false) then
-- create directory if not exist
ulib.mkdir(path)
-- change permission
ulib.chown(path, uid.id, uid.gid)
end
-- extract the zip file to it
if(ulib.unzip(zip, path)) then
-- read metadata
local meta = JSON.decodeFile(path.."/metadata.json")
meta.path = args.dest
meta.scope = "user"
local f=io.open(path.."/package.json","w")
if f then
f:write(JSON.encode(meta))
f:close()
end
result(true)
else
fail("Problem extracting zip file")
end
end
-- uninstall the package
packages.uninstall = function(path)
local osf = vfs.ospath(path)
if(osf and ulib.exists(osf) ) then
--remove it
ulib.delete(osf)
result(true)
else
fail("Cannot find package")
end
end
-- set user packages environment
packages.init = function(paths)
if(paths) then
for k,v in pairs(paths) do
local p = vfs.ospath(v)
if p and (ulib.exists(p) == false) then
ulib.mkdir(p)
-- change permission
ulib.chown(p, uid.id, uid.gid)
end
end
end
end
return packages

45
libs/shared.lua Normal file
View File

@ -0,0 +1,45 @@
local shared = {}
shared.get = function(sharedid)
if sharedid == "all" then
-- get all shared files
local db = require("dbmodel").get("sysdb", "shared", nil)
if db == nil then die("Cannot get shared database") end
local data = db:getAll()
if data == nil then die("No file found") end
local i = 1
local ret = {}
for k,v in pairs(data) do
if(ulib.exists(v.path)) then
local r = ulib.file_stat(v.path)
if(r.error == nil) then
r.path = "shared://"..v.sid
r.filename = utils.basename(v.path)
r.mime = std.mimeOf(r.filename)
ret[i] = r
i = i+1
end
else
local cond = { ["="] = { sid = v.sid } }
db:delete(cond)
end
end
db:close()
--std.json()
result(ret)
else
local p = shared.ospath(sharedid)
std.sendFile(p)
end
end
shared.ospath = function(sharedid)
local db = require("dbmodel").get("sysdb", "shared", nil)
if db == nil then die("Cannot get shared database") end
local cond = { exp = { ["="] = { sid = sharedid } } }
local data = db:find(cond)
db:close()
if data == nil or data[1] == nil then die("Cannot get shared file with: "..sharedid) end
return data[1].path
end
return shared;

27
libs/uman.lua Normal file
View File

@ -0,0 +1,27 @@
local uman={}
uman.userinfo = function(user)
local info = {}
local uid = ulib.uid(user)
if uid then
-- read the setting
-- use the decodeFile function of JSON instead
local file = require('vfs').ospath("home:///").."/.settings.json"
local st = JSON.decodeFile(file)
if(st) then
info = st
end
info.user = {
username = user,
id = uid.id,
name = user,
groups = uid.groups
}
--print(JSON.encode(info))
return info
else
return {}
end
end
return uman

241
libs/vfs.lua Normal file
View File

@ -0,0 +1,241 @@
local vfs = {}
vfs.ospath = function(path)
local user = SESSION.user
local prefix = string.match(path, "%a+:/")
if (prefix ~= nil) then
local suffix = string.gsub(path, prefix, "")
if prefix == "home:/" then
return string.format(VFS_HOME, user) .. '/' .. suffix
elseif prefix == "desktop:/" then
return string.format(VFS_HOME, user) .. "/.desktop/" .. suffix
elseif prefix == "shared:/" then
return require("shared").ospath(ulib.trim(suffix, "/"))
elseif prefix == "os:/" then
return WWW_ROOT .. "/" .. suffix
else
return nil
end
else
return nil;
end
end
vfs.delete = function(path)
local r, m = vfs.checkperm(path, "write")
if r then
if ulib.delete(m) then
-- change permission
return true, nil
else
return false, "Cant not delete the file"
end
else
return r, m
end
end
vfs.exists = function(path)
local osfile = vfs.ospath(path)
return ulib.exists(osfile)
end
vfs.fileinfo = function(vfspath)
local ospath = vfs.ospath(vfspath)
if ospath then
if (ulib.exists(ospath) == false) then
return false, "File not found"
end
local r = ulib.file_stat(ospath)
if (r.error ~= nil) then
return false, r.error
end
r.path = vfspath
r.name = utils.basename(vfspath)
r.mime = std.mimeOf(r.name)
return true, r
else
return false, "Resource not found"
end
end
vfs.mkdir = function(path)
local file = utils.basename(path)
local folder = string.gsub(path, utils.escape_pattern(file) .. "$", "")
local r, m = vfs.checkperm(folder, "write")
if r then
local osfile = m .. "/" .. file
local uid = ulib.uid(SESSION.user)
ulib.mkdir(osfile)
-- change permission
ulib.chown(osfile, uid.id, uid.gid)
return true, nil
else
return r, m
end
end
vfs.move = function(src, dest)
local file = utils.basename(dest)
local folder = string.gsub(dest, utils.escape_pattern(file), "")
local sp, sm = vfs.checkperm(src, "write")
if sp then
local dp, dm = vfs.checkperm(folder, "write")
if dp then
ulib.move(sm, dm .. "/" .. file)
-- change permission
return true, nil
else
return dp, dm
end
else
return sp, sm
end
end
vfs.write = function(path, data)
local file = utils.basename(path)
local folder = string.gsub(path, utils.escape_pattern(file), "")
local r, m = vfs.checkperm(folder, "write")
if r then
local osfile = m .. "/" .. file
if ulib.exists(osfile) then
local r1, m1 = vfs.checkperm(path, "write")
if not r1 then
return r1, m1 .. ": " .. path
end
end
local uid = ulib.uid(SESSION.user)
--
if data ~= "" then
local header = string.match(data, "^data%:[%w%.-%+]+%/[%w%.-%+]+;base64,")
if header ~= nil then
local b64data = string.gsub(data, utils.escape_pattern(header), "")
local barr = enc.b64decode(b64data)
barr:fileout(osfile)
--[[ if std.isBinary(osfile) then
else
local f = io.open(osfile, "w")
f:write(bytes.__tostring(barr))
f:close()
end ]]
else
return false, "Wrong data format"
end
else
slice.new(0):fileout(osfile)
end
-- f:close()
-- change permission
ulib.chown(osfile, uid.id, uid.gid)
return true, nil
else
return r, m .. ": " .. folder
end
end
vfs.upload = function(path)
if (not path) then
return false, "Unknown upload destination, abort!"
end
local r, m = vfs.checkperm(path, "write")
if (r) then
local uid = ulib.uid(SESSION.user)
local index = 0
while (REQUEST["upload-" .. index .. ".tmp"] ~= nil) do
local file = m .. "/" .. REQUEST["upload-" .. index .. ".file"]
local ret = ulib.move(REQUEST["upload-" .. index .. ".tmp"], file)
if not ret then
ret = ulib.send_file(REQUEST["upload-" .. index .. ".tmp"], file)
end
if not ret then
return false, "Unable to copy file"
end
ulib.chown(file, uid.id, uid.gid)
index = index + 1
end
if (index == 0) then
return false, "No file is uploaded"
end
return true, index
else
return r, m
end
end
vfs.checkperm = function(path, right)
if path:match("^shared:/.*") then
if right == "write" then
return false, "Shared file is readonly"
else
return true
end
end
local osfile = vfs.ospath(path)
local perm = vfs.perm(osfile)
-- print(osfile)
if not ulib.exists(osfile) then
return false, "Resource does not exist"
end
-- check if user own the file
if perm ~= nil then
if perm[right] == true then
-- print("Permission granted")
return true, osfile
else
LOG_INFO("Permission denie")
return false, "You dont have " .. right .. " permission on this file"
end
else
return false, "User is unrecognized"
end
end
vfs.perm = function(file)
local user = SESSION.user
local uid = ulib.uid(user)
local st = ulib.file_stat(file)
-- check if user own the file
if uid ~= nil and st ~= nil and st.perm ~= nil then
-- print(JSON.encode({uid, st}))
if (uid.id == st.uid) then -- the user owned the file
-- print("file belong to user")
return st.perm.owner
elseif uid.groups and uid.groups[st.gid] then
-- print("User belong to this group")
return st.perm.group
else
-- print("User belong to other")
return st.perm.other
end
else
return nil
end
end
vfs.readDir = function(vfspath)
if (string.sub(vfspath, -1) == "/") then
prefix = string.sub(vfspath, 1, -2)
else
prefix = vfspath
end
local ospath = vfs.ospath(vfspath)
local r = ulib.read_dir(ospath, prefix)
if (r.error ~= nil) then
return nil
end
-- add extra mime type
for k, v in pairs(r) do
v.mime = std.mimeOf(v.filename)
end
return r
end
return vfs

46
router.lua Normal file
View File

@ -0,0 +1,46 @@
-- the rewrite rule for the framework
-- should be something like this
-- ^\/apps\/+(.*)$ = /apps/router.lua?r=<1>&<query>
-- some global variables
package.path = _SERVER["LIB_DIR"].."/lua/?.lua"
require("silk.api")
-- crypto lib
enc = require("enc")
WWW_ROOT = __ROOT__.."/os"
-- add aditional paths
package.path = package.path..";"..WWW_ROOT .. '/libs/?.lua'
-- require needed library
require("common")
DIR_SEP = "/"
VFS_HOME = "/home/%s"
-- class path: path.to.class
CONTROLLER_ROOT = "os.controllers"
MODEL_ROOT = "os.models"
-- file path: path/to/file
VIEW_ROOT = WWW_ROOT..DIR_SEP.."views"
if HEADER.Host then
HTTP_ROOT= "https://"..HEADER["Host"]
else
HTTP_ROOT = "https://os.lxsang.me"
end
-- registry object store global variables
local REGISTRY = {}
-- set logging level
REGISTRY.logger = Logger:new{ level = Logger.INFO}
--REGISTRY.db = DBHelper:new{db="sysdb"}
REGISTRY.layout = 'default'
REGISTRY.fileaccess = true
--REGISTRY.db:open()
local router = Router:new{registry = REGISTRY}
REGISTRY.router = router
router:setPath(CONTROLLER_ROOT)
router:delegate()
--if REGISTRY.db then REGISTRY.db:close() end