⛄ Sync 2026-04-23 20:32:35

This commit is contained in:
github-actions[bot] committed 2026-04-23 20:32:35 +08:00
1 parent 17738c6f79
commit fe51d2b304
206 files changed
+725671

No files matched your search

+160
View File
@@ -0,0 +1,160 @@
// Copyright 2022 Jo-Philipp Wich <jo@mein.io>
// Licensed to the public under the Apache License 2.0.
import { load_catalog, change_catalog, get_translations } from 'luci.core';
const ubus_types = [
null,
'array',
'object',
'string',
null, // INT64
'number',
null, // INT16,
'boolean',
'double'
];
function ubus_reply(id, data, code, errmsg) {
const reply = { jsonrpc: '2.0', id };
if (errmsg)
reply.error = { code, message: errmsg };
else if (type(code) == 'object')
reply.result = code;
else if (data != null)
reply.result = [ code, data ];
else
reply.result = [ code ];
return reply;
}
function ubus_access(sid, obj, fun) {
return (ubus.call('session', 'access', {
ubus_rpc_session: sid,
scope: 'ubus',
object: obj,
function: fun
})?.access == true);
}
function ubus_request(req) {
if (type(req?.method) != 'string' || req?.jsonrpc != '2.0' || req?.id == null)
return ubus_reply(null, null, -32600, 'Invalid request');
if (req.method == 'call') {
if (type(req?.params) != 'array' || length(req.params) < 3)
return ubus_reply(null, null, -32600, 'Invalid parameters');
let sid = req.params[0],
obj = req.params[1],
fun = req.params[2],
arg = req.params[3] ?? {};
if (type(arg) != 'object' || exists(arg, 'ubus_rpc_session'))
return ubus_reply(req.id, null, -32602, 'Invalid parameters');
if (sid == '00000000000000000000000000000000' && ctx.authsession)
sid = ctx.authsession;
if (!ubus_access(sid, obj, fun))
return ubus_reply(req.id, null, -32002, 'Access denied');
arg.ubus_rpc_session = sid;
// clear error
ubus.error();
const res = ubus.call(obj, fun, arg);
return ubus_reply(req.id, res, ubus.error(true) ?? 0);
}
if (req.method == 'list') {
if (req?.params == null || (type(req.params) == 'array' && length(req.params) == 0)) {
return ubus_reply(req.id, null, ubus.list());
}
else if (type(req.params) == 'array') {
const rv = {};
for (let param in req.params) {
if (type(param) != 'string')
return ubus_reply(req.id, null, -32602, 'Invalid parameters');
for (let m, p in ubus.list(param)?.[0]) {
for (let pn, pt in p) {
rv[param] ??= {};
rv[param][m] ??= {};
rv[param][m][pn] = ubus_types[pt] ?? 'unknown';
}
}
}
return ubus_reply(req.id, null, rv);
}
else {
return ubus_reply(req.id, null, -32602, 'Invalid parameters')
}
}
return ubus_reply(req.id, null, -32601, 'Method not found')
}
return {
action_ubus: function() {
let request;
try { request = json(http.content()); }
catch { request = null; }
http.prepare_content('application/json; charset=UTF-8');
if (type(request) == 'object')
http.write_json(ubus_request(request));
else if (type(request) == 'array')
http.write_json(map(request, ubus_request));
else
http.write_json(ubus_reply(null, null, -32700, 'Parse error'))
},
action_translations: function(reqlang) {
if (reqlang != null && reqlang != dispatcher.lang) {
load_catalog(reqlang, '/usr/lib/lua/luci/i18n');
change_catalog(reqlang);
}
http.prepare_content('application/javascript; charset=UTF-8');
http.write('window.TR={');
get_translations((key, val) => http.write(sprintf('"%08x":%J,', key, val)));
http.write('};');
},
action_logout: function() {
const url = dispatcher.build_url();
if (ctx.authsession) {
ubus.call('session', 'destroy', { ubus_rpc_session: ctx.authsession });
if (http.getenv('HTTPS') == 'on')
http.header('Set-Cookie', `sysauth_https=; expires=Thu, 01 Jan 1970 01:00:00 GMT; path=${url}`);
else
http.header('Set-Cookie', `sysauth_http=; expires=Thu, 01 Jan 1970 01:00:00 GMT; path=${url}`);
}
http.redirect(url);
},
action_menu: function() {
const session = dispatcher.is_authenticated({ methods: [ 'cookie:sysauth_https', 'cookie:sysauth_http' ] });
const menu = dispatcher.menu_json(session?.acls ?? {}) ?? {};
http.prepare_content('application/json; charset=UTF-8');
http.write_json(menu);
}
};
+150
View File
@@ -0,0 +1,150 @@
// Copyright 2022 Jo-Philipp Wich <jo@mein.io>
// Licensed to the public under the Apache License 2.0.
import { STATUS_NO_DATA, STATUS_PERMISSION_DENIED } from 'ubus';
let last_ubus_error;
const ubus_error_map = [
200, 'OK',
400, 'Invalid command',
400, 'Invalid argument',
404, 'Method not found',
404, 'Not found',
204, 'No data',
403, 'Permission denied',
504, 'Timeout',
500, 'Not supported',
500, 'Unknown error',
503, 'Connection failed',
500, 'Out of memory',
400, 'Parse error',
500, 'System error',
];
function ubus_call(object, method, args) {
ubus.error(); // clear previous error
let res = ubus.call(object, method, args);
last_ubus_error = ubus.error(true);
return res ?? !last_ubus_error;
}
function ubus_state_to_http(err) {
let code = ubus_error_map[(err << 1) + 0] ?? 200;
let msg = ubus_error_map[(err << 1) + 1] ?? 'OK';
http.status(code, msg);
if (code != 204) {
http.prepare_content('text/plain');
http.write(msg);
}
}
function uci_apply(rollback) {
if (rollback) {
const timeout = +(config?.apply?.rollback ?? 90) || 0;
const success = ubus_call('uci', 'apply', {
ubus_rpc_session: ctx.authsession,
timeout: max(timeout, 90),
rollback: true
});
if (success) {
const token = dispatcher.randomid(16);
ubus.call('session', 'set', {
ubus_rpc_session: '00000000000000000000000000000000',
values: {
rollback: {
token,
session: ctx.authsession,
timeout: time() + timeout
}
}
});
return token;
}
return null;
}
else {
let changes = ubus_call('uci', 'changes', { ubus_rpc_session: ctx.authsession })?.changes;
for (let config in changes)
if (!ubus_call('uci', 'commit', { ubus_rpc_session: ctx.authsession, config }))
return false;
return ubus_call('uci', 'apply', {
ubus_rpc_session: ctx.authsession,
rollback: false
});
}
}
function uci_confirm(token) {
const data = ubus.call('session', 'get', {
ubus_rpc_session: '00000000000000000000000000000000',
keys: [ 'rollback' ]
})?.values?.rollback;
if (type(data?.token) != 'string' || type(data?.session) != 'string' ||
type(data?.timeout) != 'int' || data.timeout < time()) {
last_ubus_error = STATUS_NO_DATA;
return false;
}
if (token != data.token) {
last_ubus_error = STATUS_PERMISSION_DENIED;
return false;
}
if (!ubus_call('uci', 'confirm', { ubus_rpc_session: data.session }))
return false;
ubus_call('session', 'set', {
ubus_rpc_session: '00000000000000000000000000000000',
values: { rollback: {} }
});
return true;
}
return {
action_apply_rollback: function() {
const token = uci_apply(true);
if (token) {
http.prepare_content('application/json; charset=UTF-8');
http.write_json({ token });
}
else {
ubus_state_to_http(last_ubus_error);
}
},
action_apply_unchecked: function() {
uci_apply(false);
ubus_state_to_http(last_ubus_error);
},
action_confirm: function() {
uci_confirm(http.formvalue('token'));
ubus_state_to_http(last_ubus_error);
},
action_revert: function() {
for (let config in ubus_call('uci', 'changes', { ubus_rpc_session: ctx.authsession })?.changes)
if (!ubus_call('uci', 'revert', { ubus_rpc_session: ctx.authsession, config }))
break;
ubus_state_to_http(last_ubus_error);
}
};