update 2025-05-07 00:26:39

This commit is contained in:
kenzok8 2025-05-07 00:26:39 +08:00
parent f3ffb8f129
commit dac06ab82c
12 changed files with 641 additions and 544 deletions

View File

@ -8,7 +8,7 @@ LUCI_TITLE:=LuCI for Tailscale
LUCI_DEPENDS:=+tailscale LUCI_DEPENDS:=+tailscale
LUCI_PKGARCH:=all LUCI_PKGARCH:=all
PKG_VERSION:=1.2.4 PKG_VERSION:=1.2.5
include $(TOPDIR)/feeds/luci/luci.mk include $(TOPDIR)/feeds/luci/luci.mk

View File

@ -50,3 +50,9 @@ Tailscale is a zero config VPN for building secure networks.
## Thanks ## Thanks
- [Carseason/openwrt-tailscale](https://github.com/Carseason/openwrt-tailscale) - [Carseason/openwrt-tailscale](https://github.com/Carseason/openwrt-tailscale)
- [immortalwrt/luci-app-zerotier](https://github.com/immortalwrt/luci/blob/master/applications/luci-app-zerotier) - [immortalwrt/luci-app-zerotier](https://github.com/immortalwrt/luci/blob/master/applications/luci-app-zerotier)
## Screenshot
<img width="573" alt="Basic" src="https://github.com/user-attachments/assets/bfca389a-bcec-42de-b5dd-b9588fd5db23" />
<img width="577" alt="Advanced" src="https://github.com/user-attachments/assets/d60ce19e-b3f3-43a7-98fc-7df6e2231898" />
<img width="575" alt="Extra" src="https://github.com/user-attachments/assets/6de5eaa7-6c18-48b8-a44a-0eaa311b0b79" />

View File

@ -12,54 +12,52 @@
'require view'; 'require view';
return view.extend({ return view.extend({
load: function() { async load() {
return fs.exec('/sbin/ip', ['-s', '-j', 'ad']).then(function(res) { const res = await fs.exec('/sbin/ip', ['-s', '-j', 'ad']);
if (res.code !== 0 || !res.stdout || res.stdout.trim() === '') { if (res.code !== 0 || !res.stdout || res.stdout.trim() === '') {
ui.addNotification(null, E('p', {}, _('Unable to get interface info: %s.').format(res.message))); ui.addNotification(null, E('p', {}, _('Unable to get interface info: %s.').format(res.message)));
return []; return [];
} }
try { try {
const interfaces = JSON.parse(res.stdout); const interfaces = JSON.parse(res.stdout);
const tailscaleInterfaces = interfaces.filter(iface => iface.ifname.match(/tailscale[0-9]+/)); const tailscaleInterfaces = interfaces.filter(iface => iface.ifname.match(/tailscale[0-9]+/));
return tailscaleInterfaces.map(iface => { return tailscaleInterfaces.map(iface => {
const parsedInfo = { const parsedInfo = {
name: iface.ifname name: iface.ifname
}; };
const addr_info = iface.addr_info || []; const addr_info = iface.addr_info || [];
addr_info.forEach(addr => { addr_info.forEach(addr => {
if (addr.family === 'inet') { if (addr.family === 'inet') {
parsedInfo.ipv4 = addr.local; parsedInfo.ipv4 = addr.local;
} else if (addr.family === 'inet6') { } else if (addr.family === 'inet6') {
parsedInfo.ipv6 = addr.local; parsedInfo.ipv6 = addr.local;
} }
});
parsedInfo.mtu = iface.mtu;
parsedInfo.rxBytes = '%1024mB'.format(iface.stats64.rx.bytes);
parsedInfo.txBytes = '%1024mB'.format(iface.stats64.tx.bytes);
return parsedInfo;
}); });
} catch (e) {
ui.addNotification(null, E('p', {}, _('Error parsing interface info: %s.').format(e.message))); parsedInfo.mtu = iface.mtu;
return []; parsedInfo.rxBytes = '%1024mB'.format(iface.stats64.rx.bytes);
} parsedInfo.txBytes = '%1024mB'.format(iface.stats64.tx.bytes);
return parsedInfo;
});
} catch (e) {
ui.addNotification(null, E('p', {}, _('Error parsing interface info: %s.').format(e.message)));
return [];
}
},
pollData(container) {
poll.add(async () => {
const data = await this.load();
dom.content(container, this.renderContent(data));
}); });
}, },
pollData: function (container) { renderContent(data) {
poll.add(L.bind(function () { if (!Array.isArray(data) || data.length === 0) {
return this.load().then(L.bind(function (data) {
dom.content(container, this.renderContent(data));
}, this));
}, this));
},
renderContent: function (data) {
if (!Array.isArray(data)) {
return E('div', {}, _('No interface online.')); return E('div', {}, _('No interface online.'));
} }
const rows = [ const rows = [
@ -97,7 +95,7 @@ return view.extend({
return E('table', { 'class': 'table' }, rows); return E('table', { 'class': 'table' }, rows);
}, },
render: function(data) { render(data) {
const content = E([], [ const content = E([], [
E('h2', { class: 'content' }, _('Tailscale')), E('h2', { class: 'content' }, _('Tailscale')),
E('div', { class: 'cbi-map-descr' }, _('Tailscale is a cross-platform and easy to use virtual LAN.')), E('div', { class: 'cbi-map-descr' }, _('Tailscale is a cross-platform and easy to use virtual LAN.')),

View File

@ -5,67 +5,66 @@
'require view'; 'require view';
return view.extend({ return view.extend({
retrieveLog: async function() { async retrieveLog() {
return Promise.all([ const stat = await Promise.all([
L.resolveDefault(fs.stat('/sbin/logread'), null), L.resolveDefault(fs.stat('/sbin/logread'), null),
L.resolveDefault(fs.stat('/usr/sbin/logread'), null) L.resolveDefault(fs.stat('/usr/sbin/logread'), null)
]).then(function(stat) { ]);
var logger = stat[0] ? stat[0].path : stat[1] ? stat[1].path : null; const logger = stat[0] ? stat[0].path : stat[1] ? stat[1].path : null;
return fs.exec_direct(logger, [ '-e', 'tailscale' ]).then(logdata => { const logdata = await fs.exec_direct(logger, ['-e', 'tailscale']);
var statusMappings = { const statusMappings = {
'daemon.err': { status: 'StdErr', startIndex: 9 }, 'daemon.err': { status: 'StdErr', startIndex: 9 },
'daemon.notice': { status: 'Info', startIndex: 10 } 'daemon.notice': { status: 'Info', startIndex: 10 }
}; };
const loglines = logdata.trim().split(/\n/).map(function(log) { const loglines = logdata.trim().split(/\n/).map(function(log) {
var logParts = log.split(' ').filter(Boolean); const logParts = log.split(' ').filter(Boolean);
if (logParts.length >= 6) { if (logParts.length >= 6) {
var formattedTime = logParts[1] + ' ' + logParts[2] + ' - ' + logParts[3]; const formattedTime = `${logParts[1]} ${logParts[2]} - ${logParts[3]}`;
var status = logParts[5]; const status = logParts[5];
var mapping = statusMappings[status] || { status: status, startIndex: 9 }; const mapping = statusMappings[status] || { status: status, startIndex: 9 };
status = mapping.status; const newStatus = mapping.status;
var startIndex = mapping.startIndex; const startIndex = mapping.startIndex;
var message = logParts.slice(startIndex).join(' '); const message = logParts.slice(startIndex).join(' ');
return formattedTime + ' [ ' + status + ' ] - ' + message; return `${formattedTime} [ ${newStatus} ] - ${message}`;
} else { } else {
return 'Log is empty.'; return 'Log is empty.';
} }
}).filter(Boolean); }).filter(Boolean);
return { value: loglines.join('\n'), rows: loglines.length + 1 }; return { value: loglines.join('\n'), rows: loglines.length + 1 };
}).catch(function(err) {
ui.addNotification(null, E('p', {}, _('Unable to load log data: ' + err.message)));
return '';
});
});
}, },
pollLog: async function() { async pollLog() {
const element = document.getElementById('syslog'); const element = document.getElementById('syslog');
if (element) { if (element) {
const log = await this.retrieveLog(); try {
element.value = log.value; const log = await this.retrieveLog();
element.rows = log.rows; element.value = log.value;
element.rows = log.rows;
} catch (err) {
ui.addNotification(null, E('p', {}, _('Unable to load log data: ' + err.message)));
}
} }
}, },
load: async function() { load() {
poll.add(this.pollLog.bind(this)); poll.add(this.pollLog.bind(this));
return await this.retrieveLog(); return this.retrieveLog();
}, },
render: function(loglines) { render(loglines) {
var scrollDownButton = E('button', { const scrollDownButton = E('button', {
'id': 'scrollDownButton', id: 'scrollDownButton',
'class': 'cbi-button cbi-button-neutral' class: 'cbi-button cbi-button-neutral'
}, _('Scroll to tail', 'scroll to bottom (the tail) of the log file') }, _('Scroll to tail', 'scroll to bottom (the tail) of the log file')
); );
scrollDownButton.addEventListener('click', function() { scrollDownButton.addEventListener('click', function() {
scrollUpButton.scrollIntoView(); scrollUpButton.scrollIntoView();
}); });
var scrollUpButton = E('button', { const scrollUpButton = E('button', {
'id' : 'scrollUpButton', id : 'scrollUpButton',
'class': 'cbi-button cbi-button-neutral' class: 'cbi-button cbi-button-neutral'
}, _('Scroll to head', 'scroll to top (the head) of the log file') }, _('Scroll to head', 'scroll to top (the head) of the log file')
); );
scrollUpButton.addEventListener('click', function() { scrollUpButton.addEventListener('click', function() {
@ -73,16 +72,16 @@ return view.extend({
}); });
return E([], [ return E([], [
E('div', { 'id': 'content_syslog' }, [ E('div', { id: 'content_syslog' }, [
E('div', {'style': 'padding-bottom: 20px'}, [scrollDownButton]), E('div', { style: 'padding-bottom: 20px' }, [scrollDownButton]),
E('textarea', { E('textarea', {
'id': 'syslog', id: 'syslog',
'style': 'font-size:12px', style: 'font-size:12px',
'readonly': 'readonly', readonly: 'readonly',
'wrap': 'off', wrap: 'off',
'rows': loglines.rows, rows: loglines.rows,
}, [ loglines.value ]), }, [ loglines.value ]),
E('div', {'style': 'padding-bottom: 20px'}, [scrollUpButton]) E('div', { style: 'padding-bottom: 20px' }, [scrollUpButton])
]) ])
]); ]);
}, },

View File

@ -12,33 +12,32 @@
'require uci'; 'require uci';
'require view'; 'require view';
var callServiceList = rpc.declare({ const callServiceList = rpc.declare({
object: 'service', object: 'service',
method: 'list', method: 'list',
params: ['name'], params: ['name'],
expect: { '': {} } expect: { '': {} }
}); });
function getInterfaceSubnets(interfaces = ['lan', 'wan']) { async function getInterfaceSubnets(interfaces = ['lan', 'wan']) {
return network.getNetworks().then(networks => { const networks = await network.getNetworks();
return [...new Set( return [...new Set(
networks networks
.filter(ifc => interfaces.includes(ifc.getName())) .filter(ifc => interfaces.includes(ifc.getName()))
.flatMap(ifc => ifc.getIPAddrs()) .flatMap(ifc => ifc.getIPAddrs())
.filter(addr => addr.includes('/')) .filter(addr => addr.includes('/'))
.map(addr => { .map(addr => {
const [ip, cidr] = addr.split('/'); const [ip, cidr] = addr.split('/');
const ipParts = ip.split('.').map(Number); const ipParts = ip.split('.').map(Number);
const mask = ~((1 << (32 - parseInt(cidr))) - 1); const mask = ~((1 << (32 - parseInt(cidr))) - 1);
const subnetParts = ipParts.map((part, i) => (part & (mask >> (24 - i * 8))) & 255); const subnetParts = ipParts.map((part, i) => (part & (mask >> (24 - i * 8))) & 255);
return `${subnetParts.join('.')}/${cidr}`; return `${subnetParts.join('.')}/${cidr}`;
}) })
)]; )];
});
} }
function getStatus() { async function getStatus() {
var status = { const status = {
isRunning: false, isRunning: false,
backendState: undefined, backendState: undefined,
authURL: undefined, authURL: undefined,
@ -46,32 +45,32 @@ function getStatus() {
onlineExitNodes: [], onlineExitNodes: [],
subnetRoutes: [] subnetRoutes: []
}; };
return Promise.resolve(callServiceList('tailscale')).then(res => { const res = await callServiceList('tailscale');
try { try {
status.isRunning = res['tailscale']['instances']['instance1']['running']; status.isRunning = res['tailscale']['instances']['instance1']['running'];
} catch (e) { } catch (e) {
return status; return status;
} }
return fs.exec("/usr/sbin/tailscale", ["status", "--json"]).then(res => { const tailscaleRes = await fs.exec("/usr/sbin/tailscale", ["status", "--json"]);
const tailscaleStatus = JSON.parse(res.stdout.replace(/("\w+"):\s*(\d+)/g, '$1:"$2"')); const tailscaleStatus = JSON.parse(tailscaleRes.stdout.replace(/("\w+"):\s*(\d+)/g, '$1:"$2"'));
if (!tailscaleStatus.AuthURL && tailscaleStatus.BackendState === "NeedsLogin") { if (!tailscaleStatus.AuthURL && tailscaleStatus.BackendState === "NeedsLogin") {
fs.exec("/usr/sbin/tailscale", ["login"]); fs.exec("/usr/sbin/tailscale", ["login"]);
} }
status.backendState = tailscaleStatus.BackendState; status.backendState = tailscaleStatus.BackendState;
status.authURL = tailscaleStatus.AuthURL; status.authURL = tailscaleStatus.AuthURL;
status.displayName = (status.backendState === "Running") ? tailscaleStatus.User[tailscaleStatus.Self.UserID].DisplayName : undefined; status.displayName = (status.backendState === "Running") ? tailscaleStatus.User[tailscaleStatus.Self.UserID].DisplayName : undefined;
status.onlineExitNodes = Object.values(tailscaleStatus.Peer) if (tailscaleStatus.Peer) {
.flatMap(peer => (peer.ExitNodeOption && peer.Online) ? [peer.HostName] : []); status.onlineExitNodes = Object.values(tailscaleStatus.Peer)
status.subnetRoutes = Object.values(tailscaleStatus.Peer) .flatMap(peer => (peer.ExitNodeOption && peer.Online) ? [peer.HostName] : []);
.flatMap(peer => peer.PrimaryRoutes || []); status.subnetRoutes = Object.values(tailscaleStatus.Peer)
return status; .flatMap(peer => peer.PrimaryRoutes || []);
}); }
}).catch(() => status); return status;
} }
function renderStatus(isRunning) { function renderStatus(isRunning) {
var spanTemp = '<em><span style="color:%s"><strong>%s %s</strong></span></em>'; const spanTemp = '<em><span style="color:%s"><strong>%s %s</strong></span></em>';
var renderHTML; let renderHTML;
if (isRunning) { if (isRunning) {
renderHTML = String.format(spanTemp, 'green', _('Tailscale'), _('RUNNING')); renderHTML = String.format(spanTemp, 'green', _('Tailscale'), _('RUNNING'));
} else { } else {
@ -82,11 +81,11 @@ function renderStatus(isRunning) {
} }
function renderLogin(loginStatus, authURL, displayName) { function renderLogin(loginStatus, authURL, displayName) {
var spanTemp = '<span style="color:%s">%s</span>'; const spanTemp = '<span style="color:%s">%s</span>';
var renderHTML; let renderHTML;
if (loginStatus == "NeedsLogin") { if (loginStatus === "NeedsLogin") {
renderHTML = String.format('<a href="%s" target="_blank">%s</a>', authURL, _('Need to log in')); renderHTML = String.format('<a href="%s" target="_blank">%s</a>', authURL, _('Need to log in'));
} else if (loginStatus == "Running") { } else if (loginStatus === "Running") {
renderHTML = String.format('<a href="%s" target="_blank">%s</a>', 'https://login.tailscale.com/admin/machines', displayName); renderHTML = String.format('<a href="%s" target="_blank">%s</a>', 'https://login.tailscale.com/admin/machines', displayName);
renderHTML += String.format('<br><a style="color:green" id="logout_button">%s</a>', _('Log out and Unbind')); renderHTML += String.format('<br><a style="color:green" id="logout_button">%s</a>', _('Log out and Unbind'));
} else { } else {
@ -97,7 +96,7 @@ function renderLogin(loginStatus, authURL, displayName) {
} }
return view.extend({ return view.extend({
load: function() { load() {
return Promise.all([ return Promise.all([
uci.load('tailscale'), uci.load('tailscale'),
getStatus(), getStatus(),
@ -105,33 +104,32 @@ return view.extend({
]); ]);
}, },
render: function(data) { render(data) {
var m, s, o; let m, s, o;
var statusData = data[1]; const statusData = data[1];
var interfaceSubnets = data[2]; const interfaceSubnets = data[2];
var onlineExitNodes = statusData.onlineExitNodes; const onlineExitNodes = statusData.onlineExitNodes;
var subnetRoutes = statusData.subnetRoutes; const subnetRoutes = statusData.subnetRoutes;
m = new form.Map('tailscale', _('Tailscale'), _('Tailscale is a cross-platform and easy to use virtual LAN.')); m = new form.Map('tailscale', _('Tailscale'), _('Tailscale is a cross-platform and easy to use virtual LAN.'));
s = m.section(form.TypedSection); s = m.section(form.TypedSection);
s.anonymous = true; s.anonymous = true;
s.render = function () { s.render = function () {
poll.add(function() { poll.add(async function() {
return Promise.resolve(getStatus()).then(function(res) { const res = await getStatus();
var service_view = document.getElementById("service_status"); const service_view = document.getElementById("service_status");
var login_view = document.getElementById("login_status_div"); const login_view = document.getElementById("login_status_div");
service_view.innerHTML = renderStatus(res.isRunning); service_view.innerHTML = renderStatus(res.isRunning);
login_view.innerHTML = renderLogin(res.backendState, res.authURL, res.displayName); login_view.innerHTML = renderLogin(res.backendState, res.authURL, res.displayName);
var logoutButton = document.getElementById('logout_button'); const logoutButton = document.getElementById('logout_button');
if (logoutButton) { if (logoutButton) {
logoutButton.onclick = function() { logoutButton.onclick = function() {
if (confirm(_('Are you sure you want to log out and unbind the current device?'))) { if (confirm(_('Are you sure you want to log out and unbind the current device?'))) {
fs.exec("/usr/sbin/tailscale", ["logout"]); fs.exec("/usr/sbin/tailscale", ["logout"]);
}
} }
} }
}); }
}); });
return E('div', { class: 'cbi-section', id: 'status_bar' }, [ return E('div', { class: 'cbi-section', id: 'status_bar' }, [
@ -177,7 +175,7 @@ return view.extend({
s.tab('advance', _('Advanced Settings')); s.tab('advance', _('Advanced Settings'));
o = s.taboption('advance', form.Flag, 'acceptRoutes', _('Accept Routes'), _('Accept subnet routes that other nodes advertise.')); o = s.taboption('advance', form.Flag, 'accept_routes', _('Accept Routes'), _('Accept subnet routes that other nodes advertise.'));
o.default = o.disabled; o.default = o.disabled;
o.rmempty = false; o.rmempty = false;
@ -185,15 +183,15 @@ return view.extend({
o.default = ''; o.default = '';
o.rmempty = true; o.rmempty = true;
o = s.taboption('advance', form.Flag, 'acceptDNS', _('Accept DNS'), _('Accept DNS configuration from the Tailscale admin console.')); o = s.taboption('advance', form.Flag, 'accept_dns', _('Accept DNS'), _('Accept DNS configuration from the Tailscale admin console.'));
o.default = o.enabled; o.default = o.enabled;
o.rmempty = false; o.rmempty = false;
o = s.taboption('advance', form.Flag, 'advertiseExitNode', _('Exit Node'), _('Offer to be an exit node for outbound internet traffic from the Tailscale network.')); o = s.taboption('advance', form.Flag, 'advertise_exit_node', _('Exit Node'), _('Offer to be an exit node for outbound internet traffic from the Tailscale network.'));
o.default = o.disabled; o.default = o.disabled;
o.rmempty = false; o.rmempty = false;
o = s.taboption('advance', form.ListValue, 'exitNode', _('Online Exit Nodes'), _('Select an online machine name to use as an exit node.')); o = s.taboption('advance', form.ListValue, 'exit_node', _('Online Exit Nodes'), _('Select an online machine name to use as an exit node.'));
if (onlineExitNodes.length > 0) { if (onlineExitNodes.length > 0) {
o.optional = true; o.optional = true;
onlineExitNodes.forEach(function(node) { onlineExitNodes.forEach(function(node) {
@ -204,10 +202,10 @@ return view.extend({
o.readonly = true; o.readonly = true;
} }
o.default = ''; o.default = '';
o.depends('advertiseExitNode', '0'); o.depends('advertise_exit_node', '0');
o.rmempty = true; o.rmempty = true;
o = s.taboption('advance', form.DynamicList, 'advertiseRoutes', _('Expose Subnets'), _('Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>.')); o = s.taboption('advance', form.DynamicList, 'advertise_routes', _('Expose Subnets'), _('Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>.'));
if (interfaceSubnets.length > 0) { if (interfaceSubnets.length > 0) {
interfaceSubnets.forEach(function(subnet) { interfaceSubnets.forEach(function(subnet) {
o.value(subnet, subnet); o.value(subnet, subnet);
@ -216,12 +214,12 @@ return view.extend({
o.default = ''; o.default = '';
o.rmempty = true; o.rmempty = true;
o = s.taboption('advance', form.Flag, 's2s', _('Site To Site'), _('Use site-to-site layer 3 networking to connect subnets on the Tailscale network.')); o = s.taboption('advance', form.Flag, 'disable_snat_subnet_routes', _('Site To Site'), _('Use site-to-site layer 3 networking to connect subnets on the Tailscale network.'));
o.default = o.disabled; o.default = o.disabled;
o.depends('acceptRoutes', '1'); o.depends('accept_routes', '1');
o.rmempty = false; o.rmempty = false;
o = s.taboption('advance', form.DynamicList, 'subnetRoutes', _('Subnet Routes'), _('Select subnet routes advertised by other nodes in Tailscale network.')); o = s.taboption('advance', form.DynamicList, 'subnet_routes', _('Subnet Routes'), _('Select subnet routes advertised by other nodes in Tailscale network.'));
if (subnetRoutes.length > 0) { if (subnetRoutes.length > 0) {
subnetRoutes.forEach(function(route) { subnetRoutes.forEach(function(route) {
o.value(route, route); o.value(route, route);
@ -231,20 +229,25 @@ return view.extend({
o.readonly = true; o.readonly = true;
} }
o.default = ''; o.default = '';
o.depends('s2s', '1'); o.depends('disable_snat_subnet_routes', '1');
o.rmempty = true; o.rmempty = true;
o = s.taboption('advance', form.MultiValue, 'access', _('Access Control')); o = s.taboption('advance', form.MultiValue, 'access', _('Access Control'));
o.value('tsfwlan', _('Tailscale access LAN')); o.value('ts_ac_lan', _('Tailscale access LAN'));
o.value('tsfwwan', _('Tailscale access WAN')); o.value('ts_ac_wan', _('Tailscale access WAN'));
o.value('lanfwts', _('LAN access Tailscale')); o.value('lan_ac_ts', _('LAN access Tailscale'));
o.value('wanfwts', _('WAN access Tailscale')); o.value('wan_ac_ts', _('WAN access Tailscale'));
o.default = "tsfwlan tsfwwan lanfwts"; o.default = "ts_ac_lan ts_ac_wan lan_ac_ts";
o.rmempty = true; o.rmempty = true;
s.tab('extra', _('Extra Settings')); s.tab('extra', _('Extra Settings'));
o = s.taboption('extra', form.DynamicList, 'flags', _('Additional Flags'), String.format(_('List of extra flags. Format: --flags=value, e.g. <code>--exit-node=10.0.0.1</code>. <br> %s for enabling settings upon the initiation of Tailscale.'), '<a href="https://tailscale.com/kb/1241/tailscale-up" target="_blank">' + _('Available flags') + '</a>')); o = s.taboption('extra', form.DynamicList, 'flags', _('Additional Flags'),
String.format(
_('List of extra flags. Format: --flags=value, e.g. <code>--exit-node=10.0.0.1</code>. <br> %s for enabling settings upon the initiation of Tailscale.'),
'<a href="https://tailscale.com/kb/1241/tailscale-up" target="_blank">' + _('Available flags') + '</a>'
)
);
o.default = ''; o.default = '';
o.rmempty = true; o.rmempty = true;
@ -252,7 +255,7 @@ return view.extend({
s.title = _('Custom Server Settings'); s.title = _('Custom Server Settings');
s.description = String.format(_('Use %s to deploy a private server.'), '<a href="https://github.com/juanfont/headscale" target="_blank">headscale</a>'); s.description = String.format(_('Use %s to deploy a private server.'), '<a href="https://github.com/juanfont/headscale" target="_blank">headscale</a>');
o = s.option(form.Value, 'loginServer', _('Server Address')); o = s.option(form.Value, 'login_server', _('Server Address'));
o.default = ''; o.default = '';
o.rmempty = true; o.rmempty = true;

View File

@ -1,64 +1,64 @@
msgid "" msgid ""
msgstr "Content-Type: text/plain; charset=UTF-8" msgstr "Content-Type: text/plain; charset=UTF-8"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186
msgid "Accept DNS" msgid "Accept DNS"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186
msgid "Accept DNS configuration from the Tailscale admin console." msgid "Accept DNS configuration from the Tailscale admin console."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178
msgid "Accept Routes" msgid "Accept Routes"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178
msgid "Accept subnet routes that other nodes advertise." msgid "Accept subnet routes that other nodes advertise."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:235
msgid "Access Control" msgid "Access Control"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245
msgid "Additional Flags" msgid "Additional Flags"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:176
msgid "Advanced Settings" msgid "Advanced Settings"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:129 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:128
msgid "Are you sure you want to log out and unbind the current device?" msgid "Are you sure you want to log out and unbind the current device?"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:259 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:262
msgid "Auth Key" msgid "Auth Key"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:248
msgid "Available flags" msgid "Available flags"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:141
msgid "Basic Settings" msgid "Basic Settings"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:138 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:136
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:152 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:150
msgid "Collecting data ..." msgid "Collecting data ..."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:252 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255
msgid "Custom Server Settings" msgid "Custom Server Settings"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182
msgid "Device Name" msgid "Device Name"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:145 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143
msgid "Enable" msgid "Enable"
msgstr "" msgstr ""
@ -66,24 +66,24 @@ msgstr ""
msgid "Error parsing interface info: %s." msgid "Error parsing interface info: %s."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190
msgid "Exit Node" msgid "Exit Node"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208
msgid "Expose Subnets" msgid "Expose Subnets"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208
msgid "" msgid ""
"Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>." "Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:243
msgid "Extra Settings" msgid "Extra Settings"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:164 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:162
msgid "Firewall Mode" msgid "Firewall Mode"
msgstr "" msgstr ""
@ -95,11 +95,11 @@ msgstr ""
msgid "Grant access to Tailscale configuration" msgid "Grant access to Tailscale configuration"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:75 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:73
msgid "IPv4 Address" msgid "IPv4 Address"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:79 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:77
msgid "IPv6 Address" msgid "IPv6 Address"
msgstr "" msgstr ""
@ -107,15 +107,15 @@ msgstr ""
msgid "Interface Info" msgid "Interface Info"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:71 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:69
msgid "Interface Name" msgid "Interface Name"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:240 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238
msgid "LAN access Tailscale" msgid "LAN access Tailscale"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182
msgid "Leave blank to use the device's hostname." msgid "Leave blank to use the device's hostname."
msgstr "" msgstr ""
@ -125,19 +125,19 @@ msgid ""
"code>. <br> %s for enabling settings upon the initiation of Tailscale." "code>. <br> %s for enabling settings upon the initiation of Tailscale."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:91 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:90
msgid "Log out and Unbind" msgid "Log out and Unbind"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168
msgid "Logging program activities." msgid "Logging program activities."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172
msgid "Logging program errors and exceptions." msgid "Logging program errors and exceptions."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:149 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:147
msgid "Login Status" msgid "Login Status"
msgstr "" msgstr ""
@ -145,126 +145,126 @@ msgstr ""
msgid "Logs" msgid "Logs"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:83 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:81
msgid "MTU" msgid "MTU"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:93 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:92
msgid "NOT RUNNING" msgid "NOT RUNNING"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:88 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:87
msgid "Need to log in" msgid "Need to log in"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:66 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:64
msgid "Network Interface Information" msgid "Network Interface Information"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:203 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:201
msgid "No Available Exit Nodes" msgid "No Available Exit Nodes"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:230 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:228
msgid "No Available Subnet Routes" msgid "No Available Subnet Routes"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:63 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:61
msgid "No interface online." msgid "No interface online."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190
msgid "" msgid ""
"Offer to be an exit node for outbound internet traffic from the Tailscale " "Offer to be an exit node for outbound internet traffic from the Tailscale "
"network." "network."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194
msgid "Online Exit Nodes" msgid "Online Exit Nodes"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153
msgid "Port" msgid "Port"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75
msgid "RUNNING" msgid "RUNNING"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:69 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:68
msgctxt "scroll to top (the head) of the log file" msgctxt "scroll to top (the head) of the log file"
msgid "Scroll to head" msgid "Scroll to head"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:60 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:59
msgctxt "scroll to bottom (the tail) of the log file" msgctxt "scroll to bottom (the tail) of the log file"
msgid "Scroll to tail" msgid "Scroll to tail"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194
msgid "Select an online machine name to use as an exit node." msgid "Select an online machine name to use as an exit node."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222
msgid "Select subnet routes advertised by other nodes in Tailscale network." msgid "Select subnet routes advertised by other nodes in Tailscale network."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:258
msgid "Server Address" msgid "Server Address"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153
msgid "Set the Tailscale port number." msgid "Set the Tailscale port number."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217
msgid "Site To Site" msgid "Site To Site"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172
msgid "StdErr Log" msgid "StdErr Log"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168
msgid "StdOut Log" msgid "StdOut Log"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222
msgid "Subnet Routes" msgid "Subnet Routes"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:102 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:100
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114
#: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3 #: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3
msgid "Tailscale" msgid "Tailscale"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:236
msgid "Tailscale access LAN" msgid "Tailscale access LAN"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237
msgid "Tailscale access WAN" msgid "Tailscale access WAN"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:103 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:101
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114
msgid "Tailscale is a cross-platform and easy to use virtual LAN." msgid "Tailscale is a cross-platform and easy to use virtual LAN."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158
msgid "" msgid ""
"The working directory contains config files, audit logs, and runtime info." "The working directory contains config files, audit logs, and runtime info."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:87 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:85
msgid "Total Download" msgid "Total Download"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:91 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:89
msgid "Total Upload" msgid "Total Upload"
msgstr "" msgstr ""
@ -272,24 +272,24 @@ msgstr ""
msgid "Unable to get interface info: %s." msgid "Unable to get interface info: %s."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:36 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:45
msgid "Unable to load log data:" msgid "Unable to load log data:"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:253 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:256
msgid "Use %s to deploy a private server." msgid "Use %s to deploy a private server."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217
msgid "" msgid ""
"Use site-to-site layer 3 networking to connect subnets on the Tailscale " "Use site-to-site layer 3 networking to connect subnets on the Tailscale "
"network." "network."
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:241 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239
msgid "WAN access Tailscale" msgid "WAN access Tailscale"
msgstr "" msgstr ""
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158
msgid "Workdir" msgid "Workdir"
msgstr "" msgstr ""

View File

@ -7,64 +7,64 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186
msgid "Accept DNS" msgid "Accept DNS"
msgstr "允许DNS" msgstr "允许DNS"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186
msgid "Accept DNS configuration from the Tailscale admin console." msgid "Accept DNS configuration from the Tailscale admin console."
msgstr "使用 Tailscale 管理控制台的 DNS 配置。" msgstr "使用 Tailscale 管理控制台的 DNS 配置。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178
msgid "Accept Routes" msgid "Accept Routes"
msgstr "启用路由" msgstr "启用路由"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178
msgid "Accept subnet routes that other nodes advertise." msgid "Accept subnet routes that other nodes advertise."
msgstr "接受其他节点广播的子网路由。" msgstr "接受其他节点广播的子网路由。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:235
msgid "Access Control" msgid "Access Control"
msgstr "访问控制" msgstr "访问控制"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245
msgid "Additional Flags" msgid "Additional Flags"
msgstr "参数列表" msgstr "参数列表"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:176
msgid "Advanced Settings" msgid "Advanced Settings"
msgstr "高级设置" msgstr "高级设置"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:129 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:128
msgid "Are you sure you want to log out and unbind the current device?" msgid "Are you sure you want to log out and unbind the current device?"
msgstr "是否注销当前登录并且解绑当前设备?" msgstr "是否注销当前登录并且解绑当前设备?"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:259 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:262
msgid "Auth Key" msgid "Auth Key"
msgstr "认证密钥" msgstr "认证密钥"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:248
msgid "Available flags" msgid "Available flags"
msgstr "可用参数" msgstr "可用参数"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:141
msgid "Basic Settings" msgid "Basic Settings"
msgstr "基础设置" msgstr "基础设置"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:138 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:136
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:152 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:150
msgid "Collecting data ..." msgid "Collecting data ..."
msgstr "正在收集数据..." msgstr "正在收集数据..."
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:252 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255
msgid "Custom Server Settings" msgid "Custom Server Settings"
msgstr "自定义服务器" msgstr "自定义服务器"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182
msgid "Device Name" msgid "Device Name"
msgstr "设备名称" msgstr "设备名称"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:145 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143
msgid "Enable" msgid "Enable"
msgstr "启用" msgstr "启用"
@ -72,24 +72,24 @@ msgstr "启用"
msgid "Error parsing interface info: %s." msgid "Error parsing interface info: %s."
msgstr "接口信息解析错误:%s。" msgstr "接口信息解析错误:%s。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190
msgid "Exit Node" msgid "Exit Node"
msgstr "出口节点" msgstr "出口节点"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208
msgid "Expose Subnets" msgid "Expose Subnets"
msgstr "公开网段" msgstr "公开网段"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208
msgid "" msgid ""
"Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>." "Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>."
msgstr "广播子网路由至 Tailscale例如<code>10.0.0.0/24</code>。" msgstr "广播子网路由至 Tailscale例如<code>10.0.0.0/24</code>。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:243
msgid "Extra Settings" msgid "Extra Settings"
msgstr "附加设置" msgstr "附加设置"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:164 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:162
msgid "Firewall Mode" msgid "Firewall Mode"
msgstr "防火墙模式" msgstr "防火墙模式"
@ -101,11 +101,11 @@ msgstr "全局设置"
msgid "Grant access to Tailscale configuration" msgid "Grant access to Tailscale configuration"
msgstr "授予访问 Tailscale 配置的权限" msgstr "授予访问 Tailscale 配置的权限"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:75 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:73
msgid "IPv4 Address" msgid "IPv4 Address"
msgstr "IPv4地址" msgstr "IPv4地址"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:79 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:77
msgid "IPv6 Address" msgid "IPv6 Address"
msgstr "IPv6地址" msgstr "IPv6地址"
@ -113,15 +113,15 @@ msgstr "IPv6地址"
msgid "Interface Info" msgid "Interface Info"
msgstr "接口信息" msgstr "接口信息"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:71 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:69
msgid "Interface Name" msgid "Interface Name"
msgstr "接口名称" msgstr "接口名称"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:240 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238
msgid "LAN access Tailscale" msgid "LAN access Tailscale"
msgstr "本地局域网访问虚拟局域网" msgstr "本地局域网访问虚拟局域网"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182
msgid "Leave blank to use the device's hostname." msgid "Leave blank to use the device's hostname."
msgstr "留空以使用设备的主机名。" msgstr "留空以使用设备的主机名。"
@ -131,19 +131,19 @@ msgid ""
"code>. <br> %s for enabling settings upon the initiation of Tailscale." "code>. <br> %s for enabling settings upon the initiation of Tailscale."
msgstr "额外参数的列表。格式:--flags=value例如 <code>--exit-node=10.0.0.1</code>。<br> 在 Tailscale 启动时的%s。" msgstr "额外参数的列表。格式:--flags=value例如 <code>--exit-node=10.0.0.1</code>。<br> 在 Tailscale 启动时的%s。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:91 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:90
msgid "Log out and Unbind" msgid "Log out and Unbind"
msgstr "注销登录并解除绑定" msgstr "注销登录并解除绑定"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168
msgid "Logging program activities." msgid "Logging program activities."
msgstr "记录程序运行信息。" msgstr "记录程序运行信息。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172
msgid "Logging program errors and exceptions." msgid "Logging program errors and exceptions."
msgstr "记录程序错误和警告信息。" msgstr "记录程序错误和警告信息。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:149 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:147
msgid "Login Status" msgid "Login Status"
msgstr "已绑定用户" msgstr "已绑定用户"
@ -151,126 +151,126 @@ msgstr "已绑定用户"
msgid "Logs" msgid "Logs"
msgstr "日志" msgstr "日志"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:83 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:81
msgid "MTU" msgid "MTU"
msgstr "MTU" msgstr "MTU"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:93 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:92
msgid "NOT RUNNING" msgid "NOT RUNNING"
msgstr "未运行" msgstr "未运行"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:88 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:87
msgid "Need to log in" msgid "Need to log in"
msgstr "未登录" msgstr "未登录"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:66 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:64
msgid "Network Interface Information" msgid "Network Interface Information"
msgstr "网络接口信息" msgstr "网络接口信息"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:203 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:201
msgid "No Available Exit Nodes" msgid "No Available Exit Nodes"
msgstr "没有可用的出口节点" msgstr "没有可用的出口节点"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:230 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:228
msgid "No Available Subnet Routes" msgid "No Available Subnet Routes"
msgstr "没有可用的子网路由" msgstr "没有可用的子网路由"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:63 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:61
msgid "No interface online." msgid "No interface online."
msgstr "无在线接口。" msgstr "无在线接口。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190
msgid "" msgid ""
"Offer to be an exit node for outbound internet traffic from the Tailscale " "Offer to be an exit node for outbound internet traffic from the Tailscale "
"network." "network."
msgstr "作为 Tailscale 广域网出口节点。" msgstr "作为 Tailscale 广域网出口节点。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194
msgid "Online Exit Nodes" msgid "Online Exit Nodes"
msgstr "可用出口节点" msgstr "可用出口节点"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75
msgid "RUNNING" msgid "RUNNING"
msgstr "运行中" msgstr "运行中"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:69 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:68
msgctxt "scroll to top (the head) of the log file" msgctxt "scroll to top (the head) of the log file"
msgid "Scroll to head" msgid "Scroll to head"
msgstr "滚动到顶部" msgstr "滚动到顶部"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:60 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:59
msgctxt "scroll to bottom (the tail) of the log file" msgctxt "scroll to bottom (the tail) of the log file"
msgid "Scroll to tail" msgid "Scroll to tail"
msgstr "滚动到底部" msgstr "滚动到底部"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194
msgid "Select an online machine name to use as an exit node." msgid "Select an online machine name to use as an exit node."
msgstr "选择一个可用的节点名称作为出口节点使用。" msgstr "选择一个可用的节点名称作为出口节点使用。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222
msgid "Select subnet routes advertised by other nodes in Tailscale network." msgid "Select subnet routes advertised by other nodes in Tailscale network."
msgstr "选择非本设备广播的子网路由。" msgstr "选择非本设备广播的子网路由。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:258
msgid "Server Address" msgid "Server Address"
msgstr "服务器地址" msgstr "服务器地址"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153
msgid "Set the Tailscale port number." msgid "Set the Tailscale port number."
msgstr "设置 Tailscale 端口号。" msgstr "设置 Tailscale 端口号。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217
msgid "Site To Site" msgid "Site To Site"
msgstr "子网互通" msgstr "子网互通"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172
msgid "StdErr Log" msgid "StdErr Log"
msgstr "错误日志" msgstr "错误日志"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168
msgid "StdOut Log" msgid "StdOut Log"
msgstr "运行日志" msgstr "运行日志"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222
msgid "Subnet Routes" msgid "Subnet Routes"
msgstr "子网路由" msgstr "子网路由"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:102 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:100
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114
#: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3 #: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3
msgid "Tailscale" msgid "Tailscale"
msgstr "Tailscale" msgstr "Tailscale"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:236
msgid "Tailscale access LAN" msgid "Tailscale access LAN"
msgstr "虚拟局域网访问本地局域网" msgstr "虚拟局域网访问本地局域网"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237
msgid "Tailscale access WAN" msgid "Tailscale access WAN"
msgstr "虚拟局域网访问本地广域网" msgstr "虚拟局域网访问本地广域网"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:103 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:101
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114
msgid "Tailscale is a cross-platform and easy to use virtual LAN." msgid "Tailscale is a cross-platform and easy to use virtual LAN."
msgstr "Tailscale 是一个跨平台且易于使用的虚拟局域网 VPN。" msgstr "Tailscale 是一个跨平台且易于使用的虚拟局域网 VPN。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158
msgid "" msgid ""
"The working directory contains config files, audit logs, and runtime info." "The working directory contains config files, audit logs, and runtime info."
msgstr "工作目录包含配置文件、审计日志和运行时信息。" msgstr "工作目录包含配置文件、审计日志和运行时信息。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:87 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:85
msgid "Total Download" msgid "Total Download"
msgstr "总下载量" msgstr "总下载量"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:91 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:89
msgid "Total Upload" msgid "Total Upload"
msgstr "总上传量" msgstr "总上传量"
@ -278,24 +278,24 @@ msgstr "总上传量"
msgid "Unable to get interface info: %s." msgid "Unable to get interface info: %s."
msgstr "无法获取接口信息:%s。" msgstr "无法获取接口信息:%s。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:36 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:45
msgid "Unable to load log data:" msgid "Unable to load log data:"
msgstr "无法读取日志数据:" msgstr "无法读取日志数据:"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:253 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:256
msgid "Use %s to deploy a private server." msgid "Use %s to deploy a private server."
msgstr "使用 %s 部署私有服务器" msgstr "使用 %s 部署私有服务器"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217
msgid "" msgid ""
"Use site-to-site layer 3 networking to connect subnets on the Tailscale " "Use site-to-site layer 3 networking to connect subnets on the Tailscale "
"network." "network."
msgstr "使用站点到站点的三层网络连接 Tailscale 中的子网。" msgstr "使用站点到站点的三层网络连接 Tailscale 中的子网。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:241 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239
msgid "WAN access Tailscale" msgid "WAN access Tailscale"
msgstr "本地广域网访问虚拟局域网" msgstr "本地广域网访问虚拟局域网"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158
msgid "Workdir" msgid "Workdir"
msgstr "工作目录" msgstr "工作目录"

View File

@ -7,64 +7,64 @@ msgstr ""
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186
msgid "Accept DNS" msgid "Accept DNS"
msgstr "允許DNS" msgstr "允許DNS"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:188 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:186
msgid "Accept DNS configuration from the Tailscale admin console." msgid "Accept DNS configuration from the Tailscale admin console."
msgstr "使用 Tailscale 管理控制台的 DNS 設定。" msgstr "使用 Tailscale 管理控制台的 DNS 設定。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178
msgid "Accept Routes" msgid "Accept Routes"
msgstr "啟用路由" msgstr "啟用路由"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:180 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178
msgid "Accept subnet routes that other nodes advertise." msgid "Accept subnet routes that other nodes advertise."
msgstr "接受其他節點廣播的子網路由。" msgstr "接受其他節點廣播的子網路由。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:235
msgid "Access Control" msgid "Access Control"
msgstr "訪問控制" msgstr "訪問控制"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245
msgid "Additional Flags" msgid "Additional Flags"
msgstr "參數列表" msgstr "參數列表"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:178 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:176
msgid "Advanced Settings" msgid "Advanced Settings"
msgstr "高級設置" msgstr "高級設置"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:129 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:128
msgid "Are you sure you want to log out and unbind the current device?" msgid "Are you sure you want to log out and unbind the current device?"
msgstr "是否註銷當前登錄並且解綁當前設備?" msgstr "是否註銷當前登錄並且解綁當前設備?"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:259 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:262
msgid "Auth Key" msgid "Auth Key"
msgstr "認證密鑰" msgstr "認證密鑰"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:247 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:248
msgid "Available flags" msgid "Available flags"
msgstr "可用參數" msgstr "可用參數"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:141
msgid "Basic Settings" msgid "Basic Settings"
msgstr "基礎設置" msgstr "基礎設置"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:138 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:136
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:152 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:150
msgid "Collecting data ..." msgid "Collecting data ..."
msgstr "正在收集數據..." msgstr "正在收集數據..."
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:252 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255
msgid "Custom Server Settings" msgid "Custom Server Settings"
msgstr "自定義伺服器" msgstr "自定義伺服器"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182
msgid "Device Name" msgid "Device Name"
msgstr "設備名稱" msgstr "設備名稱"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:145 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:143
msgid "Enable" msgid "Enable"
msgstr "啟用" msgstr "啟用"
@ -72,24 +72,24 @@ msgstr "啟用"
msgid "Error parsing interface info: %s." msgid "Error parsing interface info: %s."
msgstr "接口信息解析錯誤:%s。" msgstr "接口信息解析錯誤:%s。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190
msgid "Exit Node" msgid "Exit Node"
msgstr "出口節點" msgstr "出口節點"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208
msgid "Expose Subnets" msgid "Expose Subnets"
msgstr "公開網段" msgstr "公開網段"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:210 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:208
msgid "" msgid ""
"Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>." "Expose physical network routes into Tailscale, e.g. <code>10.0.0.0/24</code>."
msgstr "廣播子網路由至 Tailscale例如<code>10.0.0.0/24</code>。" msgstr "廣播子網路由至 Tailscale例如<code>10.0.0.0/24</code>。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:245 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:243
msgid "Extra Settings" msgid "Extra Settings"
msgstr "附加設置" msgstr "附加設置"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:164 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:162
msgid "Firewall Mode" msgid "Firewall Mode"
msgstr "防火牆模式" msgstr "防火牆模式"
@ -101,11 +101,11 @@ msgstr "全局設置"
msgid "Grant access to Tailscale configuration" msgid "Grant access to Tailscale configuration"
msgstr "授予訪問 Tailscale 配置的權限" msgstr "授予訪問 Tailscale 配置的權限"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:75 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:73
msgid "IPv4 Address" msgid "IPv4 Address"
msgstr "IPv4地址" msgstr "IPv4地址"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:79 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:77
msgid "IPv6 Address" msgid "IPv6 Address"
msgstr "IPv6地址" msgstr "IPv6地址"
@ -113,15 +113,15 @@ msgstr "IPv6地址"
msgid "Interface Info" msgid "Interface Info"
msgstr "接口信息" msgstr "接口信息"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:71 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:69
msgid "Interface Name" msgid "Interface Name"
msgstr "接口名稱" msgstr "接口名稱"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:240 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238
msgid "LAN access Tailscale" msgid "LAN access Tailscale"
msgstr "本地局域網訪問虛擬局域網" msgstr "本地局域網訪問虛擬局域網"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:184 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:182
msgid "Leave blank to use the device's hostname." msgid "Leave blank to use the device's hostname."
msgstr "留空以使用設備的主機名。" msgstr "留空以使用設備的主機名。"
@ -131,19 +131,19 @@ msgid ""
"code>. <br> %s for enabling settings upon the initiation of Tailscale." "code>. <br> %s for enabling settings upon the initiation of Tailscale."
msgstr "額外參數的列表。格式:--flags=value例如 <code>--exit-node=10.0.0.1</code>。<br> 在 Tailscale 啟動時的%s。" msgstr "額外參數的列表。格式:--flags=value例如 <code>--exit-node=10.0.0.1</code>。<br> 在 Tailscale 啟動時的%s。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:91 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:90
msgid "Log out and Unbind" msgid "Log out and Unbind"
msgstr "註銷登錄並解除綁定" msgstr "註銷登錄並解除綁定"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168
msgid "Logging program activities." msgid "Logging program activities."
msgstr "記錄程式運行信息。" msgstr "記錄程式運行信息。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172
msgid "Logging program errors and exceptions." msgid "Logging program errors and exceptions."
msgstr "記錄程式錯誤和警告信息。" msgstr "記錄程式錯誤和警告信息。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:149 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:147
msgid "Login Status" msgid "Login Status"
msgstr "已綁定用戶" msgstr "已綁定用戶"
@ -151,126 +151,126 @@ msgstr "已綁定用戶"
msgid "Logs" msgid "Logs"
msgstr "日誌" msgstr "日誌"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:83 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:81
msgid "MTU" msgid "MTU"
msgstr "MTU" msgstr "MTU"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:93 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:92
msgid "NOT RUNNING" msgid "NOT RUNNING"
msgstr "未運行" msgstr "未運行"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:88 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:87
msgid "Need to log in" msgid "Need to log in"
msgstr "未登錄" msgstr "未登錄"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:66 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:64
msgid "Network Interface Information" msgid "Network Interface Information"
msgstr "網絡接口信息" msgstr "網絡接口信息"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:203 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:201
msgid "No Available Exit Nodes" msgid "No Available Exit Nodes"
msgstr "没有可用的出口節點" msgstr "没有可用的出口節點"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:230 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:228
msgid "No Available Subnet Routes" msgid "No Available Subnet Routes"
msgstr "沒有可用的子網路由" msgstr "沒有可用的子網路由"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:63 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:61
msgid "No interface online." msgid "No interface online."
msgstr "無在線接口。" msgstr "無在線接口。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:192 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:190
msgid "" msgid ""
"Offer to be an exit node for outbound internet traffic from the Tailscale " "Offer to be an exit node for outbound internet traffic from the Tailscale "
"network." "network."
msgstr "作為 Tailscale 廣域網出口節點。" msgstr "作為 Tailscale 廣域網出口節點。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194
msgid "Online Exit Nodes" msgid "Online Exit Nodes"
msgstr "可用出口節點" msgstr "可用出口節點"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153
msgid "Port" msgid "Port"
msgstr "端口" msgstr "端口"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75
msgid "RUNNING" msgid "RUNNING"
msgstr "運行中" msgstr "運行中"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:69 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:68
msgctxt "scroll to top (the head) of the log file" msgctxt "scroll to top (the head) of the log file"
msgid "Scroll to head" msgid "Scroll to head"
msgstr "捲動到頂部" msgstr "捲動到頂部"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:60 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:59
msgctxt "scroll to bottom (the tail) of the log file" msgctxt "scroll to bottom (the tail) of the log file"
msgid "Scroll to tail" msgid "Scroll to tail"
msgstr "捲動到尾部" msgstr "捲動到尾部"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:196 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:194
msgid "Select an online machine name to use as an exit node." msgid "Select an online machine name to use as an exit node."
msgstr "選擇一個可用的節點名稱作為出口節點使用。" msgstr "選擇一個可用的節點名稱作為出口節點使用。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222
msgid "Select subnet routes advertised by other nodes in Tailscale network." msgid "Select subnet routes advertised by other nodes in Tailscale network."
msgstr "選擇非本設備廣播的子網路由。" msgstr "選擇非本設備廣播的子網路由。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:255 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:258
msgid "Server Address" msgid "Server Address"
msgstr "伺服器地址" msgstr "伺服器地址"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:155 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:153
msgid "Set the Tailscale port number." msgid "Set the Tailscale port number."
msgstr "設置 Tailscale 端口號。" msgstr "設置 Tailscale 端口號。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217
msgid "Site To Site" msgid "Site To Site"
msgstr "子網互通" msgstr "子網互通"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:174 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:172
msgid "StdErr Log" msgid "StdErr Log"
msgstr "錯誤日誌" msgstr "錯誤日誌"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:170 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:168
msgid "StdOut Log" msgid "StdOut Log"
msgstr "運行日誌" msgstr "運行日誌"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:224 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:222
msgid "Subnet Routes" msgid "Subnet Routes"
msgstr "子網路由" msgstr "子網路由"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:102 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:100
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:76 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:75
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:78 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:77
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114
#: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3 #: luci-app-tailscale/root/usr/share/luci/menu.d/luci-app-tailscale.json:3
msgid "Tailscale" msgid "Tailscale"
msgstr "Tailscale" msgstr "Tailscale"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:238 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:236
msgid "Tailscale access LAN" msgid "Tailscale access LAN"
msgstr "虛擬局域網訪問本地局域網" msgstr "虛擬局域網訪問本地局域網"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:237
msgid "Tailscale access WAN" msgid "Tailscale access WAN"
msgstr "虛擬局域網訪問本地廣域網" msgstr "虛擬局域網訪問本地廣域網"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:103 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:101
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:115 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:114
msgid "Tailscale is a cross-platform and easy to use virtual LAN." msgid "Tailscale is a cross-platform and easy to use virtual LAN."
msgstr "Tailscale 是一個跨平台且易於使用的虛擬局域網 VPN。" msgstr "Tailscale 是一個跨平台且易於使用的虛擬局域網 VPN。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158
msgid "" msgid ""
"The working directory contains config files, audit logs, and runtime info." "The working directory contains config files, audit logs, and runtime info."
msgstr "工作目錄包含配置文件、審計日誌和運行時信息。" msgstr "工作目錄包含配置文件、審計日誌和運行時信息。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:87 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:85
msgid "Total Download" msgid "Total Download"
msgstr "總下載量" msgstr "總下載量"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:91 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/interface.js:89
msgid "Total Upload" msgid "Total Upload"
msgstr "總上傳量" msgstr "總上傳量"
@ -278,24 +278,24 @@ msgstr "總上傳量"
msgid "Unable to get interface info: %s." msgid "Unable to get interface info: %s."
msgstr "無法獲取接口信息:%s。" msgstr "無法獲取接口信息:%s。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:36 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/log.js:45
msgid "Unable to load log data:" msgid "Unable to load log data:"
msgstr "無法載入日誌檔:" msgstr "無法載入日誌檔:"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:253 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:256
msgid "Use %s to deploy a private server." msgid "Use %s to deploy a private server."
msgstr "使用 %s 部署私有伺服器" msgstr "使用 %s 部署私有伺服器"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:219 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:217
msgid "" msgid ""
"Use site-to-site layer 3 networking to connect subnets on the Tailscale " "Use site-to-site layer 3 networking to connect subnets on the Tailscale "
"network." "network."
msgstr "使用站點到站點的三層網路連線 Tailscale 中的子網。" msgstr "使用站點到站點的三層網路連線 Tailscale 中的子網。"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:241 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:239
msgid "WAN access Tailscale" msgid "WAN access Tailscale"
msgstr "本地廣域網訪問虛擬局域網" msgstr "本地廣域網訪問虛擬局域網"
#: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:160 #: luci-app-tailscale/htdocs/luci-static/resources/view/tailscale/setting.js:158
msgid "Workdir" msgid "Workdir"
msgstr "工作目錄" msgstr "工作目錄"

View File

@ -1,2 +1,43 @@
config tailscale 'settings' config tailscale 'settings'
option enabled '0' # Set whether Tailcale is enabled or not
option enabled '0'
# Set the port to listen on for incoming Tailscale packets (default 41641)
option port '41641'
# Path to config file
option config_path '/etc/tailscale'
# Default to using nftables - change below to 'iptables' if still using iptables
option fw_mode 'nftables'
# Enable stdout log
option log_stdout '1'
# Enable stderr log
option log_stderr '1'
# Accept subnet routes that other nodes advertise
#option accept_routes '1'
# Provide a hostname to use for the device instead of the one provided by the OS
#option hostname ''
# Accept DNS configuration from the admin console. (default accept)
option accept_dns '1'
# Offer to be an exit node for outbound internet traffic from the Tailscale network
#option advertise_exit_node '1'
# Provide a Tailscale IP or machine name to use as an exit node
#option exit_node ''
# Expose physical subnet routes to your entire Tailscale network (e.g. 10.0.0.0/24)
#list advertise_routes ''
# Set to '1' to disable subnet route masquerading
#option disable_snat_subnet_routes '1'
# To connect subnets within your tailnet (e.g. 192.168.1.0/24)
# Documentation https://tailscale.com/kb/1214/site-to-site
#list subnet_routes ''
# Set firewall zone (ts_ac_lan: Tailscale access LAN, ts_ac_wan: Tailscale access WAN:, lan_ac_ts: LAN access Tailscale, wan_ac_ts: WAN access Tailscale)
# Documentation https://openwrt.org/docs/guide-user/services/vpn/tailscale/start
#list access 'ts_ac_lan'
#list access 'ts_ac_wan'
#list access 'lan_ac_ts'
#list access 'wan_ac_ts'
# Set extra flags for enabling settings upon the initiation of Tailscale
# Documentation https://tailscale.com/kb/1241/tailscale-up
#list flags ''
# Provide the base URL of a custom server instead of https://controlplane.tailscale.com
#option login_server ''
# Set auth Key of custom server
#option authkey ''

View File

@ -4,7 +4,7 @@ START=90
USE_PROCD=1 USE_PROCD=1
PROG=/usr/sbin/tailscale PROG=/usr/sbin/tailscale_helper
PROGD=/usr/sbin/tailscaled PROGD=/usr/sbin/tailscaled
CONFIG_PATH=/var/lib/tailscale CONFIG_PATH=/var/lib/tailscale
@ -18,166 +18,60 @@ section_enabled() {
[ $enabled -gt 0 ] [ $enabled -gt 0 ]
} }
custom_instance() { tailscale_helper() {
local cfg="$1" local cfg="$1"
local acceptRoutes hostname acceptDNS advertiseExitNode exitNode advertiseRoutes s2s subnetRoutes flags loginServer authkey std_out std_err local accept_routes hostname accept_dns advertise_exit_node exit_node advertise_routes disable_snat_subnet_routes subnet_routes access flags login_server authkey std_out std_err
local ARGS=" up --reset"
if ! section_enabled "$cfg"; then config_get_bool accept_routes $cfg 'accept_routes'
echo "disabled in config"
return 1
fi
config_get_bool acceptRoutes $cfg 'acceptRoutes'
config_get hostname $cfg 'hostname' config_get hostname $cfg 'hostname'
config_get_bool acceptDNS $cfg 'acceptDNS' config_get_bool accept_dns $cfg 'accept_dns'
config_get_bool advertiseExitNode $cfg 'advertiseExitNode' config_get_bool advertise_exit_node $cfg 'advertise_exit_node'
config_get exitNode $cfg 'exitNode' config_get exit_node $cfg 'exit_node'
config_get advertiseRoutes $cfg 'advertiseRoutes' config_get advertise_routes $cfg 'advertise_routes'
config_get_bool s2s $cfg 's2s' config_get_bool disable_snat_subnet_routes $cfg 'disable_snat_subnet_routes'
config_get subnet_routes $cfg 'subnet_routes'
config_get access $cfg 'access'
config_get flags $cfg 'flags' config_get flags $cfg 'flags'
config_get loginServer $cfg 'loginServer' config_get login_server $cfg 'login_server'
config_get authkey $cfg 'authkey' config_get authkey $cfg 'authkey'
config_get_bool std_out $cfg 'log_stdout' config_get_bool std_out $cfg 'log_stdout'
config_get_bool std_err $cfg 'log_stderr' config_get_bool std_err $cfg 'log_stderr'
[ "$acceptRoutes" = "1" ] && ARGS="$ARGS --accept-routes=true"
[ -n "$hostname" ] && ARGS="$ARGS --hostname=$hostname"
[ "$acceptDNS" = "0" ] && ARGS="$ARGS --accept-dns=false"
[ "$advertiseExitNode" = "1" ] && ARGS="$ARGS --advertise-exit-node"
[ -n "$exitNode" ] && ARGS="$ARGS --exit-node=$exitNode --exit-node-allow-lan-access=true"
[ -n "$advertiseRoutes" ] && ARGS="$ARGS --advertise-routes=$(echo $advertiseRoutes | tr ' ' ',')"
[ "$s2s" = "1" ] && ARGS="$ARGS --snat-subnet-routes=false"
[ -n "$flags" ] && ARGS="$ARGS $flags"
[ -n "$loginServer" ] && ARGS="$ARGS --login-server=$loginServer"
[ -n "$authkey" ] && ARGS="$ARGS --authkey=$authkey"
procd_open_instance procd_open_instance
procd_set_param command $PROG $ARGS procd_set_param command $PROG
[ "$accept_routes" = "1" ] && procd_append_param command --accept-routes=true
[ -n "$hostname" ] && procd_append_param command --hostname="$hostname"
[ "$accept_dns" = "0" ] && procd_append_param command --accept-dns=false
[ "$advertise_exit_node" = "1" ] && procd_append_param command --advertise-exit-node
[ -n "$exit_node" ] && {
procd_append_param command --exit-node="$exit_node"
procd_append_param command --exit-node-allow-lan-access=true
}
[ -n "$advertise_routes" ] && procd_append_param command --advertise-routes="$(echo $advertise_routes | tr ' ' ',')"
[ "$disable_snat_subnet_routes" = "1" ] && procd_append_param command --snat-subnet-routes=false
[ -n "$flags" ] && procd_append_param command $flags
[ -n "$login_server" ] && procd_append_param command --login-server="$login_server"
[ -n "$authkey" ] && procd_append_param command --authkey="$authkey"
procd_set_param env \
ACCEPT_DNS="$accept_dns" \
EXIT_NODE="$exit_node" \
SUBNET_ROUTES="$subnet_routes" \
ACCESS="$access"
procd_set_param respawn
procd_set_param stdout "$std_out" procd_set_param stdout "$std_out"
procd_set_param stderr "$std_err" procd_set_param stderr "$std_err"
procd_close_instance procd_close_instance
(
[ -f "/var/run/tailscale.wait.pid" ] && return
touch /var/run/tailscale.wait.pid
count=0
while [ -z "$(ifconfig | grep 'tailscale' | awk '{print $1}')" ] || [ -z "$(tailscale ip -4)" ]
do
sleep 2
let count++
[ "${count}" -ge 5 ] && { rm /var/run/tailscale.wait.pid; exit 19; }
done
if [ "$acceptDNS" = "1" ]; then
MagicDNSSuffix=$(tailscale status --json | awk -F'"' '/"MagicDNSSuffix"/ {last=$(NF-1)} END {print last}')
sed -i '/100.100.100.100/d' /etc/dnsmasq.conf
echo "server=/$MagicDNSSuffix/100.100.100.100" >> /etc/dnsmasq.conf
/etc/init.d/dnsmasq reload
fi
ts0=$(ifconfig | grep 'tailscale' | awk '{print $1}')
if [ -z "$(uci -q get network.tailscale)" ]; then
uci set network.tailscale='interface'
if [ "$ts0" = *$'\n'* ]; then
uci set network.ts_lan='device'
uci set network.ts_lan.type='bridge'
uci set network.ts_lan.name='ts-lan'
for port in "${ts0}"; do
uci add_list network.ts_lan.ports=$port
done
uci set network.tailscale.proto='none'
uci set network.tailscale.device='ts-lan'
else
ts_ip=$(tailscale ip -4)
uci set network.tailscale.proto='static'
uci set network.tailscale.ipaddr=$ts_ip
uci set network.tailscale.netmask='255.0.0.0'
uci set network.tailscale.device=$ts0
fi
fi
lan2wan=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+')
if [ -n "$exitNode" ]; then
uci set firewall.@defaults[0].forward='REJECT'
[ -n $lan2wan ] && uci set firewall.@forwarding[$lan2wan].enabled='0'
else
uci -q delete firewall.@forwarding[$lan2wan].enabled
fi
config_get subnetRoutes $cfg 'subnetRoutes'
if [ -n "$subnetRoutes" ]; then
i=1
ts_ip=$(tailscale ip -4)
for route in $subnetRoutes; do
uci set network.ts_subnet$i='route'
uci set network.ts_subnet$i.interface='tailscale'
uci set network.ts_subnet$i.target=$route
uci set network.ts_subnet$i.gateway=$ts_ip
let i++
done
else
for route in $(uci show network | grep 'network.ts_subnet[0-9]\+=route' | grep -o 'network.ts_subnet[0-9]\+'); do
uci -q delete $route
done
fi
config_get access $cfg 'access'
if [ -n "$access" ]; then
if [ -z "$(uci -q get firewall.tszone)" ]; then
uci set firewall.tszone='zone'
uci set firewall.tszone.input='ACCEPT'
uci set firewall.tszone.output='ACCEPT'
uci set firewall.tszone.forward='ACCEPT'
uci set firewall.tszone.masq='1'
uci set firewall.tszone.mtu_fix='1'
uci set firewall.tszone.name='tailscale'
uci set firewall.tszone.network='tailscale'
fi
else
uci -q delete firewall.tszone
fi
if [ "${access//tsfwlan/}" != "$access" ]; then
uci set firewall.tsfwlan=forwarding
uci set firewall.tsfwlan.dest='lan'
uci set firewall.tsfwlan.src='tailscale'
else
uci -q delete firewall.tsfwlan
fi
if [ "${access//tsfwwan/}" != "$access" ]; then
uci set firewall.tsfwwan=forwarding
uci set firewall.tsfwwan.dest='wan'
uci set firewall.tsfwwan.src='tailscale'
else
uci -q delete firewall.tsfwwan
fi
if [ "${access//lanfwts/}" != "$access" ]; then
uci set firewall.lanfwts=forwarding
uci set firewall.lanfwts.dest='tailscale'
uci set firewall.lanfwts.src='lan'
else
uci -q delete firewall.lanfwts
fi
if [ "${access//wanfwts/}" != "$access" ]; then
uci set firewall.wanfwts=forwarding
uci set firewall.wanfwts.dest='tailscale'
uci set firewall.wanfwts.src='wan'
else
uci -q delete firewall.wanfwts
fi
[ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload
[ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload
rm /var/run/tailscale.wait.pid
) &
} }
start_instance() { start_instance() {
local cfg="$1" local cfg="$1"
local port config_path fw_mode std_out std_err state_file local port config_path fw_mode std_out std_err state_file
local ARGS=""
if ! section_enabled "$cfg"; then if ! section_enabled "$cfg"; then
echo "disabled in config" echo "disabled in /etc/config/tailscale"
return 1 return 1
fi fi
@ -189,38 +83,40 @@ start_instance() {
[ -d $config_path ] || mkdir -p $config_path [ -d $config_path ] || mkdir -p $config_path
[ -d $CONFIG_PATH ] || mkdir -p $CONFIG_PATH [ -d $CONFIG_PATH ] || mkdir -p $CONFIG_PATH
state_file=$config_path/tailscaled.state state_file="$config_path/tailscaled.state"
/usr/sbin/tailscaled --cleanup /usr/sbin/tailscaled --cleanup
[ -n "$port" ] && ARGS="$ARGS --port $port"
[ -n "$state_file" ] && ARGS="$ARGS --state $state_file"
procd_open_instance procd_open_instance
procd_set_param command $PROGD $ARGS procd_set_param command $PROGD
[ -n "$port" ] && procd_append_param command --port "$port"
[ -n "$state_file" ] && procd_append_param command --state "$state_file"
procd_set_param env TS_DEBUG_FIREWALL_MODE="$fw_mode" procd_set_param env TS_DEBUG_FIREWALL_MODE="$fw_mode"
procd_set_param respawn procd_set_param respawn
procd_set_param stdout "$std_out" procd_set_param stdout "$std_out"
procd_set_param stderr "$std_err" procd_set_param stderr "$std_err"
procd_close_instance procd_close_instance
tailscale_helper "$cfg"
} }
start_service() { start_service() {
config_load 'tailscale' config_load 'tailscale'
config_foreach start_instance 'tailscale' config_foreach start_instance 'tailscale'
config_foreach custom_instance 'tailscale'
} }
stop_instance() { stop_instance() {
local cfg="$1" local cfg="$1"
/usr/sbin/tailscaled --cleanup
# Remove dnsmasq settings # Remove dnsmasq settings
sed -i '/100.100.100.100/d' /etc/dnsmasq.conf MagicDNSSuffix=$(tailscale status --json | awk -F'"' '/"MagicDNSSuffix"/ {last=$(NF-1)} END {print last}')
/etc/init.d/dnsmasq reload for suffix in $(uci show dhcp | grep -E "address=.*/${MagicDNSSuffix}/" | sed "s/'//g"); do
uci -q del_list $suffix
done
# Remove network settings # Remove network settings
uci -q delete network.tailscale uci -q delete network.tailscale
uci -q delete network.ts_lan uci -q delete network.ts_lan
@ -229,18 +125,23 @@ stop_instance() {
done done
# Remove firewall settings # Remove firewall settings
lan2wan=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+') index=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+')
uci -q delete firewall.@forwarding[$lan2wan].enabled uci -q delete firewall.@forwarding[$index].enabled
uci -q delete firewall.tszone uci -q delete firewall.tszone
uci -q delete firewall.tsfwlan uci -q delete firewall.ts_ac_lan
uci -q delete firewall.tsfwwan uci -q delete firewall.ts_ac_wan
uci -q delete firewall.lanfwts uci -q delete firewall.lan_ac_ts
uci -q delete firewall.wanfwts uci -q delete firewall.wan_ac_ts
# Commit configuration changes and reload service
[ -n "$(uci changes dhcp)" ] && uci commit dhcp && /etc/init.d/dnsmasq reload
[ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload [ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload
[ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload [ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload
/usr/sbin/tailscaled --cleanup
# Remove existing link or folder # Remove existing link or folder
rm -rf $CONFIG_PATH rm -rf "$CONFIG_PATH"
} }
stop_service() { stop_service() {

View File

@ -0,0 +1,147 @@
#!/bin/sh
# Copyright (C) 2025 asvow
# SPDX-License-Identifier: GPL-3.0-only
# Error handling function
revert_exit() {
logger -p daemon.err -t tailscale_helper "$(date '+%Y/%m/%d %H:%M:%S') $1"
uci revert dhcp && uci revert network && uci revert firewall
/etc/init.d/tailscale stop
exit 1
}
# Execute tailscale up command
/usr/sbin/tailscale up --reset "$@" || revert_exit "tailscale up failed."
# Use flock to acquire an exclusive lock
LOCK_FILE="/var/lock/tailscale.lock"
exec 9> "$LOCK_FILE"
flock -xn 9 || { revert_exit "Failed to acquire lock on $LOCK_FILE"; }
trap 'rm -f "$LOCK_FILE"; exit' INT TERM EXIT
# Wait for Tailscale IPv4 address
count=0
while [ -z "$(ifconfig | grep 'tailscale' | awk '{print $1}')" ] || [ -z "$(tailscale ip -4)" ]; do
sleep 2
count=$((count + 1))
[ "${count}" -ge 5 ] && revert_exit "Failed to get Tailscale IPv4 address after 5 attempts."
done
# Configure Tailscale MagicDNS
if [ "$ACCEPT_DNS" = "1" ]; then
MagicDNSSuffix=$(tailscale status --json | awk -F'"' '/"MagicDNSSuffix"/ {last=$(NF-1)} END {print last}')
target_address="/$MagicDNSSuffix/100.100.100.100"
index=$(uci show dhcp | grep 'dhcp.@dnsmasq\[[0-9]\+\]=dnsmasq' | grep -o '[0-9]\+')
for i in $index; do
if ! uci get dhcp.@dnsmasq[$i].address 2>/dev/null | grep -qxF "$target_address"; then
uci add_list "dhcp.@dnsmasq[$i].address=$target_address" || revert_exit "Failed to add DNS address."
fi
done
fi
# Configure network interface for Tailscale
ts0=$(ifconfig | grep 'tailscale' | awk '{print $1}')
if [ -z "$(uci -q get network.tailscale)" ]; then
uci set network.tailscale='interface'
if [ "$ts0" = *$'\n'* ]; then
[ -n "$(uci batch <<-EOF 2>&1
set network.ts_lan='device'
set network.ts_lan.type='bridge'
set network.ts_lan.name='ts-lan'
set network.tailscale.proto='none'
set network.tailscale.device='ts-lan'
EOF
)" ] && revert_exit "Failed to configure network interface for Tailscale."
for port in "${ts0}"; do
uci add_list network.ts_lan.ports=$port || revert_exit "Failed to add port $port."
done
else
ts_ip=$(tailscale ip -4)
[ -n "$(uci batch <<-EOF 2>&1
set network.tailscale.proto='static'
set network.tailscale.ipaddr=$ts_ip
set network.tailscale.netmask='255.0.0.0'
set network.tailscale.device=$ts0
EOF
)" ] && revert_exit "Failed to configure network interface for Tailscale."
fi
fi
# Configure exit node firewall rules
if [ -n "$EXIT_NODE" ]; then
uci set firewall.@defaults[0].forward='REJECT' || revert_exit "Failed to set default forward policy to REJECT."
# Find the LAN to WAN forwarding rule index
index=$(uci show firewall | grep "firewall.@forwarding\[[0-9]\+\]\.src='lan'" -B 1 -A 1 | grep "firewall.@forwarding\[[0-9]\+\]\.dest='wan'" | grep -o '[0-9]\+')
[ -n "$index" ] && uci set firewall.@forwarding[$index].enabled='0' || revert_exit "Failed to disable forwarding rule."
fi
# Configure subnet routes for site to site
if [ -n "$SUBNET_ROUTES" ]; then
i=1
ts_ip=$(tailscale ip -4)
for route in $SUBNET_ROUTES; do
[ -n "$(uci batch <<-EOF 2>&1
set network.ts_subnet$i='route'
set network.ts_subnet$i.interface='tailscale'
set network.ts_subnet$i.target=$route
set network.ts_subnet$i.gateway=$ts_ip
EOF
)" ] && revert_exit "Failed to configure subnet routes for site to site."
let i++
done
fi
# Configure firewall zone and rules
if [ -n "$ACCESS" ]; then
[ -n "$(uci batch <<-EOF 2>&1
set firewall.tszone='zone'
set firewall.tszone.input='ACCEPT'
set firewall.tszone.output='ACCEPT'
set firewall.tszone.forward='ACCEPT'
set firewall.tszone.masq='1'
set firewall.tszone.mtu_fix='1'
set firewall.tszone.name='tailscale'
set firewall.tszone.network='tailscale'
EOF
)" ] && revert_exit "Failed to create firewall zone and forwarding rules for Tailscale."
fi
# Configure specific firewall forwarding rules between Tailscale, LAN, and WAN
if [ "${ACCESS//ts_ac_lan/}" != "$ACCESS" ]; then
[ -n "$(uci batch <<-EOF 2>&1
set firewall.ts_ac_lan=forwarding
set firewall.ts_ac_lan.dest='lan'
set firewall.ts_ac_lan.src='tailscale'
EOF
)" ] && revert_exit "Failed to configure ts_ac_lan firewall forwarding rules."
fi
if [ "${ACCESS//ts_ac_wan/}" != "$ACCESS" ]; then
[ -n "$(uci batch <<-EOF 2>&1
set firewall.ts_ac_wan=forwarding
set firewall.ts_ac_wan.dest='wan'
set firewall.ts_ac_wan.src='tailscale'
EOF
)" ] && revert_exit "Failed to configure ts_ac_wan firewall forwarding rules."
fi
if [ "${ACCESS//lan_ac_ts/}" != "$ACCESS" ]; then
[ -n "$(uci batch <<-EOF 2>&1
set firewall.lan_ac_ts=forwarding
set firewall.lan_ac_ts.dest='tailscale'
set firewall.lan_ac_ts.src='lan'
EOF
)" ] && revert_exit "Failed to configure lan_ac_ts firewall forwarding rules."
fi
if [ "${ACCESS//wan_ac_ts/}" != "$ACCESS" ]; then
[ -n "$(uci batch <<-EOF 2>&1
set firewall.wan_ac_ts=forwarding
set firewall.wan_ac_ts.dest='tailscale'
set firewall.wan_ac_ts.src='wan'
EOF
)" ] && revert_exit "Failed to configure wan_ac_ts firewall forwarding rules."
fi
# Commit configuration changes and reload service
[ -n "$(uci changes dhcp)" ] && uci commit dhcp && /etc/init.d/dnsmasq reload
[ -n "$(uci changes network)" ] && uci commit network && /etc/init.d/network reload
[ -n "$(uci changes firewall)" ] && uci commit firewall && /etc/init.d/firewall reload

View File

@ -3,9 +3,11 @@
"description": "Grant access to Tailscale configuration", "description": "Grant access to Tailscale configuration",
"read": { "read": {
"file": { "file": {
"/sbin/ip": [ "exec" ], "/sbin/ip -s -j ad": [ "exec" ],
"/sbin/logread": [ "exec" ], "/sbin/logread -e tailscale": [ "exec" ],
"/usr/sbin/tailscale": [ "exec" ] "/usr/sbin/tailscale status --json": [ "exec" ],
"/usr/sbin/tailscale login": [ "exec" ],
"/usr/sbin/tailscale logout": [ "exec" ]
}, },
"ubus": { "ubus": {
"service": [ "list" ] "service": [ "list" ]