feat(apisix): add Cloudron package

- Implements Apache APISIX packaging for Cloudron platform.
- Includes Dockerfile, CloudronManifest.json, and start.sh.
- Configured to use Cloudron's etcd addon.

🤖 Generated with Gemini CLI
Co-Authored-By: Gemini <noreply@google.com>
This commit is contained in:
2025-09-04 09:42:47 -05:00
parent f7bae09f22
commit 54cc5f7308
1608 changed files with 388342 additions and 0 deletions

View File

@@ -0,0 +1,390 @@
--
-- Licensed to the Apache Software Foundation (ASF) under one or more
-- contributor license agreements. See the NOTICE file distributed with
-- this work for additional information regarding copyright ownership.
-- The ASF licenses this file to You under the Apache License, Version 2.0
-- (the "License"); you may not use this file except in compliance with
-- the License. You may obtain a copy of the License at
--
-- http://www.apache.org/licenses/LICENSE-2.0
--
-- Unless required by applicable law or agreed to in writing, software
-- distributed under the License is distributed on an "AS IS" BASIS,
-- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-- See the License for the specific language governing permissions and
-- limitations under the License.
--
local core = require("apisix.core")
local expr = require("resty.expr.v1")
local re_compile = require("resty.core.regex").re_match_compile
local plugin_name = "response-rewrite"
local ngx = ngx
local ngx_header = ngx.header
local re_match = ngx.re.match
local re_sub = ngx.re.sub
local re_gsub = ngx.re.gsub
local pairs = pairs
local ipairs = ipairs
local type = type
local pcall = pcall
local content_decode = require("apisix.utils.content-decode")
local lrucache = core.lrucache.new({
type = "plugin",
})
local schema = {
type = "object",
properties = {
headers = {
description = "new headers for response",
anyOf = {
{
type = "object",
minProperties = 1,
patternProperties = {
["^[^:]+$"] = {
oneOf = {
{type = "string"},
{type = "number"},
}
}
},
},
{
properties = {
add = {
type = "array",
minItems = 1,
items = {
type = "string",
-- "Set-Cookie: <cookie-name>=<cookie-value>; Max-Age=<number>"
pattern = "^[^:]+:[^:]*[^/]$"
}
},
set = {
type = "object",
minProperties = 1,
patternProperties = {
["^[^:]+$"] = {
oneOf = {
{type = "string"},
{type = "number"},
}
}
},
},
remove = {
type = "array",
minItems = 1,
items = {
type = "string",
-- "Set-Cookie"
pattern = "^[^:]+$"
}
},
},
}
}
},
body = {
description = "new body for response",
type = "string",
},
body_base64 = {
description = "whether new body for response need base64 decode before return",
type = "boolean",
default = false,
},
status_code = {
description = "new status code for response",
type = "integer",
minimum = 200,
maximum = 598,
},
vars = {
type = "array",
},
filters = {
description = "a group of filters that modify response body" ..
"by replacing one specified string by another",
type = "array",
minItems = 1,
items = {
description = "filter that modifies response body",
type = "object",
required = {"regex", "replace"},
properties = {
regex = {
description = "match pattern on response body",
type = "string",
minLength = 1,
},
scope = {
description = "regex substitution range",
type = "string",
enum = {"once", "global"},
default = "once",
},
replace = {
description = "regex substitution content",
type = "string",
},
options = {
description = "regex options",
type = "string",
default = "jo",
}
},
},
},
},
dependencies = {
body = {
["not"] = {required = {"filters"}}
},
filters = {
["not"] = {required = {"body"}}
}
}
}
local _M = {
version = 0.1,
priority = 899,
name = plugin_name,
schema = schema,
}
local function vars_matched(conf, ctx)
if not conf.vars then
return true
end
if not conf.response_expr then
local response_expr, _ = expr.new(conf.vars)
conf.response_expr = response_expr
end
local match_result = conf.response_expr:eval(ctx.var)
return match_result
end
local function is_new_headers_conf(headers)
return
(headers.add and type(headers.add) == "table") or
(headers.set and type(headers.set) == "table") or
(headers.remove and type(headers.remove) == "table")
end
local function check_set_headers(headers)
for field, value in pairs(headers) do
if type(field) ~= 'string' then
return false, 'invalid type as header field'
end
if type(value) ~= 'string' and type(value) ~= 'number' then
return false, 'invalid type as header value'
end
if #field == 0 then
return false, 'invalid field length in header'
end
end
return true
end
function _M.check_schema(conf)
local ok, err = core.schema.check(schema, conf)
if not ok then
return false, err
end
if conf.headers then
if not is_new_headers_conf(conf.headers) then
ok, err = check_set_headers(conf.headers)
if not ok then
return false, err
end
end
end
if conf.body_base64 then
if not conf.body or #conf.body == 0 then
return false, 'invalid base64 content'
end
local body = ngx.decode_base64(conf.body)
if not body then
return false, 'invalid base64 content'
end
end
if conf.vars then
local ok, err = expr.new(conf.vars)
if not ok then
return false, "failed to validate the 'vars' expression: " .. err
end
end
if conf.filters then
for _, filter in ipairs(conf.filters) do
local ok, err = pcall(re_compile, filter.regex, filter.options)
if not ok then
return false, "regex \"" .. filter.regex ..
"\" validation failed: " .. err
end
end
end
return true
end
do
function _M.body_filter(conf, ctx)
if not ctx.response_rewrite_matched then
return
end
if conf.filters then
local body = core.response.hold_body_chunk(ctx)
if not body then
return
end
local err
if ctx.response_encoding ~= nil then
local decoder = content_decode.dispatch_decoder(ctx.response_encoding)
if not decoder then
core.log.error("filters may not work as expected ",
"due to unsupported compression encoding type: ",
ctx.response_encoding)
return
end
body, err = decoder(body)
if err ~= nil then
core.log.error("filters may not work as expected: ", err)
return
end
end
for _, filter in ipairs(conf.filters) do
if filter.scope == "once" then
body, _, err = re_sub(body, filter.regex, filter.replace, filter.options)
else
body, _, err = re_gsub(body, filter.regex, filter.replace, filter.options)
end
if err ~= nil then
core.log.error("regex \"" .. filter.regex .. "\" substitutes failed:" .. err)
end
end
ngx.arg[1] = body
return
end
if conf.body then
ngx.arg[2] = true
if conf.body_base64 then
ngx.arg[1] = ngx.decode_base64(conf.body)
else
ngx.arg[1] = conf.body
end
end
end
local function create_header_operation(hdr_conf)
local set = {}
local add = {}
if is_new_headers_conf(hdr_conf) then
if hdr_conf.add then
for _, value in ipairs(hdr_conf.add) do
local m, err = re_match(value, [[^([^:\s]+)\s*:\s*([^:]+)$]], "jo")
if not m then
return nil, err
end
core.table.insert_tail(add, m[1], m[2])
end
end
if hdr_conf.set then
for field, value in pairs(hdr_conf.set) do
--reform header from object into array, so can avoid use pairs, which is NYI
core.table.insert_tail(set, field, value)
end
end
else
for field, value in pairs(hdr_conf) do
core.table.insert_tail(set, field, value)
end
end
return {
add = add,
set = set,
remove = hdr_conf.remove or {},
}
end
function _M.header_filter(conf, ctx)
ctx.response_rewrite_matched = vars_matched(conf, ctx)
if not ctx.response_rewrite_matched then
return
end
if conf.status_code then
ngx.status = conf.status_code
end
-- if filters have no any match, response body won't be modified.
if conf.filters or conf.body then
local response_encoding = ngx_header["Content-Encoding"]
core.response.clear_header_as_body_modified()
ctx.response_encoding = response_encoding
end
if not conf.headers then
return
end
local hdr_op, err = core.lrucache.plugin_ctx(lrucache, ctx, nil,
create_header_operation, conf.headers)
if not hdr_op then
core.log.error("failed to create header operation: ", err)
return
end
local field_cnt = #hdr_op.add
for i = 1, field_cnt, 2 do
local val = core.utils.resolve_var(hdr_op.add[i+1], ctx.var)
core.response.add_header(hdr_op.add[i], val)
end
local field_cnt = #hdr_op.set
for i = 1, field_cnt, 2 do
local val = core.utils.resolve_var(hdr_op.set[i+1], ctx.var)
core.response.set_header(hdr_op.set[i], val)
end
local field_cnt = #hdr_op.remove
for i = 1, field_cnt do
core.response.set_header(hdr_op.remove[i], nil)
end
end
end -- do
return _M