diff --git a/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/controller.lua b/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/controller.lua index 28a3f80f..52ec7f0e 100644 --- a/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/controller.lua +++ b/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/controller.lua @@ -6,7 +6,8 @@ local os = require 'os' local glob = require 'posix.glob' local libgen = require 'posix.libgen' local simpleuci = require 'simple-uci' -local schema = require 'schema' +local schema = dofile('../controller/schema.lua') -- pwd is www/ +local ucl = require "ucl" package 'gluon-config-api' @@ -29,20 +30,97 @@ function config_get(parts) return config end -local parts = load_parts() - - -entry({"config"}, call(function(http, renderer) - - http:write(json.stringify(config_get(parts), true)) - http:close() -end)) - -entry({"schema"}, call(function(http, renderer) +function schema_get(parts) local total_schema = {} for _, part in pairs(parts) do - total_schema = schema.merge_schemas(total_schema, part.schema()) + total_schema = schema.merge_schemas(total_schema, part.schema(site, nil)) end - http:write(json.stringify(total_schema, true)) + return total_schema +end + +function config_set(parts, config) + local uci = simpleuci.cursor() + + for _, part in pairs(parts) do + part.set(config, uci) + end +end + +local function pump(src, snk) + while true do + local chunk, src_err = src() + local ret, snk_err = snk(chunk, src_err) + + if not (chunk and ret) then + local err = src_err or snk_err + if err then + return nil, err + else + return true + end + end + end +end + +local parts = load_parts() + +entry({"v1", "config"}, call(function(http, renderer) + if http.request.env.REQUEST_METHOD == 'GET' then + http:write(json.stringify(config_get(parts), true)) + elseif http.request.env.REQUEST_METHOD == 'POST' then + local request_body = "" + pump(http.input, function (data) + if data then + request_body = request_body .. data + end + end) + + -- Verify that we really have JSON input. UCL is able to parse other + -- config formats as well. Those config formats allow includes and so on. + -- This may be a security issue. + + local config = json.parse(request_body) + if not config then + http:status(400, 'Bad Request') + http:write('{ "status": 400, "error": "Bad JSON in Body" }\n') + http:close() + return + end + + -- Verify schema + + local parser = ucl.parser() + local res, err = parser:parse_string(request_body) + + if not res then + http:status(500, 'Internal Server Error.') + http:write('{ "status": 500, "error": "Internal UCL Parsing Failed. This should not happen at all." }\n') + http:close() + return + end + + res, err = parser:validate(schema_get(parts)) + if not res then + http:status(400, 'Bad Request') + http:write('{ "status": 400, "error": "Schema mismatch" }\n') + http:close() + return + end + + -- Apply config + + config_set(parts, config) + + http:write(json.stringify(res, true)) + else + http:status(400, 'Bad Request') + http:write('Not implemented') + end + + http:close() +end)) + +entry({"v1", "schema"}, call(function(http, renderer) + http:write(json.stringify(schema_get(parts), true)) http:close() end)) diff --git a/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/schema.lua b/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/schema.lua index 1f2a5e93..848b4b1f 100644 --- a/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/schema.lua +++ b/package/gluon-config-api/luasrc/lib/gluon/config-api/controller/schema.lua @@ -34,12 +34,12 @@ local function merged_keys(table1, table2) local keys = {} if table1 then for k, _ in pairs(table1) do - table.insert(k) + table.insert(keys, k) end end if table2 then for k, _ in pairs(table2) do - if not util.contains(k) then + if not util.contains(keys, k) then table.insert(keys, k) end end @@ -51,12 +51,12 @@ local function merged_values(table1, table2) local values = {} if table1 then for _, v in pairs(table1) do - table.insert(v) + table.insert(values, v) end end if table2 then for _, v in pairs(table2) do - if not util.contains(v) then + if not util.contains(values, v) then table.insert(values, v) end end @@ -105,7 +105,7 @@ function M.merge_schemas(schema1, schema2) local pdef2 = properties2[pkey] if pdef1 and pdef2 then - add_property(pkey, merge_schemas(pdef1, pdef2)) + add_property(pkey, M.merge_schemas(pdef1, pdef2)) elseif pdef1 then add_property(pkey, deepcopy(pdef1)) elseif pdef2 then @@ -115,14 +115,14 @@ function M.merge_schemas(schema1, schema2) -- generate merged.additionalProperties if schema1.additionalProperties and schema2.additionalProperties then - merged.additionalProperties = merge_schemas( + merged.additionalProperties = M.merge_schemas( schema1.additionalProperties, schema2.additionalProperties) else merged.additionalProperties = false end -- generate merged.required - merged.required = merged_values(schema1, schema2) + merged.required = merged_values(schema1.required, schema2.required) if #merged.required == 0 then merged.required = nil end @@ -132,6 +132,8 @@ function M.merge_schemas(schema1, schema2) -- generate merged.default merged.default = schema2.default or schema1.default + + return merged end return M diff --git a/package/gluon-config-api/luasrc/lib/gluon/config-api/parts/contact-info.lua b/package/gluon-config-api/luasrc/lib/gluon/config-api/parts/contact-info.lua index d0fb9973..4fb83396 100644 --- a/package/gluon-config-api/luasrc/lib/gluon/config-api/parts/contact-info.lua +++ b/package/gluon-config-api/luasrc/lib/gluon/config-api/parts/contact-info.lua @@ -13,11 +13,12 @@ function M.schema(site, platform) } } } - } + }, + required = { 'wizard' } } end -function M.save(config, uci) +function M.set(config, uci) local owner = uci:get_first("gluon-node-info", "owner") uci:set("gluon-node-info", owner, "contact", config.wizard.contact) diff --git a/package/gluon-config-api/luasrc/lib/gluon/config-api/parts/geo-location.lua b/package/gluon-config-api/luasrc/lib/gluon/config-api/parts/geo-location.lua new file mode 100644 index 00000000..f4794fde --- /dev/null +++ b/package/gluon-config-api/luasrc/lib/gluon/config-api/parts/geo-location.lua @@ -0,0 +1,74 @@ + +local M = {} + +function M.schema(site, platform) + local altitude = nil + + if site.config_mode.geo_location.show_altitude(false) then + altitude = { type = 'number' } + end + + return { + type = 'object', + properties = { + wizard = { + type = 'object', + properties = { + location = { + type = 'object', + properties = { + share_location = { type = 'boolean' }, + lat = { type = 'number' }, + lon = { type = 'number' }, + altitude = altitude + }, + required = { 'lat', 'lon', 'share_location' } + } + } + } + }, + required = { 'wizard' } + } +end + +function M.set(config, uci) + local location = uci:get_first("gluon-node-info", "location") + local config_location = config.wizard.location + + if config_location then + uci:set("gluon-node-info", location, "share_location", config_location.share_location) + uci:set("gluon-node-info", location, "latitude", config_location.lat) + uci:set("gluon-node-info", location, "longitude", config_location.lon) + if config_location.altitude then -- altitude is optional + uci:set("gluon-node-info", location, "altitude", config_location.altitude) -- TODO: check if the "if" is necessary + else + uci:delete("gluon-node-info", location, "altitude") + end + else + uci:set("gluon-node-info", location, "share_location", false) + uci:delete("gluon-node-info", location, "latitude") + uci:delete("gluon-node-info", location, "longitude") + uci:delete("gluon-node-info", location, "altitude") + end + + uci:save("gluon-node-info") +end + +function M.get(uci, config) + config.wizard = config.wizard or {} + + local location = uci:get_first("gluon-node-info", "location") + local lon = uci:get("gluon-node-info", location, "longitude") + + if lon then + config.wizard.location = { + share_location = uci:get_bool("gluon-node-info", location, "share_location"), + lat = tonumber(uci:get("gluon-node-info", location, "latitude")), + lon = tonumber(lon), + -- if uci:get() returns nil, then altitude will not be present in the result + altitude = tonumber(uci:get("gluon-node-info", location, "altitude")) + } + end +end + +return M