🌴 Sync 2026-03-05 23:51:42

This commit is contained in:
github-actions[bot] committed 2026-03-05 23:51:42 +08:00
1 parent 97c049ed0a
commit 0acff03804
14253 files changed
+3893239

No files matched your search

+35
View File
@@ -0,0 +1,35 @@
# Copyright (C) 2016 Openwrt.org
#
# This is free software, licensed under the Apache License, Version 2.0 .
#
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-caddy
LUCI_TITLE:=LuCI support for caddy
LUCI_DEPENDS:=
PKG_VERSION:=1.0
PKG_RELEASE:=1
define Package/$(PKG_NAME)/conffiles
/etc/caddy
endef
define Package/$(PKG_NAME)/postrm
#!/bin/sh
[ -f /etc/caddy/Caddyfile ] && rm -rf /etc/caddy/Caddyfile >/dev/null 2>&1
return 0
endef
define Package/$(PKG_NAME)/postinst
#!/bin/sh
[ -n "${IPKG_INSTROOT}" ] || {
( . /etc/uci-defaults/luci-caddy ) && rm -f /etc/uci-defaults/luci-caddy
exit 0
}
endef
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
@@ -0,0 +1,91 @@
module("luci.controller.caddy", package.seeall)
function index()
entry({"admin", "nas"}, firstchild(), _("NAS") , 45).dependent = false
if not nixio.fs.access("/etc/config/caddy") then
return
end
local page = entry({"admin", "nas", "caddy"}, alias("admin", "nas", "caddy", "basic"), _("Caddy"), 20)
page.dependent = true
page.acl_depends = { "luci-app-caddy" }
entry({"admin", "nas"}, firstchild(), "NAS", 44).dependent = false
entry({"admin", "nas", "caddy", "basic"}, cbi("caddy/caddy"), _("基本设置"), 1).leaf = true
entry({"admin", "nas", "caddy", "log"}, cbi("caddy/caddy_log"), _("日志"), 2).leaf = true
entry({"admin", "nas", "caddy", "caddy_status"}, call("caddy_status")).leaf = true
entry({"admin", "nas", "caddy", "get_log"}, call("get_log")).leaf = true
entry({"admin", "nas", "caddy", "clear_log"}, call("clear_log")).leaf = true
entry({"admin", "nas", "caddy", "admin_info"}, call("admin_info")).leaf = true
end
function caddy_status()
local e={}
local sys = require "luci.sys"
local uci = require "luci.model.uci".cursor()
local port = tonumber(uci:get_first("caddy", "caddy", "port"))
e.port = (port or 12311)
e.running=luci.sys.call("pidof caddy >/dev/null")==0
local tagfile = io.open("/tmp/caddy_time", "r")
if tagfile then
local tagcontent = tagfile:read("*all")
tagfile:close()
if tagcontent and tagcontent ~= "" then
os.execute("start_time=$(cat /tmp/caddy_time) && time=$(($(date +%s)-start_time)) && day=$((time/86400)) && [ $day -eq 0 ] && day='' || day=${day}天 && time=$(date -u -d @${time} +'%H小时%M分%S秒') && echo $day $time > /tmp/command_caddy 2>&1")
local command_output_file = io.open("/tmp/command_caddy", "r")
if command_output_file then
e.caddysta = command_output_file:read("*all")
command_output_file:close()
if e.caddysta == "" then
e.caddysta = "unknown"
end
end
end
end
local command2 = io.popen('test ! -z "`pidof caddy`" && (top -b -n1 | grep -E "$(pidof caddy)" 2>/dev/null | grep -v grep | awk \'{for (i=1;i<=NF;i++) {if ($i ~ /caddy/) break; else cpu=i}} END {print $cpu}\')')
e.caddycpu = command2:read("*all")
command2:close()
if e.caddycpu == "" then
e.caddycpu = "unknown"
end
local command3 = io.popen("test ! -z `pidof caddy` && (cat /proc/$(pidof caddy | awk '{print $NF}')/status | grep -w VmRSS | awk '{printf \"%.2f MB\", $2/1024}')")
e.caddyram = command3:read("*all")
command3:close()
if e.caddyram == "" then
e.caddyram = "unknown"
end
local command4 = io.popen("([ -s /tmp/caddy.tag ] && cat /tmp/caddy.tag ) || (echo `$(uci -q get caddy.@caddy[0].bin_dir) -v |awk '{print $1}' | sed 's/[^0-9.]*//g'` > /tmp/caddy.tag && cat /tmp/caddy.tag)")
e.caddytag = command4:read("*all")
command4:close()
if e.caddytag == "" then
e.caddytag = "unknown"
end
local command5 = io.popen("([ -s /tmp/caddynew.tag ] && cat /tmp/caddynew.tag ) || ( curl -L -k -s --connect-timeout 3 --user-agent 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36' https://api.github.com/repos/caddyserver/caddy/releases/latest | grep tag_name | sed 's/[^0-9.]*//g' >/tmp/caddynew.tag && cat /tmp/caddynew.tag )")
e.caddynewtag = command5:read("*all")
command5:close()
if e.caddynewtag == "" then
e.caddynewtag = "unknown"
end
luci.http.prepare_content("application/json")
luci.http.write_json(e)
end
function get_log()
luci.http.write(luci.sys.exec("[ -s $(uci -q get caddy.@caddy[0].log_dir) ] && cat $(uci -q get caddy.@caddy[0].log_dir)"))
end
function clear_log()
luci.sys.call("cat /dev/null > $(uci -q get caddy.@caddy[0].log_dir)")
end
function admin_info()
local validate = luci.sys.exec("$(uci -q get caddy.@caddy[0].bin_dir) validate --config /etc/caddy/Caddyfile --adapter caddyfile 2>&1")
luci.http.prepare_content("application/json")
luci.http.write_json({ validate = validate })
end
@@ -0,0 +1,127 @@
local m, s
m = Map("caddy", translate("Caddy"), translate("Caddy 是一个可扩展的服务器平台,具有自动 HTTPS 功能的快速且可扩展的多平台 HTTP/1-2-3 Web 服务器") .. "<br/>" .. "项目地址:" .. [[<a href="https://github.com/caddyserver/caddy" target="_blank">]] .. translate("github.com/caddyserver/caddy") .. "</a>&nbsp; &nbsp;&nbsp;" .. " caddy文档:" .. [[<a href="https://caddyserver.com/docs/" target="_blank">]] .. translate("caddyserver.com/docs/") .. [[</a>]])
m:section(SimpleSection).template = "caddy/caddy_status"
s = m:section(TypedSection, "caddy")
s.addremove = false
s.anonymous = true
o = s:option(Flag, "enabled", translate("Enabled"))
o.rmempty = false
o.default = 0
o = s:option(Button, "btnrm", translate("重启"))
o.inputtitle = translate("重启")
o.description = translate("在没有修改参数的情况下快速重新启动一次")
o.inputstyle = "apply"
o:depends("enabled", "1")
o.write = function()
os.execute("/etc/init.d/caddy restart")
end
e=s:option(ListValue,"cmd",translate("启动方式"),
translate("自定义配置文件启动,若不懂参数请勿选择自定义"))
e:value("默认")
e:value("自定义")
o = s:option(TextValue, "caddyfile", translate("Caddyfile配置文件"),
translate("这是caddy的启动配置文件Caddyfile,路径在/etc/caddy/Caddyfile<br>使用的命令是:caddy run --config /etc/caddy/Caddyfile --adapter caddyfile<br>如需设置密码,这里就不能使用明文密码,需要转换<br>转换命令:$(uci -q get caddy.@caddy[0].bin_dir) hash-password --plaintext 新密码"))
o.rows = 3
o.wrap = "off"
o:depends("cmd", "自定义")
o = s:option(Value, "port", translate("端口"))
o.datatype = "and(port,min(1))"
o.default = "12311"
o:depends("cmd", "默认")
o = s:option(Flag,"file_pass", translate("启用 认证"))
o.datatype = "string"
o.default = "0"
o:depends("cmd", "默认")
o = s:option(Value,"file_username", translate("用户名"))
o.datatype = "string"
o.default = "admin"
o:depends("file_pass", "1")
o = s:option(Value,"file_password", translate("密码"))
o.datatype = "string"
o.password = true
o.default = "123456"
o:depends("file_pass", "1")
o = s:option(Flag, "filezip", translate("启用 压缩"))
o.default = 0
o:depends("cmd", "默认")
o = s:option(Flag, "log", translate("启用日志"))
o.default = 1
o:depends("cmd", "默认")
o = s:option(Value, "log_dir", translate("日志路径"),
translate("日志的存放路径,填写完整的路径及日志文件名<br>建议/tmp里,例如:/tmp/caddy/requests.log"))
o.datatype = "string"
o.default = "/tmp/caddy/requests.log"
o:depends("log", "1")
o = s:option(Value, "bin_dir", translate("程序路径"),
translate("caddy二进制的存放路径,填写完整的路径及caddy名称<br>例如:/usr/bin/caddy &nbsp;&nbsp;&nbsp; 例如:/tmp/caddy<br>可自行编译对应架构的二进制程序:<a href='https://www.right.com.cn/forum/forum.php?mod=viewthread&tid=6006472&highlight=caddy' target='_blank'>编译教程</a>"))
o.datatype = "string"
o.placeholder = "/usr/bin/caddy"
o = s:option(Value, "data_dir", translate("指向路径"),
translate("指向一个路径,可在web界面访问你的文件,默认为 /mnt"))
o.datatype = "string"
o.default = "/mnt"
o:depends("cmd", "默认")
o = s:option(Flag, "webdav", translate("启用 webdav"))
o.default = "0"
o:depends("cmd", "默认")
o = s:option(Flag,"webdav_pass", translate("启用 认证"))
o.datatype = "string"
o:depends("webdav", "1")
o.default = "0"
o = s:option(Value,"webdav_username", translate("用户名"))
o.datatype = "string"
o.default = "admin"
o:depends("webdav_pass", "1")
o = s:option(Value,"webdav_password", translate("密码"))
o.datatype = "string"
o.password = true
o.default = "123456"
o:depends("webdav_pass", "1")
o = s:option(Value,"webdav_port", translate("webdav端口"))
o.datatype = "and(port,min(1))"
o.default = "12322"
o:depends("webdav", "1")
o = s:option(Flag, "webzip", translate("启用 压缩"))
o.default = 0
o:depends("webdav", "1")
o = s:option(Value, "webdav_data_dir", translate("webdav指向路径"),
translate("指向一个路径,使用webdav访问你的文件,默认为 /mnt<br>连接地址须加/dav后缀&nbsp;&nbsp;如: 192.168.1.1:12311/dav"))
o.datatype = "string"
o:depends("webdav", "1")
o.default = "/mnt"
o = s:option(Flag, "allow_wan", translate("允许从外网访问"))
o.rmempty = false
o = s:option(Flag, "api", translate("启用 API接口"))
o:depends("cmd", "默认")
o = s:option(Button, "admin_info", translate("检测配置文件"),
translate("验证Caddyfile配置文件是否正确,它会模拟启动caddy<br>但是并不会真的启动,会列出详细信息,以便修正配置文件"))
o.rawhtml = true
o.template = "caddy/admin_info"
return m
@@ -0,0 +1,5 @@
m = Map("caddy")
m:append(Template("caddy/caddy_log"))
return m
@@ -0,0 +1,25 @@
<%+cbi/valueheader%>
<script type="text/javascript">//<![CDATA[
function admin_info(btn)
{
btn.disabled = true;
btn.value = '<%:Collecting data...%>';
XHR.get('<%=luci.dispatcher.build_url("admin", "nas", "caddy", "admin_info")%>',
null,
function(x,rv)
{
var tb = document.getElementById('<%=self.option%>-status');
if (tb) {
rv.validate = rv.validate.replace(/{"level":/g, '<br>{"level":').replace(/Error/g, "<br>Error").replace(/Valid configuration/g, "<br><strong><font color='#008000'>当前配置文件正确!</font></strong>").replace(/warn/g, "<font color='#800080'>警告</font>").replace(/level/g, "<font color='#1E90FF'>等级</font>").replace(/info/g, "<font color='#00BFFF'>普通</font>").replace(/msg/g, "<font color='#FFA500'>信息</font>").replace(/error/g, "<font color='#DB7093'>错误</font>").replace(/Error:/g, "<font color='red'><strong>当前配置文件错误!</strong><br>错误位置信息:</font>");
tb.innerHTML = "<br><font color='#FF00FF'><%:状态:%></font>" + "<font color='#2F4F4F'>" + rv.validate + "</font>";
}
btn.disabled = false;
btn.value = '<%:检测%>';
}
);
return false;
}
//]]></script>
<input type="button" class="btn cbi-button cbi-button-apply" value="<%:检测%>" onclick="return admin_info(this)" />
<span id="<%=self.option%>-status"><em><%=self.value%></em></span>
<%+cbi/valuefooter%>
@@ -0,0 +1,124 @@
<script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script>
<style>
:root { --bg:#ffffff; --card:#f6f8fa; --text:#222; --border:#ddd; --muted:#666; }
@media (prefers-color-scheme: dark) { :root { --bg:#0f1115; --card:#1b1e24; --text:#e5e7eb; --border:#333; --muted:#9ca3af; } }
body { background:var(--bg); color:var(--text); margin:0; }
#app { all: unset; display:block; width:100%; padding:12px; font-family:system-ui,-apple-system; text-align:left; }
.toolbar { display:flex; justify-content:space-between; align-items:center; margin-bottom:12px; }
.toolbar select { margin-left:4px; }
button { border-radius:6px; padding:6px 10px; border:1px solid var(--border); background:transparent; color:var(--text); cursor:pointer; transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease; }
button:active { transform: scale(0.95); box-shadow: inset 0 2px 4px rgba(0,0,0,0.2); background: rgba(0,0,0,0.05); }
button.toggle-btn { background:#d0a026; color:#fff; border:none; }
button.clear-log { background:#e74c3c; color:#fff; border:none; }
button.view-json { background:#60a5fa; color:#fff; border:none; }
.log-card { all: unset; display:block; width:100%; padding:12px; margin-bottom:12px; background:var(--card); border-radius:10px; text-align:left; box-sizing:border-box; overflow:hidden; border-width:2px; border-style:solid; border-left-color:#888; }
.log-card.ok { border-color:#2ecc71; }
.log-card.warn { border-color:#f39c12; }
.log-card.err { border-color:#e74c3c; }
.log-inner { all: unset; display:block; width:100%; text-align:left; overflow-wrap:break-word; word-break:break-word; }
.log-row { all: unset; display:flex; width:100%; font-size:13px; line-height:1.8; word-break:break-all; margin-bottom:2px; text-align:left; align-items:flex-start; }
.log-row .label { all: unset; flex-shrink:0; color:var(--muted); margin-right:8px; }
.log-row a { color:#2563eb; text-decoration:underline; font-size:13px; }
.modal { display:none; position:fixed; z-index:9999; left:0; top:0; width:100%; height:100%; background-color: rgba(0,0,0,0.5); justify-content:center; align-items:center; overflow:auto; }
.modal.show { display:flex; }
.modal-content { background:#fff; border-radius:6px; max-width:700px; width:80%; max-height:70vh; overflow:auto; white-space: pre; position:relative; display:flex; flex-direction:column; }
.modal-header { display:flex; justify-content:space-between; align-items:center; padding:10px 16px; border-bottom:1px solid #ccc; background:#f3f3f3; border-top-left-radius:6px; border-top-right-radius:6px; flex-shrink:0; }
.modal-header h3 { margin:0; font-size:16px; }
.modal-close { padding:4px 8px; border:none; border-radius:6px; background:#393fec; color:#fff; cursor:pointer; }
.modal-body { padding:12px; overflow:auto; flex:1; }
#toTop { position:fixed; right:18px; bottom:120px; padding:8px 14px; border-radius:20px; background:#8644f1; color:#fff; display:none; z-index:9999; cursor:pointer; transition: transform 0.1s ease, box-shadow 0.1s ease, background 0.1s ease; }
#toTop.show { display:block; }
#toTop:active { transform: scale(0.95); box-shadow: inset 0 2px 4px rgba(0,0,0,0.2); background: rgba(0,0,0,0.05); }
@media (max-width:600px){ .log-row{font-size:12px;} }
@media (prefers-color-scheme: dark){
body { background:#0f1115; color:#e5e7eb; }
.log-card { background:#1b1e24; border-color:#333; color:#e5e7eb; }
.modal-content { background:#1b1e24; color:#e5e7eb; }
.modal-header { background:#2b2e36; border-bottom-color:#444; }
button.toggle-btn { background:#d0a026; color:#fff; }
button.clear-log { background:#b91c1c; color:#fff; }
button.view-json { background:#2563eb; color:#fff; }
.modal-close { background:#393fec; color:#fff; }
}
</style>
<div id="app">
<div class="toolbar">
<div>
<button type="button" class="toggle-btn" @click="toggle">{{ paused ? '继续刷新' : '暂停刷新' }}</button>
<button type="button" class="clear-log" @click="clear">清空日志</button>
</div>
<div style="display:flex;align-items:center;gap:12px;">
<label>
<select v-model="filterType">
<option value="all">全部</option>
<option value="warn">警告</option>
<option value="err">错误</option>
</select>
</label>
<span>共 {{ filteredLogs.length }} 条日志</span>
</div>
</div>
<div v-for="log in filteredLogs" :key="log.id" class="log-card" :class="statusClass(log.status)">
<div class="log-inner">
<div class="log-row" v-if="log.ts"><span class="label">时间:</span>{{ time(log.ts) }}</div>
<div class="log-row" v-if="log.request"><span class="label">访问:</span>{{ fullUrl(log) }}</div>
<div class="log-row" v-if="log.request && log.request.method"><span class="label">方法:</span>{{ log.request.method }}</div>
<div class="log-row" v-if="log.status"><span class="label">状态:</span>{{ log.status }}</div>
<div class="log-row" v-if="realIp(log)"><span class="label">IP:</span>{{ realIp(log) }}</div>
<div class="log-row" v-if="userAgent(log)"><span class="label">标识:</span>{{ userAgent(log) }}</div>
<div class="log-row" v-if="log.duration"><span class="label">耗时:</span>{{ (log.duration*1000).toFixed(2) }} ms</div>
<div class="log-row">
<button type="button" class="view-json" @click.prevent="showModal(log)">查看原始 JSON 日志</button>
</div>
</div>
</div>
<div class="modal" :class="{show: modalLog}" @click.self="closeModal">
<div class="modal-content">
<div class="modal-header">
<h3>完整日志</h3>
<button class="modal-close" @click.prevent="closeModal">关闭</button>
</div>
<div class="modal-body">
<pre>{{ modalLog ? JSON.stringify(modalLog,null,2) : '' }}</pre>
</div>
</div>
</div>
</div>
<div id="toTop">返回顶部</div>
<script>
const { createApp } = Vue;
createApp({
data(){ return { logs: [], paused:false, filterType:'all', modalLog:null }; },
computed:{ filteredLogs(){ if(this.filterType==='all') return this.logs; return this.logs.filter(l=>this.statusClass(l.status)===this.filterType); }},
methods:{
fetch(){ if(this.paused) return; XHR.get('<%=url([[admin]], [[nas]], [[caddy]], [[get_log]])%>', null, x=>{ if(x && x.status===200){ this.logs = x.responseText.split('\n').filter(Boolean).map((l,i)=>{ try{ let obj=JSON.parse(l); obj.id=i; return obj; }catch{return null;} }).filter(Boolean).reverse(); } }); },
toggle(){ this.paused = !this.paused; },
clear(){ XHR.get('<%=url([[admin]], [[nas]], [[caddy]], [[clear_log]])%>'); this.logs=[]; },
time(ts){ return new Date(ts*1000).toLocaleString(); },
fullUrl(l){ const h=l.request.headers||{}; const p=h['X-Forwarded-Proto']?h['X-Forwarded-Proto'][0]:'http'; return p+'://'+l.request.host+l.request.uri; },
realIp(l){ const h=l.request.headers||{}; return h['Cf-Connecting-Ip']?h['Cf-Connecting-Ip'][0]:l.request.client_ip; },
userAgent(l){ const h=l.request.headers||{}; return h['User-Agent']?h['User-Agent'][0]:'未知'; },
statusClass(c){ if(c>=500) return 'err'; if(c>=400) return 'warn'; return 'ok'; },
showModal(log){ this.modalLog = log; document.body.style.overflow = 'hidden'; },
closeModal(){ this.modalLog = null; document.body.style.overflow = ''; }
},
mounted(){ this.fetch(); setInterval(this.fetch,2000); const btn=document.getElementById('toTop'); btn.onclick=()=>window.scrollTo({top:0,behavior:'smooth'}); window.addEventListener('scroll',()=>btn.classList.toggle('show',window.scrollY>400)); }
}).mount('#app');
</script>
@@ -0,0 +1,35 @@
<%
local uci = require 'luci.model.uci'.cursor()
ssl = uci:get_first('caddy', 'caddy', 'ssl')
if ssl == '1' then
protocol="http://"
else
protocol="http://"
end
%>
<script type="text/javascript">//<![CDATA[
XHR.poll(5, '<%=url("admin/nas/caddy/caddy_status")%>', null,
function(x, st) {
var tb = document.getElementById('caddy_status');
if (st && tb) {
var caddyStatus = st.running ? "<span style='color:green;'> 运行中</span><img src='https://www.right.com.cn/forum/data/attachment/album/202401/30/073058is17bcmu7z3nmn2b.gif' width='30px' alt=''>" : "<span style='color:red;'> 未运行</span>";
var caddystaContent = st.running ? "已运行:" + "<span style='color:#DA70D6;'>" + st.caddysta + "</span>" : "";
var caddycpuContent = st.running ? "<br>" + "CPU占用:" + "<span style='color:#6A5ACD;'>" + st.caddycpu + "</span>" : "";
var caddyramContent = st.running ? "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;" + "&nbsp;内存占用:" + "<span style='color:#0000CD;'>" + st.caddyram + "</span>" : "";
var caddytagContent = st.running ? "<br>" + "当前版本:" + "<span style='color:#CD853F;'>" + st.caddytag + "</span>" : "";
var caddynewtagContent = st.running ? "&nbsp;&nbsp;&nbsp;" + "最新版本:" + "<span style='color:#FFA500;'>" + st.caddynewtag + "</span>" : "";
var caddywebContent = st.running ? "<input class=\"cbi-button cbi-button-reload mar-10\" type=\"button\" value=\" <%:Web 界面%> \" onclick=\"window.open('<%=protocol%>" + window.location.hostname + ":" + st.port + "/')\"/>" : "";
tb.innerHTML = "<b><em>Caddy " + caddyStatus + caddystaContent + caddycpuContent + caddyramContent + caddytagContent + caddynewtagContent + caddywebContent + "</b></em>";
}
}
);
//]]></script>
<style>.mar-10 {margin-left: 50px; margin-right: 10px;}</style>
<fieldset class="cbi-section">
<p id="caddy_status">
<em><b><%:Collecting data...%></b></em>
</p>
</fieldset>
+49
View File
@@ -0,0 +1,49 @@
# 此配置文件路径:/etc/caddy/Caddyfile
{ # 全局配置
# 启动 cgi 模块
order cgi before respond
# 启动 webdav 模块
order webdav before file_server
# 关闭 API 端口
admin off
}
:12311 {
#启用压缩
encode gzip
#认证,账号admin 密码123456
basicauth {
admin $2a$14$RdbOHzJhf5BaapSdlYTCbe.yWY9cEZjyDpfgwStY28K/qsM1tX8tu
}
#路径 /mnt
root * /mnt
#文件服务器 端口12311
file_server browse
log {
#日志 路径/tmp/caddy/requests.log
output file /tmp/caddy/requests.log {
roll_size 1MiB
roll_local_time
roll_keep 5
roll_keep_for 120h
}
}
}
:12322 {
#启用压缩
encode gzip
#认证,账号admin 密码123456
basicauth {
admin $2a$14$RdbOHzJhf5BaapSdlYTCbe.yWY9cEZjyDpfgwStY28K/qsM1tX8tu
}
# webdav 路径根目录 端口12322
webdav /dav/* {
prefix /dav
root /
}
}
+14
View File
@@ -0,0 +1,14 @@
config caddy
option 'enabled' '0'
option cmd '默认'
option 'port' '12311'
option 'webdav' '0'
option 'filezip' '0'
option 'webzip' '0'
option 'cgi' '0'
option 'api' '0'
option 'data_dir' '/mnt'
option 'file_pass' '0'
option 'log' '1'
option 'log_dir' '/tmp/caddy/requests.log'
option allow_wan '0'
+194
View File
@@ -0,0 +1,194 @@
#!/bin/sh /etc/rc.common
START=99
USE_PROCD=1
PROG="$(uci -q get caddy.@caddy[0].bin_dir)"
get_config() {
config_get_bool enabled $1 enabled 0
config_get cmd $1 cmd "默认"
config_get_bool webdav $1 webdav 0
config_get port $1 port 12311
config_get webdav_port $1 webdav_port 12322
config_get log $1 log 1
config_get data_dir $1 data_dir "/mnt"
config_get webdav_data_dir $1 webdav_data_dir "/mnt"
config_get log_dir $1 log_dir "/tmp/caddy/requests.log"
config_get caddy_file $1 caddy_file "/etc/caddy/Caddyfile"
config_get caddyfile $1 caddyfile
config_get file_pass $1 file_pass 0
config_get file_username $1 file_username "admin"
config_get file_password $1 file_password "123456"
config_get webdav_pass $1 webdav_pass 0
config_get webdav_username $1 webdav_username "admin"
config_get webdav_password $1 webdav_password "123456"
config_get api $1 api 0
config_get filezip $1 filezip 0
config_get webzip $1 webzip 0
config_get allow_wan $1 allow_wan 0
}
set_firewall() {
if [ "$external_access" = "allow" ]; then
uci -q delete firewall.caddy
uci set firewall.caddy=rule
uci set firewall.caddy.name="caddy"
uci set firewall.caddy.target="ACCEPT"
uci set firewall.caddy.src="wan"
uci set firewall.caddy.proto="tcp"
uci set firewall.caddy.dest_port="$port"
[ ! -z "$webdav_port" ] && uci set firewall.caddy.dest_port="$webdav_port"
uci set firewall.caddy.enabled="1"
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1
elif [ "$external_access" = "deny" ]; then
uci -q delete firewall.caddy
uci commit firewall
/etc/init.d/firewall reload >/dev/null 2>&1
fi
}
start_service() {
config_load caddy
config_foreach get_config caddy
[ $enabled -ne 1 ] && return 1
log_path="$(dirname $log_dir)"
mkdir -p $log_path
if [ ! -f "$PROG" ] ;then
uci -q set caddy.@caddy[0].enabled=0
uci commit caddy
echo "$(TZ=UTC-8 date -R +%Y年%m月%d日\ %X) : $PROG 未找到,无法启动!请确认已上传程序" >>$log_dir 2>&1
exit 1
else
chmod +x $PROG
if [ $(($($PROG -h | wc -l))) -lt 3 ] ;then
uci -q set caddy.@caddy[0].enabled=0
uci commit caddy
echo "$(TZ=UTC-8 date -R +%Y年%m月%d日\ %X) : $PROG 程序不完整或CPU架构不支持的程序,无法启动!" >>$log_dir 2>&1
exit 1
fi
fi
if [ "$cmd" = "默认" ] ;then
mkdir -p $data_dir
if [ "$log" -eq 1 ] ;then
logs="log {
output file $log_dir {
roll_size 1MiB
roll_local_time
roll_keep 5
roll_keep_for 120h
}
}"
fi
if [ "$file_pass" -eq 1 ] ;then
filepassword="$($PROG hash-password --plaintext $file_password)"
filebasicauth="basicauth {
$file_username $filepassword
}"
fi
if [ "$webdav" -eq 1 ] ;then
mkdir -p $webdav_data_dir
if [ "$webdav_pass" -eq 1 ] ;then
password="$($PROG hash-password --plaintext $webdav_password)"
basicauth="basicauth {
$webdav_username $password
}"
fi
webdavs=":$webdav_port {
$basicauth
webdav * {
prefix /dav
root $webdav_data_dir
}
}"
orderweb="order webdav before file_server # 启动 webdav 模块"
fi
if [ "$filezip" -eq 1 ] ;then
filezips="encode gzip"
fi
if [ "$webzip" -eq 1 ] ;then
webzips="encode gzip"
fi
if [ "$api" != "1" ] ;then
apis="admin off # 关闭 API 端口"
fi
if [ "$allow_wan" -eq "1" ]; then
external_access="allow"
else
external_access="deny"
fi
if [ "$webdav" -eq 1 ] || [ "$api" != "1" ] ;then
order="{ # 全局配置
$orderweb
$apis
} "
fi
set_firewall
cat > $caddy_file <<EOF
# 此配置文件路径:/etc/caddy/Caddyfile
$order
:$port {
$filezips
$filebasicauth
root * $data_dir
file_server browse
$logs
}
$webdavs
EOF
uci -q set caddy.@caddy[0].caddyfile="$(cat $caddy_file)"
uci commit caddy
else
caddyf="$(uci -q get caddy.@caddy[0].caddyfile)"
logpath="$(echo $caddyf | grep "output file" | awk -F 'output file' '{print $2}' | tr -d '{ ')"
[ ! -z "$logpath" ] && uci -q set caddy.@caddy[0].log_dir=$logpath && uci commit caddy
if [ ! -z "$caddyf" ] ;then
echo "$caddyf" >$caddy_file 2>&1
else
uci -q set caddy.@caddy[0].enabled=0
uci commit caddy
echo "$(TZ=UTC-8 date -R +%Y年%m月%d日\ %X) : $caddy_file 配置文件Caddyfile为空,无法启动!" >>$log_dir 2>&1
exit 1
fi
fi
$PROG fmt --overwrite $caddy_file
procd_open_instance caddy
procd_set_param command $PROG
procd_append_param command run
procd_append_param command --config $caddy_file
procd_append_param command --adapter caddyfile
procd_set_param stdout 0
procd_set_param stderr 0
procd_set_param respawn
procd_close_instance caddy
echo `date +%s` > /tmp/caddy_time
}
service_triggers() {
procd_add_reload_trigger "caddy"
}
stop_service() {
external_access="deny"
set_firewall
rm -rf /tmp/caddy.tag /tmp/caddynew.tag
rm -rf $(uci -q get caddy.@caddy[0].log_dir)
}
reload_service() {
stop
sleep 1
start
}
@@ -0,0 +1,20 @@
#!/bin/sh
[ ! -f "/usr/share/ucitrack/luci-app-caddy.json" ] && {
cat > /usr/share/ucitrack/luci-app-caddy.json << EEOF
{
"config": "caddy",
"init": "caddy"
}
EEOF
}
uci -q batch <<-EOF >/dev/null
delete ucitrack.@caddy[-1]
add ucitrack caddy
set ucitrack.@caddy[-1].init=caddy
commit ucitrack
EOF
rm -rf /tmp/luci-*
chmod 755 /etc/init.d/caddy
exit 0
@@ -0,0 +1,11 @@
{
"luci-app-caddy": {
"description": "Grant UCI access for luci-app-caddy",
"read": {
"uci": [ "caddy" ]
},
"write": {
"uci": [ "caddy" ]
}
}
}