update 2026-07-28 14:21:36
@@ -6,9 +6,9 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=LuCI support for linkease
|
||||
LUCI_DEPENDS:=+luci-lib-linkeasefile
|
||||
LUCI_DEPENDS:=+linkease
|
||||
LUCI_PKGARCH:=all
|
||||
PKG_VERSION:=2.1.70-r4
|
||||
PKG_VERSION:=2.1.70-r3
|
||||
# PKG_RELEASE MUST be empty for luci.mk
|
||||
PKG_RELEASE:=
|
||||
LUCI_MINIFY_CSS:=0
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
import{ab as L,i as v,ac as M}from"./index.js";var N=/\s/;function $(n){for(var r=n.length;r--&&N.test(n.charAt(r)););return r}var B=/^\s+/;function R(n){return n&&n.slice(0,$(n)+1).replace(B,"")}var k=0/0,F=/^[-+]0x[0-9a-f]+$/i,_=/^0b[01]+$/i,j=/^0o[0-7]+$/i,D=parseInt;function S(n){if(typeof n=="number")return n;if(L(n))return k;if(v(n)){var r=typeof n.valueOf=="function"?n.valueOf():n;n=v(r)?r+"":r}if(typeof n!="string")return n===0?n:+n;n=R(n);var t=_.test(n);return t||j.test(n)?D(n.slice(2),t?2:8):F.test(n)?k:+n}var H=function(){return M.Date.now()};const h=H;var P="Expected a function",U=Math.max,X=Math.min;function z(n,r,t){var u,c,l,s,i,f,o=0,b=!1,d=!1,T=!0;if(typeof n!="function")throw new TypeError(P);r=S(r)||0,v(t)&&(b=!!t.leading,d="maxWait"in t,l=d?U(S(t.maxWait)||0,r):l,T="trailing"in t?!!t.trailing:T);function x(e){var a=u,m=c;return u=c=void 0,o=e,s=n.apply(m,a),s}function W(e){return o=e,i=setTimeout(g,r),b?x(e):s}function O(e){var a=e-f,m=e-o,E=r-a;return d?X(E,l-m):E}function p(e){var a=e-f,m=e-o;return f===void 0||a>=r||a<0||d&&m>=l}function g(){var e=h();if(p(e))return y(e);i=setTimeout(g,O(e))}function y(e){return i=void 0,T&&u?x(e):(u=c=void 0,s)}function A(){i!==void 0&&clearTimeout(i),o=0,u=f=c=i=void 0}function C(){return i===void 0?s:y(h())}function I(){var e=h(),a=p(e);if(u=arguments,c=this,f=e,a){if(i===void 0)return W(f);if(d)return clearTimeout(i),i=setTimeout(g,r),x(f)}return i===void 0&&(i=setTimeout(g,r)),s}return I.cancel=A,I.flush=C,I}export{z as d};
|
||||
@@ -0,0 +1 @@
|
||||
import{e as t,r as a,F as _,y as s,S as o,q as n,a7 as c}from"./index.js";const r={class:"layout-conatiner"},d={class:"layout-f"},l={class:"layout-r"},i=t({__name:"index",setup(u){return a(!0),(e,p)=>(n(),_("div",r,[s("div",d,[o(e.$slots,"f",{},void 0,!0)]),s("div",l,[o(e.$slots,"r",{},void 0,!0)])]))}});const v=c(i,[["__scopeId","data-v-cbf72d2b"]]);export{v as l};
|
||||
@@ -0,0 +1 @@
|
||||
var g=(e,t,s)=>new Promise((a,l)=>{var c=i=>{try{n(s.next(i))}catch(d){l(d)}},u=i=>{try{n(s.throw(i))}catch(d){l(d)}},n=i=>i.done?a(i.value):Promise.resolve(i.value).then(c,u);n((s=s.apply(e,t)).next())});import{a8 as h,a9 as y,aa as f}from"./index.js";const o=e=>["linkease-file_web",e].join("/"),r={model:{get(){return localStorage.getItem(o("model"))=="true"},set(e){localStorage.setItem(o("model"),e)}},order:{get(){const e=localStorage.getItem(o("order"));return e||"date"},set(e){localStorage.setItem(o("order"),e)}},orderBy:{get(){return localStorage.getItem(o("orderBy"))||"asc"},set(e){localStorage.setItem(o("orderBy"),e)}},filter:{get(){return localStorage.getItem(o("filter"))||"default"},set(e){localStorage.setItem(o("filter"),e)}},copy:{get(){let e={type:"copy",values:[]};try{const t=localStorage.getItem(o("copy"));t&&(e=JSON.parse(t))}catch(t){}return e},set(e){const t=JSON.stringify(e);localStorage.setItem(o("copy"),t)}}},m=h("app",{state:()=>({keys:0,config:{model:r.model.get(),order:r.order.get(),orderBy:r.orderBy.get(),filter:r.filter.get()},menus:[],entries:[],checknoxEntries:[],previewImage:{enable:!1,values:[]},copy:r.copy.get(),task:0}),getters:{isCopy(){var e,t;return((t=(e=this.copy)==null?void 0:e.values)==null?void 0:t.length)>0}},actions:{reload(){this.keys++},taskIncr(e){e===void 0?this.task=0:this.task+=e},getEntries(){return g(this,null,function*(){try{const e=yield y.File.Basic.List.POST({path:"/local"});if(e.data){const{success:t,result:s,error:a}=e.data;t==0&&(this.menus=s.entries||[]),a&&f.Warning(a)}}catch(e){console.log(e),f.Error(`${e}`)}})},setConfigModel(){this.config.model=!this.config.model,r.model.set(`${this.config.model}`)},setConfigOrder(e){this.config.order=e,r.order.set(e)},setConfigOrderBy(e){this.config.orderBy=e,r.orderBy.set(e)},setConfigFilter(e){this.config.filter=e,r.filter.set(e)},setCopy(e){this.copy.type=e.type,this.copy.values=e.values||[],r.copy.set(e)}}}),S=h("editor",{state:()=>({entries:[],editableTabs:[],editableTabsValue:""}),actions:{addTab(e){for(let t=0;t<this.editableTabs.length;t++)if(this.editableTabs[t].rootPath===e.rootPath){this.editableTabsValue=e.rootPath||"";return}this.editableTabs.push(e),this.editableTabsValue=e.rootPath||""},removeTab(e){const t=this.editableTabs;let s=this.editableTabsValue;s===e&&t.forEach((a,l)=>{if(a.rootPath===e){const c=t[l+1]||t[l-1];c&&(s=c.rootPath||"")}}),this.editableTabsValue=s,this.editableTabs=t.filter(a=>a.rootPath!==e)}}});export{S as a,r as s,m as u};
|
||||
@@ -0,0 +1 @@
|
||||
var u=(c,d,n)=>new Promise((p,r)=>{var s=a=>{try{i(n.next(a))}catch(e){r(e)}},m=a=>{try{i(n.throw(a))}catch(e){r(e)}},i=a=>a.done?p(a.value):Promise.resolve(a.value).then(s,m);i((n=n.apply(c,d)).next())});import{e as S,r as x,aA as D,az as g,t as B,x as y,A as l,a2 as w,q as _,y as o,P as F,bH as I,F as f,O as C,I as b,v as V,a3 as k,bI as q,aM as A,B as N,a9 as P,a4 as T,C as L,a5 as M,a6 as O,aB as h,a7 as z}from"./index.js";import{u as G}from"./chunk.6424c2fc.js";const v=c=>(M("data-v-61900a83"),c=c(),O(),c),H=["onSubmit"],U={class:"form-item"},W=v(()=>o("div",{class:"form-item_label form-required"}," \u6570\u636E\u76EE\u5F55 ",-1)),$={class:"form-item_content"},j=v(()=>o("option",{value:"",selected:""},"\u9009\u62E9\u6570\u636E\u76EE\u5F55",-1)),J=["value"],K={class:"form-btns"},Q=v(()=>o("div",{class:"auto"},null,-1)),R=S({__name:"index",setup(c){return u(this,null,function*(){let d,n;const p=G(),r=x(!1),s=D({dataPath:""}),m=()=>u(this,null,function*(){try{const a=yield P.LocalDevice.Data.GET();if(a.data){const{success:e,result:t}=a.data;t!=null&&t.dataPath&&(s.dataPath=t.dataPath)}}catch(a){}});[d,n]=g(()=>m()),yield d,n();const i=()=>u(this,null,function*(){if(!(s.dataPath==""||s.dataPath==null)){r.value=!0;try{const a={dataPath:s.dataPath},e=yield P.LocalDevice.Data.POST(a);if(e.data){const{success:t,error:E}=e.data;E&&h.Warning(E),t===0&&h.Success("\u4FDD\u5B58\u6210\u529F")}}catch(a){h.Error(`${a}`)}finally{r.value=!1}}});return(a,e)=>(_(),B(l(w),{id:"page"},{default:y(()=>[o("form",{onSubmit:N(i,["prevent"])},[o("div",U,[W,o("div",$,[F(o("select",{placeholder:"\u9009\u62E9\u6570\u636E\u76EE\u5F55","onUpdate:modelValue":e[0]||(e[0]=t=>s.dataPath=t),required:""},[j,(_(!0),f(b,null,C(l(p).menus,t=>(_(),f(b,null,[t.name!="root"?(_(),f("option",{key:0,value:"/local/"+t.name},T(t.name),9,J)):L("",!0)],64))),256))],512),[[I,s.dataPath,void 0,{trim:!0}]])])]),o("div",K,[Q,V(l(A),{type:"primary","native-type":"submit",icon:l(q),loading:r.value,disabled:r.value},{default:y(()=>[k(" \u4FDD\u5B58 ")]),_:1},8,["icon","loading","disabled"])])],40,H)]),_:1}))})}});const aa=z(R,[["__scopeId","data-v-61900a83"]]);export{aa as default};
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
|
After Width: | Height: | Size: 8.1 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 8.2 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 2.2 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
@@ -9,6 +9,7 @@ function index()
|
||||
|
||||
entry({"admin", "services", "linkease_status"}, call("linkease_status"))
|
||||
|
||||
entry({"admin", "services", "linkease", "file"}, call("linkease_file_template")).leaf = true
|
||||
|
||||
end
|
||||
|
||||
@@ -26,3 +27,13 @@ function linkease_status()
|
||||
luci.http.write_json(status)
|
||||
end
|
||||
|
||||
function get_params(name)
|
||||
local data = {
|
||||
prefix=luci.dispatcher.build_url(unpack({"admin", "services", "linkease", name})),
|
||||
}
|
||||
return data
|
||||
end
|
||||
|
||||
function linkease_file_template()
|
||||
luci.template.render("linkease/file", get_params("file"))
|
||||
end
|
||||
|
||||
@@ -0,0 +1,191 @@
|
||||
-- Copyright 2022 xiaobao <xiaobao@linkease.com>
|
||||
-- Licensed to the public under the MIT License
|
||||
|
||||
local http = require "luci.http"
|
||||
local nixio = require "nixio"
|
||||
local ltn12 = require "luci.ltn12"
|
||||
local table = require "table"
|
||||
local util = require "luci.util"
|
||||
|
||||
module("luci.controller.linkease_backend", package.seeall)
|
||||
|
||||
local BLOCKSIZE = 2048
|
||||
local LINKEASE_UNIX = "/var/run/linkease.sock"
|
||||
|
||||
function index()
|
||||
entry({"linkease"}, call("linkease_backend")).leaf=true
|
||||
end
|
||||
|
||||
local function sink_socket(sock, io_err)
|
||||
if sock then
|
||||
return function(chunk, err)
|
||||
if not chunk then
|
||||
return 1
|
||||
else
|
||||
return sock:send(chunk)
|
||||
end
|
||||
end
|
||||
else
|
||||
return ltn12.sink.error(io_err or "unable to send socket")
|
||||
end
|
||||
end
|
||||
|
||||
local function session_retrieve(sid, allowed_users)
|
||||
local sdat = util.ubus("session", "get", { ubus_rpc_session = sid })
|
||||
if type(sdat) == "table" and
|
||||
type(sdat.values) == "table" and
|
||||
type(sdat.values.token) == "string" and
|
||||
(not allowed_users or
|
||||
util.contains(allowed_users, sdat.values.username))
|
||||
then
|
||||
return sid, sdat.values
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function get_session()
|
||||
local sid
|
||||
local key
|
||||
local sdat
|
||||
for _, key in ipairs({"sysauth_https", "sysauth_http", "sysauth"}) do
|
||||
sid = http.getcookie(key)
|
||||
if sid then
|
||||
sid, sdat = session_retrieve(sid, nil)
|
||||
if sid and sdat then
|
||||
return sid, sdat
|
||||
end
|
||||
end
|
||||
end
|
||||
return nil, nil
|
||||
end
|
||||
|
||||
local function chunksource(sock, buffer)
|
||||
buffer = buffer or ""
|
||||
return function()
|
||||
local output
|
||||
local _, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
|
||||
while not count and #buffer <= 1024 do
|
||||
local newblock, code = sock:recv(1024 - #buffer)
|
||||
if not newblock then
|
||||
return nil, code
|
||||
end
|
||||
buffer = buffer .. newblock
|
||||
_, endp, count = buffer:find("^([0-9a-fA-F]+);?.-\r\n")
|
||||
end
|
||||
count = tonumber(count, 16)
|
||||
if not count then
|
||||
return nil, -1, "invalid encoding"
|
||||
elseif count == 0 then
|
||||
return nil
|
||||
elseif count + 2 <= #buffer - endp then
|
||||
output = buffer:sub(endp+1, endp+count)
|
||||
buffer = buffer:sub(endp+count+3)
|
||||
return output
|
||||
else
|
||||
output = buffer:sub(endp+1, endp+count)
|
||||
buffer = ""
|
||||
if count - #output > 0 then
|
||||
local remain, code = sock:recvall(count-#output)
|
||||
if not remain then
|
||||
return nil, code
|
||||
end
|
||||
output = output .. remain
|
||||
count, code = sock:recvall(2)
|
||||
else
|
||||
count, code = sock:recvall(count+2-#buffer+endp)
|
||||
end
|
||||
if not count then
|
||||
return nil, code
|
||||
end
|
||||
return output
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function linkease_backend()
|
||||
local sock = nixio.socket("unix", "stream")
|
||||
if sock:connect(LINKEASE_UNIX) ~= true then
|
||||
http.status(500, "connect failed")
|
||||
return
|
||||
end
|
||||
local input = {}
|
||||
input[#input+1] = http.getenv("REQUEST_METHOD") .. " " .. http.getenv("REQUEST_URI") .. " HTTP/1.1"
|
||||
local req = http.context.request
|
||||
local start = "HTTP_"
|
||||
local start_len = string.len(start)
|
||||
local ctype = http.getenv("CONTENT_TYPE")
|
||||
if ctype then
|
||||
input[#input+1] = "Content-Type: " .. ctype
|
||||
end
|
||||
for k, v in pairs(req.message.env) do
|
||||
if string.sub(k, 1, start_len) == start and not string.find(k, "FORWARDED") then
|
||||
input[#input+1] = string.sub(k, start_len+1, string.len(k)) .. ": " .. v
|
||||
end
|
||||
end
|
||||
local sid, sdat = get_session()
|
||||
if sdat ~= nil then
|
||||
input[#input+1] = "X-Forwarded-Sid: " .. sid
|
||||
input[#input+1] = "X-Forwarded-Token: " .. sdat.token
|
||||
end
|
||||
-- input[#input+1] = "X-Forwarded-For: " .. http.getenv("REMOTE_HOST") ..":".. http.getenv("REMOTE_PORT")
|
||||
local num = tonumber(http.getenv("CONTENT_LENGTH")) or 0
|
||||
input[#input+1] = "Content-Length: " .. tostring(num)
|
||||
input[#input+1] = "\r\n"
|
||||
local source = ltn12.source.cat(ltn12.source.string(table.concat(input, "\r\n")), http.source())
|
||||
local ret = ltn12.pump.all(source, sink_socket(sock, "write sock error"))
|
||||
if ret ~= 1 then
|
||||
sock:close()
|
||||
http.status(500, "proxy error")
|
||||
return
|
||||
end
|
||||
|
||||
local linesrc = sock:linesource()
|
||||
local line, code, error = linesrc()
|
||||
if not line then
|
||||
sock:close()
|
||||
http.status(500, "response parse failed")
|
||||
return
|
||||
end
|
||||
|
||||
local protocol, status, msg = line:match("^([%w./]+) ([0-9]+) (.*)")
|
||||
if not protocol then
|
||||
sock:close()
|
||||
http.status(500, "response protocol error")
|
||||
return
|
||||
end
|
||||
num = tonumber(status) or 0
|
||||
http.status(num, msg)
|
||||
|
||||
local allow_ranges = http.getenv("SERVER_SOFTWARE") ~= "uhttpd"
|
||||
|
||||
local chunked = 0
|
||||
line = linesrc()
|
||||
while line and line ~= "" do
|
||||
local key, val = line:match("^([%w-]+)%s?:%s?(.*)")
|
||||
if key and key ~= "Status" then
|
||||
if key == "Transfer-Encoding" and val == "chunked" then
|
||||
chunked = 1
|
||||
end
|
||||
if key ~= "Connection" and key ~= "Transfer-Encoding" and ( allow_ranges or (key ~= "Content-Length" and key ~= "Accept-Ranges") ) then
|
||||
http.header(key, val)
|
||||
end
|
||||
end
|
||||
line = linesrc()
|
||||
end
|
||||
if not line then
|
||||
sock:close()
|
||||
http.status(500, "parse header failed")
|
||||
return
|
||||
end
|
||||
|
||||
local body_buffer = linesrc(true)
|
||||
if chunked == 1 then
|
||||
ltn12.pump.all(chunksource(sock, body_buffer), http.write)
|
||||
else
|
||||
local body_source = ltn12.source.cat(ltn12.source.string(body_buffer), sock:blocksource())
|
||||
ltn12.pump.all(body_source, http.write)
|
||||
end
|
||||
|
||||
sock:close()
|
||||
end
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
|
||||
<meta name="viewport" content="width=1380" />
|
||||
<title>易有云文件管理</title>
|
||||
<script>
|
||||
(function () {
|
||||
var pathe_prefix = "<%=prefix%>"
|
||||
window.path_base = pathe_prefix
|
||||
window.ver = "<%# PKG_VERSION %>"
|
||||
})();
|
||||
</script>
|
||||
<script type="module" crossorigin src="/luci-static/linkeasefile/index.js<%# ?v=PKG_VERSION %>"></script>
|
||||
<link rel="stylesheet" href="/luci-static/linkeasefile/style.css<%# ?v=PKG_VERSION %>">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
|
||||
</body>
|
||||
|
||||
</html>
|
||||