🎄 Sync 2026-07-26 03:59:09
@ -68,7 +68,7 @@ return view.extend({
|
||||
o = s.option(form.ListValue, 'fullcone', _('Enable FullCone NAT'));
|
||||
o.value('0', _('Disable'));
|
||||
o.value('1', _('FULLCONENAT'));
|
||||
o.value('2', _('Boardcom Fullcone NAT1'));
|
||||
o.value('2', _('Broadcom Fullcone NAT1'));
|
||||
addTCPCCAOption();
|
||||
if (fw4)
|
||||
o = s.option(form.Flag, 'fullcone6', _('Enable FullCone NAT6'));
|
||||
|
||||
1
luci-app-homeproxy/.gitignore
vendored
@ -1 +0,0 @@
|
||||
.DS_Store
|
||||
@ -1,340 +0,0 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Ty Coon>, 1 April 1989
|
||||
Ty Coon, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Library General
|
||||
Public License instead of this License.
|
||||
@ -1,19 +1,16 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
#
|
||||
# Copyright (C) 2022-2023 ImmortalWrt.org
|
||||
# Copyright (C) 2022-2024 ImmortalWrt.org
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
LUCI_TITLE:=The modern ImmortalWrt proxy platform for ARM64/AMD64
|
||||
LUCI_PKGARCH:=all
|
||||
LUCI_DEPENDS:= \
|
||||
+sing-box \
|
||||
+firewall4 \
|
||||
+kmod-nft-tproxy \
|
||||
+ucode-mod-digest
|
||||
|
||||
PKG_NAME:=luci-app-homeproxy
|
||||
|
||||
define Package/luci-app-homeproxy/conffiles
|
||||
/etc/config/homeproxy
|
||||
/etc/homeproxy/certs/
|
||||
|
||||
@ -1,5 +0,0 @@
|
||||
TODO:
|
||||
- Subscription page slow response with a large number of nodes
|
||||
- Refactor nft rules
|
||||
- Move ACL settings to a dedicated page
|
||||
- Any other improvements
|
||||
@ -1543,87 +1543,6 @@ return view.extend({
|
||||
/* Direct domain list end */
|
||||
/* ACL settings end */
|
||||
|
||||
/* Dashboard settings start */
|
||||
s.tab('dashboard', _('Dashboard Settings'));
|
||||
|
||||
o = s.taboption('dashboard', form.Flag, 'dashboard_enabled', _('Enable Dashboard'),
|
||||
_('Enable sing-box API and dashboard.<br/><strong style="color:red">Warning: The API will be accessible to all devices on the LAN.</strong>'));
|
||||
o.default = o.disabled;
|
||||
o.rmempty = false;
|
||||
|
||||
o = s.taboption('dashboard', form.Value, 'dashboard_secret', _('Dashboard Secret'),
|
||||
_('API authentication secret. Required for security since the API is LAN-accessible.'));
|
||||
o.password = true;
|
||||
o.depends('dashboard_enabled', '1');
|
||||
o.rmempty = false;
|
||||
o.validate = function(section_id, value) {
|
||||
let enabled = this.section.formvalue(section_id, 'dashboard_enabled');
|
||||
if (enabled === '1') {
|
||||
if (!value || value.length < 6)
|
||||
return _('Dashboard secret must be at least 6 characters.');
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
o = s.taboption('dashboard', form.Value, 'dashboard_port', _('Dashboard Port'),
|
||||
_('The listen port for sing-box API and dashboard.'));
|
||||
o.datatype = 'port';
|
||||
o.placeholder = '9090';
|
||||
o.depends('dashboard_enabled', '1');
|
||||
|
||||
o = s.taboption('dashboard', form.Value, 'dashboard_url', _('Dashboard URL'),
|
||||
_('URL to download dashboard files. Leave empty to use Zashboard by default.'));
|
||||
o.placeholder = 'https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn.zip';
|
||||
o.depends('dashboard_enabled', '1');
|
||||
|
||||
o = s.taboption('dashboard', form.ListValue, 'dashboard_detour', _('Download outbound'),
|
||||
_('Outbound used to download dashboard files.'));
|
||||
o.load = function(section_id) {
|
||||
delete this.keylist;
|
||||
delete this.vallist;
|
||||
|
||||
this.value('direct-out', _('Direct'));
|
||||
let routing_mode = uci.get('homeproxy', 'config', 'routing_mode');
|
||||
if (routing_mode !== 'custom') {
|
||||
let main_node = uci.get('homeproxy', 'config', 'main_node');
|
||||
if (main_node && main_node !== 'nil')
|
||||
this.value('main-out', _('Main node (proxy)'));
|
||||
} else {
|
||||
uci.sections(data[0], 'routing_node', (res) => {
|
||||
if (res.enabled === '1')
|
||||
this.value(res['.name'], res.label);
|
||||
});
|
||||
}
|
||||
|
||||
return this.super('load', section_id);
|
||||
};
|
||||
o.default = 'direct-out';
|
||||
o.depends('dashboard_enabled', '1');
|
||||
|
||||
o = s.taboption('dashboard', form.DummyValue, '_dashboard_open', _('Open Dashboard'));
|
||||
o.depends('dashboard_enabled', '1');
|
||||
o.rawhtml = true;
|
||||
o.cfgvalue = function(section_id) {
|
||||
let port = uci.get('homeproxy', 'config', 'dashboard_port') || '9090';
|
||||
let host = window.location.hostname;
|
||||
let url = 'http://' + host + ':' + port + '/ui/';
|
||||
return E('div', {}, [
|
||||
E('button', {
|
||||
'class': 'btn cbi-button cbi-button-action',
|
||||
'click': function() {
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}, [ _('Open Dashboard') ]),
|
||||
E('div', { 'style': 'margin-top:8px; color:#888; font-size:90%' }, [
|
||||
E('div', {}, _('Dashboard URL: %s').format(url)),
|
||||
E('div', { 'style': 'margin-top:4px' },
|
||||
_('If the dashboard asks for connection settings, fill in: Host = <code>%s</code>, Port = <code>%s</code>, Secret = your configured secret above.').format(host, port)
|
||||
)
|
||||
])
|
||||
]);
|
||||
};
|
||||
/* Dashboard settings end */
|
||||
|
||||
return m.render();
|
||||
}
|
||||
});
|
||||
|
||||
@ -47,7 +47,7 @@ function getConnStat(o, site) {
|
||||
let ele = o.default.firstElementChild.nextElementSibling;
|
||||
if (ret.result) {
|
||||
ele.style.setProperty('color', 'green');
|
||||
ele.innerHTML = ret.elapsed ? _('passed (%dms)').format(ret.elapsed) : _('passed');
|
||||
ele.innerHTML = _('passed');
|
||||
} else {
|
||||
ele.style.setProperty('color', 'red');
|
||||
ele.innerHTML = _('failed');
|
||||
@ -238,24 +238,6 @@ return view.extend({
|
||||
o = s.option(form.DummyValue, '_check_google', _('Google'));
|
||||
o.cfgvalue = L.bind(getConnStat, this, o, 'google');
|
||||
|
||||
if (uci.get('homeproxy', 'config', 'dashboard_enabled') === '1') {
|
||||
s = m.section(form.NamedSection, 'config', 'homeproxy', _('Dashboard'));
|
||||
s.anonymous = true;
|
||||
|
||||
o = s.option(form.DummyValue, '_dashboard_btn', _('Zashboard'));
|
||||
o.rawhtml = true;
|
||||
o.cfgvalue = function(section_id) {
|
||||
let port = uci.get('homeproxy', 'config', 'dashboard_port') || '9090';
|
||||
return E('button', {
|
||||
'class': 'btn cbi-button cbi-button-action',
|
||||
'click': function() {
|
||||
let url = window.location.protocol + '//' + window.location.hostname + ':' + port + '/ui/';
|
||||
window.open(url, '_blank');
|
||||
}
|
||||
}, [ _('Open Dashboard') ]);
|
||||
};
|
||||
}
|
||||
|
||||
s = m.section(form.NamedSection, 'config', 'homeproxy', _('Resources management'));
|
||||
s.anonymous = true;
|
||||
|
||||
|
||||
@ -2958,9 +2958,6 @@ msgstr ""
|
||||
msgid "passed"
|
||||
msgstr ""
|
||||
|
||||
msgid "passed (%dms)"
|
||||
msgstr ""
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:791
|
||||
msgid "private key"
|
||||
msgstr ""
|
||||
@ -3052,57 +3049,3 @@ msgstr ""
|
||||
#: htdocs/luci-static/resources/homeproxy.js:328
|
||||
msgid "valid uuid"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashboard Settings"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable Dashboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Enable sing-box API and dashboard.<br/><strong style=\"color:red\">Warning: The API will be accessible to all devices on the LAN.</strong>"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open Dashboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashboard Secret"
|
||||
msgstr ""
|
||||
|
||||
msgid "API authentication secret. Required for security since the API is LAN-accessible."
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashboard secret must be at least 6 characters."
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashboard Port"
|
||||
msgstr ""
|
||||
|
||||
msgid "The listen port for sing-box API and dashboard."
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashboard URL"
|
||||
msgstr ""
|
||||
|
||||
msgid "URL to download dashboard files. Leave empty to use Zashboard by default."
|
||||
msgstr ""
|
||||
|
||||
msgid "Download outbound"
|
||||
msgstr ""
|
||||
|
||||
msgid "Outbound used to download dashboard files."
|
||||
msgstr ""
|
||||
|
||||
msgid "Main node (proxy)"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Zashboard"
|
||||
msgstr ""
|
||||
|
||||
msgid "Dashboard URL: %s"
|
||||
msgstr ""
|
||||
|
||||
msgid "If the dashboard asks for connection settings, fill in: Host = <code>%s</code>, Port = <code>%s</code>, Secret = your configured secret above."
|
||||
msgstr ""
|
||||
|
||||
@ -3027,9 +3027,6 @@ msgstr "packet addr (v2ray-core v5+)"
|
||||
msgid "passed"
|
||||
msgstr "通过"
|
||||
|
||||
msgid "passed (%dms)"
|
||||
msgstr "通过(%dms)"
|
||||
|
||||
#: htdocs/luci-static/resources/view/homeproxy/server.js:791
|
||||
msgid "private key"
|
||||
msgstr "私钥"
|
||||
@ -3122,57 +3119,3 @@ msgstr "有效端口值"
|
||||
#: htdocs/luci-static/resources/homeproxy.js:328
|
||||
msgid "valid uuid"
|
||||
msgstr "有效 uuid"
|
||||
|
||||
msgid "Dashboard Settings"
|
||||
msgstr "面板设置"
|
||||
|
||||
msgid "Enable Dashboard"
|
||||
msgstr "启用面板"
|
||||
|
||||
msgid "Enable sing-box API and dashboard.<br/><strong style=\"color:red\">Warning: The API will be accessible to all devices on the LAN.</strong>"
|
||||
msgstr "启用 sing-box API 和面板。<br/><strong style=\"color:red\">警告:API 将对局域网内所有设备开放访问。</strong>"
|
||||
|
||||
msgid "Open Dashboard"
|
||||
msgstr "打开面板"
|
||||
|
||||
msgid "Dashboard Secret"
|
||||
msgstr "面板密钥"
|
||||
|
||||
msgid "API authentication secret. Required for security since the API is LAN-accessible."
|
||||
msgstr "API 认证密钥。由于 API 可被局域网访问,必须设置此密钥以确保安全。"
|
||||
|
||||
msgid "Dashboard secret must be at least 6 characters."
|
||||
msgstr "面板密钥长度不能少于 6 位。"
|
||||
|
||||
msgid "Dashboard Port"
|
||||
msgstr "面板端口"
|
||||
|
||||
msgid "The listen port for sing-box API and dashboard."
|
||||
msgstr "sing-box API 和面板的监听端口。"
|
||||
|
||||
msgid "Dashboard URL"
|
||||
msgstr "面板下载地址"
|
||||
|
||||
msgid "URL to download dashboard files. Leave empty to use Zashboard by default."
|
||||
msgstr "面板文件的下载地址。留空则默认使用 Zashboard。"
|
||||
|
||||
msgid "Download outbound"
|
||||
msgstr "下载出站"
|
||||
|
||||
msgid "Outbound used to download dashboard files."
|
||||
msgstr "用于下载面板文件的出站节点。"
|
||||
|
||||
msgid "Main node (proxy)"
|
||||
msgstr "主节点(代理)"
|
||||
|
||||
msgid "Dashboard"
|
||||
msgstr "面板"
|
||||
|
||||
msgid "Zashboard"
|
||||
msgstr "Zashboard"
|
||||
|
||||
msgid "Dashboard URL: %s"
|
||||
msgstr "面板地址:%s"
|
||||
|
||||
msgid "If the dashboard asks for connection settings, fill in: Host = <code>%s</code>, Port = <code>%s</code>, Secret = your configured secret above."
|
||||
msgstr "若面板要求填写连接设置,请填入:Host(主机)= <code>%s</code>,Port(端口)= <code>%s</code>,Secret(密钥)= 上方配置的面板密钥。"
|
||||
|
||||
@ -33,9 +33,6 @@ config homeproxy 'config'
|
||||
option ipv6_support '1'
|
||||
option github_token ''
|
||||
option log_level 'warn'
|
||||
option dashboard_enabled '0'
|
||||
option dashboard_port '9090'
|
||||
option dashboard_secret ''
|
||||
|
||||
config homeproxy 'control'
|
||||
option lan_proxy_mode 'disabled'
|
||||
|
||||
@ -1 +1 @@
|
||||
20260424052559
|
||||
20260704060757
|
||||
|
||||
@ -1,12 +1,6 @@
|
||||
2001:250::/30
|
||||
2001:254::/31
|
||||
2001:256:100::/48
|
||||
2001:678:120::/48
|
||||
2001:678:53c::/48
|
||||
2001:678:10d0::/48
|
||||
2001:678:1184::/48
|
||||
2001:67c:ebc::/48
|
||||
2001:7fa:5::/48
|
||||
2001:c68::/32
|
||||
2001:cc0::/32
|
||||
2001:da8::/32
|
||||
@ -17,30 +11,16 @@
|
||||
2001:dc7::/32
|
||||
2001:dd8:1::/48
|
||||
2001:dd9::/48
|
||||
2001:df0:ac40::/48
|
||||
2001:df1:da00::/48
|
||||
2001:df3:3a80::/48
|
||||
2001:df3:8b80::/48
|
||||
2001:df5:4740::/48
|
||||
2001:df6:40::/48
|
||||
2001:df7:1480::/48
|
||||
2001:4510:400::/40
|
||||
2400:1160::/32
|
||||
2400:3200::/32
|
||||
2400:5280:f803::/48
|
||||
2400:54a0:21c2::/47
|
||||
2400:5a00::/32
|
||||
2400:5a60:2::/48
|
||||
2400:5f60::/32
|
||||
2400:6000::/32
|
||||
2400:6460::/39
|
||||
2400:6460:300::/40
|
||||
2400:6460:500::/40
|
||||
2400:6460:600::/40
|
||||
2400:6600::/32
|
||||
2400:6e60:1301::/48
|
||||
2400:75aa::/32
|
||||
2400:7bc0:20::/43
|
||||
2400:7fc0::/40
|
||||
2400:7fc0:220::/44
|
||||
2400:7fc0:240::/44
|
||||
@ -56,16 +36,10 @@
|
||||
2400:8200::/32
|
||||
2400:89c0:1010::/44
|
||||
2400:89c0:1050::/46
|
||||
2400:89c0:1130::/44
|
||||
2400:89c0:1150::/48
|
||||
2400:89c0:2100::/48
|
||||
2400:89c0:2200::/48
|
||||
2400:89c0:3010::/44
|
||||
2400:89c0:6000::/48
|
||||
2400:89c0:6100::/48
|
||||
2400:8fc0:571::/48
|
||||
2400:8fc0::/32
|
||||
2400:9020:f010::/46
|
||||
2400:9340::/32
|
||||
2400:9380:8001::/48
|
||||
2400:9380:8003::/48
|
||||
2400:9380:8021::/48
|
||||
@ -113,10 +87,9 @@
|
||||
2400:dd00::/28
|
||||
2400:ee00:ffec::/46
|
||||
2400:ee00:fff0::/44
|
||||
2400:f6e0::/32
|
||||
2400:f720::/32
|
||||
2400:fe00::/32
|
||||
2401:20::/40
|
||||
2401:20:1330::/46
|
||||
2401:800::/32
|
||||
2401:1160::/32
|
||||
2401:1320::/32
|
||||
@ -128,20 +101,11 @@
|
||||
2401:3480:3000::/36
|
||||
2401:34a0::/31
|
||||
2401:3800::/32
|
||||
2401:70e0::/32
|
||||
2401:5c20:10::/48
|
||||
2401:71c0::/48
|
||||
2401:7660::/48
|
||||
2401:8a00:11::/48
|
||||
2401:8a00:12::/48
|
||||
2401:7e00::/32
|
||||
2401:8be0::/48
|
||||
2401:8d00::/46
|
||||
2401:8d00:4::/48
|
||||
2401:8d00:7::/48
|
||||
2401:8d00:8::/47
|
||||
2401:8d00:b::/48
|
||||
2401:8d00:c::/48
|
||||
2401:8d00:f::/48
|
||||
2401:8d00:14::/47
|
||||
2401:9a00::/44
|
||||
2401:9a00:10::/46
|
||||
2401:a140:1::/48
|
||||
@ -161,49 +125,26 @@
|
||||
2401:cc00::/32
|
||||
2401:ce00::/32
|
||||
2401:d0e0:3::/48
|
||||
2401:d180::/46
|
||||
2401:d180:10::/47
|
||||
2401:d180:111::/48
|
||||
2401:d180:2120::/48
|
||||
2401:d920::/48
|
||||
2401:de00::/32
|
||||
2401:ec00::/32
|
||||
2401:f860:85::/48
|
||||
2401:f860:87::/48
|
||||
2401:f860:88::/47
|
||||
2401:f860:90::/48
|
||||
2401:f860:92::/47
|
||||
2401:f860:94::/48
|
||||
2401:f860:96::/47
|
||||
2401:f860:100::/40
|
||||
2401:fa00:40::/43
|
||||
2401:fa00:40::/48
|
||||
2402:840:d000::/46
|
||||
2402:840:e000::/46
|
||||
2402:840:f000::/38
|
||||
2402:c60::/48
|
||||
2402:1440::/32
|
||||
2402:1460::/32
|
||||
2402:18a0::/32
|
||||
2402:2000::/32
|
||||
2402:20e0:f000::/48
|
||||
2402:3180::/46
|
||||
2402:3180:8000::/33
|
||||
2402:33e0::/32
|
||||
2402:36e0::/44
|
||||
2402:3c00::/32
|
||||
2402:3f80:1400::/40
|
||||
2402:4b80::/32
|
||||
2402:4e00::/32
|
||||
2402:5ec0::/32
|
||||
2402:6e80::/32
|
||||
2402:6f40::/48
|
||||
2402:6f40:2::/48
|
||||
2402:6f40:2::/47
|
||||
2402:7d80::/48
|
||||
2402:7d80:240::/48
|
||||
2402:7d80:8888::/48
|
||||
2402:8bc0::/32
|
||||
2402:8cc0::/40
|
||||
2402:8cc0:200::/40
|
||||
2402:92c0::/48
|
||||
2402:93c0::/48
|
||||
2402:93c0:20::/48
|
||||
@ -216,11 +157,9 @@
|
||||
2402:b8c0:186::/48
|
||||
2402:d340::/32
|
||||
2402:db40:5100::/48
|
||||
2402:db40:5900::/48
|
||||
2402:db40:5f00::/46
|
||||
2402:dfc0::/44
|
||||
2402:dfc0:50::/44
|
||||
2402:e380:100::/40
|
||||
2402:e480::/32
|
||||
2402:e740::/32
|
||||
2402:e7c0::/32
|
||||
@ -239,26 +178,18 @@
|
||||
2403:1ec0:1400::/48
|
||||
2403:1ec0:1600::/48
|
||||
2403:1ec0:1610::/48
|
||||
2403:1ec0:1900::/48
|
||||
2403:1ec0:1910::/44
|
||||
2403:1ec0:1920::/43
|
||||
2403:1ec0:1940::/44
|
||||
2403:2040::/48
|
||||
2403:2b40::/32
|
||||
2403:3140::/32
|
||||
2403:4240::/32
|
||||
2403:4280::/47
|
||||
2403:4300::/32
|
||||
2403:4c80::/48
|
||||
2403:5c80::/48
|
||||
2403:6380:40::/46
|
||||
2403:6380:60::/44
|
||||
2403:6740::/48
|
||||
2403:6a00::/32
|
||||
2403:7580::/32
|
||||
2403:8080:101::/48
|
||||
2403:8c00::/32
|
||||
2403:9b00:2000::/48
|
||||
2403:9b00:2400::/48
|
||||
2403:9b00:6000::/48
|
||||
2403:a100::/48
|
||||
@ -266,8 +197,6 @@
|
||||
2403:a140:100::/40
|
||||
2403:a200::/32
|
||||
2403:ac00::/32
|
||||
2403:ad80:8008::/48
|
||||
2403:ad80:8047::/48
|
||||
2403:b400::/32
|
||||
2403:c980::/32
|
||||
2403:d400::/32
|
||||
@ -321,9 +250,9 @@
|
||||
2404:c300::/32
|
||||
2404:c940::/48
|
||||
2404:e280::/47
|
||||
2404:e5c0::/32
|
||||
2404:e8c0::/32
|
||||
2404:f4c0::/32
|
||||
2405:80:10::/48
|
||||
2405:80:13::/48
|
||||
2405:6c0:2::/48
|
||||
2405:6c0:4::/48
|
||||
2405:1480:1000::/48
|
||||
@ -332,7 +261,7 @@
|
||||
2405:3140:11::/48
|
||||
2405:3140:21::/48
|
||||
2405:3140:31::/48
|
||||
2405:66c0::/32
|
||||
2405:3140:3a::/48
|
||||
2405:68c0:21::/48
|
||||
2405:6940::/48
|
||||
2405:6f00:c101::/48
|
||||
@ -343,84 +272,17 @@
|
||||
2405:78c0:6e00::/43
|
||||
2405:8a40::/32
|
||||
2405:a900:ffee::/48
|
||||
2405:a900:fffe::/48
|
||||
2405:ad00::/32
|
||||
2405:b7c0::/32
|
||||
2405:d900::/32
|
||||
2405:e000::/32
|
||||
2405:f580::/32
|
||||
2405:f940::/32
|
||||
2406:280::/32
|
||||
2406:840:9000::/44
|
||||
2406:840:e080::/44
|
||||
2406:840:e0cf::/48
|
||||
2406:840:e0e0::/46
|
||||
2406:840:e0e4::/47
|
||||
2406:840:e0e8::/48
|
||||
2406:840:e10f::/48
|
||||
2406:840:e14f::/48
|
||||
2406:840:e260::/48
|
||||
2406:840:e620::/48
|
||||
2406:840:e666::/47
|
||||
2406:840:e720::/44
|
||||
2406:840:e80f::/48
|
||||
2406:840:eb00::/46
|
||||
2406:840:eb04::/47
|
||||
2406:840:eb07::/48
|
||||
2406:840:eb08::/48
|
||||
2406:840:eb0b::/48
|
||||
2406:840:eb0f::/48
|
||||
2406:840:ee40::/47
|
||||
2406:840:ee44::/48
|
||||
2406:840:ee4b::/48
|
||||
2406:840:ee4d::/48
|
||||
2406:840:eee5::/48
|
||||
2406:840:f200::/48
|
||||
2406:840:f203::/48
|
||||
2406:840:f380::/44
|
||||
2406:840:f860::/46
|
||||
2406:840:f868::/47
|
||||
2406:840:f86c::/48
|
||||
2406:840:fc80::/45
|
||||
2406:840:fc88::/48
|
||||
2406:840:fc8f::/48
|
||||
2406:840:fcd0::/48
|
||||
2406:840:fcf0::/46
|
||||
2406:840:fcf4::/47
|
||||
2406:840:fd40::/43
|
||||
2406:840:fd60::/48
|
||||
2406:840:fd67::/48
|
||||
2406:840:fd70::/48
|
||||
2406:840:fd77::/48
|
||||
2406:840:fd80::/48
|
||||
2406:840:fd87::/48
|
||||
2406:840:fd90::/44
|
||||
2406:840:fda0::/43
|
||||
2406:840:fdc0::/44
|
||||
2406:840:fe27::/48
|
||||
2406:840:fe90::/46
|
||||
2406:840:fe94::/48
|
||||
2406:840:fe96::/47
|
||||
2406:840:fe98::/46
|
||||
2406:840:feab::/48
|
||||
2406:840:feac::/48
|
||||
2406:840:feaf::/48
|
||||
2406:840:fec0::/48
|
||||
2406:840:fec4::/48
|
||||
2406:840:feca::/48
|
||||
2406:840:fecc::/47
|
||||
2406:840:fecf::/48
|
||||
2406:840:fed8::/48
|
||||
2406:840:fedf::/48
|
||||
2406:840:fef3::/48
|
||||
2406:840:fef9::/48
|
||||
2406:840:fefb::/48
|
||||
2406:840:fefc::/48
|
||||
2406:840:feff::/48
|
||||
2406:840:9680::/44
|
||||
2406:840:f889::/48
|
||||
2406:1080::/48
|
||||
2406:2700::/32
|
||||
2406:3340::/32
|
||||
2406:3d80::/32
|
||||
2406:4d00::/48
|
||||
2406:52c0::/32
|
||||
2406:5340:6666::/48
|
||||
@ -447,6 +309,7 @@
|
||||
2407:6c40:1210::/48
|
||||
2407:6c40:1500::/48
|
||||
2407:6c40:1600::/40
|
||||
2407:7d00:1::/48
|
||||
2407:8f40:2::/48
|
||||
2407:9f00::/32
|
||||
2407:ad80::/32
|
||||
@ -467,29 +330,19 @@
|
||||
2408:8000:4000::/34
|
||||
2408:8000:8000::/33
|
||||
2408:8001::/32
|
||||
2408:8020::/30
|
||||
2408:8024::/31
|
||||
2408:8024:8000::/33
|
||||
2408:8025::/32
|
||||
2408:8026::/32
|
||||
2408:802a:8000::/33
|
||||
2408:802c::/32
|
||||
2408:803e::/32
|
||||
2408:8056::/32
|
||||
2408:805c::/30
|
||||
2408:8060::/33
|
||||
2408:80c2::/33
|
||||
2408:80c5::/33
|
||||
2408:80ca::/33
|
||||
2408:80ca:8000::/34
|
||||
2408:80da::/33
|
||||
2408:80da:8000::/34
|
||||
2408:80e0:4000::/34
|
||||
2408:80e0:8000::/33
|
||||
2408:80e0:4000::/36
|
||||
2408:80e2::/33
|
||||
2408:80e9:4000::/34
|
||||
2408:80ea:4000::/34
|
||||
2408:80ea:8000::/33
|
||||
2408:80f0:4000::/34
|
||||
2408:80f0:8000::/33
|
||||
2408:80f0:4000::/36
|
||||
2408:80f1::/42
|
||||
2408:80f1:40::/43
|
||||
2408:80f1:70::/44
|
||||
@ -504,8 +357,6 @@
|
||||
2408:80f1:200::/40
|
||||
2408:80f5:4000::/34
|
||||
2408:80f9:4000::/34
|
||||
2408:80fa:4000::/34
|
||||
2408:80fa:8000::/33
|
||||
2408:8120:1::/48
|
||||
2408:8120:2::/48
|
||||
2408:8120:7000::/36
|
||||
@ -728,86 +579,103 @@
|
||||
2408:840c:dd00::/40
|
||||
2408:840c:de00::/40
|
||||
2408:840d::/42
|
||||
2408:840d:40::/43
|
||||
2408:840d:200::/42
|
||||
2408:840d:400::/42
|
||||
2408:840d:440::/43
|
||||
2408:840d:600::/42
|
||||
2408:840d:d00::/42
|
||||
2408:840d:d40::/43
|
||||
2408:840d:f00::/42
|
||||
2408:840d:1100::/42
|
||||
2408:840d:1140::/43
|
||||
2408:840d:1300::/42
|
||||
2408:840d:1a00::/42
|
||||
2408:840d:1a40::/43
|
||||
2408:840d:1c00::/42
|
||||
2408:840d:1e00::/42
|
||||
2408:840d:1e40::/43
|
||||
2408:840d:2000::/42
|
||||
2408:840d:2700::/42
|
||||
2408:840d:2740::/43
|
||||
2408:840d:2900::/42
|
||||
2408:840d:2b00::/42
|
||||
2408:840d:2b40::/43
|
||||
2408:840d:2d00::/42
|
||||
2408:840d:3400::/42
|
||||
2408:840d:3440::/43
|
||||
2408:840d:3600::/42
|
||||
2408:840d:3800::/42
|
||||
2408:840d:3840::/43
|
||||
2408:840d:3a00::/42
|
||||
2408:840d:4e00::/42
|
||||
2408:840d:4e40::/43
|
||||
2408:840d:5000::/42
|
||||
2408:840d:5200::/42
|
||||
2408:840d:5240::/43
|
||||
2408:840d:5400::/42
|
||||
2408:840d:5b00::/42
|
||||
2408:840d:5b40::/43
|
||||
2408:840d:5d00::/42
|
||||
2408:840d:5f00::/42
|
||||
2408:840d:5f40::/43
|
||||
2408:840d:6100::/42
|
||||
2408:840d:6800::/42
|
||||
2408:840d:6840::/43
|
||||
2408:840d:6a00::/42
|
||||
2408:840d:6c00::/42
|
||||
2408:840d:6c40::/43
|
||||
2408:840d:6e00::/42
|
||||
2408:840d:7500::/42
|
||||
2408:840d:7540::/43
|
||||
2408:840d:7700::/42
|
||||
2408:840d:7900::/42
|
||||
2408:840d:7940::/43
|
||||
2408:840d:7b00::/42
|
||||
2408:840d:8200::/42
|
||||
2408:840d:8240::/43
|
||||
2408:840d:8400::/42
|
||||
2408:840d:8600::/42
|
||||
2408:840d:8640::/43
|
||||
2408:840d:8800::/42
|
||||
2408:840d:8f00::/42
|
||||
2408:840d:8f40::/43
|
||||
2408:840d:9100::/42
|
||||
2408:840d:9300::/42
|
||||
2408:840d:9340::/43
|
||||
2408:840d:9500::/42
|
||||
2408:840d:9c00::/42
|
||||
2408:840d:9c40::/43
|
||||
2408:840d:9e00::/42
|
||||
2408:840d:a000::/42
|
||||
2408:840d:a040::/43
|
||||
2408:840d:a200::/42
|
||||
2408:840d:a900::/42
|
||||
2408:840d:a940::/43
|
||||
2408:840d:ab00::/42
|
||||
2408:840d:ad00::/42
|
||||
2408:840d:ad40::/43
|
||||
2408:840d:af00::/42
|
||||
2408:840d:b600::/42
|
||||
2408:840d:b640::/43
|
||||
2408:840d:b800::/42
|
||||
2408:840d:ba00::/42
|
||||
2408:840d:ba40::/43
|
||||
2408:840d:bc00::/42
|
||||
2408:840d:c300::/42
|
||||
2408:840d:c340::/43
|
||||
2408:840d:c500::/42
|
||||
2408:840d:c700::/42
|
||||
2408:840d:c740::/43
|
||||
2408:840d:c900::/42
|
||||
2408:840d:d000::/42
|
||||
2408:840d:d040::/43
|
||||
2408:840d:d200::/42
|
||||
2408:840d:d400::/42
|
||||
2408:840d:d440::/43
|
||||
2408:840d:d600::/42
|
||||
2408:840d:dd00::/42
|
||||
2408:840d:dd40::/43
|
||||
2408:840d:de00::/42
|
||||
2408:840e:dd00::/40
|
||||
2408:840e:de00::/40
|
||||
@ -949,6 +817,7 @@
|
||||
2408:8878::/31
|
||||
2408:887e::/32
|
||||
2408:8906:20::/44
|
||||
2408:8906:40::/43
|
||||
2408:8907:9000::/44
|
||||
2408:890c::/31
|
||||
2408:8912::/31
|
||||
@ -985,13 +854,9 @@
|
||||
2408:8a05:8000::/36
|
||||
2408:8a06::/47
|
||||
2408:8a06:100::/47
|
||||
2408:8a07::/40
|
||||
2408:8a21:4000::/35
|
||||
2408:8a22:9200::/39
|
||||
2408:8a22:9400::/38
|
||||
2408:8a22:9800::/40
|
||||
2408:8a22:9a00::/39
|
||||
2408:8a22:9c00::/38
|
||||
2408:8a22:a000::/37
|
||||
2408:8a22::/35
|
||||
2408:8a23:4000::/34
|
||||
2408:8a24:4000::/34
|
||||
2408:8a26:c000::/34
|
||||
@ -1004,6 +869,7 @@
|
||||
2409:27fb::/48
|
||||
2409:27fc::/48
|
||||
2409:27fe::/33
|
||||
2409:6100::/44
|
||||
2409:8000::/20
|
||||
240a:2000::/40
|
||||
240a:2001:100::/40
|
||||
@ -1038,6 +904,7 @@
|
||||
240a:40c1:2000::/43
|
||||
240a:40c1:4000::/43
|
||||
240a:40c1:6000::/43
|
||||
240a:40c1:8000::/43
|
||||
240a:40c1:a000::/43
|
||||
240a:40c1:c000::/43
|
||||
240a:40c1:e000::/43
|
||||
@ -1073,32 +940,10 @@
|
||||
240b:e004::/30
|
||||
240b:e008::/29
|
||||
240b:e010::/32
|
||||
240c:6::/47
|
||||
240c:6:3::/48
|
||||
240c:f:1::/48
|
||||
240c:4000::/22
|
||||
240c:c000::/20
|
||||
240d:4000::/21
|
||||
240e::/20
|
||||
2602:2e0:ff::/48
|
||||
2602:f46d:6::/48
|
||||
2602:f612::/40
|
||||
2602:f7ee:ee::/48
|
||||
2602:f92a:1314::/48
|
||||
2602:f92a:a440::/44
|
||||
2602:f92a:a46d::/48
|
||||
2602:f92a:a46f::/48
|
||||
2602:f92a:a470::/48
|
||||
2602:f92a:dead::/48
|
||||
2602:f93b:c00::/38
|
||||
2602:f9ba:a8::/48
|
||||
2602:f9f6:450::/48
|
||||
2602:fab0:11::/48
|
||||
2602:feda:1bf::/48
|
||||
2602:feda:1d1::/48
|
||||
2602:feda:1df::/48
|
||||
2602:feda:2d0::/44
|
||||
2602:feda:2f0::/44
|
||||
2605:9d80:8001::/48
|
||||
2605:9d80:8011::/48
|
||||
2605:9d80:8021::/48
|
||||
@ -1114,7 +959,6 @@
|
||||
2605:9d80:9092::/48
|
||||
2804:1e48:9001::/48
|
||||
2804:1e48:9002::/48
|
||||
2a01:f100:1f8::/48
|
||||
2a04:3e00:1002::/48
|
||||
2a04:f580:8010::/47
|
||||
2a04:f580:8090::/48
|
||||
@ -1139,249 +983,26 @@
|
||||
2a04:f580:9270::/48
|
||||
2a04:f580:9280::/48
|
||||
2a04:f580:9290::/48
|
||||
2a06:3600:e000::/40
|
||||
2a06:3600:fb00::/40
|
||||
2a06:3600:fc00::/38
|
||||
2a06:3605::/32
|
||||
2a06:3606::/31
|
||||
2a06:9f81:4600::/43
|
||||
2a06:9f81:4620::/44
|
||||
2a06:9f81:4640::/43
|
||||
2a06:a005:260::/43
|
||||
2a06:a005:280::/43
|
||||
2a06:a005:2a0::/44
|
||||
2a06:a005:a13::/48
|
||||
2a06:a005:1c40::/44
|
||||
2a09:b280:ff81::/48
|
||||
2a09:b280:ff83::/48
|
||||
2a09:b280:ff84::/47
|
||||
2a0a:2840::/30
|
||||
2a0a:2845:aab8::/46
|
||||
2a0a:2846::/48
|
||||
2a0a:6040:ec00::/40
|
||||
2a0a:6044:6600::/40
|
||||
2a0a:d681:e000::/40
|
||||
2a0a:d681:fb00::/40
|
||||
2a0a:d681:fc00::/38
|
||||
2a0b:2542::/48
|
||||
2a0b:4e07:b8::/47
|
||||
2a0c:9a40:8fc1::/48
|
||||
2a0c:9a40:8fc2::/47
|
||||
2a0c:9a40:8fc4::/48
|
||||
2a0c:b641:571::/48
|
||||
2a0c:b641:722::/47
|
||||
2a0e:aa01:1fff::/48
|
||||
2a0e:aa06::/40
|
||||
2a0e:aa06:490::/44
|
||||
2a0e:aa06:500::/44
|
||||
2a0e:aa06:520::/48
|
||||
2a0e:aa06:525::/48
|
||||
2a0e:aa06:541::/48
|
||||
2a0e:aa06:56a::/48
|
||||
2a0e:aa07:e01a::/47
|
||||
2a0e:aa07:e021::/48
|
||||
2a0e:aa07:e030::/48
|
||||
2a0e:aa07:e035::/48
|
||||
2a0e:aa07:e039::/48
|
||||
2a0e:aa07:e151::/48
|
||||
2a0e:aa07:e155::/48
|
||||
2a0e:aa07:e15f::/48
|
||||
2a0e:aa07:e160::/47
|
||||
2a0e:aa07:e162::/48
|
||||
2a0e:aa07:e16a::/48
|
||||
2a0e:aa07:e1a0::/44
|
||||
2a0e:aa07:e210::/48
|
||||
2a0e:aa07:e21c::/47
|
||||
2a0e:aa07:e220::/44
|
||||
2a0e:aa07:f0d0::/46
|
||||
2a0e:aa07:f0d4::/47
|
||||
2a0e:aa07:f0d8::/48
|
||||
2a0e:aa07:f0de::/47
|
||||
2a0e:b107:12b::/48
|
||||
2a0e:b107:272::/48
|
||||
2a0a:d685:1ff::/48
|
||||
2a0a:d685:300::/40
|
||||
2a0b:4340:90::/48
|
||||
2a0b:4340:97::/48
|
||||
2a0d:c7c7:400::/38
|
||||
2a0e:aa06:49f::/48
|
||||
2a0e:b107:740::/44
|
||||
2a0e:b107:c10::/48
|
||||
2a0e:b107:178d::/48
|
||||
2a0e:b107:178e::/48
|
||||
2a0f:1cc5:20::/44
|
||||
2a0f:1cc5:110::/44
|
||||
2a0f:1cc5:130::/44
|
||||
2a0f:1cc5:140::/44
|
||||
2a0f:1cc5:600::/47
|
||||
2a0f:1cc5:603::/48
|
||||
2a0f:1cc5:645::/48
|
||||
2a0f:1cc5:661::/48
|
||||
2a0f:1cc5:662::/48
|
||||
2a0f:1cc5:690::/48
|
||||
2a0f:1cc5:6a0::/48
|
||||
2a0f:1cc5:900::/40
|
||||
2a0f:1cc5:c01::/48
|
||||
2a0f:1cc5:f00::/46
|
||||
2a0f:1cc5:f04::/47
|
||||
2a0f:1cc5:f06::/48
|
||||
2a0f:1cc5:f08::/47
|
||||
2a0f:1cc5:fff::/48
|
||||
2a0f:1cc5:1310::/44
|
||||
2a0f:1cc5:1400::/48
|
||||
2a0f:1cc5:1c00::/47
|
||||
2a0f:1cc5:1c02::/48
|
||||
2a0f:1cc5:1c30::/48
|
||||
2a0f:1cc5:1cc0::/48
|
||||
2a0f:1cc5:1d10::/47
|
||||
2a0f:1cc5:1d14::/48
|
||||
2a0f:1cc5:2d01::/48
|
||||
2a0f:1cc5:2d03::/48
|
||||
2a0f:1cc5:ffff::/48
|
||||
2a0f:1cc6:b110::/47
|
||||
2a0f:2706::/32
|
||||
2a0f:6282:d000::/48
|
||||
2a0f:6284:300::/40
|
||||
2a0f:6284:400::/42
|
||||
2a0f:6284:440::/43
|
||||
2a0f:6284:3000::/39
|
||||
2a0f:6284:4c00::/44
|
||||
2a0f:6284:4c20::/44
|
||||
2a0f:6284:4c30::/48
|
||||
2a0f:6284:4c40::/43
|
||||
2a0f:6284:4c60::/44
|
||||
2a0f:6284:4c80::/43
|
||||
2a0f:6284:4ca0::/44
|
||||
2a0f:6284:4cd0::/44
|
||||
2a0f:6284:4f00::/40
|
||||
2a0f:6284:e000::/35
|
||||
2a0f:7803:f680::/44
|
||||
2a0f:7803:fa21::/48
|
||||
2a0f:7803:fa22::/47
|
||||
2a0f:7803:fa24::/46
|
||||
2a0f:7803:fe81::/48
|
||||
2a0f:7803:fe82::/48
|
||||
2a0f:85c1:ba5::/48
|
||||
2a0f:85c1:bfe::/48
|
||||
2a0f:85c1:ce1::/48
|
||||
2a0f:85c1:cf1::/48
|
||||
2a0f:9400:6110::/48
|
||||
2a0f:9400:7700::/48
|
||||
2a10:ccc6:66cc::/46
|
||||
2a12:cb46::/36
|
||||
2a12:cb47:ffff::/48
|
||||
2a12:f8c3::/36
|
||||
2a13:1800::/48
|
||||
2a13:1800:10::/48
|
||||
2a13:1800:80::/44
|
||||
2a13:1800:300::/44
|
||||
2a0e:b107:2440::/44
|
||||
2a0f:1cc5:1600::/44
|
||||
2a0f:1cc5:4700::/40
|
||||
2a12:cb41:1200::/44
|
||||
2a12:cb41:1300::/43
|
||||
2a12:cb41:13f0::/44
|
||||
2a13:1801:180::/43
|
||||
2a13:a5c3:ff21::/48
|
||||
2a13:a5c3:ff50::/44
|
||||
2a13:a5c7:1800::/40
|
||||
2a13:a5c7:2121::/48
|
||||
2a13:a5c7:2801::/48
|
||||
2a13:a5c7:3100::/40
|
||||
2a13:a5c7:3301::/48
|
||||
2a13:a5c7:3302::/48
|
||||
2a13:a5c7:3304::/48
|
||||
2a13:a5c7:3306::/47
|
||||
2a13:a5c7:3309::/48
|
||||
2a13:aac4:f000::/44
|
||||
2a14:7c0:5103::/48
|
||||
2a14:4c41::/32
|
||||
2a14:67c1:70::/47
|
||||
2a14:67c1:74::/48
|
||||
2a14:67c1:702::/47
|
||||
2a14:67c1:a020::/48
|
||||
2a14:67c1:a023::/48
|
||||
2a14:67c1:a024::/48
|
||||
2a14:67c1:a02a::/48
|
||||
2a14:67c1:a02f::/48
|
||||
2a14:67c1:a040::/47
|
||||
2a14:67c1:a100::/43
|
||||
2a14:67c1:a121::/48
|
||||
2a14:67c1:a123::/48
|
||||
2a14:67c1:a125::/48
|
||||
2a14:67c1:a128::/48
|
||||
2a14:67c1:a144::/48
|
||||
2a14:67c1:b000::/48
|
||||
2a14:67c1:b065::/48
|
||||
2a14:67c1:b066::/48
|
||||
2a14:67c1:b068::/47
|
||||
2a14:67c1:b100::/46
|
||||
2a14:67c1:b105::/48
|
||||
2a14:67c1:b107::/48
|
||||
2a14:67c1:b110::/48
|
||||
2a14:67c1:b130::/46
|
||||
2a14:67c1:b134::/48
|
||||
2a14:67c1:b136::/48
|
||||
2a14:67c1:b141::/48
|
||||
2a14:67c1:b142::/47
|
||||
2a14:67c1:b146::/48
|
||||
2a14:67c1:b460::/44
|
||||
2a14:67c1:b4b0::/44
|
||||
2a14:67c1:b4e0::/44
|
||||
2a14:67c1:b500::/47
|
||||
2a14:67c1:b511::/48
|
||||
2a14:67c1:b514::/48
|
||||
2a14:67c1:b530::/44
|
||||
2a14:67c1:b561::/48
|
||||
2a14:67c1:b563::/48
|
||||
2a14:67c1:b578::/48
|
||||
2a14:67c1:b582::/48
|
||||
2a14:67c1:b586::/47
|
||||
2a14:67c1:b588::/47
|
||||
2a14:67c1:b590::/48
|
||||
2a14:67c1:b599::/48
|
||||
2a14:67c1:b5e0::/44
|
||||
2a14:67c1:c300::/40
|
||||
2a14:67c1:c600::/40
|
||||
2a14:67c2:800::/40
|
||||
2a14:67c2:c32::/48
|
||||
2a14:67c2:c64::/48
|
||||
2a14:67c3:30::/44
|
||||
2a14:67c3:c0::/48
|
||||
2a14:67c3:d0::/44
|
||||
2a14:67c3:520::/44
|
||||
2a14:67c3:e622::/48
|
||||
2a14:7580:730::/44
|
||||
2a14:7580:740::/44
|
||||
2a14:7580:777::/48
|
||||
2a14:7580:d000::/37
|
||||
2a14:7580:d800::/39
|
||||
2a14:7580:df00::/40
|
||||
2a14:7580:fe00::/40
|
||||
2a14:7580:ffe4::/48
|
||||
2a14:7580:fff4::/48
|
||||
2a14:7580:fff7::/48
|
||||
2a14:7580:fffa::/48
|
||||
2a14:7581:ffb::/48
|
||||
2a14:7581:30b6::/48
|
||||
2a14:7581:3100::/40
|
||||
2a14:7581:3401::/48
|
||||
2a14:7583:f224::/48
|
||||
2a14:7583:f228::/48
|
||||
2a14:7583:f22c::/48
|
||||
2a14:7583:f244::/48
|
||||
2a14:7583:f264::/48
|
||||
2a14:7583:f268::/48
|
||||
2a14:7583:f300::/46
|
||||
2a14:7583:f304::/47
|
||||
2a14:7583:f306::/48
|
||||
2a14:7583:f411::/48
|
||||
2a14:7583:f4f0::/47
|
||||
2a14:7583:f4f4::/48
|
||||
2a14:7583:f4fe::/48
|
||||
2a14:7583:f500::/48
|
||||
2a14:7583:f701::/48
|
||||
2a14:7583:f703::/48
|
||||
2a14:7583:f704::/46
|
||||
2a14:7583:f708::/46
|
||||
2a14:7583:f70c::/48
|
||||
2a14:7583:f743::/48
|
||||
2a14:7583:f744::/48
|
||||
2a14:7583:f764::/48
|
||||
2a14:7586:6100::/47
|
||||
2a14:7586:6103::/48
|
||||
2a14:7586:6104::/48
|
||||
2a14:7586:6107::/48
|
||||
2a14:7586:6110::/48
|
||||
2a14:7dc0:506::/48
|
||||
2a13:a5c3:f000::/40
|
||||
2a14:7580:d00::/43
|
||||
2a14:7580:d20::/46
|
||||
2a14:7580:d41::/48
|
||||
2a14:7580:d42::/47
|
||||
2a14:7580:dff::/48
|
||||
2c0f:f7a8:8011::/48
|
||||
2c0f:f7a8:8050::/48
|
||||
2c0f:f7a8:805f::/48
|
||||
@ -1390,5 +1011,4 @@
|
||||
2c0f:f7a8:8211::/48
|
||||
2c0f:f7a8:9020::/48
|
||||
2c0f:f7a8:9041::/48
|
||||
2c0f:f7a8:9211::/48
|
||||
2c0f:f7a8:9220::/48
|
||||
|
||||
@ -1 +1 @@
|
||||
20260424052559
|
||||
20260704060757
|
||||
|
||||
@ -1 +1 @@
|
||||
202604232238
|
||||
202607052256
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
173ng.com
|
||||
18board.com
|
||||
18comic.org
|
||||
18mh.net
|
||||
1984bbs.com
|
||||
1991way.com
|
||||
1e100.net
|
||||
@ -25,6 +26,7 @@
|
||||
2047.one
|
||||
2049bbs.xyz
|
||||
233abc.com
|
||||
24hbook.com
|
||||
24hrs.ca
|
||||
32red.com
|
||||
36rain.com
|
||||
@ -72,6 +74,7 @@
|
||||
9cache.com
|
||||
9gag.com
|
||||
9news.com.au
|
||||
9oju31.launches.appsflyersdk.com
|
||||
a-normal-day.com
|
||||
a248.e.akamai.net
|
||||
aamacau.com
|
||||
@ -86,8 +89,10 @@ about.gitlab.com
|
||||
about.me
|
||||
abplive.com
|
||||
acast.com
|
||||
accountboy.com
|
||||
accountkit.com
|
||||
acevpn.com
|
||||
acg.mn
|
||||
acg.rip
|
||||
acg18.me
|
||||
acgbox.org
|
||||
@ -126,6 +131,7 @@ aiosearch.com
|
||||
aiph.net
|
||||
airconsole.com
|
||||
airitilibrary.com
|
||||
airuniversity.af.edu
|
||||
airvpn.org
|
||||
ait.org.tw
|
||||
aiweiweiblog.com
|
||||
@ -205,13 +211,18 @@ aoxvpn.com
|
||||
apartmentratings.com
|
||||
apartments.com
|
||||
apat1989.org
|
||||
api-gl.lbkrs.com
|
||||
api-secure.recaptcha.net
|
||||
api-verify.recaptcha.net
|
||||
api.ai
|
||||
api.futunn.com
|
||||
api.lbkrs.com
|
||||
api.moomoobull.com
|
||||
api.palworldgame.com
|
||||
api.pureapk.com
|
||||
api.recaptcha.net
|
||||
api.steampowered.com
|
||||
api5.futunn.com
|
||||
apiary.io
|
||||
apigee.com
|
||||
apk.support
|
||||
@ -257,6 +268,7 @@ arte.tv
|
||||
artstation.com
|
||||
arvanstorage.ir
|
||||
arweave.org
|
||||
as.mr
|
||||
asacp.org
|
||||
asiaharvest.org
|
||||
asianage.com
|
||||
@ -321,6 +333,7 @@ bangchen.net
|
||||
bangdream.space
|
||||
bangkokpost.com
|
||||
bangumi.moe
|
||||
bangumi.tv
|
||||
bangyoulater.com
|
||||
bankmobilevibe.com
|
||||
bannedbook.org
|
||||
@ -376,6 +389,7 @@ bettween.com
|
||||
betvictor.com
|
||||
bfnn.org
|
||||
bfsh.hk
|
||||
bgm.tv
|
||||
bgme.me
|
||||
bgvpn.com
|
||||
biblesforamerica.org
|
||||
@ -386,6 +400,7 @@ bigjapanesesex.com
|
||||
bignews.org
|
||||
bigone.com
|
||||
bild.de
|
||||
bilinovel.com
|
||||
biliworld.com
|
||||
binance.com
|
||||
binance.org
|
||||
@ -413,6 +428,7 @@ bl-doujinsouko.com
|
||||
blacked.com
|
||||
blackmagicdesign.com
|
||||
blackvpn.com
|
||||
bleepingcomputer.com
|
||||
blinkx.com
|
||||
blip.tv
|
||||
blockcast.it
|
||||
@ -525,6 +541,7 @@ bookdepository.com
|
||||
books.com.tw
|
||||
booktopia.com.au
|
||||
bookwalker.com.tw
|
||||
boomplay.com
|
||||
bootstrapcdn.com
|
||||
borgenmagazine.com
|
||||
botanwang.com
|
||||
@ -648,6 +665,7 @@ cdjp.org
|
||||
cdn-images.mailchimp.com
|
||||
cdn-telegram.org
|
||||
cdn.arstechnica.net
|
||||
cdn.futustatic.com
|
||||
cdn.jwplayer.com
|
||||
cdn.printfriendly.com
|
||||
cdn.seatguru.com
|
||||
@ -671,6 +689,7 @@ centurys.net
|
||||
certificate-transparency.org
|
||||
certificate.revocationcheck.com
|
||||
cfr.org
|
||||
cg.play-analytics.com
|
||||
cgdepot.org
|
||||
change.org
|
||||
changp.com
|
||||
@ -692,6 +711,7 @@ chenpokongvip.com
|
||||
chenshan20042005.wordpress.com
|
||||
cherrysave.com
|
||||
chhongbi.org
|
||||
chii.in
|
||||
china-mmm.net
|
||||
china-review.com.ua
|
||||
china-week.com
|
||||
@ -736,6 +756,7 @@ chineseupress.com
|
||||
chingcheong.com
|
||||
chinman.net
|
||||
chobit.cc
|
||||
chonglangtv.org
|
||||
chrdnet.com
|
||||
christianfreedom.org
|
||||
christianstudy.com
|
||||
@ -850,6 +871,7 @@ crazypool.org
|
||||
crazyshit.com
|
||||
crbug.com
|
||||
crchina.org
|
||||
creader.com
|
||||
creaders.net
|
||||
creativelab5.com
|
||||
cristyli.com
|
||||
@ -871,6 +893,7 @@ csw.org.uk
|
||||
ct.org.tw
|
||||
ctinets.com
|
||||
ctinews.com
|
||||
ctinsider.com
|
||||
ctitv.com.tw
|
||||
ctowc.org
|
||||
cts.com.tw
|
||||
@ -913,6 +936,7 @@ darpa.mil
|
||||
darrenliuwei.com
|
||||
dashlane.com
|
||||
data-vocabulary.org
|
||||
data18.com
|
||||
daum.net
|
||||
david-kilgour.com
|
||||
dawangidc.com
|
||||
@ -1011,7 +1035,9 @@ doublethinklab.org
|
||||
douchi.space
|
||||
dougscripts.com
|
||||
doujincafe.com
|
||||
dowjones.io
|
||||
download.dappcdn.com
|
||||
download.futunn.com
|
||||
dpool.top
|
||||
dpp.org.tw
|
||||
dpr.info
|
||||
@ -1024,6 +1050,7 @@ dropboxapi.com
|
||||
dropboxusercontent.com
|
||||
dscn.info
|
||||
dstk.dk
|
||||
dt18.com
|
||||
dtiblog.com
|
||||
dtic.mil
|
||||
dubox.com
|
||||
@ -1065,6 +1092,7 @@ ecfa.org.tw
|
||||
ecimg.tw
|
||||
economist.com
|
||||
edgecastcdn.net
|
||||
edigest.hk
|
||||
edubridge.com
|
||||
edx-cdn.org
|
||||
eesti.ee
|
||||
@ -1135,6 +1163,7 @@ expatshield.com
|
||||
expecthim.com
|
||||
expekt.com
|
||||
exploader.net
|
||||
expressnews.com
|
||||
expressvpn.com
|
||||
exrates.me
|
||||
extmatrix.com
|
||||
@ -1143,6 +1172,7 @@ eyny.com
|
||||
f-droid.org
|
||||
f2pool.com
|
||||
f8.com
|
||||
f95zone.to
|
||||
facebook.com
|
||||
facebook.de
|
||||
facebook.design
|
||||
@ -1324,12 +1354,42 @@ funkyimg.com
|
||||
furbo.org
|
||||
furinkan.com
|
||||
furrybar.com
|
||||
futu.cn
|
||||
futu.com
|
||||
futu.link
|
||||
futu0.com
|
||||
futu1.com
|
||||
futu2.com
|
||||
futu3.com
|
||||
futu4.com
|
||||
futu5.com
|
||||
futu6.com
|
||||
futu7.com
|
||||
futu8.com
|
||||
futu9.com
|
||||
futuau.com
|
||||
futubull.cn
|
||||
futuchain.com
|
||||
futuesop.com
|
||||
futufin.com
|
||||
futuhk.com
|
||||
futuhk2.com
|
||||
futuhkapp.com
|
||||
futuhn.com
|
||||
futuholdings.com
|
||||
futuinc.com
|
||||
futuniuniu.com
|
||||
futunn.com
|
||||
futuremessage.org
|
||||
futustatic.com
|
||||
fututrade.com
|
||||
fututrustee.com
|
||||
fw.cm
|
||||
fxcm-chinese.com
|
||||
fxnetworks.com
|
||||
g-area.org
|
||||
g-queen.com
|
||||
g.ai
|
||||
g.co
|
||||
g0v.social
|
||||
g6hentai.com
|
||||
@ -1346,6 +1406,7 @@ gaoming.net
|
||||
gaopi.net
|
||||
gartlive.com
|
||||
garudalinux.org
|
||||
gate.com
|
||||
gate.io
|
||||
gatecoin.com
|
||||
gather.com
|
||||
@ -1361,10 +1422,12 @@ gdaily.org
|
||||
geek-art.net
|
||||
gekikame.com
|
||||
gelbooru.com
|
||||
gencraft.com
|
||||
generated.photos
|
||||
genius.com
|
||||
geph.io
|
||||
get.app
|
||||
get.dappcdn.com
|
||||
get.dev
|
||||
get.how
|
||||
get.page
|
||||
@ -1722,6 +1785,7 @@ gtv.org
|
||||
gtv1.org
|
||||
gu-chu-sum.org
|
||||
guaguass.com
|
||||
guangming.com.my
|
||||
guishan.org
|
||||
gumroad.com
|
||||
gunsamerica.com
|
||||
@ -1775,6 +1839,7 @@ heyuedi.com
|
||||
hicairo.com
|
||||
hiccears.com
|
||||
hidden-advent.org
|
||||
hiddify.com
|
||||
hide.me
|
||||
hideipvpn.com
|
||||
hideman.net
|
||||
@ -1790,6 +1855,7 @@ hindustantimes.com
|
||||
hinet.net
|
||||
hitbtc.com
|
||||
hitomi.la
|
||||
hive.blog
|
||||
hiveon.net
|
||||
hk-pub.com
|
||||
hk.gradconnection.com
|
||||
@ -1810,6 +1876,7 @@ hkgolden.com
|
||||
hkgpao.com
|
||||
hklts.org.hk
|
||||
hkmap.live
|
||||
hkong.hk
|
||||
hkopentv.com
|
||||
hkpeanut.com
|
||||
hkreporter.com
|
||||
@ -1964,6 +2031,7 @@ indiatoday.in
|
||||
indiemerch.com
|
||||
inews-api.tvb.com
|
||||
info-graf.fr
|
||||
infosec.exchange
|
||||
infura.io
|
||||
inherit.live
|
||||
initiativesforchina.org
|
||||
@ -2023,6 +2091,7 @@ itasoftware.com
|
||||
itch.io
|
||||
itemfix.com
|
||||
itiger.com
|
||||
itigerup.com
|
||||
itshidden.com
|
||||
itweet.net
|
||||
iuhrdf.org
|
||||
@ -2102,6 +2171,7 @@ kanshifang.com
|
||||
kantie.org
|
||||
kaotic.com
|
||||
karayou.com
|
||||
kathmandupost.com
|
||||
kawaiikawaii.jp
|
||||
kawase.com
|
||||
kb.monitorware.com
|
||||
@ -2111,6 +2181,7 @@ kenengba.com
|
||||
kepard.com
|
||||
kex.com
|
||||
keycdn.com
|
||||
kfor.com
|
||||
khatrimaza.org
|
||||
kichiku-doujinko.com
|
||||
kik.com
|
||||
@ -2160,12 +2231,15 @@ landofhope.tv
|
||||
lantern.io
|
||||
laogai.org
|
||||
laogairesearch.org
|
||||
laohu8.com
|
||||
laqingdan.net
|
||||
larsgeorge.com
|
||||
lastcombat.com
|
||||
lastfm.es
|
||||
lausan.hk
|
||||
lbank.info
|
||||
lbctrl.com
|
||||
lbkrs.com
|
||||
ldplayer.net
|
||||
ldplayer.tw
|
||||
le-vpn.com
|
||||
@ -2176,7 +2250,9 @@ left21.hk
|
||||
legra.ph
|
||||
leisurepro.com
|
||||
lematin.ch
|
||||
lemonde.fr
|
||||
lenwhite.com
|
||||
leonardo.ai
|
||||
lesoir.be
|
||||
letscorp.net
|
||||
lexica.art
|
||||
@ -2188,6 +2264,7 @@ liaowangxizang.net
|
||||
liberal.org.hk
|
||||
libertysculpturepark.com
|
||||
libertytimes.com.tw
|
||||
libgen.is
|
||||
library-access.sk
|
||||
library.usc.cuhk.edu.hk
|
||||
libredd.it
|
||||
@ -2231,6 +2308,15 @@ localdomain.ws
|
||||
lockestek.com
|
||||
login.target.com
|
||||
logos.com.hk
|
||||
longbridge.cloud
|
||||
longbridge.com
|
||||
longbridge.global
|
||||
longbridge.hk
|
||||
longbridge.sg
|
||||
longbridgeapp.com
|
||||
longbridgehk.com
|
||||
longportapp.cn
|
||||
longportapp.com
|
||||
longtermly.net
|
||||
longtoes.com
|
||||
lookpic.com
|
||||
@ -2283,6 +2369,7 @@ martau.com
|
||||
martsangkagyuofficial.org
|
||||
marxist.net
|
||||
marxists.org
|
||||
mas.to
|
||||
mash.to
|
||||
mastodon.cloud
|
||||
mastodon.host
|
||||
@ -2309,6 +2396,7 @@ meansys.com
|
||||
media.nu.nl
|
||||
mediachinese.com
|
||||
mediafreakcity.com
|
||||
mediamatters.org
|
||||
mediawiki.org
|
||||
medium.com
|
||||
meee.com.tw
|
||||
@ -2346,6 +2434,7 @@ mfxmedia.com
|
||||
mgoon.com
|
||||
mgstage.com
|
||||
mh4u.org
|
||||
mhwindow.org
|
||||
microvpn.com
|
||||
mihua.org
|
||||
mij.rip
|
||||
@ -2388,6 +2477,7 @@ mixx.com
|
||||
mizzmona.com
|
||||
mji.rip
|
||||
mjj.rip
|
||||
mjj.today
|
||||
mjlsh.usc.cuhk.edu.hk
|
||||
mlc.ai
|
||||
mlzs.work
|
||||
@ -2414,6 +2504,9 @@ monitorchina.org
|
||||
monocloud.me
|
||||
monster.com
|
||||
moodyz.com
|
||||
moomoo.com
|
||||
moomooequity.com
|
||||
moomootrustee.com
|
||||
moon.fm
|
||||
moonbbs.com
|
||||
moonbingo.com
|
||||
@ -2425,6 +2518,7 @@ mosavi.io
|
||||
motherless.com
|
||||
movements.org
|
||||
moviefap.com
|
||||
mozilla.ai
|
||||
mpettis.com
|
||||
mpfinance.com
|
||||
mpinews.com
|
||||
@ -2437,6 +2531,7 @@ mthruf.com
|
||||
mubi.com
|
||||
mullvad.net
|
||||
multiply.com
|
||||
music.amazon.com
|
||||
musixmatch.com
|
||||
muzi.com
|
||||
muzi.net
|
||||
@ -2487,6 +2582,7 @@ ncn.org
|
||||
ndi.org
|
||||
nekoslovakia.net
|
||||
neo-miracle.com
|
||||
neodb.social
|
||||
neowin.net
|
||||
nephobox.com
|
||||
netalert.me
|
||||
@ -2510,10 +2606,12 @@ news1.kr
|
||||
news18.com
|
||||
newsancai.com
|
||||
newsblur.com
|
||||
newshub.co.nz
|
||||
newsmax.com
|
||||
newstamago.com
|
||||
newstapa.org
|
||||
newstatesman.com
|
||||
newstimes.com
|
||||
newsweek.com
|
||||
newtalk.tw
|
||||
newthuhole.com
|
||||
@ -2528,6 +2626,8 @@ nflximg.com
|
||||
nflximg.net
|
||||
nflxso.net
|
||||
nflxvideo.net
|
||||
nfsc.press
|
||||
nfscofficial.com
|
||||
nftstorage.link
|
||||
nga.mil
|
||||
nhentai.net
|
||||
@ -2647,6 +2747,13 @@ ooni.io
|
||||
ooni.org
|
||||
open.firstory.me
|
||||
openai.com
|
||||
openapi-quote.longbridge.cn
|
||||
openapi-quote.longbridge.com
|
||||
openapi-trade.longbridge.cn
|
||||
openapi-trade.longbridge.com
|
||||
openapi.futunn.com
|
||||
openapi.longbridge.cn
|
||||
openapi.longbridge.com
|
||||
openart.ai
|
||||
opencritic.com
|
||||
opendemocracy.net
|
||||
@ -2673,6 +2780,7 @@ oursteps.com.au
|
||||
ourtv.hk
|
||||
overcast.fm
|
||||
overdaily.org
|
||||
overdrive.com
|
||||
overplay.net
|
||||
oversea.istarshine.com
|
||||
ovpn.com
|
||||
@ -2685,7 +2793,6 @@ pachosting.com
|
||||
pacom.mil
|
||||
pacopacomama.com
|
||||
page.link
|
||||
pages.dev
|
||||
paimon.moe
|
||||
pancakeswap.finance
|
||||
pandafan.pub
|
||||
@ -2712,6 +2819,7 @@ pbs.org
|
||||
pbworks.com
|
||||
pbxes.com
|
||||
pbxes.org
|
||||
pcgamesn.com
|
||||
pcgamestorrents.com
|
||||
pcij.org
|
||||
pct.org.tw
|
||||
@ -2724,11 +2832,13 @@ pekingduck.org
|
||||
pemulihan.or.id
|
||||
pen.io
|
||||
pendrivelinux.com
|
||||
penguin.com.au
|
||||
pentoy.hk
|
||||
peoplenews.tw
|
||||
peopo.org
|
||||
perfect-privacy.com
|
||||
periscope.tv
|
||||
perma.cc
|
||||
perplexity.ai
|
||||
pewresearch.org
|
||||
phayul.com
|
||||
@ -2900,12 +3010,15 @@ qmp4.com
|
||||
qoos.com
|
||||
qq.co.za
|
||||
qstatus.com
|
||||
qtcard.futunn.com
|
||||
qtcardfthk.futufin.com
|
||||
qtrac.eu
|
||||
questvisual.com
|
||||
quitccp.org
|
||||
quiz.directory
|
||||
quora.com
|
||||
quoracdn.net
|
||||
quote.longbridge.global
|
||||
quran.com
|
||||
qz.com
|
||||
r-pool.net
|
||||
@ -2931,6 +3044,7 @@ raindrop.io
|
||||
raizoji.or.jp
|
||||
rakuten.co.jp
|
||||
ramcity.com.au
|
||||
raphael.app
|
||||
rapidmoviez.com
|
||||
rapidvpn.com
|
||||
rarbgprx.org
|
||||
@ -2973,6 +3087,7 @@ rentry.co
|
||||
renyurenquan.org
|
||||
resilio.com
|
||||
resistchina.org
|
||||
restofworld.org
|
||||
retweetrank.com
|
||||
reuters.com
|
||||
reutersmedia.net
|
||||
@ -3009,6 +3124,8 @@ rthk.org.hk
|
||||
rti.org.tw
|
||||
rti.tw
|
||||
rtm.tnt-ea.com
|
||||
rts.ch
|
||||
rule34.us
|
||||
rule34.xxx
|
||||
rule34video.com
|
||||
rumble.com
|
||||
@ -3043,10 +3160,11 @@ savetibetstore.org
|
||||
saveuighur.org
|
||||
sbme.me
|
||||
sbti.unun.dev
|
||||
scache.vzw.com
|
||||
scache1.vzw.com
|
||||
scache2.vzw.com
|
||||
schema.org
|
||||
schwab.co.uk
|
||||
schwab.com
|
||||
schwab.com.cn
|
||||
schwab.com.hk
|
||||
scmp.com
|
||||
scramble.io
|
||||
scratch.mit.edu
|
||||
@ -3085,6 +3203,7 @@ sexinsex.net
|
||||
sf.net
|
||||
sfshibao.com
|
||||
sftuk.org
|
||||
sgqt0j.launches.appsflyersdk.com
|
||||
shadeyouvpn.com
|
||||
shadowsocks.be
|
||||
shadowsocks.com.hk
|
||||
@ -3109,11 +3228,13 @@ shodanhq.com
|
||||
shooshtime.com
|
||||
shopee.tw
|
||||
shopping.yahoo.co.jp
|
||||
shortconn.im.qcloud.com
|
||||
showwe.tw
|
||||
shutterstock.com
|
||||
shwchurch.org
|
||||
shwchurch3.com
|
||||
sidelinesnews.com
|
||||
sider.ai
|
||||
signal.org
|
||||
silvergatebank.com
|
||||
simbolostwitter.com
|
||||
@ -3123,6 +3244,7 @@ simplecd.org
|
||||
simpleswap.io
|
||||
simplex.chat
|
||||
sina.com.hk
|
||||
sinchew.com.my
|
||||
singaporepools.com.sg
|
||||
singlelogin.se
|
||||
singtao.com
|
||||
@ -3131,6 +3253,7 @@ sinoants.com
|
||||
sinoca.com
|
||||
sinocast.com
|
||||
sinoinsider.com
|
||||
sinyalee.com
|
||||
sipml5.org
|
||||
sis001.com
|
||||
site.new
|
||||
@ -3166,7 +3289,7 @@ socialblade.com
|
||||
socks-proxy.net
|
||||
sockslist.net
|
||||
socrec.org
|
||||
softether-download.com
|
||||
softether.co.jp
|
||||
softether.org
|
||||
softfamous.com
|
||||
softwarebychuck.com
|
||||
@ -3187,6 +3310,7 @@ sorting-algorithms.com
|
||||
sos.org
|
||||
sosad.fun
|
||||
sosreader.com
|
||||
sotwe.com
|
||||
soubory.com
|
||||
soulcaliburhentai.net
|
||||
soundcloud.com
|
||||
@ -3199,6 +3323,7 @@ southmongolia.org
|
||||
southnews.com.tw
|
||||
southpark.cc.com
|
||||
sowers.org.hk
|
||||
spaceforce.mil
|
||||
spaces.hightail.com
|
||||
spacex.com
|
||||
spankbang.com
|
||||
@ -3225,7 +3350,6 @@ springwood.me
|
||||
sproutcore.com
|
||||
squirrelvpn.com
|
||||
ss-link.com
|
||||
ss7.vzw.com
|
||||
ssglobal.co
|
||||
ssl.webpack.de
|
||||
sspanel.net
|
||||
@ -3290,11 +3414,14 @@ supchina.com
|
||||
superpages.com
|
||||
supervpn.net
|
||||
superzooi.com
|
||||
support.futunn.com
|
||||
supremecourt.gov
|
||||
suprememastertv.com
|
||||
surfeasy.com
|
||||
surfeasy.com.au
|
||||
surfshark.com
|
||||
surrenderat20.net
|
||||
suyingtv.com
|
||||
swagbucks.com
|
||||
swapspace.co
|
||||
swissinfo.ch
|
||||
@ -3345,6 +3472,7 @@ tbsec.org
|
||||
tbsn.org
|
||||
tbssqh.org
|
||||
teachparentstech.org
|
||||
techbang.com
|
||||
technews.tw
|
||||
techviz.net
|
||||
teck.in
|
||||
@ -3375,6 +3503,8 @@ tfhub.dev
|
||||
tfiflve.com
|
||||
tg-me.com
|
||||
tg.dev
|
||||
tgstat.com
|
||||
the-sun.com
|
||||
theatlantic.com
|
||||
theatrum-belli.com
|
||||
thebcomplex.com
|
||||
@ -3397,6 +3527,7 @@ thehansindia.com
|
||||
thehindu.com
|
||||
thehun.net
|
||||
theinitium.com
|
||||
theintercept.com
|
||||
thenewslens.com
|
||||
thepiratebay.org
|
||||
theporndude.com
|
||||
@ -3407,6 +3538,7 @@ thestandnews.com
|
||||
thetatoken.org
|
||||
thetibetpost.com
|
||||
thetvdb.com
|
||||
theweek.com
|
||||
thewgo.org
|
||||
thewirechina.com
|
||||
theync.com
|
||||
@ -3474,6 +3606,12 @@ tibettelegraph.com
|
||||
tibettimes.net
|
||||
tibettruth.com
|
||||
tibetwrites.org
|
||||
tigerbbs.cn
|
||||
tigerbbs.com
|
||||
tigerbrokers.com
|
||||
tigerbrokers.net
|
||||
tigerbrokers.nz
|
||||
tigerfintech.com
|
||||
tigervpn.com
|
||||
tiktok.com
|
||||
tiktokcdn-eu.com
|
||||
@ -3536,6 +3674,9 @@ totalvpn.com
|
||||
tou.tv
|
||||
tpi.org.tw
|
||||
tracfone.com
|
||||
trade.futunn.com
|
||||
trade.longportapp.com
|
||||
tradeup.com
|
||||
tradingview.com
|
||||
translate.goog
|
||||
transparency.org
|
||||
@ -3544,6 +3685,7 @@ trendsmap.com
|
||||
tronscan.org
|
||||
trouw.nl
|
||||
trt.net.tr
|
||||
trtworld.com
|
||||
truebuddha-md.org
|
||||
trustwallet.com
|
||||
truthsocial.com
|
||||
@ -3580,6 +3722,7 @@ tv.com
|
||||
tv.jtbc.joins.com
|
||||
tvbanywhere.com
|
||||
tvboxnow.com
|
||||
tvdy1.com
|
||||
tvunetworks.com
|
||||
tw-blog.com
|
||||
tw.jiepang.com
|
||||
@ -3638,6 +3781,7 @@ twitturk.com
|
||||
twitturly.com
|
||||
twkan.com
|
||||
twreporter.org
|
||||
twstalker.com
|
||||
twt.tl
|
||||
twtkr.com
|
||||
twttr.com
|
||||
@ -3666,6 +3810,7 @@ ulop.net
|
||||
ultrasurf.us
|
||||
ultravpn.com
|
||||
ultravpn.fr
|
||||
umap.openstreetmap.fr
|
||||
unblock-us.com
|
||||
unblock.cn.com
|
||||
unblockdmm.com
|
||||
@ -3689,6 +3834,7 @@ upghsbc.com
|
||||
upmedia.mg
|
||||
upornia.com
|
||||
uproxy.org
|
||||
upsangel.com
|
||||
uptodown.com
|
||||
uraban.me
|
||||
urbandictionary.com
|
||||
@ -3736,6 +3882,7 @@ vaticannews.va
|
||||
vatn.org
|
||||
vcf-online.org
|
||||
vcfbuilder.org
|
||||
veed.io
|
||||
vegas.williamhill.com
|
||||
vegasred.com
|
||||
venetianmacao.com
|
||||
@ -3787,6 +3934,7 @@ vod.wwe.com
|
||||
voicettank.org
|
||||
vot.org
|
||||
vovo2000.com
|
||||
vox.com
|
||||
voxer.com
|
||||
vpl.bibliocommons.com
|
||||
vpn.ac
|
||||
@ -3848,6 +3996,7 @@ wanz-factory.com
|
||||
warroom.org
|
||||
waselpro.com
|
||||
washingtonpost.com
|
||||
washingtontimes.com
|
||||
watchinese.com
|
||||
watchmygf.net
|
||||
watchout.tw
|
||||
@ -3856,6 +4005,7 @@ wav.tv
|
||||
waveprotocol.org
|
||||
waybig.com
|
||||
waymo.com
|
||||
wbrks.com
|
||||
wd.bible
|
||||
wealth.com.tw
|
||||
wearn.com
|
||||
@ -3865,6 +4015,7 @@ webmproject.org
|
||||
webpkgcache.com
|
||||
webrtc.org
|
||||
websdr.org
|
||||
webshare.io
|
||||
website.new
|
||||
webwarper.net
|
||||
wechatlawsuit.com
|
||||
@ -3962,12 +4113,14 @@ work2icu.org
|
||||
workatruna.com
|
||||
workerempowerment.org
|
||||
workers.dev
|
||||
worldjournal.com
|
||||
worldvpn.net
|
||||
wowgirls.com
|
||||
wowhead.com
|
||||
wowporn.com
|
||||
woyaolian.org
|
||||
wp.com
|
||||
wplace.live
|
||||
wpoforum.com
|
||||
writesonic.com
|
||||
wsj.com
|
||||
@ -4032,6 +4185,7 @@ xgmyd.com
|
||||
xhamster.com
|
||||
xianjian.tw
|
||||
xiaohexie.com
|
||||
xiaohu8.com
|
||||
xiaolan.me
|
||||
xiaoma.org
|
||||
xiaomi.eu
|
||||
@ -4056,6 +4210,7 @@ xn--noss43i.com
|
||||
xn--oiq.cc
|
||||
xn--p8j9a0d9c9a.xn--q9jyb4c
|
||||
xn--u2u927b.com
|
||||
xn--vuqv2cf7wzyig79c.com
|
||||
xnpool.com
|
||||
xnxx.com
|
||||
xpud.org
|
||||
@ -4064,6 +4219,7 @@ xskywalker.com
|
||||
xt.com
|
||||
xt.pub
|
||||
xtube.com
|
||||
xuan.com.my
|
||||
xuchao.net
|
||||
xuchao.org
|
||||
xuehua.us
|
||||
@ -4102,7 +4258,6 @@ yeyeclub.com
|
||||
yfsp.tv
|
||||
yhcw.net
|
||||
yibaochina.com
|
||||
yidio.com
|
||||
yigeni.com
|
||||
yipub.com
|
||||
yizhihongxing.com
|
||||
@ -4165,11 +4320,14 @@ zh.ecdm.wikia.com
|
||||
zh.pokerstrategy.com
|
||||
zh.wikiquote.org
|
||||
zhangtianliang.com
|
||||
zhangzhehan.net
|
||||
zhanlve.org
|
||||
zhao.1984.city
|
||||
zhengjian.org
|
||||
zhengwunet.org
|
||||
zhenxiang.biz
|
||||
zhijianfengyi.cn
|
||||
zhijianfengyi.com
|
||||
zhizhu.top
|
||||
zhongguo.ca
|
||||
zhongguotese.net
|
||||
|
||||
@ -1 +1 @@
|
||||
202604232238
|
||||
202607052256
|
||||
|
||||
@ -52,12 +52,6 @@ const dns_port = uci.get(uciconfig, uciinfra, 'dns_port') || '5333';
|
||||
|
||||
const ntp_server = uci.get(uciconfig, uciinfra, 'ntp_server') || 'time.apple.com';
|
||||
|
||||
const dashboard_enabled = uci.get(uciconfig, ucimain, 'dashboard_enabled') || '0';
|
||||
const dashboard_port = uci.get(uciconfig, ucimain, 'dashboard_port') || '9090';
|
||||
const dashboard_secret = uci.get(uciconfig, ucimain, 'dashboard_secret');
|
||||
const dashboard_url = uci.get(uciconfig, ucimain, 'dashboard_url');
|
||||
const dashboard_detour = uci.get(uciconfig, ucimain, 'dashboard_detour');
|
||||
|
||||
const ipv6_support = uci.get(uciconfig, ucimain, 'ipv6_support') || '0';
|
||||
|
||||
let main_node, main_udp_node, dedicated_udp_node, default_outbound, default_outbound_dns,
|
||||
@ -127,10 +121,10 @@ if (match(proxy_mode, /redirect/)) {
|
||||
self_mark = uci.get(uciconfig, 'infra', 'self_mark') || '100';
|
||||
redirect_port = uci.get(uciconfig, 'infra', 'redirect_port') || '5331';
|
||||
}
|
||||
if (match(proxy_mode), /tproxy/)
|
||||
if (match(proxy_mode, /tproxy/))
|
||||
if (main_udp_node !== 'nil' || routing_mode === 'custom')
|
||||
tproxy_port = uci.get(uciconfig, 'infra', 'tproxy_port') || '5332';
|
||||
if (match(proxy_mode), /tun/) {
|
||||
if (match(proxy_mode, /tun/)) {
|
||||
tun_name = uci.get(uciconfig, uciinfra, 'tun_name') || 'singtun0';
|
||||
tun_addr4 = uci.get(uciconfig, uciinfra, 'tun_addr4') || '172.19.0.1/30';
|
||||
tun_addr6 = uci.get(uciconfig, uciinfra, 'tun_addr6') || 'fdfe:dcba:9876::1/126';
|
||||
@ -930,7 +924,6 @@ if (!isEmpty(main_node)) {
|
||||
user: cfg.user,
|
||||
rule_set: get_ruleset(cfg.rule_set),
|
||||
rule_set_ip_cidr_match_source: strToBool(cfg.rule_set_ip_cidr_match_source),
|
||||
rule_set_ip_cidr_accept_empty: strToBool(cfg.rule_set_ip_cidr_accept_empty),
|
||||
invert: strToBool(cfg.invert),
|
||||
action: cfg.action,
|
||||
outbound: get_outbound(cfg.outbound),
|
||||
@ -976,28 +969,6 @@ if (routing_mode in ['bypass_mainland_china', 'custom']) {
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
if (dashboard_enabled === '1') {
|
||||
if (!config.experimental)
|
||||
config.experimental = {};
|
||||
|
||||
let download_detour = 'direct-out';
|
||||
if (!isEmpty(dashboard_detour)) {
|
||||
if (dashboard_detour in ['direct-out', 'main-out'])
|
||||
download_detour = dashboard_detour;
|
||||
else
|
||||
download_detour = get_outbound(dashboard_detour);
|
||||
}
|
||||
|
||||
config.experimental.clash_api = {
|
||||
external_controller: '0.0.0.0:' + dashboard_port,
|
||||
secret: !isEmpty(dashboard_secret) ? dashboard_secret : null,
|
||||
external_ui: RUN_DIR + '/ui',
|
||||
external_ui_download_url: !isEmpty(dashboard_url) ? dashboard_url :
|
||||
'https://github.com/Zephyruso/zashboard/releases/latest/download/dist-cdn.zip',
|
||||
external_ui_download_detour: download_detour,
|
||||
};
|
||||
}
|
||||
/* Experimental end */
|
||||
|
||||
system('mkdir -p ' + RUN_DIR);
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
'use strict';
|
||||
|
||||
import { cursor } from 'uci';
|
||||
import { isEmpty, parseURL } from 'homeproxy';
|
||||
import { isEmpty, parseURL, validation } from 'homeproxy';
|
||||
|
||||
const uci = cursor();
|
||||
|
||||
|
||||
@ -137,12 +137,7 @@ const methods = {
|
||||
break;
|
||||
}
|
||||
|
||||
let start = clock(true);
|
||||
let ret = system(`/usr/bin/wget --spider -qT3 ${url} 2>"/dev/null"`, 3100);
|
||||
let end = clock(true);
|
||||
let elapsed = (end[0] - start[0]) * 1000 + int((end[1] - start[1]) / 1000000);
|
||||
|
||||
return { result: (ret === 0), elapsed: elapsed };
|
||||
return { result: (system(`/usr/bin/wget --spider -qT3 ${url} 2>"/dev/null"`, 3100) === 0) };
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-passwall2
|
||||
PKG_VERSION:=26.7.16
|
||||
PKG_RELEASE:=64
|
||||
PKG_RELEASE:=65
|
||||
PKG_PO_VERSION:=$(PKG_VERSION)
|
||||
|
||||
PKG_CONFIG_DEPENDS:= \
|
||||
|
||||
@ -70,6 +70,7 @@ function index()
|
||||
entry({"admin", "services", appname, "get_now_use_node"}, call("get_now_use_node")).leaf = true
|
||||
entry({"admin", "services", appname, "get_redir_log"}, call("get_redir_log")).leaf = true
|
||||
entry({"admin", "services", appname, "get_socks_log"}, call("get_socks_log")).leaf = true
|
||||
entry({"admin", "services", appname, "get_acl_log"}, call("get_acl_log")).leaf = true
|
||||
entry({"admin", "services", appname, "get_log"}, call("get_log")).leaf = true
|
||||
entry({"admin", "services", appname, "clear_log"}, call("clear_log")).leaf = true
|
||||
entry({"admin", "services", appname, "index_status"}, call("index_status")).leaf = true
|
||||
@ -322,6 +323,18 @@ function get_socks_log()
|
||||
end
|
||||
end
|
||||
|
||||
function get_acl_log()
|
||||
local id = http.formvalue("id")
|
||||
local path = "/tmp/log/passwall2_acl_" .. id .. ".log"
|
||||
if nixio.fs.access(path) then
|
||||
local content = luci.sys.exec("tail -n 5000 '" .. path .. "'")
|
||||
content = content:gsub("\n", "<br />")
|
||||
http.write(content)
|
||||
else
|
||||
http.write(string.format("<script>alert('%s');window.close();</script>", i18n.translate("Not enabled log")))
|
||||
end
|
||||
end
|
||||
|
||||
function get_log()
|
||||
-- luci.sys.exec("[ -f /tmp/log/passwall2.log ] && sed '1!G;h;$!d' /tmp/log/passwall2.log > /tmp/log/passwall2_show.log")
|
||||
http.write(luci.sys.exec("[ -f '/tmp/log/passwall2.log' ] && cat /tmp/log/passwall2.log"))
|
||||
|
||||
@ -78,6 +78,33 @@ o = s:option(Value, "remarks", translate("Remarks"))
|
||||
o.default = arg[1]
|
||||
o.rmempty = false
|
||||
|
||||
---- Log
|
||||
o = s:option(Flag, "log", translate("Enable Log"))
|
||||
o.default = 0
|
||||
o.rmempty = false
|
||||
|
||||
o = s:option(ListValue, "loglevel", translate("Log Level"))
|
||||
o.default = "warn"
|
||||
o:value("debug", "Debug")
|
||||
o:value("info", "Info")
|
||||
o:value("warn", "Warning")
|
||||
o:value("error", "Error")
|
||||
o:depends("log", "1")
|
||||
|
||||
o = s:option(DummyValue, "_acl_log", translate("Log File"))
|
||||
o.rawhtml = true
|
||||
o.cfgvalue = function(t, n)
|
||||
local log_path = "/tmp/log/passwall2_acl_" .. arg[1] .. ".log"
|
||||
local log_url = api.url("get_acl_log") .. "?id=" .. arg[1]
|
||||
return string.format(
|
||||
'<code>%s</code> <input class="btn cbi-button cbi-button-apply" type="button" value="%s" onclick="window.open(\'%s\', \'_blank\')" />',
|
||||
log_path,
|
||||
translate("View Log"),
|
||||
log_url
|
||||
)
|
||||
end
|
||||
o:depends("log", "1")
|
||||
|
||||
o = s:option(Value, "interface", translate("Source Interface"))
|
||||
o:value("", translate("All"))
|
||||
local iface = api.get_network_devices()
|
||||
|
||||
@ -1021,11 +1021,15 @@ acl_app() {
|
||||
dnsmasq_port=${GLOBAL_DNSMASQ_PORT:-11400}
|
||||
for item in $items; do
|
||||
index=$(expr $index + 1)
|
||||
local enabled sid remarks sources interface tcp_no_redir_ports udp_no_redir_ports node direct_dns_query_strategy remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy
|
||||
local enabled sid remarks sources interface tcp_no_redir_ports udp_no_redir_ports node direct_dns_query_strategy remote_dns_protocol remote_dns remote_dns_doh remote_dns_client_ip remote_dns_detour remote_fakedns remote_dns_query_strategy log loglevel log_file
|
||||
local _ip _mac _iprange _ipset _ip_or_mac source_list config_file
|
||||
local sid=$(uci -q show "${CONFIG}.${item}" | grep "=acl_rule" | awk -F '=' '{print $1}' | awk -F '.' '{print $2}')
|
||||
[ "$(config_n_get $sid enabled)" = "1" ] || continue
|
||||
eval $(uci -q show "${CONFIG}.${item}" | cut -d'.' -sf 3-)
|
||||
log=${log:-0}
|
||||
loglevel=${loglevel:-warn}
|
||||
log_file="/dev/null"
|
||||
[ "${log}" = "1" ] && log_file="/tmp/log/passwall2_acl_${sid}.log"
|
||||
|
||||
if [ -n "${sources}" ]; then
|
||||
for s in $sources; do
|
||||
@ -1121,7 +1125,8 @@ acl_app() {
|
||||
direct_dns_query_strategy=${direct_dns_query_strategy} \
|
||||
remote_dns_protocol=${remote_dns_protocol} remote_dns_tcp_server=${remote_dns} remote_dns_udp_server=${remote_dns} remote_dns_doh="${remote_dns}" \
|
||||
remote_dns_client_ip=${remote_dns_client_ip} remote_dns_detour=${remote_dns_detour} remote_fakedns=${remote_fakedns} remote_dns_query_strategy=${remote_dns_query_strategy} \
|
||||
config_file=${config_file}
|
||||
config_file=${config_file} \
|
||||
log_file="${log_file}" loglevel="${loglevel}"
|
||||
local status=$?
|
||||
if [ "$status" != 0 ]; then
|
||||
log_i18n 2 "[%s] process %s error, skip this transparent proxy!" "${remarks}" "${config_file}"
|
||||
|
||||
@ -194,7 +194,7 @@ return view.extend({
|
||||
o.value('0', _('Disable'))
|
||||
if (features.hasXTFULLCONENAT || features.hasNFTFULLCONENAT) {
|
||||
o.value('1', _('FULLCONENAT'));
|
||||
o.value('2', _('Boardcom Fullcone NAT1'));
|
||||
o.value('2', _('Broadcom Fullcone NAT1'));
|
||||
}
|
||||
o.default = '0';
|
||||
o.rmempty = false;
|
||||
|
||||
@ -5,8 +5,8 @@ msgstr "Content-Type: text/plain; charset=UTF-8"
|
||||
msgid "Acceleration Status"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:182
|
||||
msgid "Boardcom fullcone"
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:197
|
||||
msgid "Broadcom Fullcone NAT1"
|
||||
msgstr ""
|
||||
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:151
|
||||
|
||||
@ -12,10 +12,6 @@ msgstr ""
|
||||
msgid "Acceleration Status"
|
||||
msgstr "加速状态"
|
||||
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:182
|
||||
msgid "Boardcom fullcone"
|
||||
msgstr "Boardcom fullcone"
|
||||
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:151
|
||||
msgid "Bridge Acceleration"
|
||||
msgstr "桥接加速"
|
||||
@ -93,9 +89,9 @@ msgstr "全锥形 NAT1"
|
||||
msgid "FULLCONENAT"
|
||||
msgstr "兼容模式全锥形 NAT1"
|
||||
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:177
|
||||
msgid "Boardcom Fullcone NAT1"
|
||||
msgstr "高性能 Boardcom 全锥形 NAT1"
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:197
|
||||
msgid "Broadcom Fullcone NAT1"
|
||||
msgstr "高性能 Broadcom 全锥形 NAT1"
|
||||
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:178
|
||||
msgid "Full cone NAT (NAT1) can improve gaming performance effectively."
|
||||
@ -175,7 +171,3 @@ msgstr "Turbo ACC 网络加速设置"
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:181
|
||||
msgid "xt_FULLCONENAT"
|
||||
msgstr "兼容模式"
|
||||
|
||||
#: applications/luci-app-turboacc/htdocs/luci-static/resources/view/turboacc.js:182
|
||||
msgid "Boardcom fullcone"
|
||||
msgstr "高性能模式"
|
||||
|
||||
@ -104,9 +104,9 @@ local methods = {
|
||||
(readfile("/sys/module/nft_fullcone/refcnt") or "0") ~= "0" then
|
||||
fctype = "nft_FULLCONENAT"
|
||||
elseif sys.call("nft list ruleset 2>/dev/null | grep -q 'masquerade fullcone'") == 0 then
|
||||
fctype = "Boardcom Fullcone NAT1"
|
||||
fctype = "Broadcom Fullcone NAT1"
|
||||
elseif sys.call("iptables -t nat -L zone_wan_postrouting 2>/dev/null | grep -q fullcone") == 0 then
|
||||
fctype = "Boardcom Fullcone NAT1"
|
||||
fctype = "Broadcom Fullcone NAT1"
|
||||
end
|
||||
print(json.stringify({ type = fctype }))
|
||||
end
|
||||
|
||||
@ -45,7 +45,7 @@ label, fixing a typo, adjusting a translation string).
|
||||
- **S4** — Memory has a ceiling. → references/server.md
|
||||
- **L1** — Kill render-blocking requests. → references/loading.md
|
||||
- **L2** — Download once, cache forever. → references/loading.md
|
||||
- **L3** — Ship compressed. → references/loading.md
|
||||
- **L3** — Make identity-transfer assets compact. → references/loading.md
|
||||
- **R1** — Animate compositor properties only. → references/runtime.md
|
||||
- **R2** — JS must not force synchronous layout. → references/runtime.md
|
||||
- **R3** — Accessibility is functionality. → references/runtime.md
|
||||
|
||||
@ -11,8 +11,12 @@ address, so they stay local).
|
||||
|
||||
| Metric | Budget | Track | Source |
|
||||
|---|---|---|---|
|
||||
| main.css (gzip-transferred) | ≤ 30 KB | size | measured 2026-07 (28 KB) |
|
||||
| Per-page cold transfer (all theme assets, gzip) | ≤ 60 KB | size | sum of current gzip sizes + headroom |
|
||||
| main.css (identity/raw) | ≤ 190 KB | size | production build, 2026-07 (183,820 B) |
|
||||
| login.css (identity/raw) | ≤ 12 KB | size | production build, 2026-07 (10,935 B, token-pruned) |
|
||||
| menu-aurora.js (identity/raw) | ≤ 20 KB | size | production build, 2026-07 (19,100 B) |
|
||||
| Default logo (identity/raw) | ≤ 16 KB | size | production build, 2026-07 (15,057 B) |
|
||||
| Core admin cold theme assets (identity/raw) | ≤ 250 KB | size | main CSS + menu JS + default font + logo, 2026-07 (241,557 B) |
|
||||
| Login cold theme assets, excluding configured background (identity/raw) | ≤ 55 KB | size | login CSS + default font + logo, 2026-07 (49,572 B) |
|
||||
| Blocking requests before first paint | ≤ 4 | count | current waterfall |
|
||||
| Repeat-visit asset requests | ≈ 0 | count | target state; package-built CSS/JS URLs are versioned, but long-lived cache headers still need live verification |
|
||||
| TTFB, login page (device) | proposed: ≤ 130 ms | latency | local device baseline, 2026-07 |
|
||||
@ -25,16 +29,22 @@ Budget revisions require a new baseline entry under `../baselines/`.
|
||||
## Optimization ledger
|
||||
|
||||
### Landed
|
||||
(compositor animation rework; mega-menu idle pre-measurement; on-demand
|
||||
patches; `font-display: swap`)
|
||||
|
||||
- Compositor animation rework; mega-menu idle pre-measurement; on-demand
|
||||
patches; `font-display: swap` and inline `@font-face` CSS.
|
||||
- Tailwind `source(none)`, login-only Preflight removal, native scrollbar
|
||||
styling, and local fade animation; unused CSS plugins removed.
|
||||
- Shared SVG custom properties prevent repeated mask payloads.
|
||||
- Terser compression/mangling with LuCI loader directives preserved.
|
||||
- Default logo raster resized inside its compatibility SVG wrapper.
|
||||
- Login template reuses its board/UCI reads when including `header.ut`.
|
||||
- Package-root `.DS_Store` metadata removed and covered by a regression test.
|
||||
- login.css pruned to its reachable custom properties at build time (the
|
||||
shared token sheet is admin-sized; the login page consumes a fraction).
|
||||
|
||||
### Pending
|
||||
| Item | Principle | Estimated gain |
|
||||
|---|---|---|
|
||||
| Precompressed `.gz` assets | S1+L3 | ~260 KB → ~35 KB cold |
|
||||
| Terser `compress`+`mangle` in `vite.config.ts` | L3 | ~20 KB → ~10 KB |
|
||||
| Inline `@font-face` + preload woff2 | L1 | −1 blocking RTT |
|
||||
| SVGO `logo.svg` | L3 | 45 KB → est. < 20 KB |
|
||||
| Long-lived cache headers for versioned CSS/JS | L2 | after LuCI build-time `?v=$(PKG_VERSION)`, kills per-click 304s if headers permit disk/memory cache reuse |
|
||||
| `defer` head scripts | L1 | needs on-device timing verification |
|
||||
|
||||
@ -50,6 +60,9 @@ patches; `font-display: swap`)
|
||||
`/luci-static/aurora/login.css?v=1.0.7`. Do not re-propose manual
|
||||
cache-versioning for these links unless inspecting the installed package
|
||||
or live HTML proves the rewrite did not happen.
|
||||
- **No gzip on the target uhttpd** — budgets and the bench harness use raw
|
||||
identity bytes. Do not add precompressed sidecars unless the deployed HTTP
|
||||
server is changed and live response headers prove negotiation works.
|
||||
|
||||
### Accepted exceptions
|
||||
|
||||
|
||||
@ -54,21 +54,23 @@ served from disk/memory cache).
|
||||
|
||||
**Quantify.** Repeat-visit request count.
|
||||
|
||||
## L3 — Ship compressed
|
||||
## L3 — Make identity-transfer assets compact
|
||||
|
||||
**Why.** An uncompressed asset costs both wire time on the slow link (see
|
||||
The Wire) and CPU on the slow server (see server.md S1) — compression is the
|
||||
one change that helps both planes at once.
|
||||
**Why.** This deployment's stock `uhttpd` serves identity responses even when
|
||||
the browser advertises gzip. Raw output size is therefore both flash cost and
|
||||
wire cost; minification and dead-code removal must do the work that transfer
|
||||
compression normally would.
|
||||
|
||||
**Do / Don't.** Precompress at build time so `uhttpd` can serve `foo.gz`
|
||||
verbatim (cross-reference server.md S1). Run shipped SVGs through SVGO
|
||||
(example: aurora's `logo.svg`, which doubles as the favicon, ships at 45 KB —
|
||||
an unoptimized SVG is a common place for dead editor metadata to hide).
|
||||
Choose sane image formats (WebP/AVIF over unoptimized PNG/JPEG) for anything
|
||||
that isn't vector.
|
||||
**Do / Don't.** Disable broad Tailwind content scanning when all utilities are
|
||||
declared with `@apply`; omit Preflight from isolated entries that only need a
|
||||
small reset; remove unused plugins; minify and mangle JS while preserving
|
||||
LuCI's loader directives; define repeated SVG data URLs once as custom
|
||||
properties. Resize or re-encode raster assets at their real display needs and
|
||||
keep generated metadata out of package roots. Do not ship precompressed
|
||||
sidecars when the target server cannot negotiate them.
|
||||
|
||||
**Verify.** `bench.mjs`'s gzip-negotiated rows; `du` on the build output
|
||||
directory to catch anything that grew.
|
||||
**Verify.** `bench.mjs`'s identity rows, automated raw-size budgets, and `du`
|
||||
on the build output directory to catch anything that grew.
|
||||
|
||||
**Quantify.** Per-page transferred bytes (cold load); individual asset
|
||||
sizes.
|
||||
@ -79,5 +81,5 @@ sizes.
|
||||
|---|---|---|
|
||||
| Blocking requests before first paint | DevTools Network (Slow 4G) | `aurora-budgets.md` |
|
||||
| Repeat-visit request count | DevTools Network | `aurora-budgets.md` |
|
||||
| Transferred bytes (cold, gzip) | `bench.mjs` | `aurora-budgets.md` |
|
||||
| Transferred bytes (cold, identity/raw) | `bench.mjs` | `aurora-budgets.md` |
|
||||
| Individual asset size | `du` / `bench.mjs` | `aurora-budgets.md` |
|
||||
|
||||
@ -23,15 +23,19 @@ or returns an HTTP error status mid-run, it aborts with a one-line message
|
||||
naming the target that failed — it does not silently skip it.
|
||||
|
||||
**What it measures.** `BENCH_RUNS` (default 10) samples per target via
|
||||
`curl`, reduced to a median, against 5 fixed targets:
|
||||
`curl`, reduced to a median, against 6 fixed targets. All static assets use
|
||||
identity transfer because the target `uhttpd` cannot serve gzip:
|
||||
|
||||
1. `GET /cgi-bin/luci` (the login page) — the honest unauthenticated target:
|
||||
it's the one page a bench script can hit without a session, and TTFB
|
||||
comparisons must compare like with like.
|
||||
2. `main.css`, identity (no `Accept-Encoding`)
|
||||
3. `main.css`, gzip-negotiated
|
||||
4. `login.css`, gzip-negotiated
|
||||
5. the theme's menu script, gzip-negotiated (example: aurora's `menu-aurora.js`)
|
||||
A 403 is accepted for this target only because some LuCI builds render the
|
||||
login form with that status; other error responses still abort the run.
|
||||
2. `main.css`, identity
|
||||
3. `login.css`, identity
|
||||
4. the theme's menu script, identity (example: aurora's `menu-aurora.js`)
|
||||
5. the default first-paint font, identity
|
||||
6. the default logo, identity
|
||||
|
||||
Per target it reports TTFB, total time, transferred bytes, and HTTP status,
|
||||
summarized as a Markdown table on stdout.
|
||||
|
||||
@ -11,20 +11,18 @@ same 1–2 cores and 64–512 MB of RAM as the rest of OpenWrt. Nothing here is
|
||||
is already slow. Anything that can be computed once, at build time, should
|
||||
never run again inside a request handler.
|
||||
|
||||
**Do / Don't.** Precompress assets at build time and let `uhttpd` serve the
|
||||
precompressed file verbatim when the client sends `Accept-Encoding: gzip` —
|
||||
this makes the request-time cost *decrease*, since `uhttpd` has no dynamic
|
||||
compression of its own. Generate derived CSS/config/tokens at build time,
|
||||
never per-request (example: aurora's generated `_tokens.css`, built from
|
||||
`.dev/tokens/` and never touched at runtime). When a genuinely per-request
|
||||
computation is unavoidable, record the accepted exception with its rationale
|
||||
in the theme budget sheet (`aurora-budgets.md`) so a future session doesn't re-litigate it
|
||||
(example: aurora's per-request `lsdir()` patch discovery in `header.ut` — one
|
||||
cheap readdir, measured negligible and accepted as-is).
|
||||
**Do / Don't.** Generate and minify derived CSS, JS, config, tokens and image
|
||||
assets at build time, never per-request (example: aurora's generated
|
||||
`_tokens.css`). This deployment uses stock `uhttpd` without gzip support, so
|
||||
raw file size is the wire size: do not add `.gz` files that the server will
|
||||
never select. When a genuinely per-request computation is unavoidable,
|
||||
record the accepted exception with its rationale in the theme budget sheet
|
||||
(`aurora-budgets.md`) so a future session doesn't re-litigate it (example:
|
||||
aurora's per-request `lsdir()` patch discovery in `header.ut` — one cheap
|
||||
readdir, measured negligible and accepted as-is).
|
||||
|
||||
**Verify.** `top` on the device shows no request-time compression process;
|
||||
`curl -H 'Accept-Encoding: gzip' -w '%{size_download}'` against the asset
|
||||
shows the byte drop versus an uncompressed request.
|
||||
**Verify.** A production build is deterministic and already minified; the
|
||||
identity-transfer rows in `bench.mjs` match the files' raw byte sizes.
|
||||
|
||||
**Quantify.** Transferred bytes; package/flash size delta.
|
||||
|
||||
@ -55,10 +53,10 @@ the branch against the base.
|
||||
slow link, and it costs it on every page view — this is not "just bandwidth,"
|
||||
it's compute on the same constrained cores serving the request.
|
||||
|
||||
**Do / Don't.** Set (and respect) a per-page transferred-bytes budget.
|
||||
Prefer gzip-negotiated transfer over raw bytes wherever the client supports
|
||||
it (example: aurora's `main.css` is 211,725 B raw but ~28 KB gzip — always
|
||||
measure and budget the negotiated size, not the raw one).
|
||||
**Do / Don't.** Set (and respect) a per-page transferred-bytes budget. On this
|
||||
stock `uhttpd` deployment, measure identity responses and optimize the raw
|
||||
files themselves: restrict Tailwind source detection, remove unused framework
|
||||
layers/plugins, minify JS, deduplicate inlined SVGs, and resize raster assets.
|
||||
|
||||
**Verify.** The `bench.mjs` bytes column, compared against the theme's own
|
||||
budget table.
|
||||
@ -85,5 +83,5 @@ after the change.
|
||||
|---|---|---|
|
||||
| TTFB | `bench.mjs` | `aurora-budgets.md` |
|
||||
| ubus/uci/fs call count | diff review | `aurora-budgets.md` |
|
||||
| Transferred bytes (gzip) | `bench.mjs` | `aurora-budgets.md` |
|
||||
| Transferred bytes (identity/raw) | `bench.mjs` | `aurora-budgets.md` |
|
||||
| uhttpd VmRSS | `bench.mjs` device report | `aurora-budgets.md` |
|
||||
|
||||
@ -20,8 +20,12 @@ export function parseCurlMetrics(line) {
|
||||
};
|
||||
}
|
||||
|
||||
export function assertOkStatus(sample) {
|
||||
if (!Number.isInteger(sample.status) || sample.status < 200 || sample.status >= 400) {
|
||||
export function assertOkStatus(sample, allowedStatuses = []) {
|
||||
if (
|
||||
!Number.isInteger(sample.status) ||
|
||||
(!(sample.status >= 200 && sample.status < 400) &&
|
||||
!allowedStatuses.includes(sample.status))
|
||||
) {
|
||||
throw new Error(`HTTP ${sample.status}`);
|
||||
}
|
||||
return sample;
|
||||
|
||||
@ -44,6 +44,11 @@ test("assertOkStatus: rejects 404 samples", () => {
|
||||
);
|
||||
});
|
||||
|
||||
test("assertOkStatus: permits an explicitly expected login-page 403", () => {
|
||||
const sample = parseCurlMetrics("0.010000\t0.020000\t5788\t403\n");
|
||||
assert.equal(assertOkStatus(sample, [403]), sample);
|
||||
});
|
||||
|
||||
test("summarize: medians of times, first sample's bytes/status, run count", () => {
|
||||
const s = summarize([
|
||||
{ ttfbMs: 50, totalMs: 200, bytes: 1000, status: 200 },
|
||||
|
||||
@ -49,11 +49,19 @@ const CURL_FORMAT = "%{time_starttransfer}\\t%{time_total}\\t%{size_download}\\t
|
||||
// unauthenticated bench can measure, and TTFB comparisons must compare like
|
||||
// with like (spec: error handling / edge cases).
|
||||
const TARGETS = [
|
||||
{ label: "GET /cgi-bin/luci (login page)", path: "/cgi-bin/luci" },
|
||||
{
|
||||
label: "GET /cgi-bin/luci (login page)",
|
||||
path: "/cgi-bin/luci",
|
||||
allowedStatuses: [403],
|
||||
},
|
||||
{ label: "main.css (identity)", path: "/luci-static/aurora/main.css" },
|
||||
{ label: "main.css (gzip negotiated)", path: "/luci-static/aurora/main.css", gzip: true },
|
||||
{ label: "login.css (gzip negotiated)", path: "/luci-static/aurora/login.css", gzip: true },
|
||||
{ label: "menu-aurora.js (gzip negotiated)", path: "/luci-static/resources/menu-aurora.js", gzip: true },
|
||||
{ label: "login.css (identity)", path: "/luci-static/aurora/login.css" },
|
||||
{ label: "menu-aurora.js (identity)", path: "/luci-static/resources/menu-aurora.js" },
|
||||
{
|
||||
label: "Lato regular font (identity)",
|
||||
path: "/luci-static/aurora/fonts/lato-v24-latin-regular.woff2",
|
||||
},
|
||||
{ label: "logo.svg (identity)", path: "/luci-static/aurora/images/logo.svg" },
|
||||
];
|
||||
|
||||
async function measureOnce(target) {
|
||||
@ -63,10 +71,12 @@ async function measureOnce(target) {
|
||||
"-w", CURL_FORMAT,
|
||||
"--max-time", "30",
|
||||
];
|
||||
if (target.gzip) args.push("-H", "Accept-Encoding: gzip");
|
||||
args.push(`${HOST}${target.path}`);
|
||||
const { stdout } = await execFileAsync("curl", args);
|
||||
return assertOkStatus(parseCurlMetrics(stdout));
|
||||
return assertOkStatus(
|
||||
parseCurlMetrics(stdout),
|
||||
target.allowedStatuses ?? [],
|
||||
);
|
||||
}
|
||||
|
||||
async function measureTarget(target) {
|
||||
|
||||
@ -103,8 +103,8 @@ This will be compiled to standard CSS that works in all browsers.
|
||||
|
||||
The theme has two independent Tailwind CSS v4 entry points, both sourced from `.dev/src/media/`:
|
||||
|
||||
- **`main.css`** — the LuCI admin UI. It contains no rules of its own; it's a pure import manifest that pulls in (in order) `_tokens.css` (OKLCH theme tokens, mapped via `@theme inline`), `_base.css`, `_elements.css`, `_layout.css`, every file in `components/` (one partial per UI component — buttons, cards, modals, tables, etc.), and `_utilities.css`.
|
||||
- **`login.css`** — the standalone login page (`sysauth.ut`). Self-contained: imports Tailwind and `_tokens.css` directly.
|
||||
- **`main.css`** — the LuCI admin UI. It is an import manifest that disables Tailwind's automatic source scan (`source(none)`) and pulls in (in order) `_tokens.css` (OKLCH theme tokens, mapped via `@theme inline`), shared `_icons.css`, `_base.css`, `_elements.css`, `_layout.css`, every file in `components/` (one partial per UI component — buttons, cards, modals, tables, etc.), and `_utilities.css`.
|
||||
- **`login.css`** — the standalone login page (`sysauth.ut`). Self-contained: imports Tailwind theme/utilities with `source(none)`, omits full Preflight in favor of a tiny local reset, and imports `_tokens.css` directly. At build time the `login-css-prune` plugin (`vite.config.ts`) strips every custom property the page's var() chains never reach, so the admin-sized token sheet ships login-sized.
|
||||
|
||||
Third-party compatibility patches are **not** bundled into `main.css` — they are split into per-page files under `media/patches/` and loaded on demand (see [On-Demand Third-Party Patches](#on-demand-third-party-patches) below).
|
||||
|
||||
|
||||
@ -22,10 +22,8 @@
|
||||
"lightningcss": "^1.32.0",
|
||||
"prettier": "^3.6.2",
|
||||
"prettier-plugin-tailwindcss": "^0.7.0",
|
||||
"tailwind-scrollbar": "^4.0.2",
|
||||
"tailwindcss": "^4.1.11",
|
||||
"terser": "^5.43.1",
|
||||
"tw-animate-css": "^1.4.0",
|
||||
"vite": "^7.1.1"
|
||||
},
|
||||
"packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be"
|
||||
|
||||
@ -32,18 +32,12 @@ importers:
|
||||
prettier-plugin-tailwindcss:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.2(prettier@3.8.1)
|
||||
tailwind-scrollbar:
|
||||
specifier: ^4.0.2
|
||||
version: 4.0.2(react@19.1.1)(tailwindcss@4.3.2)
|
||||
tailwindcss:
|
||||
specifier: ^4.1.11
|
||||
version: 4.3.2
|
||||
terser:
|
||||
specifier: ^5.43.1
|
||||
version: 5.46.0
|
||||
tw-animate-css:
|
||||
specifier: ^1.4.0
|
||||
version: 1.4.0
|
||||
vite:
|
||||
specifier: ^7.1.1
|
||||
version: 7.3.1(@types/node@24.10.9)(jiti@2.7.0)(lightningcss@1.32.0)(terser@5.46.0)
|
||||
@ -466,9 +460,6 @@ packages:
|
||||
'@types/node@24.10.9':
|
||||
resolution: {integrity: sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==}
|
||||
|
||||
'@types/prismjs@1.26.5':
|
||||
resolution: {integrity: sha512-AUZTa7hQ2KY5L7AmtSiqxlhWxb4ina0yd8hNbl4TWuqnv/pFP0nDMb3YrfSBf4hJVGLh2YEIBfKaBW/9UEl6IQ==}
|
||||
|
||||
acorn@8.15.0:
|
||||
resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==}
|
||||
engines: {node: '>=0.4.0'}
|
||||
@ -490,10 +481,6 @@ packages:
|
||||
caniuse-lite@1.0.30001799:
|
||||
resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==}
|
||||
|
||||
clsx@2.1.1:
|
||||
resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
colorjs.io@0.6.1:
|
||||
resolution: {integrity: sha512-8lyR2wHzuIykCpqHKgluGsqQi5iDm3/a2IgP2GBZrasn2sBRkE4NOGsglZxWLs/jZQoNkmA/KM/8NV16rLUdBg==}
|
||||
|
||||
@ -698,15 +685,6 @@ packages:
|
||||
engines: {node: '>=14'}
|
||||
hasBin: true
|
||||
|
||||
prism-react-renderer@2.4.1:
|
||||
resolution: {integrity: sha512-ey8Ls/+Di31eqzUxC46h8MksNuGx/n0AAC8uKpwFau4RPDYLuE3EXTp8N8G2vX2N7UC/+IXeNUnlWBGGcAG+Ig==}
|
||||
peerDependencies:
|
||||
react: '>=16.0.0'
|
||||
|
||||
react@19.1.1:
|
||||
resolution: {integrity: sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
rollup@4.57.1:
|
||||
resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==}
|
||||
engines: {node: '>=18.0.0', npm: '>=8.0.0'}
|
||||
@ -723,12 +701,6 @@ packages:
|
||||
resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
tailwind-scrollbar@4.0.2:
|
||||
resolution: {integrity: sha512-wAQiIxAPqk0MNTPptVe/xoyWi27y+NRGnTwvn4PQnbvB9kp8QUBiGl/wsfoVBHnQxTmhXJSNt9NHTmcz9EivFA==}
|
||||
engines: {node: '>=12.13.0'}
|
||||
peerDependencies:
|
||||
tailwindcss: 4.x
|
||||
|
||||
tailwindcss@4.3.2:
|
||||
resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==}
|
||||
|
||||
@ -745,9 +717,6 @@ packages:
|
||||
resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
|
||||
tw-animate-css@1.4.0:
|
||||
resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==}
|
||||
|
||||
undici-types@7.16.0:
|
||||
resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==}
|
||||
|
||||
@ -1054,8 +1023,6 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 7.16.0
|
||||
|
||||
'@types/prismjs@1.26.5': {}
|
||||
|
||||
acorn@8.15.0: {}
|
||||
|
||||
baseline-browser-mapping@2.10.37: {}
|
||||
@ -1072,8 +1039,6 @@ snapshots:
|
||||
|
||||
caniuse-lite@1.0.30001799: {}
|
||||
|
||||
clsx@2.1.1: {}
|
||||
|
||||
colorjs.io@0.6.1: {}
|
||||
|
||||
commander@2.20.3: {}
|
||||
@ -1202,14 +1167,6 @@ snapshots:
|
||||
|
||||
prettier@3.8.1: {}
|
||||
|
||||
prism-react-renderer@2.4.1(react@19.1.1):
|
||||
dependencies:
|
||||
'@types/prismjs': 1.26.5
|
||||
clsx: 2.1.1
|
||||
react: 19.1.1
|
||||
|
||||
react@19.1.1: {}
|
||||
|
||||
rollup@4.57.1:
|
||||
dependencies:
|
||||
'@types/estree': 1.0.8
|
||||
@ -1250,13 +1207,6 @@ snapshots:
|
||||
|
||||
source-map@0.6.1: {}
|
||||
|
||||
tailwind-scrollbar@4.0.2(react@19.1.1)(tailwindcss@4.3.2):
|
||||
dependencies:
|
||||
prism-react-renderer: 2.4.1(react@19.1.1)
|
||||
tailwindcss: 4.3.2
|
||||
transitivePeerDependencies:
|
||||
- react
|
||||
|
||||
tailwindcss@4.3.2: {}
|
||||
|
||||
tapable@2.3.3: {}
|
||||
@ -1273,8 +1223,6 @@ snapshots:
|
||||
fdir: 6.5.0(picomatch@4.0.3)
|
||||
picomatch: 4.0.3
|
||||
|
||||
tw-animate-css@1.4.0: {}
|
||||
|
||||
undici-types@7.16.0: {}
|
||||
|
||||
update-browserslist-db@1.2.3(browserslist@4.28.2):
|
||||
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 15 KiB |
@ -1 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1760890864667" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="18898" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M918.8 83.7c-43.5-30.3-104-19.5-134.6 24.1l-425.8 606-127-135.7c-36.4-38.9-98.1-41.1-137-4.8-39 36.2-41.1 97.7-4.7 136.6l206.5 220.7c4.7 5 9.8 9.4 15.1 13.1 0.7 0.5 1.4 1 2.2 1.5 43.5 30.3 104 19.5 134.6-24.1l494-703.2c30.7-43.5 20.1-103.9-23.3-134.2z" fill="#0f162b" p-id="18899"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path d="M918.8 83.7c-43.5-30.3-104-19.5-134.6 24.1l-425.8 606-127-135.7c-36.4-38.9-98.1-41.1-137-4.8-39 36.2-41.1 97.7-4.7 136.6l206.5 220.7c4.7 5 9.8 9.4 15.1 13.1.7.5 1.4 1 2.2 1.5 43.5 30.3 104 19.5 134.6-24.1l494-703.2c30.7-43.5 20.1-103.9-23.3-134.2z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 628 B After Width: | Height: | Size: 330 B |
@ -1 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1758357645269" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7610" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M512 85.333333c235.648 0 426.666667 191.018667 426.666667 426.666667s-191.018667 426.666667-426.666667 426.666667S85.333333 747.648 85.333333 512 276.352 85.333333 512 85.333333z m0 128a298.666667 298.666667 0 1 0 0 597.333334 298.666667 298.666667 0 0 0 0-597.333334z" fill="#000000" fill-opacity=".05" p-id="7611"></path><path d="M813.696 813.696c166.613333-166.613333 166.613333-436.778667 0-603.392-166.613333-166.613333-436.778667-166.613333-603.392 0A64 64 0 0 0 300.8 300.8a298.666667 298.666667 0 1 1 422.4 422.4 64 64 0 0 0 90.496 90.496z" fill="#000000" p-id="7612"></path></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024"><path opacity=".05" d="M512 85.333c235.648 0 426.667 191.019 426.667 426.667S747.648 938.667 512 938.667 85.333 747.648 85.333 512 276.352 85.333 512 85.333zm0 128a298.667 298.667 0 1 0 0 597.334 298.667 298.667 0 0 0 0-597.334z"/><path d="M813.696 813.696c166.613-166.613 166.613-436.779 0-603.392s-436.779-166.613-603.392 0a64 64 0 0 0 90.496 90.496 298.667 298.667 0 1 1 422.4 422.4 64 64 0 0 0 90.496 90.496z"/></svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 922 B After Width: | Height: | Size: 486 B |
@ -1,9 +1,9 @@
|
||||
*:not(html) {
|
||||
@apply scrollbar-thumb-rounded-full scrollbar-thin scrollbar-track-transparent scrollbar-thumb-text-muted;
|
||||
@apply [scrollbar-color:var(--text-muted)_transparent] [scrollbar-width:thin];
|
||||
}
|
||||
|
||||
html {
|
||||
@apply scrollbar-thumb-rounded-full scrollbar-thin scrollbar-track-surface scrollbar-thumb-text-muted;
|
||||
@apply [scrollbar-color:var(--text-muted)_var(--surface)] [scrollbar-width:thin];
|
||||
|
||||
body {
|
||||
@apply text-text relative flex min-h-dvh flex-col text-sm leading-relaxed font-normal;
|
||||
|
||||
32
luci-theme-aurora/.dev/src/media/_icons.css
Normal file
@ -0,0 +1,32 @@
|
||||
/* Shared encoded icon payloads. Referencing these variables from masks keeps
|
||||
Vite from inlining the same SVG once per selector and vendor declaration. */
|
||||
:root {
|
||||
@apply [--icon-activity:url('@assets/icons/activity.svg')];
|
||||
@apply [--icon-adjustments:url('@assets/icons/adjustments.svg')];
|
||||
@apply [--icon-apps:url('@assets/icons/apps.svg')];
|
||||
@apply [--icon-arrow-down:url('@assets/icons/arrow-down.svg')];
|
||||
@apply [--icon-arrow-down-dark:url('@assets/icons/arrow-down-dark.svg')];
|
||||
@apply [--icon-arrow-right:url('@assets/icons/arrow-right.svg')];
|
||||
@apply [--icon-box:url('@assets/icons/box.svg')];
|
||||
@apply [--icon-category:url('@assets/icons/category.svg')];
|
||||
@apply [--icon-chart-bar:url('@assets/icons/chart-bar.svg')];
|
||||
@apply [--icon-check:url('@assets/icons/check.svg')];
|
||||
@apply [--icon-cpu:url('@assets/icons/cpu.svg')];
|
||||
@apply [--icon-error:url('@assets/icons/error.svg')];
|
||||
@apply [--icon-gauge:url('@assets/icons/gauge.svg')];
|
||||
@apply [--icon-info:url('@assets/icons/info.svg')];
|
||||
@apply [--icon-package:url('@assets/icons/package.svg')];
|
||||
@apply [--icon-phone:url('@assets/icons/phone.svg')];
|
||||
@apply [--icon-radio:url('@assets/icons/radio.svg')];
|
||||
@apply [--icon-refresh:url('@assets/icons/refresh.svg')];
|
||||
@apply [--icon-refresh-off:url('@assets/icons/refresh-off.svg')];
|
||||
@apply [--icon-router:url('@assets/icons/router.svg')];
|
||||
@apply [--icon-search:url('@assets/icons/search.svg')];
|
||||
@apply [--icon-server:url('@assets/icons/server.svg')];
|
||||
@apply [--icon-settings:url('@assets/icons/settings.svg')];
|
||||
@apply [--icon-shield:url('@assets/icons/shield.svg')];
|
||||
@apply [--icon-spinning:url('@assets/icons/spinning.svg')];
|
||||
@apply [--icon-tag:url('@assets/icons/tag.svg')];
|
||||
@apply [--icon-world:url('@assets/icons/world.svg')];
|
||||
@apply [--icon-x:url('@assets/icons/x.svg')];
|
||||
}
|
||||
@ -114,9 +114,12 @@ header {
|
||||
|
||||
/* First-level icon: mask + currentColor so it follows the theme.
|
||||
--menu-icon comes from the shared name → icon map in
|
||||
components/_nav.css; unmapped → default. */
|
||||
components/_nav.css; unmapped → the var() fallback. The default
|
||||
must live in the fallback, not as a --menu-icon declaration
|
||||
here: this nested chain compiles to (0,5,1) and would outrank
|
||||
every (0,2,0) [data-section] mapping. */
|
||||
&::before {
|
||||
@apply size-6 shrink-0 bg-current content-[''] [mask:var(--menu-icon,url('@assets/icons/category.svg'))_center/contain_no-repeat];
|
||||
@apply size-6 shrink-0 bg-current content-[''] [mask:var(--menu-icon,var(--icon-category))_center/contain_no-repeat];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -165,19 +168,19 @@ header {
|
||||
}
|
||||
|
||||
& .board-line-host::before {
|
||||
@apply [mask:url('@assets/icons/router.svg')_center/contain_no-repeat];
|
||||
@apply [mask:var(--icon-router)_center/contain_no-repeat];
|
||||
}
|
||||
|
||||
& .board-line-model::before {
|
||||
@apply [mask:url('@assets/icons/cpu.svg')_center/contain_no-repeat];
|
||||
@apply [mask:var(--icon-cpu)_center/contain_no-repeat];
|
||||
}
|
||||
|
||||
& .board-line-firmware::before {
|
||||
@apply [mask:url('@assets/icons/package.svg')_center/contain_no-repeat];
|
||||
@apply [mask:var(--icon-package)_center/contain_no-repeat];
|
||||
}
|
||||
|
||||
& .board-line-kernel::before {
|
||||
@apply [mask:url('@assets/icons/tag.svg')_center/contain_no-repeat];
|
||||
@apply [mask:var(--icon-tag)_center/contain_no-repeat];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -318,21 +321,21 @@ header {
|
||||
@apply before:text-text size-5 cursor-pointer text-[0px] transition-transform duration-150 before:absolute before:size-5 before:bg-current hover:scale-105 active:scale-95;
|
||||
|
||||
&[data-indicator="media_error"] {
|
||||
@apply before:[mask:url('@assets/icons/error.svg')_center/cover_no-repeat];
|
||||
@apply before:[mask:var(--icon-error)_center/cover_no-repeat];
|
||||
}
|
||||
|
||||
&[data-indicator="poll-status"] {
|
||||
&[data-style="active"] {
|
||||
@apply before:[mask:url('@assets/icons/refresh.svg')_center/cover_no-repeat];
|
||||
@apply before:[mask:var(--icon-refresh)_center/cover_no-repeat];
|
||||
}
|
||||
|
||||
&[data-style="inactive"] {
|
||||
@apply before:[mask:url('@assets/icons/refresh-off.svg')_center/cover_no-repeat];
|
||||
@apply before:[mask:var(--icon-refresh-off)_center/cover_no-repeat];
|
||||
}
|
||||
}
|
||||
|
||||
&[data-indicator="uci-changes"] {
|
||||
@apply relative before:[mask:url('@assets/icons/settings.svg')_center/cover_no-repeat];
|
||||
@apply relative before:[mask:var(--icon-settings)_center/cover_no-repeat];
|
||||
|
||||
&[data-count]:not([data-count="0"]) {
|
||||
@apply after:bg-danger after:text-on-brand after:absolute after:-top-0.5 after:-right-0.5 after:z-10 after:flex after:min-h-3 after:min-w-3 after:items-center after:justify-center after:rounded-full after:px-0.5 after:text-[8px] after:leading-none after:font-bold after:shadow-sm after:content-[attr(data-count)];
|
||||
@ -349,9 +352,9 @@ body[data-nav-type="sidebar"] {
|
||||
reads as the content area's toolbar, not a page-wide bar.
|
||||
A CSS grid (not flex-wrap) keeps the header row at its natural height —
|
||||
so collapsing the sidebar never stretches it — lets the sidebar column
|
||||
animate to zero, and lets #maincontent fluidly reclaim the freed space.
|
||||
collapse to zero, and lets #maincontent reclaim the freed space.
|
||||
overflow-x-clip keeps the sliding sidebar out of horizontal overflow. */
|
||||
@apply md:grid md:grid-cols-[17rem_minmax(0,1fr)] md:grid-rows-[auto_minmax(0,1fr)] md:overflow-x-clip md:transition-[grid-template-columns] md:duration-[250ms] md:ease-out;
|
||||
@apply md:grid md:grid-cols-[17rem_minmax(0,1fr)] md:grid-rows-[auto_minmax(0,1fr)] md:overflow-x-clip;
|
||||
|
||||
&.sidebar-collapsed {
|
||||
@apply md:grid-cols-[0_minmax(0,1fr)];
|
||||
@ -396,8 +399,9 @@ body[data-nav-type="sidebar"] {
|
||||
@apply border-hairline sticky top-0 hidden h-dvh w-full overflow-hidden rounded-none border-r transition-[visibility] duration-[250ms] md:block;
|
||||
}
|
||||
|
||||
/* Visibility flips at the end of the slide so collapsed controls leave the
|
||||
tab order without clipping the animation. */
|
||||
/* The grid column snaps closed to avoid a full-page layout animation.
|
||||
Visibility removes collapsed controls from the tab order; on expansion,
|
||||
the inner panel still enters via compositor-only translate + opacity. */
|
||||
&.sidebar-collapsed .sidebar-panel {
|
||||
@apply invisible;
|
||||
}
|
||||
|
||||
@ -51,8 +51,14 @@
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes aurora-fade-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.fade-in {
|
||||
@apply animate-in fade-in-0 fill-mode-backwards duration-[250ms];
|
||||
@apply animate-[aurora-fade-in_250ms_ease_both];
|
||||
}
|
||||
|
||||
.fade-out {
|
||||
@ -61,7 +67,7 @@
|
||||
|
||||
.spinning {
|
||||
@apply relative inline-block pl-8!;
|
||||
@apply before:absolute before:top-1/2 before:left-[6px] before:size-5 before:-translate-y-1/2 before:animate-spin before:bg-current before:[mask:url('@assets/icons/spinning.svg')_center/cover_no-repeat];
|
||||
@apply before:absolute before:top-1/2 before:left-[6px] before:size-5 before:-translate-y-1/2 before:animate-spin before:bg-current before:[mask:var(--icon-spinning)_center/cover_no-repeat];
|
||||
#view > & {
|
||||
@apply first:mx-auto first:grid first:min-h-48 first:w-fit first:place-items-center first:max-md:min-h-[calc(100dvh-4rem)];
|
||||
}
|
||||
|
||||
@ -27,11 +27,11 @@
|
||||
@apply relative size-5 cursor-pointer text-[0px] before:absolute before:size-5 before:bg-current before:transition-transform before:duration-150 before:ease-out hover:before:scale-110 active:before:scale-95;
|
||||
|
||||
&[data-style="active"] {
|
||||
@apply before:rotate-0 before:[mask:url('@assets/icons/arrow-right.svg')_center/cover_no-repeat];
|
||||
@apply before:rotate-0 before:[mask:var(--icon-arrow-right)_center/cover_no-repeat];
|
||||
}
|
||||
|
||||
&[data-style="inactive"] {
|
||||
@apply before:rotate-90 before:[mask:url('@assets/icons/arrow-right.svg')_center/cover_no-repeat];
|
||||
@apply before:rotate-90 before:[mask:var(--icon-arrow-right)_center/cover_no-repeat];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -28,7 +28,7 @@
|
||||
@apply text-text-muted relative mt-1 pl-4 leading-none wrap-break-word;
|
||||
|
||||
&:not(:empty) {
|
||||
@apply before:bg-info before:absolute before:-left-0.5 before:inline-block before:size-4 before:[mask:url('@assets/icons/info.svg')_center/cover_no-repeat];
|
||||
@apply before:bg-info before:absolute before:-left-0.5 before:inline-block before:size-4 before:[mask:var(--icon-info)_center/cover_no-repeat];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,11 +27,11 @@ input[type="checkbox"] {
|
||||
}
|
||||
|
||||
input[type="radio"] {
|
||||
@apply before:rounded-full after:[mask:url('@assets/icons/radio.svg')_center/cover_no-repeat];
|
||||
@apply before:rounded-full after:[mask:var(--icon-radio)_center/cover_no-repeat];
|
||||
}
|
||||
|
||||
input[type="checkbox"] {
|
||||
@apply before:rounded after:[mask:url('@assets/icons/check.svg')_center/cover_no-repeat];
|
||||
@apply before:rounded after:[mask:var(--icon-check)_center/cover_no-repeat];
|
||||
}
|
||||
|
||||
.cbi-checkbox {
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
/* hover:no-underline: the global `a:hover { underline }` in _elements.css
|
||||
outranks a bare class (a:hover is 0,1,1), so nav rows must opt out at
|
||||
the same specificity tier. */
|
||||
@apply text-text hover:bg-hover-faint block rounded-xl px-3 py-1.5 no-underline transition-all duration-150 hover:no-underline;
|
||||
@apply text-text hover:bg-hover-faint block rounded-xl px-3 py-1.5 no-underline transition-colors duration-150 hover:no-underline;
|
||||
}
|
||||
|
||||
.navigation-direct {
|
||||
@ -33,7 +33,7 @@
|
||||
}
|
||||
|
||||
.navigation-group-toggle {
|
||||
@apply after:size-3.5 after:shrink-0 after:bg-current after:opacity-55 after:transition-[transform,opacity] after:duration-[250ms] after:content-[''] after:[mask:url('@assets/icons/arrow-right.svg')_center/cover_no-repeat] hover:after:opacity-100;
|
||||
@apply after:size-3.5 after:shrink-0 after:bg-current after:opacity-55 after:transition-[transform,opacity] after:duration-[250ms] after:content-[''] after:[mask:var(--icon-arrow-right)_center/cover_no-repeat] hover:after:opacity-100;
|
||||
}
|
||||
|
||||
.nav-category-label {
|
||||
@ -90,38 +90,38 @@
|
||||
The only maintenance point: add new first-level sections here; anything
|
||||
unmapped falls back to the default icon at the consumer. */
|
||||
.desktop-nav-title[data-section="status"] {
|
||||
@apply [--menu-icon:url('@assets/icons/activity.svg')];
|
||||
@apply [--menu-icon:var(--icon-activity)];
|
||||
}
|
||||
.desktop-nav-title[data-section="statistics"] {
|
||||
@apply [--menu-icon:url('@assets/icons/chart-bar.svg')];
|
||||
@apply [--menu-icon:var(--icon-chart-bar)];
|
||||
}
|
||||
.desktop-nav-title[data-section="nlbw"] {
|
||||
@apply [--menu-icon:url('@assets/icons/gauge.svg')];
|
||||
@apply [--menu-icon:var(--icon-gauge)];
|
||||
}
|
||||
.desktop-nav-title[data-section="system"] {
|
||||
@apply [--menu-icon:url('@assets/icons/settings.svg')];
|
||||
@apply [--menu-icon:var(--icon-settings)];
|
||||
}
|
||||
.desktop-nav-title[data-section="services"] {
|
||||
@apply [--menu-icon:url('@assets/icons/apps.svg')];
|
||||
@apply [--menu-icon:var(--icon-apps)];
|
||||
}
|
||||
.desktop-nav-title[data-section="nas"] {
|
||||
@apply [--menu-icon:url('@assets/icons/server.svg')];
|
||||
@apply [--menu-icon:var(--icon-server)];
|
||||
}
|
||||
.desktop-nav-title[data-section="control"] {
|
||||
@apply [--menu-icon:url('@assets/icons/adjustments.svg')];
|
||||
@apply [--menu-icon:var(--icon-adjustments)];
|
||||
}
|
||||
.desktop-nav-title[data-section="network"] {
|
||||
@apply [--menu-icon:url('@assets/icons/world.svg')];
|
||||
@apply [--menu-icon:var(--icon-world)];
|
||||
}
|
||||
.desktop-nav-title[data-section="vpn"] {
|
||||
@apply [--menu-icon:url('@assets/icons/shield.svg')];
|
||||
@apply [--menu-icon:var(--icon-shield)];
|
||||
}
|
||||
.desktop-nav-title[data-section="docker"] {
|
||||
@apply [--menu-icon:url('@assets/icons/box.svg')];
|
||||
@apply [--menu-icon:var(--icon-box)];
|
||||
}
|
||||
/* "asterisk" covers VoIP/PBX apps (e.g. luci-app-asterisk forks) — no
|
||||
official OpenWrt package registers a verified menu.d node under this
|
||||
name, so this is a best-effort key. */
|
||||
.desktop-nav-title[data-section="asterisk"] {
|
||||
@apply [--menu-icon:url('@assets/icons/phone.svg')];
|
||||
@apply [--menu-icon:var(--icon-phone)];
|
||||
}
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
.mobile-menu-overlay {
|
||||
@apply max-md:bg-mega-menu-bg max-md:invisible max-md:fixed max-md:inset-0 max-md:z-60 max-md:opacity-0 max-md:backdrop-blur-lg max-md:backdrop-saturate-150 md:hidden;
|
||||
/* The mobile surface is opaque, so filtering pixels behind it has no visual
|
||||
effect and only adds a full-screen paint/compositing cost. */
|
||||
@apply max-md:bg-mega-menu-bg max-md:invisible max-md:fixed max-md:inset-0 max-md:z-60 max-md:opacity-0 md:hidden;
|
||||
|
||||
/* Transitions live on the OPEN state only: opening fades in, closing cuts
|
||||
instantly. Link taps close into a full-page navigation, and any close
|
||||
|
||||
@ -30,7 +30,7 @@
|
||||
search icon carries the same stroke weight as the poll/changes ones
|
||||
sitting next to it. */
|
||||
&::before {
|
||||
@apply size-5 bg-current content-[''] [mask:url('@assets/icons/search.svg')_center/contain_no-repeat];
|
||||
@apply size-5 bg-current content-[''] [mask:var(--icon-search)_center/contain_no-repeat];
|
||||
}
|
||||
|
||||
&:hover {
|
||||
@ -59,7 +59,7 @@
|
||||
@apply max-md:border-hairline max-md:bg-surface max-md:border-b max-md:px-4 max-md:py-2.5;
|
||||
|
||||
&::before {
|
||||
@apply text-text-subtle size-5 shrink-0 bg-current content-[''] [mask:url('@assets/icons/search.svg')_center/contain_no-repeat];
|
||||
@apply text-text-subtle size-5 shrink-0 bg-current content-[''] [mask:var(--icon-search)_center/contain_no-repeat];
|
||||
}
|
||||
|
||||
& .cmdk-input {
|
||||
@ -72,7 +72,7 @@
|
||||
@apply text-text-subtle grid size-5 shrink-0 cursor-pointer place-items-center rounded-md border-0 bg-transparent p-0 shadow-none transition-colors duration-150;
|
||||
|
||||
&::before {
|
||||
@apply size-4 bg-current content-[''] [mask:url('@assets/icons/x.svg')_center/contain_no-repeat];
|
||||
@apply size-4 bg-current content-[''] [mask:var(--icon-x)_center/contain_no-repeat];
|
||||
}
|
||||
|
||||
&:hover {
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
select {
|
||||
@apply text-text border-hairline bg-control-bg focus:border-brand focus:ring-focus-ring min-w-52.5 max-w-full appearance-none md:max-w-100 rounded-2xl border px-3 py-1.5 pr-10 text-sm font-normal shadow-sm transition-[border-color,box-shadow] duration-150 focus:ring-2 focus:outline-none;
|
||||
@apply bg-[url('@assets/icons/arrow-down.svg')] bg-size-[16px] bg-position-[right_0.75rem_center] bg-no-repeat dark:bg-[url('@assets/icons/arrow-down-dark.svg')];
|
||||
@apply bg-size-[16px] bg-position-[right_0.75rem_center] bg-no-repeat [background-image:var(--icon-arrow-down)] dark:[background-image:var(--icon-arrow-down-dark)];
|
||||
&[disabled] {
|
||||
@apply cursor-not-allowed opacity-(--app-opacity-disabled);
|
||||
}
|
||||
|
||||
@ -4,18 +4,48 @@
|
||||
* Licensed to the public under the Apache License 2.0.
|
||||
*/
|
||||
|
||||
@import "tailwindcss";
|
||||
/* Login only needs Tailwind's theme variables and generated utilities. The
|
||||
full Preflight reset is intentionally omitted from this standalone entry. */
|
||||
@layer theme, base, components, utilities;
|
||||
@import "tailwindcss/theme.css" layer(theme);
|
||||
@import "tailwindcss/utilities.css" layer(utilities) source(none);
|
||||
@import "./_tokens.css";
|
||||
|
||||
@import "./_reduced-motion.css";
|
||||
@import "./_base.css";
|
||||
|
||||
/* See main.css: keep dev-only tooling out of Tailwind's source detection. */
|
||||
@source not "../../vite.config.ts";
|
||||
@source not "../../scripts/*.client.js";
|
||||
|
||||
@custom-variant dark (&:where([data-darkmode=true], [data-darkmode=true] *));
|
||||
|
||||
/* Minimal reset for this template, replacing the much larger Preflight. */
|
||||
@layer base {
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
@apply box-border;
|
||||
}
|
||||
|
||||
body,
|
||||
hr,
|
||||
p,
|
||||
button,
|
||||
input {
|
||||
@apply m-0;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
@apply [font:inherit];
|
||||
}
|
||||
|
||||
a {
|
||||
@apply no-underline;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
@apply hidden!;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes divider-in {
|
||||
from {
|
||||
opacity: 0;
|
||||
@ -40,7 +70,7 @@
|
||||
|
||||
/* Full image fades in on top once loaded (compositor-only opacity change) */
|
||||
.login-bg::after {
|
||||
@apply pointer-events-none absolute inset-0 bg-cover bg-center bg-no-repeat opacity-0 [will-change:opacity] content-[''] [transition:opacity_0.5s_ease] [background-image:var(--login-bg)];
|
||||
@apply pointer-events-none absolute inset-0 bg-cover bg-center bg-no-repeat opacity-0 content-[''] [transition:opacity_0.5s_ease] [background-image:var(--login-bg)];
|
||||
}
|
||||
|
||||
.login-bg.full-loaded::after {
|
||||
@ -102,7 +132,7 @@
|
||||
/* Focus ring: opacity-only transition runs on compositor thread, not main thread —
|
||||
border-color changes require a repaint (~400 ms INP); opacity changes do not. */
|
||||
.input-wrap::after {
|
||||
@apply border-brand pointer-events-none absolute -inset-px rounded-[calc(var(--radius-2xl)+1px)] border-2 opacity-0 [will-change:opacity] content-[''] [transition:opacity_150ms_ease];
|
||||
@apply border-brand pointer-events-none absolute -inset-px rounded-[calc(var(--radius-2xl)+1px)] border-2 opacity-0 content-[''] [transition:opacity_150ms_ease];
|
||||
}
|
||||
|
||||
.input-wrap:focus-within::after {
|
||||
|
||||
@ -3,24 +3,15 @@
|
||||
* Licensed under the Apache License, Version 2.0.
|
||||
*/
|
||||
|
||||
@import "tailwindcss";
|
||||
|
||||
/* Dev-only tooling holds prose and plain CSS whose bare words (static,
|
||||
transition, relative, …) Tailwind's automatic source detection would
|
||||
otherwise read as utility candidates and emit into this shipped stylesheet.
|
||||
Nothing here renders markup, so none of it belongs in the scan. */
|
||||
@source not "../../vite.config.ts";
|
||||
@source not "../../scripts/*.client.js";
|
||||
/* Every shipped utility is referenced through @apply in these CSS sources.
|
||||
Disabling automatic repository scanning prevents class-like words in docs,
|
||||
scripts and fixtures from leaking into the OpenWrt payload. */
|
||||
@import "tailwindcss" source(none);
|
||||
|
||||
@custom-variant dark (&:where([data-darkmode=true], [data-darkmode=true] *));
|
||||
|
||||
@plugin "tailwind-scrollbar" {
|
||||
nocompatible: true;
|
||||
}
|
||||
|
||||
@import "tw-animate-css";
|
||||
|
||||
@import "./_tokens.css";
|
||||
@import "./_icons.css";
|
||||
|
||||
@import "./_base.css";
|
||||
@import "./_elements.css";
|
||||
|
||||
101
luci-theme-aurora/.dev/tests/build-performance.test.js
Normal file
@ -0,0 +1,101 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readdirSync, readFileSync, statSync } from "node:fs";
|
||||
import { join, resolve } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
const projectRoot = resolve(import.meta.dirname, "../..");
|
||||
const output = resolve(projectRoot, "htdocs/luci-static");
|
||||
|
||||
const asset = (path) => resolve(output, path);
|
||||
const bytes = (path) => statSync(asset(path)).size;
|
||||
|
||||
test("production assets stay within raw-transfer budgets", () => {
|
||||
const main = bytes("aurora/main.css");
|
||||
const login = bytes("aurora/login.css");
|
||||
const menu = bytes("resources/menu-aurora.js");
|
||||
const font = bytes("aurora/fonts/lato-v24-latin-regular.woff2");
|
||||
const logo = bytes("aurora/images/logo.svg");
|
||||
|
||||
assert.ok(main <= 190_000, "main.css exceeds 190 KB");
|
||||
assert.ok(login <= 12_000, "login.css exceeds 12 KB");
|
||||
assert.ok(menu <= 20_000, "menu-aurora.js exceeds 20 KB");
|
||||
assert.ok(logo <= 16_000, "logo.svg exceeds 16 KB");
|
||||
assert.ok(main + menu + font + logo <= 250_000, "admin assets exceed 250 KB");
|
||||
assert.ok(login + font + logo <= 55_000, "login assets exceed 55 KB");
|
||||
});
|
||||
|
||||
test("compressed LuCI JS preserves its loader directives", () => {
|
||||
const js = readFileSync(asset("resources/menu-aurora.js"), "utf8");
|
||||
assert.match(js, /["']require baseclass["'];["']require ui["'];/);
|
||||
|
||||
const module = new Function("baseclass", "ui", js)(
|
||||
{ extend: (value) => value },
|
||||
{},
|
||||
);
|
||||
assert.equal(typeof module.__init__, "function");
|
||||
});
|
||||
|
||||
test("compiled CSS does not duplicate SVG payloads for every mask property", () => {
|
||||
const css = readFileSync(asset("aurora/main.css"), "utf8");
|
||||
const dataUrls = css.match(/data:image\/svg\+xml,[^"]+/g) ?? [];
|
||||
const encodedBytes = dataUrls.reduce((sum, value) => sum + value.length, 0);
|
||||
|
||||
assert.equal(
|
||||
dataUrls.length,
|
||||
new Set(dataUrls).size,
|
||||
"duplicate SVG data URL",
|
||||
);
|
||||
assert.ok(encodedBytes <= 17_000, `SVG data URLs occupy ${encodedBytes} B`);
|
||||
});
|
||||
|
||||
test("pruned login.css keeps every consumed variable resolvable", () => {
|
||||
const css = readFileSync(asset("aurora/login.css"), "utf8");
|
||||
const declared = new Set(
|
||||
[...css.matchAll(/[{;](--[\w-]+):/g)].map((m) => m[1]),
|
||||
);
|
||||
const registered = new Set(
|
||||
[...css.matchAll(/@property\s+(--[\w-]+)/g)].map((m) => m[1]),
|
||||
);
|
||||
// header.ut injects these from UCI at render time — consumed here, never
|
||||
// declared here.
|
||||
const injected = new Set(["--login-bg", "--login-bg-lqip"]);
|
||||
|
||||
const unresolvable = [];
|
||||
for (const [, name, delim] of css.matchAll(/var\(\s*(--[\w-]+)\s*([,)])/g)) {
|
||||
const hasFallback = delim === ",";
|
||||
if (
|
||||
!declared.has(name) &&
|
||||
!registered.has(name) &&
|
||||
!injected.has(name) &&
|
||||
!hasFallback
|
||||
) {
|
||||
unresolvable.push(name);
|
||||
}
|
||||
}
|
||||
assert.deepEqual(unresolvable, []);
|
||||
|
||||
// The prune must actually strip admin-only tokens from the shared sheet.
|
||||
for (const adminOnly of ["--mega-menu-bg", "--icon-", "--sidebar"]) {
|
||||
assert.ok(!css.includes(adminOnly), `${adminOnly} should be pruned`);
|
||||
}
|
||||
// And the login page's own consumed tokens must survive, light and dark.
|
||||
for (const kept of ["--surface:", "--brand:", "--control-bg:"]) {
|
||||
const count = css.split(kept).length - 1;
|
||||
assert.ok(count >= 2, `${kept} should be declared for both modes`);
|
||||
}
|
||||
});
|
||||
|
||||
test("package roots contain no macOS metadata", () => {
|
||||
const offenders = [];
|
||||
const visit = (dir) => {
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const path = join(dir, entry.name);
|
||||
if (entry.name === ".DS_Store") offenders.push(path);
|
||||
else if (entry.isDirectory()) visit(path);
|
||||
}
|
||||
};
|
||||
|
||||
visit(resolve(projectRoot, "htdocs"));
|
||||
visit(resolve(projectRoot, "ucode"));
|
||||
assert.deepEqual(offenders, []);
|
||||
});
|
||||
@ -108,7 +108,7 @@ test("shared navigation styles own accordion animation without a guide rail", ()
|
||||
"after:transition-[transform,opacity]",
|
||||
"after:duration-[250ms]",
|
||||
]);
|
||||
assert.match(toggle, /arrow-right\.svg/);
|
||||
assert.match(toggle, /var\(--icon-arrow-right\)/);
|
||||
assertIncludesUtilities(region, [
|
||||
"grid",
|
||||
"grid-rows-[0fr]",
|
||||
@ -218,7 +218,11 @@ test("mega-menu category masks use Tailwind arbitrary utilities", () => {
|
||||
|
||||
assert.match(
|
||||
icon,
|
||||
/@apply[^;]*\[mask:var\(--menu-icon,url\(["']@assets\/icons\/category\.svg["']\)\)_center\/contain_no-repeat\]/,
|
||||
/@apply[^;]*\[mask:var\(--menu-icon,var\(--icon-category\)\)_center\/contain_no-repeat\]/,
|
||||
);
|
||||
// The default lives in the var() fallback only. Declaring --menu-icon on
|
||||
// the title compiles into a (0,5,1) selector chain that outranks every
|
||||
// .desktop-nav-title[data-section=…] (0,2,0) mapping in _nav.css.
|
||||
assert.doesNotMatch(title, /--menu-icon:/);
|
||||
assert.doesNotMatch(layoutStyles, /^\s*mask\s*:/m);
|
||||
});
|
||||
|
||||
32
luci-theme-aurora/.dev/tests/runtime-performance.test.js
Normal file
@ -0,0 +1,32 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import test from "node:test";
|
||||
|
||||
const projectRoot = resolve(import.meta.dirname, "../..");
|
||||
const source = (path) => readFileSync(resolve(projectRoot, path), "utf8");
|
||||
|
||||
test("navigation transitions avoid blanket and full-page layout animation", () => {
|
||||
const nav = source(".dev/src/media/components/_nav.css");
|
||||
const layout = source(".dev/src/media/_layout.css");
|
||||
|
||||
assert.doesNotMatch(nav, /\btransition-all\b/);
|
||||
assert.doesNotMatch(layout, /transition-\[grid-template-columns\]/);
|
||||
});
|
||||
|
||||
test("login effects do not reserve compositor layers permanently", () => {
|
||||
const login = source(".dev/src/media/login.css");
|
||||
assert.doesNotMatch(login, /\[will-change:opacity\]/);
|
||||
});
|
||||
|
||||
test("login template reuses server data when including the header", () => {
|
||||
const sysauth = source("ucode/template/themes/aurora/sysauth.ut");
|
||||
const header = source("ucode/template/themes/aurora/header.ut");
|
||||
|
||||
assert.match(
|
||||
sysauth,
|
||||
/include\('header',\s*\{[^}]*prefetched_boardinfo:\s*boardinfo[^}]*prefetched_tokens:\s*themeTokens[^}]*\}\)/s,
|
||||
);
|
||||
assert.match(header, /prefetched_boardinfo\s*\?\?/);
|
||||
assert.match(header, /prefetched_tokens\s*\?\?/);
|
||||
});
|
||||
@ -114,7 +114,10 @@ test("mega-menu reveal is compositor-only and the frost never overlaps it", () =
|
||||
// The curtain carries the blur permanently (Apple's globalnav-curtain) and
|
||||
// fades it with opacity/visibility. Close must start that fade immediately;
|
||||
// delaying the curtain left blur visible after the sheet had collapsed.
|
||||
assert.ok(overlay.includes("max-md:backdrop-blur-lg"), "mobile overlay blur");
|
||||
assert.ok(
|
||||
!overlay.includes("max-md:backdrop-blur-lg"),
|
||||
"opaque mobile overlay must not blur hidden pixels",
|
||||
);
|
||||
const curtain = overlay
|
||||
.split("\n")
|
||||
.find((l) => l.includes("bg-mega-menu-scrim"));
|
||||
|
||||
@ -7,9 +7,9 @@ import tailwindcss from "@tailwindcss/vite";
|
||||
import browserslist from "browserslist";
|
||||
import { exec } from "child_process";
|
||||
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
||||
import { mkdir, readdir, readFile, writeFile } from "fs/promises";
|
||||
import { mkdir, readdir, readFile, rm, writeFile } from "fs/promises";
|
||||
import type { IncomingMessage, ServerResponse } from "http";
|
||||
import { browserslistToTargets } from "lightningcss";
|
||||
import { browserslistToTargets, transform as lightningcssTransform } from "lightningcss";
|
||||
import { basename, dirname, join, relative, resolve, sep } from "path";
|
||||
import { minify as terserMinify } from "terser";
|
||||
import { promisify } from "util";
|
||||
@ -27,6 +27,10 @@ const CURRENT_DIR = process.cwd();
|
||||
const PROJECT_ROOT = resolve(CURRENT_DIR, "..");
|
||||
const BUILD_OUTPUT = resolve(PROJECT_ROOT, "htdocs/luci-static");
|
||||
|
||||
const LIGHTNINGCSS_TARGETS = browserslistToTargets(
|
||||
browserslist("last 4 versions, Firefox ESR, not dead"),
|
||||
);
|
||||
|
||||
function createLuciJsCompressPlugin(): Plugin {
|
||||
let outDir: string;
|
||||
|
||||
@ -48,8 +52,10 @@ function createLuciJsCompressPlugin(): Plugin {
|
||||
const sourceCode = await readFile(join(srcDir, relPath), "utf-8");
|
||||
const compressed = await terserMinify(sourceCode, {
|
||||
parse: { bare_returns: true },
|
||||
compress: false,
|
||||
mangle: false,
|
||||
/* LuCI dependency declarations are string directives. Keep
|
||||
them while enabling normal compression and local mangling. */
|
||||
compress: { directives: false, passes: 2 },
|
||||
mangle: true,
|
||||
format: { comments: false, beautify: false },
|
||||
});
|
||||
const outputPath = join(outDir, "resources", normalized);
|
||||
@ -66,6 +72,94 @@ function createLuciJsCompressPlugin(): Plugin {
|
||||
};
|
||||
}
|
||||
|
||||
async function removeMacOsMetadata(dir: string): Promise<void> {
|
||||
if (!existsSync(dir)) return;
|
||||
for (const entry of await readdir(dir, { withFileTypes: true })) {
|
||||
const path = join(dir, entry.name);
|
||||
if (entry.isDirectory()) await removeMacOsMetadata(path);
|
||||
else if (entry.name === ".DS_Store") await rm(path, { force: true });
|
||||
}
|
||||
}
|
||||
|
||||
/* login.css imports the full shared token sheet but the login page consumes
|
||||
only a fraction of it. Prune every custom-property declaration (and --tw-*
|
||||
@property registration) that no var() reference can reach, following
|
||||
declaration-value chains (--a: var(--b) keeps --b alive). --login-bg and
|
||||
--login-bg-lqip stay consumed-without-declaration by design: header.ut
|
||||
injects them from UCI at render time, so pruning never touches consumers. */
|
||||
function createLoginCssPrunePlugin(): Plugin {
|
||||
return {
|
||||
name: "login-css-prune",
|
||||
apply: "build",
|
||||
enforce: "post",
|
||||
async closeBundle() {
|
||||
const path = resolve(BUILD_OUTPUT, "aurora/login.css");
|
||||
if (!existsSync(path)) return;
|
||||
const css = await readFile(path, "utf-8");
|
||||
|
||||
// Roots are vars consumed by normal declarations; custom-property
|
||||
// declarations only contribute edges to the reachability walk.
|
||||
const VALUE = `(?:"[^"]*"|'[^']*'|[^;{}"'])*`;
|
||||
const roots = new Set<string>();
|
||||
const edges = new Map<string, Set<string>>();
|
||||
for (const [, name, value] of css.matchAll(
|
||||
new RegExp(`(?<=[{;])(--[\\w-]+|[a-zA-Z-]+):(${VALUE})`, "g"),
|
||||
)) {
|
||||
const refs = [...value.matchAll(/var\(\s*(--[\w-]+)/g)].map((m) => m[1]);
|
||||
if (!name.startsWith("--")) refs.forEach((r) => roots.add(r));
|
||||
else {
|
||||
const deps = edges.get(name) ?? new Set<string>();
|
||||
refs.forEach((r) => deps.add(r));
|
||||
edges.set(name, deps);
|
||||
}
|
||||
}
|
||||
const keep = new Set(roots);
|
||||
const stack = [...roots];
|
||||
while (stack.length) {
|
||||
for (const dep of edges.get(stack.pop()!) ?? []) {
|
||||
if (!keep.has(dep)) {
|
||||
keep.add(dep);
|
||||
stack.push(dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const pruned = css
|
||||
.replace(
|
||||
new RegExp(`(?<=[{;])(--[\\w-]+):${VALUE};?`, "g"),
|
||||
(decl, name) => (keep.has(name) ? decl : ""),
|
||||
)
|
||||
.replace(/@property\s+(--[\w-]+)\{[^{}]*\}/g, (rule, name) =>
|
||||
keep.has(name) ? rule : "",
|
||||
);
|
||||
|
||||
// Re-minify with the shared targets: validates the edited syntax and
|
||||
// drops any rule the pruning emptied out.
|
||||
const { code } = lightningcssTransform({
|
||||
filename: "login.css",
|
||||
code: Buffer.from(pruned),
|
||||
minify: true,
|
||||
targets: LIGHTNINGCSS_TARGETS,
|
||||
});
|
||||
await writeFile(path, code);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createPackageHygienePlugin(): Plugin {
|
||||
return {
|
||||
name: "package-hygiene",
|
||||
apply: "build",
|
||||
enforce: "post",
|
||||
async closeBundle() {
|
||||
await Promise.all([
|
||||
removeMacOsMetadata(resolve(PROJECT_ROOT, "htdocs")),
|
||||
removeMacOsMetadata(resolve(PROJECT_ROOT, "ucode")),
|
||||
]);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
// On-demand third-party patches: serve src/media/patches/<page>.css at
|
||||
// /luci-static/aurora/patches/<page>.css in dev. Without this, header.ut's patch
|
||||
// <link> falls through to the OpenWrt proxy (404 / stale router asset) and patch
|
||||
@ -789,12 +883,12 @@ export default defineConfig(({ mode }) => {
|
||||
createMockPlugin(),
|
||||
createUtSyncPlugin(OPENWRT_SSH_HOST),
|
||||
createLuciJsCompressPlugin(),
|
||||
createLoginCssPrunePlugin(),
|
||||
createPackageHygienePlugin(),
|
||||
],
|
||||
css: {
|
||||
lightningcss: {
|
||||
targets: browserslistToTargets(
|
||||
browserslist("last 4 versions, Firefox ESR, not dead"),
|
||||
),
|
||||
targets: LIGHTNINGCSS_TARGETS,
|
||||
},
|
||||
},
|
||||
build: {
|
||||
|
||||
@ -8,8 +8,8 @@ include $(TOPDIR)/rules.mk
|
||||
LUCI_TITLE:=Aurora Theme (A modern browser theme built with Vite and Tailwind CSS)
|
||||
LUCI_DEPENDS:=+luci-base
|
||||
|
||||
PKG_VERSION:=1.1.2
|
||||
PKG_RELEASE:=55
|
||||
PKG_VERSION:=1.1.3
|
||||
PKG_RELEASE:=56
|
||||
PKG_LICENSE:=Apache-2.0
|
||||
|
||||
LUCI_MINIFY_CSS:=
|
||||
|
||||
|
Before Width: | Height: | Size: 44 KiB After Width: | Height: | Size: 15 KiB |
@ -14,10 +14,10 @@
|
||||
import { cursor } from 'uci';
|
||||
import { lsdir, readfile } from 'fs';
|
||||
|
||||
const boardinfo = ubus.call('system', 'board') ?? {};
|
||||
const boardinfo = prefetched_boardinfo ?? (ubus.call('system', 'board') ?? {});
|
||||
const hostname = striptags(boardinfo.hostname ?? '?');
|
||||
const uci = cursor();
|
||||
const tokens = uci.get_all('aurora', 'theme') || {};
|
||||
const tokens = prefetched_tokens ?? (uci.get_all('aurora', 'theme') || {});
|
||||
|
||||
const page_segs = length(ctx?.request_path) ? ctx.request_path : (ctx?.path || []);
|
||||
const page = join('-', page_segs);
|
||||
|
||||
@ -14,7 +14,11 @@ const themeTokens = cursor().get_all('aurora', 'theme') || {};
|
||||
const loginLogoSvg = themeTokens.logo_svg || 'logo.svg';
|
||||
const loginIconVer = themeTokens.icon_cache_version || '0';
|
||||
-%}
|
||||
{% include('header', { blank_page: true }) %}
|
||||
{% include('header', {
|
||||
blank_page: true,
|
||||
prefetched_boardinfo: boardinfo,
|
||||
prefetched_tokens: themeTokens
|
||||
}) %}
|
||||
|
||||
{% if (themeTokens.struct_login_bg): %}
|
||||
<div class="login-bg" aria-hidden="true"></div>
|
||||
|
||||