Update On Sun Mar 2 19:33:11 CET 2025

This commit is contained in:
github-action[bot]
2025-03-02 19:33:11 +01:00
parent 2e98cc0017
commit f673e85f4b
102 changed files with 1874 additions and 700 deletions

1
.github/update.log vendored
View File

@@ -929,3 +929,4 @@ Update On Wed Feb 26 19:35:34 CET 2025
Update On Thu Feb 27 19:34:57 CET 2025
Update On Fri Feb 28 19:34:20 CET 2025
Update On Sat Mar 1 19:31:26 CET 2025
Update On Sun Mar 2 19:33:02 CET 2025

View File

@@ -21,10 +21,14 @@ type Client struct {
dialOut util.DialOutFunc
sessionCounter atomic.Uint64
sessionCounter atomic.Uint64
idleSession *skiplist.SkipList[uint64, *Session]
idleSessionLock sync.Mutex
sessions map[uint64]*Session
sessionsLock sync.Mutex
padding *atomic.TypedValue[*padding.PaddingFactory]
idleSessionTimeout time.Duration
@@ -33,6 +37,7 @@ type Client struct {
func NewClient(ctx context.Context, dialOut util.DialOutFunc, _padding *atomic.TypedValue[*padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration, minIdleSession int) *Client {
c := &Client{
sessions: make(map[uint64]*Session),
dialOut: dialOut,
padding: _padding,
idleSessionTimeout: idleSessionTimeout,
@@ -130,15 +135,35 @@ func (c *Client) createSession(ctx context.Context) (*Session, error) {
c.idleSessionLock.Lock()
c.idleSession.Remove(math.MaxUint64 - session.seq)
c.idleSessionLock.Unlock()
c.sessionsLock.Lock()
delete(c.sessions, session.seq)
c.sessionsLock.Unlock()
}
c.sessionsLock.Lock()
c.sessions[session.seq] = session
c.sessionsLock.Unlock()
session.Run()
return session, nil
}
func (c *Client) Close() error {
c.dieCancel()
c.minIdleSession = 0
go c.idleCleanupExpTime(time.Time{})
c.sessionsLock.Lock()
sessionToClose := make([]*Session, 0, len(c.sessions))
for seq, session := range c.sessions {
sessionToClose = append(sessionToClose, session)
delete(c.sessions, seq)
}
c.sessionsLock.Unlock()
for _, session := range sessionToClose {
session.Close()
}
return nil
}

View File

@@ -46,20 +46,27 @@ jobs:
libxdo-dev
webkit2gtk-driver
xvfb
- uses: Swatinem/rust-cache@v2
with:
workspaces: './backend/'
prefix-key: 'rust-stable'
shared-key: 'ci'
save-if: ${{ github.ref == 'refs/heads/main' || github.ref == 'refs/heads/dev' }}
- uses: maxim-lobanov/setup-xcode@v1
if: startsWith(matrix.targets.os, 'macos-')
with:
xcode-version: '15.0'
xcode-version: 'latest-stable'
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 20
node-version: 22
- uses: actions/cache@v4
name: Cache Rust dependencies
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- uses: pnpm/action-setup@v4
name: Install pnpm
@@ -97,6 +104,81 @@ jobs:
env:
NODE_OPTIONS: '--max_old_space_size=4096'
# TODO: support test cross-platform
build:
name: Build Tauri
strategy:
matrix:
targets:
- os: ubuntu-latest
- os: macos-latest
- os: windows-latest
fail-fast: false
runs-on: ${{ matrix.targets.os }}
needs: lint
steps:
- uses: actions/checkout@v4
- name: Tauri dependencies
if: startsWith(matrix.targets.os, 'ubuntu-')
run: >-
sudo apt-get update &&
sudo apt-get install -y
libgtk-3-dev
libayatana-appindicator3-dev
libwebkit2gtk-4.1-dev
librsvg2-dev
libxdo-dev
webkit2gtk-driver
xvfb
- uses: maxim-lobanov/setup-xcode@v1
if: startsWith(matrix.targets.os, 'macos-')
with:
xcode-version: 'latest-stable'
- name: Install Node.js
uses: actions/setup-node@v4
with:
node-version: 22
- uses: actions/cache@v4
name: Cache Rust dependencies
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
- uses: pnpm/action-setup@v4
name: Install pnpm
with:
run_install: false
- name: Get pnpm store directory
shell: bash
run: |
echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV
- uses: actions/cache@v4
name: Setup pnpm cache
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-pnpm-store-
- name: Install dependencies
run: pnpm install --no-frozen-lockfile
- name: Prepare sidecar and resources
run: pnpm check
- name: Build Tauri
run: pnpm tauri build
env:
NODE_OPTIONS: '--max_old_space_size=4096'
# 以后完善了新的测试套件后再添加
# test_unit:
# name: Unit Test

View File

@@ -84,12 +84,6 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libxdo-dev libappindicator3-dev librsvg2-dev patchelf openssl
- uses: Swatinem/rust-cache@v2
with:
workspaces: './backend/'
prefix-key: 'rust-stable'
key: ubuntu-latest
shared-key: 'release'
- name: Install Node latest
uses: actions/setup-node@v4

View File

@@ -56,13 +56,6 @@ jobs:
rustup install stable --profile minimal --no-self-update
rustup default stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: './backend/'
prefix-key: 'rust-stable'
key: 'macos-latest'
shared-key: 'release'
- name: Install Rust intel target
if: ${{ inputs.aarch64 == false }}
run: |
@@ -127,6 +120,9 @@ jobs:
echo "Uploading $file to release"
gh release upload ${{ inputs.tag }} "$file" --clobber
done
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload to Github Artifact
uses: actions/upload-artifact@v4

View File

@@ -85,13 +85,6 @@ jobs:
run: |
rustup target add ${{ inputs.arch }}-pc-windows-msvc
- uses: Swatinem/rust-cache@v2
with:
workspaces: './backend/'
prefix-key: 'rust-stable'
key: windows-latest
shared-key: 'release'
- name: Install Node latest
uses: actions/setup-node@v4
with:

View File

@@ -1387,13 +1387,17 @@ dependencies = [
"boa_engine",
"boa_gc",
"boa_parser",
"futures-concurrency",
"futures-util",
"indoc",
"isahc",
"log",
"rustc-hash 2.1.1",
"smol",
"test-log",
"textwrap",
"tracing",
"url",
]
[[package]]
@@ -2143,6 +2147,16 @@ dependencies = [
"version_check",
]
[[package]]
name = "cordyceps"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec10f0a762d93c4498d2e97a333805cb6250d60bead623f71d8034f9a4152ba3"
dependencies = [
"loom",
"tracing",
]
[[package]]
name = "core-foundation"
version = "0.9.4"
@@ -2608,6 +2622,12 @@ version = "1.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "339544cc9e2c4dc3fc7149fd630c5f22263a4fdf18a98afd0075784968b5cf00"
[[package]]
name = "diatomic-waker"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab03c107fafeb3ee9f5925686dbb7a73bc76e3932abb0d2b365cb64b169cf04c"
[[package]]
name = "diff"
version = "0.1.13"
@@ -3366,6 +3386,12 @@ version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
[[package]]
name = "fixedbitset"
version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99"
[[package]]
name = "flate2"
version = "1.1.0"
@@ -3535,6 +3561,19 @@ dependencies = [
"futures-util",
]
[[package]]
name = "futures-buffered"
version = "0.2.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe940397c8b744b9c2c974791c2c08bca2c3242ce0290393249e98f215a00472"
dependencies = [
"cordyceps",
"diatomic-waker",
"futures-core",
"pin-project-lite",
"spin",
]
[[package]]
name = "futures-channel"
version = "0.3.31"
@@ -3545,6 +3584,21 @@ dependencies = [
"futures-sink",
]
[[package]]
name = "futures-concurrency"
version = "7.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eb68017df91f2e477ed4bea586c59eaecaa47ed885a770d0444e21e62572cd2"
dependencies = [
"fixedbitset 0.5.7",
"futures-buffered",
"futures-core",
"futures-lite 2.6.0",
"pin-project",
"slab",
"smallvec",
]
[[package]]
name = "futures-core"
version = "0.3.31"
@@ -3760,6 +3814,19 @@ dependencies = [
"x11",
]
[[package]]
name = "generator"
version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cc16584ff22b460a382b7feec54b23d2908d858152e5739a120b949293bd74e"
dependencies = [
"cc",
"libc",
"log",
"rustversion",
"windows 0.48.0",
]
[[package]]
name = "generic-array"
version = "0.14.7"
@@ -5504,6 +5571,19 @@ version = "0.4.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
[[package]]
name = "loom"
version = "0.5.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff50ecb28bb86013e935fb6683ab1f6d3a20016f123c76fd4c27470076ac30f5"
dependencies = [
"cfg-if",
"generator",
"scoped-tls",
"tracing",
"tracing-subscriber",
]
[[package]]
name = "loop9"
version = "0.1.5"
@@ -7390,7 +7470,7 @@ version = "0.6.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
dependencies = [
"fixedbitset",
"fixedbitset 0.4.2",
"indexmap 2.7.1",
]
@@ -12410,6 +12490,15 @@ dependencies = [
"windows-version",
]
[[package]]
name = "windows"
version = "0.48.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f"
dependencies = [
"windows-targets 0.48.5",
]
[[package]]
name = "windows"
version = "0.52.0"

View File

@@ -13,9 +13,13 @@ boa_gc = { version = "0.20" }
boa_parser = { version = "0.20" }
isahc = "1.7"
futures-util = "0.3"
futures-concurrency = "7"
smol = "2"
tracing = "0.1"
url = "2"
log = "0.4"
[dev-dependencies]
indoc = "2"
textwrap = "0.16"
test-log = "0.2"

View File

@@ -0,0 +1,73 @@
use std::rc::Rc;
use boa_engine::module::ModuleLoader;
use url::Url;
pub struct CombineModuleLoader {
simple: Rc<boa_engine::module::SimpleModuleLoader>,
http: Rc<super::http::HttpModuleLoader>,
}
impl CombineModuleLoader {
pub fn new(
simple: boa_engine::module::SimpleModuleLoader,
http: super::http::HttpModuleLoader,
) -> Self {
Self {
simple: Rc::new(simple),
http: Rc::new(http),
}
}
pub fn clone_simple(&self) -> Rc<boa_engine::module::SimpleModuleLoader> {
self.simple.clone()
}
pub fn clone_http(&self) -> Rc<super::http::HttpModuleLoader> {
self.http.clone()
}
}
impl ModuleLoader for CombineModuleLoader {
fn load_imported_module(
&self,
referrer: boa_engine::module::Referrer,
specifier: boa_engine::JsString,
finish_load: Box<
dyn FnOnce(boa_engine::JsResult<boa_engine::Module>, &mut boa_engine::Context),
>,
context: &mut boa_engine::Context,
) {
let specifier_str = specifier.to_std_string_escaped();
match Url::parse(&specifier_str) {
Ok(url) if url.scheme() == "http" || url.scheme() == "https" => {
self.http
.load_imported_module(referrer, specifier, finish_load, context);
}
_ => {
self.simple
.load_imported_module(referrer, specifier, finish_load, context);
}
}
}
fn get_module(&self, _specifier: boa_engine::JsString) -> Option<boa_engine::Module> {
let specifier_str = _specifier.to_std_string_escaped();
match Url::parse(&specifier_str) {
Ok(url) if url.scheme() == "http" || url.scheme() == "https" => {
self.http.get_module(_specifier)
}
_ => self.simple.get_module(_specifier),
}
}
fn register_module(&self, _specifier: boa_engine::JsString, _module: boa_engine::Module) {
let specifier_str = _specifier.to_std_string_escaped();
match Url::parse(&specifier_str) {
Ok(url) if url.scheme() == "http" || url.scheme() == "https" => {
self.http.register_module(_specifier, _module);
}
_ => self.simple.register_module(_specifier, _module),
}
}
}

View File

@@ -1,4 +1,3 @@
// Most code is taken from https://github.com/boa-dev/boa/blob/main/examples/src/bin/module_fetch.rs
use std::{
cell::{Cell, RefCell},
collections::VecDeque,
@@ -40,7 +39,7 @@ impl ModuleLoader for HttpModuleLoader {
// Adding some prints to show the non-deterministic nature of the async fetches.
// Try to run the example several times to see how sometimes the fetches start in order
// but finish in disorder.
tracing::debug!("Fetching `{url}`...");
log::debug!("fetching `{url}`...");
// This could also retry fetching in case there's an error while requesting the module.
let body: Result<_, isahc::Error> = async {
let mut response = Request::get(&url)
@@ -52,7 +51,7 @@ impl ModuleLoader for HttpModuleLoader {
Ok(response.text().await?)
}
.await;
tracing::debug!("Finished fetching `{url}`");
log::debug!("finished fetching `{url}`");
// Since the async context cannot take the `context` by ref, we have to continue
// parsing inside a new `NativeJob` that will be enqueued into the promise job queue.
@@ -93,8 +92,8 @@ impl ModuleLoader for HttpModuleLoader {
}
}
#[allow(dead_code)]
fn main() -> JsResult<()> {
#[test]
fn test_http_module_loader() -> JsResult<()> {
// A simple snippet that imports modules from the web instead of the file system.
const SRC: &str = r#"
import YAML from 'https://esm.run/yaml@2.3.4';
@@ -184,6 +183,12 @@ pub struct Queue<'a> {
jobs: RefCell<VecDeque<NativeJob>>,
}
impl Default for Queue<'_> {
fn default() -> Self {
Self::new(LocalExecutor::new())
}
}
impl<'a> Queue<'a> {
fn new(executor: LocalExecutor<'a>) -> Self {
Self {
@@ -194,12 +199,6 @@ impl<'a> Queue<'a> {
}
}
impl Default for Queue<'_> {
fn default() -> Self {
Self::new(LocalExecutor::new())
}
}
impl JobQueue for Queue<'_> {
fn enqueue_promise_job(&self, job: NativeJob, _context: &mut Context) {
self.jobs.borrow_mut().push_back(job);

View File

@@ -1,7 +1,9 @@
#![allow(dead_code)]
use std::{cell::RefCell, collections::HashMap, rc::Rc};
use boa_engine::module::ModuleLoader as BoaModuleLoader;
use std::sync::Mutex;
pub mod combine;
pub mod http;
pub struct ModuleLoader(Vec<Rc<dyn BoaModuleLoader>>, Mutex<HashMap<String, usize>>);

View File

@@ -274,15 +274,3 @@ impl Drop for ClashConnectionsConnectorInner {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_connect_clash_server() {
"ws://127.0.0.1:12649:10808/connections"
.into_client_request()
.unwrap();
}
}

View File

@@ -12,7 +12,7 @@ use boa_engine::{
use boa_utils::{
Console,
module::{
ModuleLoader,
combine::CombineModuleLoader,
http::{HttpModuleLoader, Queue},
},
};
@@ -77,12 +77,11 @@ pub struct BoaRunner {
impl BoaRunner {
pub fn try_new() -> Result<Self> {
let simple_loader = Rc::new(SimpleModuleLoader::new(CUSTOM_SCRIPTS_DIR.as_path())?);
let http_loader: Rc<dyn BoaModuleLoader> = Rc::new(HttpModuleLoader);
let loader = Rc::new(ModuleLoader::from(vec![
simple_loader.clone() as Rc<dyn BoaModuleLoader>,
http_loader,
]));
let loader = Rc::new(CombineModuleLoader::new(
SimpleModuleLoader::new(CUSTOM_SCRIPTS_DIR.as_path())?,
HttpModuleLoader::default(),
));
let simple_loader = loader.clone_simple();
let queue = Rc::new(Queue::default());
let context = Context::builder()
.job_queue(queue)
@@ -333,6 +332,7 @@ mod utils {
}
}
#[cfg(test)]
mod test {
#[test]
fn test_wrap_script_if_not_esm() {
@@ -463,7 +463,7 @@ const foreignNameservers = [
});
}
#[test]
#[test_log::test]
fn test_process_honey_with_fetch() {
use super::{super::runner::Runner, JSRunner};
let runner = JSRunner::try_new().unwrap();

View File

@@ -207,6 +207,7 @@ impl Indexer {
.unwrap()
.replace(".", "_")
.replace(" ", "_")
.replace("-", "__")
))
.await
.context("failed to create index")?;

View File

@@ -139,6 +139,5 @@ mod test {
.await
.unwrap();
println!("{:?}", results);
assert_eq!(results.len(), 5);
}
}

View File

@@ -11,7 +11,7 @@
"build": "tsc"
},
"dependencies": {
"@tanstack/react-query": "5.66.9",
"@tanstack/react-query": "5.66.11",
"@tauri-apps/api": "2.2.0",
"ahooks": "3.8.4",
"dayjs": "1.11.13",

View File

@@ -50,6 +50,13 @@ export const useClashConnections = () => {
const query = useQuery<ClashConnection[]>({
queryKey: [CLASH_CONNECTIONS_QUERY_KEY],
queryFn: () => {
return (
queryClient.getQueryData<ClashConnection[]>([
CLASH_CONNECTIONS_QUERY_KEY,
]) || []
)
},
})
const deleteConnections = useMutation({

View File

@@ -16,6 +16,9 @@ export const useClashLogs = () => {
const query = useQuery<ClashLog[]>({
queryKey: [CLASH_LOGS_QUERY_KEY],
queryFn: () => {
return queryClient.getQueryData<ClashLog[]>([CLASH_LOGS_QUERY_KEY]) || []
},
})
const clean = useMutation({

View File

@@ -1,4 +1,4 @@
import { useQuery } from '@tanstack/react-query'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { CLASH_MEMORY_QUERY_KEY } from './consts'
export type ClashMemory = {
@@ -7,8 +7,15 @@ export type ClashMemory = {
}
export const useClashMemory = () => {
const queryClient = useQueryClient()
const query = useQuery<ClashMemory[]>({
queryKey: [CLASH_MEMORY_QUERY_KEY],
queryFn: () => {
return (
queryClient.getQueryData<ClashMemory[]>([CLASH_MEMORY_QUERY_KEY]) || []
)
},
})
return query

View File

@@ -1,4 +1,4 @@
import { useQuery } from '@tanstack/react-query'
import { useQuery, useQueryClient } from '@tanstack/react-query'
import { CLASH_TRAAFFIC_QUERY_KEY } from './consts'
export type ClashTraffic = {
@@ -7,8 +7,16 @@ export type ClashTraffic = {
}
export const useClashTraffic = () => {
const queryClient = useQueryClient()
const query = useQuery<ClashTraffic[]>({
queryKey: [CLASH_TRAAFFIC_QUERY_KEY],
queryFn: () => {
return (
queryClient.getQueryData<ClashTraffic[]>([CLASH_TRAAFFIC_QUERY_KEY]) ||
[]
)
},
})
return query

View File

@@ -33,7 +33,7 @@
"i18next": "24.2.2",
"jotai": "2.12.1",
"json-schema": "0.4.0",
"material-react-table": "3.2.0",
"material-react-table": "3.2.1",
"monaco-editor": "0.52.2",
"mui-color-input": "5.0.1",
"react": "19.0.0",
@@ -55,7 +55,7 @@
"@emotion/react": "11.14.0",
"@iconify/json": "2.2.311",
"@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.66.9",
"@tanstack/react-query": "5.66.11",
"@tanstack/react-router": "1.112.0",
"@tanstack/router-devtools": "1.112.0",
"@tanstack/router-plugin": "1.112.0",

View File

@@ -3,6 +3,7 @@ import dayjs from 'dayjs'
import { useSetAtom } from 'jotai'
import { lazy, Suspense, useCallback, useState, useTransition } from 'react'
import { useTranslation } from 'react-i18next'
import { IS_NIGHTLY } from '@/consts'
import { UpdaterIgnoredAtom } from '@/store/updater'
import { formatError } from '@/utils'
import { message } from '@/utils/notification'
@@ -71,6 +72,10 @@ export default function UpdaterDialog({
})
})
const releasesPageUrl = IS_NIGHTLY
? `https://github.com/libnyanpasu/clash-nyanpasu/releases/tag/pre-releases`
: `https://github.com/libnyanpasu/clash-nyanpasu/releases/tag/v${update.version}`
return (
<BaseDialog
{...others}
@@ -105,9 +110,7 @@ export default function UpdaterDialog({
variant="contained"
size="small"
onClick={() => {
openThat(
`https://github.com/libnyanpasu/clash-nyanpasu/releases/tag/v${update.version}`,
)
openThat(releasesPageUrl)
}}
>
{t('updater.go')}

View File

@@ -1,3 +1,8 @@
/* eslint-disable */
// @ts-nocheck
import { getSystem } from '@nyanpasu/ui'
export const OS = getSystem()
export const IS_NIGHTLY = window.__IS_NIGHTLY__ === true

View File

@@ -14,6 +14,8 @@ import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import legacy from '@vitejs/plugin-legacy'
import react from '@vitejs/plugin-react-swc'
const IS_NIGHTLY = process.env.NIGHTLY?.toLowerCase() === 'true'
const devtools = () => {
return {
name: 'react-devtools',
@@ -26,7 +28,17 @@ const devtools = () => {
}
}
const IS_NIGHTLY = process.env.NIGHTLY?.toLowerCase() === 'true'
const builtinVars = () => {
return {
name: 'built-in-vars',
transformIndexHtml(html: string) {
return html.replace(
/<\/head>/,
`<script>window.__IS_NIGHTLY__ = true</script></head>`,
)
},
}
}
// https://vitejs.dev/config/
export default defineConfig(({ command, mode }) => {
@@ -80,6 +92,7 @@ export default defineConfig(({ command, mode }) => {
},
},
}),
builtinVars(),
TanStackRouterVite(),
svgr(),
react({

View File

@@ -65,7 +65,7 @@
"@tauri-apps/cli": "2.2.7",
"@types/fs-extra": "11.0.4",
"@types/lodash-es": "4.17.12",
"@types/node": "22.13.5",
"@types/node": "22.13.8",
"@typescript-eslint/eslint-plugin": "8.25.0",
"@typescript-eslint/parser": "8.25.0",
"autoprefixer": "10.4.20",
@@ -106,7 +106,7 @@
"stylelint-scss": "6.11.1",
"tailwindcss": "4.0.9",
"tsx": "4.19.3",
"typescript": "5.7.3",
"typescript": "5.8.2",
"typescript-eslint": "8.25.0"
},
"packageManager": "pnpm@10.5.2",

File diff suppressed because it is too large Load Diff

View File

@@ -1,2 +1,2 @@
[toolchain]
channel = "nightly"
channel = "nightly-2025-02-25"

View File

@@ -92,12 +92,6 @@ jobs:
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: pnpm portable ${{ matrix.target }} --alpha
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
alpha-for-linux-arm:
strategy:
fail-fast: false
@@ -347,7 +341,7 @@ jobs:
- Linux arm64 architecture: arm64.deb/aarch64.rpm
- Linux armv7架构: armhf.deb/armhfp.rpm
### Windows (Win7 用户请查看下面FAQ中的解决方案)
### Windows (不再支持Win7)
#### 正常版本(推荐)
- 64位: x64-setup.exe
- arm64架构: arm64-setup.exe

View File

@@ -0,0 +1,60 @@
name: Label Issue
on:
workflow_dispatch:
issues:
types: [opened, edited]
jobs:
label-issue:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Label issue if it contains "windows"
uses: actions/github-script@v7
with:
script: |
const { data: issue } = await github.rest.issues.get({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const labelsToAdd = [];
const existingLabels = issue.labels.map(label => label.name);
const bodyContent = issue.body.toLowerCase();
// Check for keywords and add corresponding labels
if (bodyContent.includes('windows')) {
labelsToAdd.push('windows');
}
if (bodyContent.includes('linux')) {
labelsToAdd.push('linux');
}
if (bodyContent.includes('macos')) {
labelsToAdd.push('macos');
}
if (bodyContent.includes("托盘") || bodyContent.includes("tray")) {
labelsToAdd.push('tray');
}
if (bodyContent.includes("菜单") || bodyContent.includes("menu")) {
labelsToAdd.push('menu');
}
// Add labels if any match
// Filter out labels that already exist to avoid duplication
const newLabelsToAdd = labelsToAdd.filter(label => !existingLabels.includes(label));
// Add labels if there are new ones to add
if (newLabelsToAdd.length > 0) {
await github.rest.issues.addLabels({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
labels: newLabelsToAdd,
});
}

View File

@@ -87,12 +87,6 @@ jobs:
tauriScript: pnpm
args: --target ${{ matrix.target }}
- name: Portable Bundle
if: matrix.os == 'windows-latest'
run: pnpm portable ${{ matrix.target }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
release-for-linux-arm:
strategy:
fail-fast: false
@@ -221,9 +215,6 @@ jobs:
- os: windows-latest
target: x86_64-pc-windows-msvc
arch: x64
- os: windows-latest
target: i686-pc-windows-msvc
arch: x86
- os: windows-latest
target: aarch64-pc-windows-msvc
arch: arm64

View File

@@ -38,7 +38,7 @@ npm install pnpm -g
pnpm install
```
### Download the Clash Mihomo Core Binary
### Download the Mihomo Core Binary
You have two options for downloading the clash binary:
@@ -48,7 +48,7 @@ You have two options for downloading the clash binary:
# Use '--force' to force update to the latest version
# pnpm run check --force
```
- Manually download it from the [Clash Meta release](https://github.com/MetaCubeX/Clash.Meta/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
- Manually download it from the [Mihomo release](https://github.com/MetaCubeX/mihomo/releases). After downloading, rename the binary according to the [Tauri configuration](https://tauri.app/v1/api/config#bundleconfig.externalbin).
### Run the Development Server
@@ -65,11 +65,35 @@ pnpm dev:diff
To build this project:
```shell
pnpm run build
pnpm build
```
For a faster build, use the following command
```shell
pnpm build:fast
```
This uses Rust's fast-release profile which significantly reduces compilation time by disabling optimization and LTO. The resulting binary will be larger and less performant than the standard build, but it's useful for testing changes quickly.
The `Artifacts` will display in the `log` in the Terminal.
### Build clean
To clean rust build:
```shell
pnpm clean
```
### Portable Version (Windows Only)
To package portable version after the build:
```shell
pnpm portable
```
## Contributing Your Changes
Once you have made your changes:

View File

@@ -1,13 +1,12 @@
{
"name": "clash-verge",
"version": "2.1.2",
"version": "2.1.3-alpha",
"license": "GPL-3.0-only",
"scripts": {
"dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
"dev:fast": "cross-env RUST_BACKTRACE=1 tauri dev",
"dev:diff": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev",
"dev": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev -- --profile fast-dev",
"dev:diff": "cross-env RUST_BACKTRACE=1 tauri dev -f verge-dev -- --profile fast-dev",
"build": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build",
"build:fast": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build",
"build:fast": "cross-env NODE_OPTIONS='--max-old-space-size=4096' tauri build -- --profile fast-release",
"tauri": "tauri",
"web:dev": "vite",
"web:build": "tsc --noEmit && vite build",
@@ -18,7 +17,8 @@
"portable": "node scripts/portable.mjs",
"portable-fixed-webview2": "node scripts/portable-fixed-webview2.mjs",
"fix-alpha-version": "node scripts/alpha_version.mjs",
"prepare": "husky"
"prepare": "husky",
"clean": "cd ./src-tauri && cargo clean && cd -"
},
"dependencies": {
"@dnd-kit/core": "^6.3.1",

View File

@@ -26,29 +26,41 @@ async function getLatestCommitHash() {
/**
* @param string 传入格式化后的hash
* 将新的版本号写入文件 package.json
* 将新的版本号写入文件 package.json / tauri.conf.json
*/
async function updatePackageVersion(newVersion) {
// 获取内容根目录
const _dirname = process.cwd();
const packageJsonPath = path.join(_dirname, "package.json");
const tauriDir = path.join(_dirname, "src-tauri");
const internalfile = path.join(tauriDir, "tauri.conf.json");
try {
const data = await fs.readFile(packageJsonPath, "utf8");
const tauriData = await fs.readFile(internalfile, "utf8");
const packageJson = JSON.parse(data);
const initversion = packageJson.version;
// 将匹配到的第一个 "alpha" => 具体的hash
const fixversion = initversion.replace("alpha", newVersion);
packageJson.version = fixversion;
const tauriJson = JSON.parse(tauriData);
let result = packageJson.version.replace("alpha", newVersion);
console.log("[INFO]: Current version is: ", result);
packageJson.version = result;
tauriJson.version = result;
// 写入版本号
await fs.writeFile(
packageJsonPath,
JSON.stringify(packageJson, null, 2),
"utf8",
);
console.log(`Alpha version update to: ${fixversion}`);
await fs.writeFile(
internalfile,
JSON.stringify(tauriJson, null, 2),
"utf8",
);
console.log(`[INFO]: Alpha version update to: ${newVersion}`);
} catch (error) {
console.error("pnpm run fix-alpha-version ERROR", error);
}
}
const newVersion = await getLatestCommitHash();
updatePackageVersion(newVersion);
updatePackageVersion(newVersion).catch(console.error);

View File

@@ -2,20 +2,16 @@ import fs from "fs";
import path from "path";
import AdmZip from "adm-zip";
import { createRequire } from "module";
import { getOctokit, context } from "@actions/github";
import fsp from "fs/promises";
const target = process.argv.slice(2)[0];
const alpha = process.argv.slice(2)[1];
const ARCH_MAP = {
"x86_64-pc-windows-msvc": "x64",
"i686-pc-windows-msvc": "x86",
"aarch64-pc-windows-msvc": "arm64",
};
const PROCESS_MAP = {
x64: "x64",
ia32: "x86",
arm64: "arm64",
};
const arch = target ? ARCH_MAP[target] : PROCESS_MAP[process.arch];
@@ -37,10 +33,9 @@ async function resolvePortable() {
if (!fs.existsSync(path.join(configDir, "PORTABLE"))) {
await fsp.writeFile(path.join(configDir, "PORTABLE"), "");
}
const zip = new AdmZip();
zip.addLocalFile(path.join(releaseDir, "Clash Verge.exe"));
zip.addLocalFile(path.join(releaseDir, "clash-verge.exe"));
zip.addLocalFile(path.join(releaseDir, "verge-mihomo.exe"));
zip.addLocalFile(path.join(releaseDir, "verge-mihomo-alpha.exe"));
zip.addLocalFolder(path.join(releaseDir, "resources"), "resources");
@@ -49,46 +44,9 @@ async function resolvePortable() {
const require = createRequire(import.meta.url);
const packageJson = require("../package.json");
const { version } = packageJson;
const zipFile = `Clash.Verge_${version}_${arch}_portable.zip`;
zip.writeZip(zipFile);
console.log("[INFO]: create portable zip successfully");
// push release assets
if (process.env.GITHUB_TOKEN === undefined) {
throw new Error("GITHUB_TOKEN is required");
}
const options = { owner: context.repo.owner, repo: context.repo.repo };
const github = getOctokit(process.env.GITHUB_TOKEN);
const tag = alpha ? "alpha" : process.env.TAG_NAME || `v${version}`;
console.log("[INFO]: upload to ", tag);
const { data: release } = await github.rest.repos.getReleaseByTag({
...options,
tag,
});
let assets = release.assets.filter((x) => {
return x.name === zipFile;
});
if (assets.length > 0) {
let id = assets[0].id;
await github.rest.repos.deleteReleaseAsset({
...options,
asset_id: id,
});
}
console.log(release.name);
await github.rest.repos.uploadReleaseAsset({
...options,
release_id: release.id,
name: zipFile,
data: zip.toBuffer(),
});
}
resolvePortable().catch(console.error);

View File

@@ -57,13 +57,6 @@ pub fn copy_clash_env() {
let http_proxy = format!("http://{clash_verge_rev_ip}:{}", port);
let socks5_proxy = format!("socks5://{clash_verge_rev_ip}:{}", port);
let sh =
format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}");
let cmd: String = format!("set http_proxy={http_proxy}\r\nset https_proxy={http_proxy}");
let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
let nu: String =
format!("load-env {{ http_proxy: \"{http_proxy}\", https_proxy: \"{http_proxy}\" }}");
let cliboard = app_handle.clipboard();
let env_type = { Config::verge().latest().env_type.clone() };
let env_type = match env_type {
@@ -77,11 +70,20 @@ pub fn copy_clash_env() {
default.to_string()
}
};
match env_type.as_str() {
"bash" => cliboard.write_text(sh).unwrap_or_default(),
"cmd" => cliboard.write_text(cmd).unwrap_or_default(),
"powershell" => cliboard.write_text(ps).unwrap_or_default(),
"nushell" => cliboard.write_text(nu).unwrap_or_default(),
_ => log::error!(target: "app", "copy_clash_env: Invalid env type! {env_type}"),
let export_text = match env_type.as_str() {
"bash" => format!("export https_proxy={http_proxy} http_proxy={http_proxy} all_proxy={socks5_proxy}"),
"cmd" => format!("set http_proxy={http_proxy}\r\nset https_proxy={http_proxy}"),
"powershell" => format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\""),
"nushell" => format!("load-env {{ http_proxy: \"{http_proxy}\", https_proxy: \"{http_proxy}\" }}"),
"fish" => format!("set -x http_proxy {http_proxy}; set -x https_proxy {http_proxy}"),
_ => {
log::error!(target: "app", "copy_clash_env: Invalid env type! {env_type}");
return;
}
};
if let Err(_) = cliboard.write_text(export_text) {
log::error!(target: "app", "Failed to write to clipboard");
}
}

View File

@@ -11,6 +11,8 @@ use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_deep_link::DeepLinkExt;
use std::sync::{Mutex, Once};
use tauri::AppHandle;
#[cfg(target_os = "macos")]
use tauri::Manager;
/// A global singleton handle to the application.
pub struct AppHandleManager {
@@ -203,6 +205,11 @@ pub fn run() {
app.run(|app_handle, e| match e {
tauri::RunEvent::Ready | tauri::RunEvent::Resumed => {
AppHandleManager::global().init(app_handle.clone());
#[cfg(target_os = "macos")]
{
let main_window = AppHandleManager::global().get_handle().get_webview_window("main").unwrap();
let _ = main_window.set_title("Clash Verge");
}
}
#[cfg(target_os = "macos")]
tauri::RunEvent::Reopen { has_visible_windows, .. } => {
@@ -220,6 +227,7 @@ pub fn run() {
if label == "main" {
match event {
tauri::WindowEvent::CloseRequested { api, .. } => {
#[cfg(target_os = "macos")]
AppHandleManager::global().set_activation_policy_accessory();
if core::handle::Handle::global().is_exiting() {
return;

View File

@@ -1,7 +1,9 @@
use crate::config::IVerge;
use crate::utils::error;
use crate::{config::Config, config::PrfItem, core::*, utils::init, utils::server};
use crate::{log_err, wrap_err, AppHandleManager};
use crate::{log_err, wrap_err};
#[cfg(target_os = "macos")]
use crate::AppHandleManager;
use anyhow::{bail, Result};
use once_cell::sync::OnceCell;
use percent_encoding::percent_decode_str;
@@ -133,6 +135,7 @@ pub fn create_window() {
log::info!(target: "app", "Starting to create window");
let app_handle = handle::Handle::global().app_handle().unwrap();
#[cfg(target_os = "macos")]
AppHandleManager::global().set_activation_policy_regular();
if let Some(window) = handle::Handle::global().get_window() {

View File

@@ -1,4 +1,5 @@
{
"version": "2.1.3-alpha",
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"bundle": {
"active": true,
@@ -25,7 +26,6 @@
"devUrl": "http://localhost:3000/"
},
"productName": "Clash Verge",
"version": "2.1.2",
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"plugins": {
"updater": {

View File

@@ -32,11 +32,6 @@
}
},
"app": {
"windows": [
{
"title": "Clash Verge"
}
],
"trayIcon": {
"iconPath": "icons/tray-icon-mono.ico",
"iconAsTemplate": true

View File

@@ -490,7 +490,7 @@ export const ProxyGroups = (props: Props) => {
scrollerRef.current = ref as Element;
}}
components={{
Footer: () => <div style={{ height: "2px" }} />,
Footer: () => <div style={{ height: "8px" }} />,
}}
// 添加平滑滚动设置
initialScrollTop={scrollPositionRef.current[mode]}
@@ -508,8 +508,13 @@ export const ProxyGroups = (props: Props) => {
)}
/>
<ScrollTopButton show={showScrollTop} onClick={scrollToTop} />
<AlphabetSelector ref={alphabetSelectorRef} style={{ maxHeight }}>
<AlphabetSelector
ref={alphabetSelectorRef}
style={{
maxHeight,
right: 10,
}}
>
<div className="scroll-container">
<div ref={letterContainerRef} className="letter-container">
{groupFirstLetters.map((name) => (

View File

@@ -66,8 +66,8 @@ export const ProxyRender = (props: RenderProps) => {
style={{
background: itembackgroundcolor,
height: "100%",
margin: "8px 8px",
borderRadius: "8px",
margin: "10px 16px",
borderRadius: "10px",
}}
onClick={() => onHeadState(group.name, { open: !headState?.open })}
>
@@ -131,7 +131,7 @@ export const ProxyRender = (props: RenderProps) => {
if (type === 1) {
return (
<ProxyHead
sx={{ pl: 2, pr: 3, mt: indent ? 1 : 0.5, mb: 1 }}
sx={{ pl: 3, pr: 3.5, mt: indent ? 1 : 0.5, mb: 1 }}
url={group.testUrl}
groupName={group.name}
headState={headState!}
@@ -191,10 +191,10 @@ export const ProxyRender = (props: RenderProps) => {
sx={{
height: 56,
display: "grid",
gap: 1,
pl: 2,
pr: 2,
pb: 1,
gap: 1.5,
pl: 3,
pr: 3.5,
pb: 1.25,
gridTemplateColumns: `repeat(${item.col! || 2}, 1fr)`,
}}
>

View File

@@ -142,8 +142,9 @@ const SettingVergeBasic = ({ onError }: Props) => {
>
<Select size="small" sx={{ width: 140, "> div": { py: "7.5px" } }}>
<MenuItem value="bash">Bash</MenuItem>
<MenuItem value="cmd">CMD</MenuItem>
<MenuItem value="fish">Fish</MenuItem>
<MenuItem value="nushell">Nushell</MenuItem>
<MenuItem value="cmd">CMD</MenuItem>
<MenuItem value="powershell">PowerShell</MenuItem>
</Select>
</GuardState>

View File

@@ -5,18 +5,19 @@ import { useClashInfo } from "./use-clash";
import dayjs from "dayjs";
import { create } from "zustand";
import { useVisibility } from "./use-visibility";
import {
useGlobalLogData,
clearGlobalLogs,
LogLevel,
ILogItem,
} from "@/services/global-log-service";
// 为了向后兼容,导出相同的类型
export type { LogLevel };
export type { ILogItem };
const MAX_LOG_NUM = 1000;
export type LogLevel = "warning" | "info" | "debug" | "error" | "all";
interface ILogItem {
time?: string;
type: string;
payload: string;
[key: string]: any;
}
const buildWSUrl = (server: string, secret: string, logLevel: LogLevel) => {
const baseUrl = `ws://${server}/logs`;
const params = new URLSearchParams();
@@ -57,45 +58,6 @@ const useLogStore = create<LogStore>(
}),
);
export const useLogData = (logLevel: LogLevel) => {
const { clashInfo } = useClashInfo();
const [enableLog] = useEnableLog();
const { logs, appendLog } = useLogStore();
const pageVisible = useVisibility();
export const useLogData = useGlobalLogData;
useEffect(() => {
if (!enableLog || !clashInfo || !pageVisible) return;
const { server = "", secret = "" } = clashInfo;
const wsUrl = buildWSUrl(server, secret, logLevel);
let isActive = true;
const socket = createSockette(wsUrl, {
onmessage(event) {
if (!isActive) return;
const data = JSON.parse(event.data) as ILogItem;
const time = dayjs().format("MM-DD HH:mm:ss");
appendLog({ ...data, time });
},
onerror() {
if (!isActive) return;
socket.close();
},
});
return () => {
isActive = false;
socket.close();
};
}, [clashInfo, enableLog, logLevel]);
// 根据当前选择的日志等级过滤日志
return logLevel === "all"
? logs
: logs.filter((log) => log.type.toLowerCase() === logLevel);
};
// 导出清空日志的方法
export const clearLogs = () => {
useLogStore.getState().clearLogs();
};
export const clearLogs = clearGlobalLogs;

View File

@@ -13,7 +13,7 @@ import { useVerge } from "@/hooks/use-verge";
import LogoSvg from "@/assets/image/logo.svg?react";
import iconLight from "@/assets/image/icon_light.svg?react";
import iconDark from "@/assets/image/icon_dark.svg?react";
import { useThemeMode } from "@/services/states";
import { useThemeMode, useEnableLog } from "@/services/states";
import { Notice } from "@/components/base";
import { LayoutItem } from "@/components/layout/layout-item";
import { LayoutControl } from "@/components/layout/layout-control";
@@ -28,6 +28,8 @@ import React from "react";
import { TransitionGroup, CSSTransition } from "react-transition-group";
import { useListen } from "@/hooks/use-listen";
import { listen } from "@tauri-apps/api/event";
import { useClashInfo } from "@/hooks/use-clash";
import { initGlobalLogService } from "@/services/global-log-service";
const appWindow = getCurrentWebviewWindow();
export let portableFlag = false;
@@ -126,6 +128,8 @@ const Layout = () => {
const { t } = useTranslation();
const { theme } = useCustomTheme();
const { verge } = useVerge();
const { clashInfo } = useClashInfo();
const [enableLog] = useEnableLog();
const { language, start_page } = verge ?? {};
const navigate = useNavigate();
const location = useLocation();
@@ -140,6 +144,15 @@ const Layout = () => {
[t, navigate],
);
// 初始化全局日志服务
useEffect(() => {
if (clashInfo) {
const { server = "", secret = "" } = clashInfo;
// 使用本地存储中的enableLog值初始化全局日志服务
initGlobalLogService(server, secret, enableLog, "info");
}
}, [clashInfo, enableLog]);
// 设置监听器
useEffect(() => {
const listeners = [

View File

@@ -8,7 +8,8 @@ import {
PlayCircleOutlineRounded,
PauseCircleOutlineRounded,
} from "@mui/icons-material";
import { useLogData, LogLevel, clearLogs } from "@/hooks/use-log-data";
import { LogLevel, clearLogs } from "@/hooks/use-log-data";
import { useClashInfo } from "@/hooks/use-clash";
import { useEnableLog } from "@/services/states";
import { BaseEmpty, BasePage } from "@/components/base";
import LogItem from "@/components/log/log-item";
@@ -16,10 +17,17 @@ import { useTheme } from "@mui/material/styles";
import { BaseSearchBox } from "@/components/base/base-search-box";
import { BaseStyledSelect } from "@/components/base/base-styled-select";
import { SearchState } from "@/components/base/base-search-box";
import {
useGlobalLogData,
clearGlobalLogs,
changeLogLevel,
toggleLogEnabled,
} from "@/services/global-log-service";
const LogPage = () => {
const { t } = useTranslation();
const [enableLog, setEnableLog] = useEnableLog();
const { clashInfo } = useClashInfo();
const theme = useTheme();
const isDark = theme.palette.mode === "dark";
const [logLevel, setLogLevel] = useLocalStorage<LogLevel>(
@@ -27,7 +35,7 @@ const LogPage = () => {
"info",
);
const [match, setMatch] = useState(() => (_: string) => true);
const logData = useLogData(logLevel);
const logData = useGlobalLogData(logLevel);
const [searchState, setSearchState] = useState<SearchState>();
const filterLogs = useMemo(() => {
@@ -44,6 +52,22 @@ const LogPage = () => {
: [];
}, [logData, logLevel, match]);
const handleLogLevelChange = (newLevel: LogLevel) => {
setLogLevel(newLevel);
if (clashInfo) {
const { server = "", secret = "" } = clashInfo;
changeLogLevel(newLevel, server, secret);
}
};
const handleToggleLog = () => {
if (clashInfo) {
const { server = "", secret = "" } = clashInfo;
toggleLogEnabled(server, secret);
setEnableLog(!enableLog);
}
};
return (
<BasePage
full
@@ -60,7 +84,7 @@ const LogPage = () => {
title={t("Pause")}
size="small"
color="inherit"
onClick={() => setEnableLog((e) => !e)}
onClick={handleToggleLog}
>
{enableLog ? (
<PauseCircleOutlineRounded />
@@ -74,7 +98,7 @@ const LogPage = () => {
size="small"
variant="contained"
onClick={() => {
clearLogs();
clearGlobalLogs();
}}
>
{t("Clear")}
@@ -95,7 +119,7 @@ const LogPage = () => {
>
<BaseStyledSelect
value={logLevel}
onChange={(e) => setLogLevel(e.target.value as LogLevel)}
onChange={(e) => handleLogLevelChange(e.target.value as LogLevel)}
>
<MenuItem value="all">ALL</MenuItem>
<MenuItem value="info">INFO</MenuItem>

View File

@@ -0,0 +1,168 @@
// 全局日志服务,使应用在任何页面都能收集日志
import { create } from "zustand";
import { createSockette } from "../utils/websocket";
import dayjs from "dayjs";
import { useState, useEffect } from "react";
// 最大日志数量
const MAX_LOG_NUM = 1000;
export type LogLevel = "warning" | "info" | "debug" | "error" | "all";
export interface ILogItem {
time?: string;
type: string;
payload: string;
[key: string]: any;
}
interface GlobalLogStore {
logs: ILogItem[];
enabled: boolean;
isConnected: boolean;
currentLevel: LogLevel;
setEnabled: (enabled: boolean) => void;
setCurrentLevel: (level: LogLevel) => void;
clearLogs: () => void;
appendLog: (log: ILogItem) => void;
}
// 创建全局状态存储
export const useGlobalLogStore = create<GlobalLogStore>((set) => ({
logs: [],
enabled: false,
isConnected: false,
currentLevel: "info",
setEnabled: (enabled) => set({ enabled }),
setCurrentLevel: (currentLevel) => set({ currentLevel }),
clearLogs: () => set({ logs: [] }),
appendLog: (log: ILogItem) =>
set((state) => {
const newLogs =
state.logs.length >= MAX_LOG_NUM
? [...state.logs.slice(1), log]
: [...state.logs, log];
return { logs: newLogs };
}),
}));
// 构建WebSocket URL
const buildWSUrl = (server: string, secret: string, logLevel: LogLevel) => {
const baseUrl = `ws://${server}/logs`;
const params = new URLSearchParams();
if (secret) {
params.append("token", secret);
}
if (logLevel === "all") {
params.append("level", "debug");
} else {
params.append("level", logLevel);
}
const queryString = params.toString();
return queryString ? `${baseUrl}?${queryString}` : baseUrl;
};
// 初始化全局日志服务
let globalLogSocket: any = null;
export const initGlobalLogService = (
server: string,
secret: string,
enabled: boolean = false,
logLevel: LogLevel = "info",
) => {
const { appendLog, setEnabled } = useGlobalLogStore.getState();
// 更新启用状态
setEnabled(enabled);
// 如果不启用或没有服务器信息,则不初始化
if (!enabled || !server) {
closeGlobalLogConnection();
return;
}
// 关闭现有连接
closeGlobalLogConnection();
// 创建新的WebSocket连接
const wsUrl = buildWSUrl(server, secret, logLevel);
globalLogSocket = createSockette(wsUrl, {
onmessage(event) {
try {
const data = JSON.parse(event.data) as ILogItem;
const time = dayjs().format("MM-DD HH:mm:ss");
appendLog({ ...data, time });
} catch (error) {
console.error("Failed to parse log data:", error);
}
},
onerror() {
console.error("Log WebSocket connection error");
closeGlobalLogConnection();
},
onclose() {
console.log("Log WebSocket connection closed");
useGlobalLogStore.setState({ isConnected: false });
},
onopen() {
console.log("Log WebSocket connection opened");
useGlobalLogStore.setState({ isConnected: true });
},
});
};
// 关闭全局日志连接
export const closeGlobalLogConnection = () => {
if (globalLogSocket) {
globalLogSocket.close();
globalLogSocket = null;
useGlobalLogStore.setState({ isConnected: false });
}
};
// 切换日志级别
export const changeLogLevel = (
level: LogLevel,
server: string,
secret: string,
) => {
const { enabled } = useGlobalLogStore.getState();
useGlobalLogStore.setState({ currentLevel: level });
if (enabled && server) {
initGlobalLogService(server, secret, enabled, level);
}
};
// 切换启用状态
export const toggleLogEnabled = (server: string, secret: string) => {
const { enabled, currentLevel } = useGlobalLogStore.getState();
const newEnabled = !enabled;
useGlobalLogStore.setState({ enabled: newEnabled });
if (newEnabled && server) {
initGlobalLogService(server, secret, newEnabled, currentLevel);
} else {
closeGlobalLogConnection();
}
};
// 获取日志清理函数
export const clearGlobalLogs = () => {
useGlobalLogStore.getState().clearLogs();
};
// 自定义钩子,用于获取过滤后的日志数据
export const useGlobalLogData = (logLevel: LogLevel = "all") => {
const logs = useGlobalLogStore((state) => state.logs);
// 根据当前选择的日志等级过滤日志
if (logLevel === "all") {
return logs;
} else {
return logs.filter((log) => log.type.toLowerCase() === logLevel);
}
};

View File

@@ -694,7 +694,7 @@ interface IVergeConfig {
app_log_level?: "trace" | "debug" | "info" | "warn" | "error" | string;
language?: string;
tray_event?: "main_window" | "system_proxy" | "tun_mode" | string;
env_type?: "bash" | "cmd" | "powershell" | string;
env_type?: "bash" | "cmd" | "powershell" | "fish" | string;
startup_script?: string;
start_page?: string;
clash_core?: string;

View File

@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=pcie_mhi
PKG_VERSION:=1.3.6
PKG_RELEASE:=1
PKG_RELEASE:=2
include $(INCLUDE_DIR)/kernel.mk
include $(INCLUDE_DIR)/package.mk

View File

@@ -68,15 +68,13 @@ struct rmnet_nss_cb {
int (*nss_tx)(struct sk_buff *skb);
};
static struct rmnet_nss_cb __read_mostly *nss_cb = NULL;
#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018) || defined(CONFIG_PINCTRL_IPQ8074)
//#ifdef CONFIG_RMNET_DATA //spf12.x have no macro defined, just for spf11.x
#if defined(CONFIG_PINCTRL_IPQ807x) || defined(CONFIG_PINCTRL_IPQ5018)
#ifdef CONFIG_RMNET_DATA
#define CONFIG_QCA_NSS_DRV
/* define at qsdk/qca/src/linux-4.4/net/rmnet_data/rmnet_data_main.c */ //for spf11.x
/* define at qsdk/qca/src/datarmnet/core/rmnet_config.c */ //for spf12.x
/* define at qsdk/qca/src/linux-4.4/net/rmnet_data/rmnet_data_main.c */
/* set at qsdk/qca/src/data-kernel/drivers/rmnet-nss/rmnet_nss.c */
/* need add DEPENDS:= kmod-rmnet-core in feeds/makefile */
extern struct rmnet_nss_cb *rmnet_nss_callbacks __rcu __read_mostly;
//#endif
#endif
#endif
static const unsigned char node_id[ETH_ALEN] = {0x02, 0x50, 0xf4, 0x00, 0x00, 0x00};

View File

@@ -561,6 +561,7 @@ CONFIG_SCSI_SAS_LIBSAS=y
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SENSORS_ARM_SCMI is not set
CONFIG_SENSORS_ARM_SCPI=y
CONFIG_SENSORS_PWM_FAN=y
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_DWLIB=y

View File

@@ -625,6 +625,7 @@ CONFIG_SCSI_SAS_LIBSAS=y
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SENSORS_ARM_SCMI is not set
CONFIG_SENSORS_ARM_SCPI=y
CONFIG_SENSORS_PWM_FAN=y
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_DWLIB=y

View File

@@ -605,6 +605,7 @@ CONFIG_SCSI_SAS_LIBSAS=y
# CONFIG_SECURITY_DMESG_RESTRICT is not set
# CONFIG_SENSORS_ARM_SCMI is not set
CONFIG_SENSORS_ARM_SCPI=y
CONFIG_SENSORS_PWM_FAN=y
CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
CONFIG_SERIAL_8250_DW=y
CONFIG_SERIAL_8250_DWLIB=y

View File

@@ -21,10 +21,14 @@ type Client struct {
dialOut util.DialOutFunc
sessionCounter atomic.Uint64
sessionCounter atomic.Uint64
idleSession *skiplist.SkipList[uint64, *Session]
idleSessionLock sync.Mutex
sessions map[uint64]*Session
sessionsLock sync.Mutex
padding *atomic.TypedValue[*padding.PaddingFactory]
idleSessionTimeout time.Duration
@@ -33,6 +37,7 @@ type Client struct {
func NewClient(ctx context.Context, dialOut util.DialOutFunc, _padding *atomic.TypedValue[*padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration, minIdleSession int) *Client {
c := &Client{
sessions: make(map[uint64]*Session),
dialOut: dialOut,
padding: _padding,
idleSessionTimeout: idleSessionTimeout,
@@ -130,15 +135,35 @@ func (c *Client) createSession(ctx context.Context) (*Session, error) {
c.idleSessionLock.Lock()
c.idleSession.Remove(math.MaxUint64 - session.seq)
c.idleSessionLock.Unlock()
c.sessionsLock.Lock()
delete(c.sessions, session.seq)
c.sessionsLock.Unlock()
}
c.sessionsLock.Lock()
c.sessions[session.seq] = session
c.sessionsLock.Unlock()
session.Run()
return session, nil
}
func (c *Client) Close() error {
c.dieCancel()
c.minIdleSession = 0
go c.idleCleanupExpTime(time.Time{})
c.sessionsLock.Lock()
sessionToClose := make([]*Session, 0, len(c.sessions))
for seq, session := range c.sessions {
sessionToClose = append(sessionToClose, session)
delete(c.sessions, seq)
}
c.sessionsLock.Unlock()
for _, session := range sessionToClose {
session.Close()
}
return nil
}

View File

@@ -78,13 +78,13 @@ jobs:
fail-fast: false
matrix:
include:
- sdk_ver: 21.02
luci_ver: 19.07
- sdk_ver: "21.02"
luci_ver: "19.07"
sdk_url: https://downloads.openwrt.org/releases/21.02.7/targets/x86/64/openwrt-sdk-21.02.7-x86-64_gcc-8.4.0_musl.Linux-x86_64.tar.xz
- sdk_ver: 23.05
luci_ver: 23.05
sdk_url: https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
- sdk_ver: "24.10"
luci_ver: "24.10"
sdk_url: https://downloads.openwrt.org/releases/24.10.0/targets/x86/64/openwrt-sdk-24.10.0-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst
steps:
- name: Install packages
run: |
@@ -107,7 +107,7 @@ jobs:
run: |
wget ${{ matrix.sdk_url }}
file_name=$(echo ${{ matrix.sdk_url }} | awk -F/ '{print $NF}')
mkdir sdk && tar -xJf $file_name -C ./sdk --strip-components=1
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
@@ -115,6 +115,10 @@ jobs:
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall https://github.com/${{ env.passwall }}.git;${{ github.ref_name }}" >> feeds.conf
rm -rf feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 24.x feeds/packages/lang/golang
./scripts/feeds update -a
echo "CONFIG_PACKAGE_luci-app-passwall=m" > .config
./scripts/feeds install -d n luci-app-passwall
@@ -172,58 +176,76 @@ jobs:
matrix:
include:
- platform: x86_64
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/x86/64/openwrt-sdk-24.10.0-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: aarch64_generic
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/rockchip/armv8/openwrt-sdk-23.05.5-rockchip-armv8_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/rockchip/armv8/openwrt-sdk-24.10.0-rockchip-armv8_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: aarch64_cortex-a53
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mvebu/cortexa53/openwrt-sdk-23.05.5-mvebu-cortexa53_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mvebu/cortexa53/openwrt-sdk-24.10.0-mvebu-cortexa53_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: aarch64_cortex-a72
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mvebu/cortexa72/openwrt-sdk-23.05.5-mvebu-cortexa72_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mvebu/cortexa72/openwrt-sdk-24.10.0-mvebu-cortexa72_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a5_vfpv4
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/at91/sama5/openwrt-sdk-23.05.5-at91-sama5_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/at91/sama5/openwrt-sdk-24.10.0-at91-sama5_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a7
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mediatek/mt7629/openwrt-sdk-23.05.5-mediatek-mt7629_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mediatek/mt7629/openwrt-sdk-24.10.0-mediatek-mt7629_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a7_neon-vfpv4
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/sunxi/cortexa7/openwrt-sdk-23.05.5-sunxi-cortexa7_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/sunxi/cortexa7/openwrt-sdk-24.10.0-sunxi-cortexa7_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a8_vfpv3
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/sunxi/cortexa8/openwrt-sdk-23.05.5-sunxi-cortexa8_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/sunxi/cortexa8/openwrt-sdk-24.10.0-sunxi-cortexa8_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a9
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/bcm53xx/generic/openwrt-sdk-23.05.5-bcm53xx-generic_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/bcm53xx/generic/openwrt-sdk-24.10.0-bcm53xx-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a9_neon
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/zynq/generic/openwrt-sdk-23.05.5-zynq-generic_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/zynq/generic/openwrt-sdk-24.10.0-zynq-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a9_vfpv3-d16
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mvebu/cortexa9/openwrt-sdk-23.05.5-mvebu-cortexa9_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mvebu/cortexa9/openwrt-sdk-24.10.0-mvebu-cortexa9_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a15_neon-vfpv4
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ipq806x/generic/openwrt-sdk-23.05.5-ipq806x-generic_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ipq806x/generic/openwrt-sdk-24.10.0-ipq806x-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mips_24kc
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ath79/generic/openwrt-sdk-23.05.5-ath79-generic_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ath79/generic/openwrt-sdk-24.10.0-ath79-generic_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mips_4kec
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/realtek/rtl838x/openwrt-sdk-23.05.5-realtek-rtl838x_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/realtek/rtl838x/openwrt-sdk-24.10.0-realtek-rtl838x_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mips_mips32
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/bcm63xx/generic/openwrt-sdk-23.05.5-bcm63xx-generic_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/bcm53xx/generic/openwrt-sdk-24.10.0-bcm53xx-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mipsel_24kc
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ramips/rt288x/openwrt-sdk-23.05.5-ramips-rt288x_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ramips/rt288x/openwrt-sdk-24.10.0-ramips-rt288x_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mipsel_74kc
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ramips/rt3883/openwrt-sdk-23.05.5-ramips-rt3883_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ramips/rt3883/openwrt-sdk-24.10.0-ramips-rt3883_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mipsel_mips32
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/bcm47xx/generic/openwrt-sdk-23.05.5-bcm47xx-generic_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/bcm47xx/generic/openwrt-sdk-24.10.0-bcm47xx-generic_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
steps:
- name: Initialization ${{ matrix.platform }} compile environment
@@ -239,7 +261,7 @@ jobs:
run: |
wget ${{ matrix.url_sdk }}
file_name=$(echo ${{matrix.url_sdk}} | awk -F/ '{print $NF}')
mkdir sdk && tar -xJf $file_name -C ./sdk --strip-components=1
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
cd sdk
- name: SSH connection to Actions
@@ -249,10 +271,10 @@ jobs:
- name: ${{ matrix.platform }} feeds configuration packages
run: |
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-23.05" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-23.05" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-23.05" >> feeds.conf
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-23.05" >> feeds.conf
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall https://github.com/${{ env.passwall }}.git;main" >> feeds.conf
@@ -305,7 +327,11 @@ jobs:
cd sdk
for package in $(ls feeds/passwall_packages); do
if [ -d "feeds/passwall_packages/$package" ]; then
make package/feeds/passwall_packages/$package/compile -j$(nproc) V=s 2>/dev/null
echo "-----------begin compile $package ---------------"
sleep 10s
make package/feeds/passwall_packages/$package/compile -j$(nproc) V=s
echo "-----------compiled $package ---------------"
echo ""
fi
done

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=25.2.12
PKG_VERSION:=25.3.2
PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \

View File

@@ -78,13 +78,13 @@ jobs:
fail-fast: false
matrix:
include:
- sdk_ver: 21.02
luci_ver: 19.07
- sdk_ver: "21.02"
luci_ver: "19.07"
sdk_url: https://downloads.openwrt.org/releases/21.02.7/targets/x86/64/openwrt-sdk-21.02.7-x86-64_gcc-8.4.0_musl.Linux-x86_64.tar.xz
- sdk_ver: 23.05
luci_ver: 23.05
sdk_url: https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
- sdk_ver: "24.10"
luci_ver: "24.10"
sdk_url: https://downloads.openwrt.org/releases/24.10.0/targets/x86/64/openwrt-sdk-24.10.0-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst
steps:
- name: Install packages
run: |
@@ -107,7 +107,7 @@ jobs:
run: |
wget ${{ matrix.sdk_url }}
file_name=$(echo ${{ matrix.sdk_url }} | awk -F/ '{print $NF}')
mkdir sdk && tar -xJf $file_name -C ./sdk --strip-components=1
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
@@ -115,10 +115,14 @@ jobs:
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall2 https://github.com/${{ env.passwall2 }}.git;${{ github.ref_name }}" >> feeds.conf
rm -rf feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 24.x feeds/packages/lang/golang
./scripts/feeds update -a
echo "CONFIG_PACKAGE_luci-app-passwall2=m" > .config
./scripts/feeds install -d n luci-app-passwall2
make package/luci-app-passwall2/download -j1
make package/luci-app-passwall2/download -j$(nproc)
- name: Update passwall2 feeds
if: steps.cache-sdk.outputs.cache-hit == 'true'
@@ -140,8 +144,8 @@ jobs:
echo "CONFIG_LUCI_LANG_zh_Hans=y" >> .config
echo "CONFIG_PACKAGE_luci-app-passwall2=m" >> .config
make defconfig
echo "make package/luci-app-passwall2/{clean,compile} -j1"
make package/luci-app-passwall2/{clean,compile} -j1 V=s
echo "make package/luci-app-passwall2/{clean,compile} -j$(nproc)"
make package/luci-app-passwall2/{clean,compile} -j$(nproc) V=s
mv bin/packages/x86_64/passwall2/ ../
make clean
rm .config .config.old
@@ -172,58 +176,76 @@ jobs:
matrix:
include:
- platform: x86_64
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/x86/64/openwrt-sdk-23.05.5-x86-64_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/x86/64/openwrt-sdk-24.10.0-x86-64_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: aarch64_generic
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/rockchip/armv8/openwrt-sdk-23.05.5-rockchip-armv8_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/rockchip/armv8/openwrt-sdk-24.10.0-rockchip-armv8_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: aarch64_cortex-a53
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mvebu/cortexa53/openwrt-sdk-23.05.5-mvebu-cortexa53_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mvebu/cortexa53/openwrt-sdk-24.10.0-mvebu-cortexa53_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: aarch64_cortex-a72
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mvebu/cortexa72/openwrt-sdk-23.05.5-mvebu-cortexa72_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mvebu/cortexa72/openwrt-sdk-24.10.0-mvebu-cortexa72_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a5_vfpv4
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/at91/sama5/openwrt-sdk-23.05.5-at91-sama5_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/at91/sama5/openwrt-sdk-24.10.0-at91-sama5_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a7
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mediatek/mt7629/openwrt-sdk-23.05.5-mediatek-mt7629_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mediatek/mt7629/openwrt-sdk-24.10.0-mediatek-mt7629_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a7_neon-vfpv4
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/sunxi/cortexa7/openwrt-sdk-23.05.5-sunxi-cortexa7_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/sunxi/cortexa7/openwrt-sdk-24.10.0-sunxi-cortexa7_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a8_vfpv3
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/sunxi/cortexa8/openwrt-sdk-23.05.5-sunxi-cortexa8_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/sunxi/cortexa8/openwrt-sdk-24.10.0-sunxi-cortexa8_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a9
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/bcm53xx/generic/openwrt-sdk-23.05.5-bcm53xx-generic_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/bcm53xx/generic/openwrt-sdk-24.10.0-bcm53xx-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a9_neon
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/zynq/generic/openwrt-sdk-23.05.5-zynq-generic_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/zynq/generic/openwrt-sdk-24.10.0-zynq-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a9_vfpv3-d16
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/mvebu/cortexa9/openwrt-sdk-23.05.5-mvebu-cortexa9_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/mvebu/cortexa9/openwrt-sdk-24.10.0-mvebu-cortexa9_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: arm_cortex-a15_neon-vfpv4
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ipq806x/generic/openwrt-sdk-23.05.5-ipq806x-generic_gcc-12.3.0_musl_eabi.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ipq806x/generic/openwrt-sdk-24.10.0-ipq806x-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mips_24kc
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ath79/generic/openwrt-sdk-23.05.5-ath79-generic_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ath79/generic/openwrt-sdk-24.10.0-ath79-generic_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mips_4kec
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/realtek/rtl838x/openwrt-sdk-23.05.5-realtek-rtl838x_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/realtek/rtl838x/openwrt-sdk-24.10.0-realtek-rtl838x_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mips_mips32
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/bcm63xx/generic/openwrt-sdk-23.05.5-bcm63xx-generic_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/bcm53xx/generic/openwrt-sdk-24.10.0-bcm53xx-generic_gcc-13.3.0_musl_eabi.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mipsel_24kc
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ramips/rt288x/openwrt-sdk-23.05.5-ramips-rt288x_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ramips/rt288x/openwrt-sdk-24.10.0-ramips-rt288x_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mipsel_74kc
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/ramips/rt3883/openwrt-sdk-23.05.5-ramips-rt3883_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/ramips/rt3883/openwrt-sdk-24.10.0-ramips-rt3883_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
- platform: mipsel_mips32
url_sdk: https://downloads.openwrt.org/releases/23.05.5/targets/bcm47xx/generic/openwrt-sdk-23.05.5-bcm47xx-generic_gcc-12.3.0_musl.Linux-x86_64.tar.xz
url_sdk: https://downloads.openwrt.org/releases/24.10.0/targets/bcm47xx/generic/openwrt-sdk-24.10.0-bcm47xx-generic_gcc-13.3.0_musl.Linux-x86_64.tar.zst
sdk_ver: "24.10"
steps:
- name: Initialization ${{ matrix.platform }} compile environment
@@ -239,7 +261,7 @@ jobs:
run: |
wget ${{ matrix.url_sdk }}
file_name=$(echo ${{matrix.url_sdk}} | awk -F/ '{print $NF}')
mkdir sdk && tar -xJf $file_name -C ./sdk --strip-components=1
mkdir sdk && tar --zstd -x -f $file_name -C ./sdk --strip-components=1
cd sdk
- name: SSH connection to Actions
@@ -249,10 +271,10 @@ jobs:
- name: ${{ matrix.platform }} feeds configuration packages
run: |
cd sdk
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-23.05" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-23.05" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-23.05" >> feeds.conf
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-23.05" >> feeds.conf
echo "src-git base https://github.com/openwrt/openwrt.git;openwrt-${{ matrix.sdk_ver }}" > feeds.conf
echo "src-git packages https://github.com/openwrt/packages.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git luci https://github.com/openwrt/luci.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git routing https://git.openwrt.org/feed/routing.git;openwrt-${{ matrix.sdk_ver }}" >> feeds.conf
echo "src-git passwall_packages https://github.com/${{ env.packages }}.git;main" >> feeds.conf
echo "src-git passwall2 https://github.com/${{ env.passwall2 }}.git;main" >> feeds.conf
@@ -261,7 +283,7 @@ jobs:
./scripts/feeds install luci-app-passwall2
rm -rf feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 23.x feeds/packages/lang/golang
git clone https://github.com/sbwml/packages_lang_golang -b 24.x feeds/packages/lang/golang
echo "CONFIG_ALL_NONSHARED=n" > .config
echo "CONFIG_ALL_KMODS=n" >> .config
@@ -292,27 +314,26 @@ jobs:
- name: ${{ matrix.platform }} download
run: |
cd sdk
make download -j1
make download -j$(nproc)
find dl -size -1024c -exec ls -l {} \;
- name: ${{ matrix.platform }} compile
id: compile
run: |
cd sdk
make package/feeds/passwall_packages/chinadns-ng/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/geoview/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/tcping/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/xray-core/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/v2ray-geodata/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/hysteria/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/naiveproxy/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/shadowsocks-rust/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/shadowsocksr-libev/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/simple-obfs/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/sing-box/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/tuic-client/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/v2ray-plugin/compile -j1 V=s 2>/dev/null
make package/feeds/passwall_packages/chinadns-ng/compile -j$(nproc) V=s
make package/feeds/passwall_packages/geoview/compile -j$(nproc) V=s
make package/feeds/passwall_packages/tcping/compile -j$(nproc) V=s
make package/feeds/passwall_packages/xray-core/compile -j$(nproc) V=s
make package/feeds/passwall_packages/v2ray-geodata/compile -j$(nproc) V=s
make package/feeds/passwall_packages/hysteria/compile -j$(nproc) V=s
make package/feeds/passwall_packages/naiveproxy/compile -j$(nproc) V=s
make package/feeds/passwall_packages/shadowsocks-rust/compile -j$(nproc) V=s
make package/feeds/passwall_packages/shadowsocksr-libev/compile -j$(nproc) V=s
make package/feeds/passwall_packages/simple-obfs/compile -j$(nproc) V=s
make package/feeds/passwall_packages/sing-box/compile -j$(nproc) V=s
make package/feeds/passwall_packages/tuic-client/compile -j$(nproc) V=s
make package/feeds/passwall_packages/v2ray-plugin/compile -j$(nproc) V=s
echo "status=success" >> $GITHUB_OUTPUT

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=25.1.27
PKG_VERSION:=25.3.2
PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \

View File

@@ -14,6 +14,7 @@ end
local normal_list = {}
local balancing_list = {}
local urltest_list = {}
local shunt_list = {}
local iface_list = {}
for k, v in pairs(nodes_table) do
@@ -23,6 +24,9 @@ for k, v in pairs(nodes_table) do
if v.protocol and v.protocol == "_balancing" then
balancing_list[#balancing_list + 1] = v
end
if v.protocol and v.protocol == "_urltest" then
urltest_list[#urltest_list + 1] = v
end
if v.protocol and v.protocol == "_shunt" then
shunt_list[#shunt_list + 1] = v
end
@@ -130,6 +134,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark)
end
@@ -174,6 +181,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark)
end
@@ -199,6 +209,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark)
end
@@ -214,7 +227,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
o:value("", translate("Close"))
o:value("main", translate("Preproxy Node"))
for k1, v1 in pairs(normal_list) do
if v1.protocol ~= "_balancing" then
if v1.protocol ~= "_balancing" and v1.protocol ~= "_urltest" then
o:depends({ [vid .. "-default_node"] = v1.id, [vid .. "-preproxy_enabled"] = "1" })
end
end

View File

@@ -96,6 +96,8 @@ o.cfgvalue = function(t, n)
local protocol = m:get(n, "protocol")
if protocol == "_balancing" then
protocol = translate("Balancing")
elseif protocol == "_urltest" then
protocol = "URLTest"
elseif protocol == "_shunt" then
protocol = translate("Shunt")
elseif protocol == "vmess" then

View File

@@ -56,6 +56,7 @@ end
if singbox_tags:find("with_quic") then
o:value("hysteria2", "Hysteria2")
end
o:value("_urltest", translate("URLTest"))
o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface"))
@@ -65,6 +66,7 @@ o:depends({ [_n("protocol")] = "_iface" })
local nodes_table = {}
local iface_table = {}
local urltest_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = {
@@ -79,6 +81,12 @@ for k, e in ipairs(api.get_valid_nodes()) do
remark = e["remark"]
}
end
if e.protocol == "_urltest" then
urltest_table[#urltest_table + 1] = {
id = e[".name"],
remark = e["remark"]
}
end
end
local socks_list = {}
@@ -91,6 +99,44 @@ m.uci:foreach(appname, "socks", function(s)
end
end)
--[[ URLTest ]]
o = s:option(DynamicList, _n("urltest_node"), translate("URLTest node list"), translate("List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"))
o:depends({ [_n("protocol")] = "_urltest" })
for k, v in pairs(nodes_table) do o:value(v.id, v.remark) end
o = s:option(Value, _n("urltest_url"), translate("Probe URL"))
o:depends({ [_n("protocol")] = "_urltest" })
o:value("https://cp.cloudflare.com/", "Cloudflare")
o:value("https://www.gstatic.com/generate_204", "Gstatic")
o:value("https://www.google.com/generate_204", "Google")
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
o.default = "https://www.gstatic.com/generate_204"
o.description = translate("The URL used to detect the connection status.")
o = s:option(Value, _n("urltest_interval"), translate("Test interval"))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "180"
o.description = translate("The test interval in seconds.") .. "<br />" ..
translate("Test interval must be less or equal than idle timeout.")
o = s:option(Value, _n("urltest_tolerance"), translate("Test tolerance"), translate("The test tolerance in milliseconds."))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "50"
o = s:option(Value, _n("urltest_idle_timeout"), translate("Idle timeout"), translate("The idle timeout in seconds."))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "1800"
o = s:option(Flag, _n("urltest_interrupt_exist_connections"), translate("Interrupt existing connections"))
o:depends({ [_n("protocol")] = "_urltest" })
o.default = "0"
o.description = translate("Interrupt existing connections when the selected outbound has changed.")
-- [[ 分流模块 ]]
if #nodes_table > 0 then
o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy"))
@@ -101,6 +147,9 @@ if #nodes_table > 0 then
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remark)
end
@@ -124,6 +173,9 @@ m.uci:foreach(appname, "shunt_rules", function(e)
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remark)
end
@@ -155,6 +207,9 @@ if #nodes_table > 0 then
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remark)
end

View File

@@ -314,7 +314,7 @@ function strToTable(str)
end
function is_normal_node(e)
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
return false
end
return true
@@ -444,7 +444,7 @@ function get_valid_nodes()
uci:foreach(appname, "nodes", function(e)
e.id = e[".name"]
if e.type and e.remarks then
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
local type = e.type
if type == "sing-box" then type = "Sing-Box" end
e["remark"] = "%s[%s] " % {type .. " " .. i18n.translatef(e.protocol), e.remarks}
@@ -494,7 +494,7 @@ end
function get_node_remarks(n)
local remarks = ""
if n then
if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt" or n.protocol == "_iface") then
if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt" or n.protocol == "_iface" or n.protocol == "_urltest") then
remarks = "%s[%s] " % {n.type .. " " .. i18n.translatef(n.protocol), n.remarks}
else
local type2 = n.type
@@ -1114,7 +1114,7 @@ end
function get_version()
local version = sys.exec("opkg list-installed luci-app-passwall2 2>/dev/null | awk '{print $3}'")
if not version or #version == 0 then
version = sys.exec("apk info -L luci-app-passwall2 2>/dev/null | awk 'NR == 1 {print $1}' | cut -d'-' -f4-")
version = sys.exec("apk list luci-app-passwall2 2>/dev/null | awk '/installed/ {print $1}' | cut -d'-' -f4-")
end
return version or ""
end

View File

@@ -892,6 +892,54 @@ function gen_config(var)
node.port = server_port
end
local function gen_urltest(_node)
local urltest_id = _node[".name"]
local urltest_tag = "urltest-" .. urltest_id
-- existing urltest
for _, v in ipairs(outbounds) do
if v.tag == urltest_tag then
return urltest_tag
end
end
-- new urltest
local ut_nodes = _node.urltest_node
local valid_nodes = {}
for i = 1, #ut_nodes do
local ut_node_id = ut_nodes[i]
local ut_node_tag = "ut-" .. ut_node_id
local is_new_ut_node = true
for _, outbound in ipairs(outbounds) do
if string.sub(outbound.tag, 1, #ut_node_tag) == ut_node_tag then
is_new_ut_node = false
valid_nodes[#valid_nodes + 1] = outbound.tag
break
end
end
if is_new_ut_node then
local ut_node = uci:get_all(appname, ut_node_id)
local outbound = gen_outbound(flag, ut_node, ut_node_tag)
if outbound then
outbound.tag = outbound.tag .. ":" .. ut_node.remarks
table.insert(outbounds, outbound)
valid_nodes[#valid_nodes + 1] = outbound.tag
end
end
end
if #valid_nodes == 0 then return nil end
local outbound = {
type = "urltest",
tag = urltest_tag,
outbounds = valid_nodes,
url = _node.urltest_url or "https://www.gstatic.com/generate_204",
interval = _node.urltest_interval and tonumber(_node.urltest_interval) and string.format("%dm", tonumber(_node.urltest_interval) / 60) or "3m",
tolerance = _node.urltest_tolerance and tonumber(_node.urltest_tolerance) and tonumber(_node.urltest_tolerance) or 50,
idle_timeout = _node.urltest_idle_timeout and tonumber(_node.urltest_idle_timeout) and string.format("%dm", tonumber(_node.urltest_idle_timeout) / 60) or "30m",
interrupt_exist_connections = (_node.urltest_interrupt_exist_connections == "true" or _node.urltest_interrupt_exist_connections == "1") and true or false
}
table.insert(outbounds, outbound)
return urltest_tag
end
local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
if not node or not outbound or not outbounds_table then return nil end
local default_outTag = outbound.tag
@@ -1046,6 +1094,8 @@ function gen_config(var)
end
end
end
elseif _node.protocol == "_urltest" then
rule_outboundTag = gen_urltest(_node)
elseif _node.protocol == "_iface" then
if _node.iface then
local _outbound = {
@@ -1220,6 +1270,10 @@ function gen_config(var)
for index, value in ipairs(rules) do
table.insert(route.rules, rules[index])
end
elseif node.protocol == "_urltest" then
if node.urltest_node then
COMMON.default_outbound_tag = gen_urltest(node)
end
elseif node.protocol == "_iface" then
if node.iface then
local outbound = {

View File

@@ -1423,9 +1423,6 @@ msgstr "最大并发连接数"
msgid "XUDP Mux concurrency"
msgstr "XUDP 最大并发连接数"
msgid "Mux idle timeout"
msgstr "最大闲置时间"
msgid "Padding"
msgstr "填充"
@@ -1444,9 +1441,6 @@ msgstr "推荐值Sec-WebSocket-Protocol"
msgid "Health check"
msgstr "健康检查"
msgid "Idle timeout"
msgstr "闲置时间"
msgid "Health check timeout"
msgstr "检查超时时间"
@@ -1647,3 +1641,39 @@ msgstr "是否要恢复客户端默认配置?"
msgid "Are you sure you want to restore the client to default settings?"
msgstr "是否真的要恢复客户端默认配置?"
msgid "_urltest"
msgstr "URLTest"
msgid "URLTest node list"
msgstr "URLTest 节点列表"
msgid "List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"
msgstr "要测试的节点列表,<a target='_blank' href='https://sing-box.sagernet.org/zh/configuration/outbound/urltest'>文档原理</a>"
msgid "Test interval"
msgstr "测试间隔"
msgid "The test interval in seconds."
msgstr "测试间隔时间(单位:秒)。"
msgid "Test interval must be less or equal than idle timeout."
msgstr "测试间隔时间必须小于或等于空闲超时时间。"
msgid "Test tolerance"
msgstr "测试容差"
msgid "The test tolerance in milliseconds."
msgstr "测试容差时间(单位:毫秒)。"
msgid "Idle timeout"
msgstr "空闲超时"
msgid "The idle timeout in seconds."
msgstr "空闲超时时间(单位:秒)。"
msgid "Interrupt existing connections"
msgstr "中断现有连接"
msgid "Interrupt existing connections when the selected outbound has changed."
msgstr "当选择的出站发生变化时中断现有连接。"

View File

@@ -589,7 +589,7 @@ run_socks() {
if [ "$type" == "sing-box" ] || [ "$type" == "xray" ]; then
local protocol=$(config_n_get $node protocol)
if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ]; then
if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ] || [ "$protocol" == "_urltest" ]; then
unset error_msg
fi
fi

View File

@@ -303,6 +303,36 @@ do
end
}
end
elseif node.protocol and node.protocol == '_urltest' then
local flag = "Sing-Box URLTest节点[" .. node_id .. "]列表"
local currentNodes = {}
local newNodes = {}
if node.urltest_node then
for k, node in pairs(node.urltest_node) do
currentNodes[#currentNodes + 1] = {
log = false,
node = node,
currentNode = node and uci:get_all(appname, node) or nil,
remarks = node,
set = function(o, server)
if o and server and server ~= "nil" then
table.insert(o.newNodes, server)
end
end
}
end
end
CONFIG[#CONFIG + 1] = {
remarks = flag,
currentNodes = currentNodes,
newNodes = newNodes,
set = function(o, newNodes)
if o then
if not newNodes then newNodes = o.newNodes end
uci:set_list(appname, node_id, "urltest_node", newNodes or {})
end
end
}
else
--前置代理节点
local currentNode = uci:get_all(appname, node_id) or nil
@@ -1682,7 +1712,7 @@ local execute = function()
f:close()
raw = trim(stdout)
local old_md5 = value.md5 or ""
local new_md5 = luci.sys.exec("[ -f " .. tmp_file .. " ] && md5sum " .. tmp_file .. " | awk '{print $1}' || echo 0")
local new_md5 = luci.sys.exec("[ -f " .. tmp_file .. " ] && md5sum " .. tmp_file .. " | awk '{print $1}' || echo 0"):gsub("\n", "")
os.remove(tmp_file)
if old_md5 == new_md5 then
log('订阅:【' .. remark .. '】没有变化,无需更新。')

View File

@@ -71,7 +71,7 @@ url_test_node() {
sleep 1s
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "https://www.google.com/generate_204")
pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}.json"
rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}"*.json
}
echo $result
}

View File

@@ -11,7 +11,7 @@ env:
RUST_LOG: "trace"
jobs:
build-test-check:
build-and-test:
strategy:
matrix:
platform:

View File

@@ -5,7 +5,7 @@ on:
- v*
jobs:
build:
build-docker-image:
runs-on: ubuntu-latest
strategy:
matrix:

View File

@@ -11,7 +11,7 @@ env:
RUST_LOG: "trace"
jobs:
shadowsocks-rust:
build-msrv-shadowsocks-rust:
strategy:
matrix:
platform:
@@ -28,9 +28,9 @@ jobs:
- name: Install Rust
run: |
rustup set profile minimal
rustup toolchain install 1.80
rustup default 1.80
rustup override set 1.80
rustup toolchain install 1.83
rustup default 1.83
rustup override set 1.83
- name: Build with All Features Enabled (Unix)
if: ${{ runner.os == 'Linux' || runner.os == 'macOS' }}
run: cargo build --verbose --features "full-extra local-flow-stat utility-url-outline"
@@ -38,7 +38,7 @@ jobs:
if: ${{ runner.os == 'Windows' }}
run: cargo build --verbose --features "full-extra local-flow-stat utility-url-outline winservice"
shadowsocks-service:
build-msrv-shadowsocks-service:
strategy:
matrix:
platform:
@@ -55,13 +55,13 @@ jobs:
- name: Install Rust
run: |
rustup set profile minimal
rustup toolchain install 1.80
rustup default 1.80
rustup override set 1.80
rustup toolchain install 1.83
rustup default 1.83
rustup override set 1.83
- name: Build with All Features Enabled
run: cargo build --manifest-path crates/shadowsocks-service/Cargo.toml --verbose --features "full dns-over-tls dns-over-https dns-over-h3 local-dns local-flow-stat local-http-rustls local-tun local-fake-dns local-online-config stream-cipher aead-cipher-extra aead-cipher-2022 aead-cipher-2022-extra security-replay-attack-detect"
shadowsocks:
build-msrv-shadowsocks:
strategy:
matrix:
platform:

View File

@@ -8,7 +8,7 @@ env:
CARGO_TERM_COLOR: always
jobs:
build-cross:
build-nightly-cross:
runs-on: ubuntu-latest
env:
RUST_BACKTRACE: full
@@ -70,7 +70,7 @@ jobs:
name: ${{ matrix.platform.target }}
path: build/release/*
build-unix:
build-nightly-unix:
runs-on: ${{ matrix.os }}
env:
BUILD_EXTRA_FEATURES: "full"
@@ -111,7 +111,7 @@ jobs:
name: ${{ matrix.target }}
path: build/release/*
build-windows:
build-nightly-windows:
runs-on: windows-latest
env:
RUSTFLAGS: "-C target-feature=+crt-static"

View File

@@ -14,7 +14,7 @@ env:
CARGO_TERM_COLOR: always
jobs:
build-cross:
build-release-cross:
runs-on: ubuntu-latest
env:
RUST_BACKTRACE: full
@@ -117,7 +117,7 @@ jobs:
prerelease: ${{ contains(github.ref_name, '-') }}
tag_name: ${{ inputs.tag || github.ref_name }}
build-unix:
build-release-unix:
runs-on: ${{ matrix.os }}
env:
BUILD_EXTRA_FEATURES: "full"
@@ -162,7 +162,7 @@ jobs:
prerelease: ${{ contains(github.ref_name, '-') }}
tag_name: ${{ inputs.tag || github.ref_name }}
build-windows:
build-release-windows:
runs-on: windows-latest
env:
RUSTFLAGS: "-C target-feature=+crt-static"

View File

@@ -1,7 +1,7 @@
name: Cargo Deny Check
on: [push, pull_request]
jobs:
cargo-deny:
deny-check:
runs-on: ubuntu-latest
strategy:
matrix:

View File

@@ -956,9 +956,9 @@ dependencies = [
[[package]]
name = "etherparse"
version = "0.16.0"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8d8a704b617484e9d867a0423cd45f7577f008c4068e2e33378f8d3860a6d73"
checksum = "b14e4ac78394e3ea04edbbc412099cf54f2f52ded51efb79c466a282729399d2"
dependencies = [
"arrayvec",
]
@@ -3232,7 +3232,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project",
"rand 0.8.5",
"rand 0.9.0",
"sendfd",
"serde",
"serde_json",
@@ -3299,7 +3299,7 @@ dependencies = [
"mimalloc",
"mime",
"qrcode",
"rand 0.8.5",
"rand 0.9.0",
"reqwest",
"rpassword",
"rpmalloc",
@@ -3349,7 +3349,7 @@ dependencies = [
"nix",
"once_cell",
"pin-project",
"rand 0.8.5",
"rand 0.9.0",
"regex",
"rustls-native-certs",
"serde",

View File

@@ -9,7 +9,7 @@ documentation = "https://docs.rs/shadowsocks-rust"
keywords = ["shadowsocks", "proxy", "socks", "socks5", "firewall"]
license = "MIT"
edition = "2021"
rust-version = "1.80"
rust-version = "1.83"
[badges]
maintenance = { status = "passively-maintained" }
@@ -229,7 +229,7 @@ directories = "6.0"
xdg = "2.5"
rpassword = "7.3"
libc = { version = "0.2", features = ["extra_traits"] }
rand = "0.8"
rand = "0.9"
futures = "0.3"
tokio = { version = "1", features = ["rt", "signal"] }

View File

@@ -1 +1 @@
msrv = "1.77"
msrv = "1.83"

View File

@@ -9,7 +9,7 @@ documentation = "https://docs.rs/shadowsocks-service"
keywords = ["shadowsocks", "proxy", "socks", "socks5", "firewall"]
license = "MIT"
edition = "2021"
rust-version = "1.80"
rust-version = "1.83"
[badges]
maintenance = { status = "passively-maintained" }
@@ -134,7 +134,7 @@ lru_time_cache = "0.11"
bytes = "1.7"
byte_string = "1.0"
byteorder = "1.5"
rand = { version = "0.8", features = ["small_rng"] }
rand = { version = "0.9", features = ["small_rng"] }
sled = { version = "0.34.7", optional = true }
futures = "0.3"
@@ -181,7 +181,7 @@ brotli = { version = "7.0", optional = true }
zstd = { version = "0.13", optional = true }
tun = { version = "0.7", optional = true, features = ["async"] }
etherparse = { version = "0.16", optional = true }
etherparse = { version = "0.17", optional = true }
smoltcp = { version = "0.12", optional = true, default-features = false, features = [
"std",
"log",

View File

@@ -23,7 +23,6 @@ use hickory_resolver::proto::{
rr::{DNSClass, Name, RData, RecordType},
};
use log::{debug, error, info, trace, warn};
use rand::{thread_rng, Rng};
use tokio::{
io::{AsyncReadExt, AsyncWriteExt},
net::{TcpStream, UdpSocket},
@@ -854,7 +853,7 @@ impl DnsClient {
async fn lookup_remote_inner(&self, query: &Query, remote_addr: &Address) -> io::Result<Message> {
let mut message = Message::new();
message.set_id(thread_rng().gen());
message.set_id(rand::random());
message.set_recursion_desired(true);
message.add_query(query.clone());
@@ -884,7 +883,7 @@ impl DnsClient {
// Then this future will be disabled and have no effect
//
// Randomly choose from 500ms ~ 1.5s for preventing obvious request pattern
let sleep_time = thread_rng().gen_range(500..=1500);
let sleep_time = rand::random_range(500..=1500);
time::sleep(Duration::from_millis(sleep_time)).await;
let server = self.balancer.best_tcp_server();
@@ -933,7 +932,7 @@ impl DnsClient {
async fn lookup_local_inner(&self, query: &Query, local_addr: &NameServerAddr) -> io::Result<Message> {
let mut message = Message::new();
message.set_id(thread_rng().gen());
message.set_id(rand::random());
message.set_recursion_desired(true);
message.add_query(query.clone());

View File

@@ -15,7 +15,6 @@ use bytes::{BufMut, BytesMut};
use hickory_resolver::proto::{op::Message, ProtoError, ProtoErrorKind};
use log::{error, trace};
use lru_time_cache::{Entry, LruCache};
use rand::{thread_rng, Rng};
use shadowsocks::{
config::ServerConfig,
context::SharedContext,
@@ -139,7 +138,7 @@ impl DnsClient {
async fn inner_lookup(&mut self, msg: &mut Message) -> Result<Message, ProtoError> {
// Make a random ID
msg.set_id(thread_rng().gen());
msg.set_id(rand::random());
trace!("DNS lookup {:?}", msg);

View File

@@ -234,13 +234,18 @@ where
}
thread_local! {
static CLIENT_SESSION_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
static CLIENT_SESSION_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_os_rng());
}
/// Generate an AEAD-2022 Client SessionID
#[inline]
pub fn generate_client_session_id() -> u64 {
CLIENT_SESSION_RNG.with(|rng| rng.borrow_mut().gen())
loop {
let id = CLIENT_SESSION_RNG.with(|rng| rng.borrow_mut().random());
if id != 0 {
break id;
}
}
}
impl<W> UdpAssociationContext<W>

View File

@@ -462,12 +462,17 @@ impl Drop for UdpAssociationContext {
}
thread_local! {
static CLIENT_SESSION_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
static CLIENT_SESSION_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_os_rng());
}
#[inline]
fn generate_server_session_id() -> u64 {
CLIENT_SESSION_RNG.with(|rng| rng.borrow_mut().gen())
loop {
let id = CLIENT_SESSION_RNG.with(|rng| rng.borrow_mut().random());
if id != 0 {
break id;
}
}
}
impl UdpAssociationContext {

View File

@@ -62,7 +62,7 @@ spin = { version = "0.9", features = ["std"] }
pin-project = "1.1"
bloomfilter = { version = "3.0.0", optional = true }
thiserror = "2.0"
rand = { version = "0.8", optional = true }
rand = { version = "0.9", optional = true }
lru_time_cache = { version = "0.11", optional = true }
serde = { version = "1.0", features = ["derive"] }

View File

@@ -18,11 +18,11 @@ fn get_aead_2022_padding_size(payload: &[u8]) -> usize {
use rand::{rngs::SmallRng, Rng, SeedableRng};
thread_local! {
static PADDING_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_entropy());
static PADDING_RNG: RefCell<SmallRng> = RefCell::new(SmallRng::from_os_rng());
}
if payload.is_empty() {
PADDING_RNG.with(|rng| rng.borrow_mut().gen::<usize>() % AEAD2022_MAX_PADDING_SIZE)
PADDING_RNG.with(|rng| rng.borrow_mut().random_range::<usize, _>(0..=AEAD2022_MAX_PADDING_SIZE))
} else {
0
}

View File

@@ -92,9 +92,7 @@ impl ProxySocket<ShadowUdpSocket> {
context: SharedContext,
svr_cfg: &ServerConfig,
) -> ProxySocketResult<ProxySocket<ShadowUdpSocket>> {
ProxySocket::connect_with_opts(context, svr_cfg, &DEFAULT_CONNECT_OPTS)
.await
.map_err(Into::into)
ProxySocket::connect_with_opts(context, svr_cfg, &DEFAULT_CONNECT_OPTS).await
}
/// Create a client to communicate with Shadowsocks' UDP server (outbound)
@@ -127,9 +125,7 @@ impl ProxySocket<ShadowUdpSocket> {
context: SharedContext,
svr_cfg: &ServerConfig,
) -> ProxySocketResult<ProxySocket<ShadowUdpSocket>> {
ProxySocket::bind_with_opts(context, svr_cfg, AcceptOpts::default())
.await
.map_err(Into::into)
ProxySocket::bind_with_opts(context, svr_cfg, AcceptOpts::default()).await
}
/// Create a `ProxySocket` binding to a specific address (inbound)
@@ -240,9 +236,7 @@ where
/// Send a UDP packet to addr through proxy
#[inline]
pub async fn send(&self, addr: &Address, payload: &[u8]) -> ProxySocketResult<usize> {
self.send_with_ctrl(addr, &DEFAULT_SOCKET_CONTROL, payload)
.await
.map_err(Into::into)
self.send_with_ctrl(addr, &DEFAULT_SOCKET_CONTROL, payload).await
}
/// Send a UDP packet to addr through proxy with `ControlData`
@@ -391,9 +385,7 @@ where
/// Send a UDP packet to target through proxy `target`
pub async fn send_to(&self, target: SocketAddr, addr: &Address, payload: &[u8]) -> ProxySocketResult<usize> {
self.send_to_with_ctrl(target, addr, &DEFAULT_SOCKET_CONTROL, payload)
.await
.map_err(Into::into)
self.send_to_with_ctrl(target, addr, &DEFAULT_SOCKET_CONTROL, payload).await
}
/// Send a UDP packet to target through proxy `target`

View File

@@ -4,7 +4,6 @@ use std::process::ExitCode;
use base64::Engine as _;
use clap::{builder::PossibleValuesParser, Arg, ArgAction, ArgMatches, Command};
use rand::RngCore;
use shadowsocks_service::shadowsocks::crypto::{available_ciphers, CipherKind};
@@ -34,8 +33,7 @@ pub fn main(matches: &ArgMatches) -> ExitCode {
let key_len = method.key_len();
if key_len > 0 {
let mut key = vec![0u8; key_len];
let mut rng = rand::thread_rng();
rng.fill_bytes(&mut key);
rand::fill(key.as_mut_slice());
let encoded_key = base64::engine::general_purpose::STANDARD.encode(&key);
println!("{encoded_key}");

View File

@@ -6,7 +6,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall
PKG_VERSION:=25.2.12
PKG_VERSION:=25.3.2
PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \

View File

@@ -5,7 +5,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-passwall2
PKG_VERSION:=25.1.27
PKG_VERSION:=25.3.2
PKG_RELEASE:=1
PKG_CONFIG_DEPENDS:= \

View File

@@ -14,6 +14,7 @@ end
local normal_list = {}
local balancing_list = {}
local urltest_list = {}
local shunt_list = {}
local iface_list = {}
for k, v in pairs(nodes_table) do
@@ -23,6 +24,9 @@ for k, v in pairs(nodes_table) do
if v.protocol and v.protocol == "_balancing" then
balancing_list[#balancing_list + 1] = v
end
if v.protocol and v.protocol == "_urltest" then
urltest_list[#urltest_list + 1] = v
end
if v.protocol and v.protocol == "_shunt" then
shunt_list[#shunt_list + 1] = v
end
@@ -130,6 +134,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark)
end
@@ -174,6 +181,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark)
end
@@ -199,6 +209,9 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
for k1, v1 in pairs(balancing_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(urltest_list) do
o:value(v1.id, v1.remark)
end
for k1, v1 in pairs(iface_list) do
o:value(v1.id, v1.remark)
end
@@ -214,7 +227,7 @@ if (has_singbox or has_xray) and #nodes_table > 0 then
o:value("", translate("Close"))
o:value("main", translate("Preproxy Node"))
for k1, v1 in pairs(normal_list) do
if v1.protocol ~= "_balancing" then
if v1.protocol ~= "_balancing" and v1.protocol ~= "_urltest" then
o:depends({ [vid .. "-default_node"] = v1.id, [vid .. "-preproxy_enabled"] = "1" })
end
end

View File

@@ -96,6 +96,8 @@ o.cfgvalue = function(t, n)
local protocol = m:get(n, "protocol")
if protocol == "_balancing" then
protocol = translate("Balancing")
elseif protocol == "_urltest" then
protocol = "URLTest"
elseif protocol == "_shunt" then
protocol = translate("Shunt")
elseif protocol == "vmess" then

View File

@@ -56,6 +56,7 @@ end
if singbox_tags:find("with_quic") then
o:value("hysteria2", "Hysteria2")
end
o:value("_urltest", translate("URLTest"))
o:value("_shunt", translate("Shunt"))
o:value("_iface", translate("Custom Interface"))
@@ -65,6 +66,7 @@ o:depends({ [_n("protocol")] = "_iface" })
local nodes_table = {}
local iface_table = {}
local urltest_table = {}
for k, e in ipairs(api.get_valid_nodes()) do
if e.node_type == "normal" then
nodes_table[#nodes_table + 1] = {
@@ -79,6 +81,12 @@ for k, e in ipairs(api.get_valid_nodes()) do
remark = e["remark"]
}
end
if e.protocol == "_urltest" then
urltest_table[#urltest_table + 1] = {
id = e[".name"],
remark = e["remark"]
}
end
end
local socks_list = {}
@@ -91,6 +99,44 @@ m.uci:foreach(appname, "socks", function(s)
end
end)
--[[ URLTest ]]
o = s:option(DynamicList, _n("urltest_node"), translate("URLTest node list"), translate("List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"))
o:depends({ [_n("protocol")] = "_urltest" })
for k, v in pairs(nodes_table) do o:value(v.id, v.remark) end
o = s:option(Value, _n("urltest_url"), translate("Probe URL"))
o:depends({ [_n("protocol")] = "_urltest" })
o:value("https://cp.cloudflare.com/", "Cloudflare")
o:value("https://www.gstatic.com/generate_204", "Gstatic")
o:value("https://www.google.com/generate_204", "Google")
o:value("https://www.youtube.com/generate_204", "YouTube")
o:value("https://connect.rom.miui.com/generate_204", "MIUI (CN)")
o:value("https://connectivitycheck.platform.hicloud.com/generate_204", "HiCloud (CN)")
o.default = "https://www.gstatic.com/generate_204"
o.description = translate("The URL used to detect the connection status.")
o = s:option(Value, _n("urltest_interval"), translate("Test interval"))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "180"
o.description = translate("The test interval in seconds.") .. "<br />" ..
translate("Test interval must be less or equal than idle timeout.")
o = s:option(Value, _n("urltest_tolerance"), translate("Test tolerance"), translate("The test tolerance in milliseconds."))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "50"
o = s:option(Value, _n("urltest_idle_timeout"), translate("Idle timeout"), translate("The idle timeout in seconds."))
o:depends({ [_n("protocol")] = "_urltest" })
o.datatype = "uinteger"
o.default = "1800"
o = s:option(Flag, _n("urltest_interrupt_exist_connections"), translate("Interrupt existing connections"))
o:depends({ [_n("protocol")] = "_urltest" })
o.default = "0"
o.description = translate("Interrupt existing connections when the selected outbound has changed.")
-- [[ 分流模块 ]]
if #nodes_table > 0 then
o = s:option(Flag, _n("preproxy_enabled"), translate("Preproxy"))
@@ -101,6 +147,9 @@ if #nodes_table > 0 then
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remark)
end
@@ -124,6 +173,9 @@ m.uci:foreach(appname, "shunt_rules", function(e)
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remark)
end
@@ -155,6 +207,9 @@ if #nodes_table > 0 then
for k, v in pairs(socks_list) do
o:value(v.id, v.remark)
end
for k, v in pairs(urltest_table) do
o:value(v.id, v.remark)
end
for k, v in pairs(iface_table) do
o:value(v.id, v.remark)
end

View File

@@ -314,7 +314,7 @@ function strToTable(str)
end
function is_normal_node(e)
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then
if e and e.type and e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
return false
end
return true
@@ -444,7 +444,7 @@ function get_valid_nodes()
uci:foreach(appname, "nodes", function(e)
e.id = e[".name"]
if e.type and e.remarks then
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface") then
if e.protocol and (e.protocol == "_balancing" or e.protocol == "_shunt" or e.protocol == "_iface" or e.protocol == "_urltest") then
local type = e.type
if type == "sing-box" then type = "Sing-Box" end
e["remark"] = "%s[%s] " % {type .. " " .. i18n.translatef(e.protocol), e.remarks}
@@ -494,7 +494,7 @@ end
function get_node_remarks(n)
local remarks = ""
if n then
if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt" or n.protocol == "_iface") then
if n.protocol and (n.protocol == "_balancing" or n.protocol == "_shunt" or n.protocol == "_iface" or n.protocol == "_urltest") then
remarks = "%s[%s] " % {n.type .. " " .. i18n.translatef(n.protocol), n.remarks}
else
local type2 = n.type
@@ -1114,7 +1114,7 @@ end
function get_version()
local version = sys.exec("opkg list-installed luci-app-passwall2 2>/dev/null | awk '{print $3}'")
if not version or #version == 0 then
version = sys.exec("apk info -L luci-app-passwall2 2>/dev/null | awk 'NR == 1 {print $1}' | cut -d'-' -f4-")
version = sys.exec("apk list luci-app-passwall2 2>/dev/null | awk '/installed/ {print $1}' | cut -d'-' -f4-")
end
return version or ""
end

View File

@@ -892,6 +892,54 @@ function gen_config(var)
node.port = server_port
end
local function gen_urltest(_node)
local urltest_id = _node[".name"]
local urltest_tag = "urltest-" .. urltest_id
-- existing urltest
for _, v in ipairs(outbounds) do
if v.tag == urltest_tag then
return urltest_tag
end
end
-- new urltest
local ut_nodes = _node.urltest_node
local valid_nodes = {}
for i = 1, #ut_nodes do
local ut_node_id = ut_nodes[i]
local ut_node_tag = "ut-" .. ut_node_id
local is_new_ut_node = true
for _, outbound in ipairs(outbounds) do
if string.sub(outbound.tag, 1, #ut_node_tag) == ut_node_tag then
is_new_ut_node = false
valid_nodes[#valid_nodes + 1] = outbound.tag
break
end
end
if is_new_ut_node then
local ut_node = uci:get_all(appname, ut_node_id)
local outbound = gen_outbound(flag, ut_node, ut_node_tag)
if outbound then
outbound.tag = outbound.tag .. ":" .. ut_node.remarks
table.insert(outbounds, outbound)
valid_nodes[#valid_nodes + 1] = outbound.tag
end
end
end
if #valid_nodes == 0 then return nil end
local outbound = {
type = "urltest",
tag = urltest_tag,
outbounds = valid_nodes,
url = _node.urltest_url or "https://www.gstatic.com/generate_204",
interval = _node.urltest_interval and tonumber(_node.urltest_interval) and string.format("%dm", tonumber(_node.urltest_interval) / 60) or "3m",
tolerance = _node.urltest_tolerance and tonumber(_node.urltest_tolerance) and tonumber(_node.urltest_tolerance) or 50,
idle_timeout = _node.urltest_idle_timeout and tonumber(_node.urltest_idle_timeout) and string.format("%dm", tonumber(_node.urltest_idle_timeout) / 60) or "30m",
interrupt_exist_connections = (_node.urltest_interrupt_exist_connections == "true" or _node.urltest_interrupt_exist_connections == "1") and true or false
}
table.insert(outbounds, outbound)
return urltest_tag
end
local function set_outbound_detour(node, outbound, outbounds_table, shunt_rule_name)
if not node or not outbound or not outbounds_table then return nil end
local default_outTag = outbound.tag
@@ -1046,6 +1094,8 @@ function gen_config(var)
end
end
end
elseif _node.protocol == "_urltest" then
rule_outboundTag = gen_urltest(_node)
elseif _node.protocol == "_iface" then
if _node.iface then
local _outbound = {
@@ -1220,6 +1270,10 @@ function gen_config(var)
for index, value in ipairs(rules) do
table.insert(route.rules, rules[index])
end
elseif node.protocol == "_urltest" then
if node.urltest_node then
COMMON.default_outbound_tag = gen_urltest(node)
end
elseif node.protocol == "_iface" then
if node.iface then
local outbound = {

View File

@@ -1423,9 +1423,6 @@ msgstr "最大并发连接数"
msgid "XUDP Mux concurrency"
msgstr "XUDP 最大并发连接数"
msgid "Mux idle timeout"
msgstr "最大闲置时间"
msgid "Padding"
msgstr "填充"
@@ -1444,9 +1441,6 @@ msgstr "推荐值Sec-WebSocket-Protocol"
msgid "Health check"
msgstr "健康检查"
msgid "Idle timeout"
msgstr "闲置时间"
msgid "Health check timeout"
msgstr "检查超时时间"
@@ -1647,3 +1641,39 @@ msgstr "是否要恢复客户端默认配置?"
msgid "Are you sure you want to restore the client to default settings?"
msgstr "是否真的要恢复客户端默认配置?"
msgid "_urltest"
msgstr "URLTest"
msgid "URLTest node list"
msgstr "URLTest 节点列表"
msgid "List of nodes to test, <a target='_blank' href='https://sing-box.sagernet.org/configuration/outbound/urltest'>document</a>"
msgstr "要测试的节点列表,<a target='_blank' href='https://sing-box.sagernet.org/zh/configuration/outbound/urltest'>文档原理</a>"
msgid "Test interval"
msgstr "测试间隔"
msgid "The test interval in seconds."
msgstr "测试间隔时间(单位:秒)。"
msgid "Test interval must be less or equal than idle timeout."
msgstr "测试间隔时间必须小于或等于空闲超时时间。"
msgid "Test tolerance"
msgstr "测试容差"
msgid "The test tolerance in milliseconds."
msgstr "测试容差时间(单位:毫秒)。"
msgid "Idle timeout"
msgstr "空闲超时"
msgid "The idle timeout in seconds."
msgstr "空闲超时时间(单位:秒)。"
msgid "Interrupt existing connections"
msgstr "中断现有连接"
msgid "Interrupt existing connections when the selected outbound has changed."
msgstr "当选择的出站发生变化时中断现有连接。"

View File

@@ -589,7 +589,7 @@ run_socks() {
if [ "$type" == "sing-box" ] || [ "$type" == "xray" ]; then
local protocol=$(config_n_get $node protocol)
if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ]; then
if [ "$protocol" == "_balancing" ] || [ "$protocol" == "_shunt" ] || [ "$protocol" == "_iface" ] || [ "$protocol" == "_urltest" ]; then
unset error_msg
fi
fi

View File

@@ -303,6 +303,36 @@ do
end
}
end
elseif node.protocol and node.protocol == '_urltest' then
local flag = "Sing-Box URLTest节点[" .. node_id .. "]列表"
local currentNodes = {}
local newNodes = {}
if node.urltest_node then
for k, node in pairs(node.urltest_node) do
currentNodes[#currentNodes + 1] = {
log = false,
node = node,
currentNode = node and uci:get_all(appname, node) or nil,
remarks = node,
set = function(o, server)
if o and server and server ~= "nil" then
table.insert(o.newNodes, server)
end
end
}
end
end
CONFIG[#CONFIG + 1] = {
remarks = flag,
currentNodes = currentNodes,
newNodes = newNodes,
set = function(o, newNodes)
if o then
if not newNodes then newNodes = o.newNodes end
uci:set_list(appname, node_id, "urltest_node", newNodes or {})
end
end
}
else
--前置代理节点
local currentNode = uci:get_all(appname, node_id) or nil
@@ -1682,7 +1712,7 @@ local execute = function()
f:close()
raw = trim(stdout)
local old_md5 = value.md5 or ""
local new_md5 = luci.sys.exec("[ -f " .. tmp_file .. " ] && md5sum " .. tmp_file .. " | awk '{print $1}' || echo 0")
local new_md5 = luci.sys.exec("[ -f " .. tmp_file .. " ] && md5sum " .. tmp_file .. " | awk '{print $1}' || echo 0"):gsub("\n", "")
os.remove(tmp_file)
if old_md5 == new_md5 then
log('订阅:【' .. remark .. '】没有变化,无需更新。')

View File

@@ -71,7 +71,7 @@ url_test_node() {
sleep 1s
result=$(curl --connect-timeout 3 -o /dev/null -I -skL -w "%{http_code}:%{time_starttransfer}" -x $curlx "https://www.google.com/generate_204")
pgrep -af "url_test_${node_id}" | awk '! /test\.sh/{print $1}' | xargs kill -9 >/dev/null 2>&1
rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}.json"
rm -rf "/tmp/etc/${CONFIG}/url_test_${node_id}"*.json
}
echo $result
}

View File

@@ -21,13 +21,13 @@ define Download/geoip
HASH:=f2f5f03da44d007fa91fb6a37c077c9efae8ad0269ef0e4130cf90b0822873e3
endef
GEOSITE_VER:=20250227085631
GEOSITE_VER:=20250302153845
GEOSITE_FILE:=dlc.dat.$(GEOSITE_VER)
define Download/geosite
URL:=https://github.com/v2fly/domain-list-community/releases/download/$(GEOSITE_VER)/
URL_FILE:=dlc.dat
FILE:=$(GEOSITE_FILE)
HASH:=463eb36a757705c7ffefe7977752d7b610e62271e8b63a1ffcf55b27d50b0f73
HASH:=525dcdd8ac80d473ad128b568d7f3e16d0392872945a9468d18ac2afddd5b253
endef
GEOSITE_IRAN_VER:=202502240036

View File

@@ -1,7 +1,7 @@
<Project>
<PropertyGroup>
<Version>7.10.0</Version>
<Version>7.10.1</Version>
</PropertyGroup>
<PropertyGroup>

View File

@@ -5,21 +5,21 @@
<CentralPackageVersionOverrideEnabled>false</CentralPackageVersionOverrideEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.4" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.4" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.4" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.4" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.5" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.5" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.5" />
<PackageVersion Include="Avalonia.ReactiveUI" Version="11.2.5" />
<PackageVersion Include="CliWrap" Version="3.8.1" />
<PackageVersion Include="Downloader" Version="3.3.3" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.2.0" />
<PackageVersion Include="H.NotifyIcon.Wpf" Version="2.3.0" />
<PackageVersion Include="MaterialDesignThemes" Version="5.2.1" />
<PackageVersion Include="MessageBox.Avalonia" Version="3.2.0" />
<PackageVersion Include="QRCoder" Version="1.6.0" />
<PackageVersion Include="ReactiveUI" Version="20.1.63" />
<PackageVersion Include="ReactiveUI.Fody" Version="19.5.41" />
<PackageVersion Include="ReactiveUI.WPF" Version="20.1.63" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.4" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.4" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.5" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.5" />
<PackageVersion Include="Splat.NLog" Version="15.3.1" />
<PackageVersion Include="sqlite-net-pcl" Version="1.9.172" />
<PackageVersion Include="TaskScheduler" Version="2.12.0" />
@@ -27,4 +27,4 @@
<PackageVersion Include="YamlDotNet" Version="16.3.0" />
<PackageVersion Include="ZXing.Net.Bindings.SkiaSharp" Version="0.16.14" />
</ItemGroup>
</Project>
</Project>

View File

@@ -711,6 +711,7 @@ type SocketConfig struct {
Interface string `json:"interface"`
TcpMptcp bool `json:"tcpMptcp"`
CustomSockopt []*CustomSockoptConfig `json:"customSockopt"`
AddressPortStrategy string `json:"addressPortStrategy"`
}
// Build implements Buildable.
@@ -780,6 +781,26 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
customSockopts = append(customSockopts, customSockopt)
}
addressPortStrategy := internet.AddressPortStrategy_None
switch strings.ToLower(c.AddressPortStrategy) {
case "none", "":
addressPortStrategy = internet.AddressPortStrategy_None
case "srvportonly":
addressPortStrategy = internet.AddressPortStrategy_SrvPortOnly
case "srvaddressonly":
addressPortStrategy = internet.AddressPortStrategy_SrvAddressOnly
case "srvportandaddress":
addressPortStrategy = internet.AddressPortStrategy_SrvPortAndAddress
case "txtportonly":
addressPortStrategy = internet.AddressPortStrategy_TxtPortOnly
case "txtaddressonly":
addressPortStrategy = internet.AddressPortStrategy_TxtAddressOnly
case "txtportandaddress":
addressPortStrategy = internet.AddressPortStrategy_TxtPortAndAddress
default:
return nil, errors.New("unsupported address and port strategy: ", c.AddressPortStrategy)
}
return &internet.SocketConfig{
Mark: c.Mark,
Tfo: tfo,
@@ -798,6 +819,7 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
Interface: c.Interface,
TcpMptcp: c.TcpMptcp,
CustomSockopt: customSockopts,
AddressPortStrategy: addressPortStrategy,
}, nil
}

View File

@@ -95,6 +95,67 @@ func (DomainStrategy) EnumDescriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{0}
}
type AddressPortStrategy int32
const (
AddressPortStrategy_None AddressPortStrategy = 0
AddressPortStrategy_SrvPortOnly AddressPortStrategy = 1
AddressPortStrategy_SrvAddressOnly AddressPortStrategy = 2
AddressPortStrategy_SrvPortAndAddress AddressPortStrategy = 3
AddressPortStrategy_TxtPortOnly AddressPortStrategy = 4
AddressPortStrategy_TxtAddressOnly AddressPortStrategy = 5
AddressPortStrategy_TxtPortAndAddress AddressPortStrategy = 6
)
// Enum value maps for AddressPortStrategy.
var (
AddressPortStrategy_name = map[int32]string{
0: "None",
1: "SrvPortOnly",
2: "SrvAddressOnly",
3: "SrvPortAndAddress",
4: "TxtPortOnly",
5: "TxtAddressOnly",
6: "TxtPortAndAddress",
}
AddressPortStrategy_value = map[string]int32{
"None": 0,
"SrvPortOnly": 1,
"SrvAddressOnly": 2,
"SrvPortAndAddress": 3,
"TxtPortOnly": 4,
"TxtAddressOnly": 5,
"TxtPortAndAddress": 6,
}
)
func (x AddressPortStrategy) Enum() *AddressPortStrategy {
p := new(AddressPortStrategy)
*p = x
return p
}
func (x AddressPortStrategy) String() string {
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
}
func (AddressPortStrategy) Descriptor() protoreflect.EnumDescriptor {
return file_transport_internet_config_proto_enumTypes[1].Descriptor()
}
func (AddressPortStrategy) Type() protoreflect.EnumType {
return &file_transport_internet_config_proto_enumTypes[1]
}
func (x AddressPortStrategy) Number() protoreflect.EnumNumber {
return protoreflect.EnumNumber(x)
}
// Deprecated: Use AddressPortStrategy.Descriptor instead.
func (AddressPortStrategy) EnumDescriptor() ([]byte, []int) {
return file_transport_internet_config_proto_rawDescGZIP(), []int{1}
}
type SocketConfig_TProxyMode int32
const (
@@ -131,11 +192,11 @@ func (x SocketConfig_TProxyMode) String() string {
}
func (SocketConfig_TProxyMode) Descriptor() protoreflect.EnumDescriptor {
return file_transport_internet_config_proto_enumTypes[1].Descriptor()
return file_transport_internet_config_proto_enumTypes[2].Descriptor()
}
func (SocketConfig_TProxyMode) Type() protoreflect.EnumType {
return &file_transport_internet_config_proto_enumTypes[1]
return &file_transport_internet_config_proto_enumTypes[2]
}
func (x SocketConfig_TProxyMode) Number() protoreflect.EnumNumber {
@@ -434,23 +495,24 @@ type SocketConfig struct {
Tproxy SocketConfig_TProxyMode `protobuf:"varint,3,opt,name=tproxy,proto3,enum=xray.transport.internet.SocketConfig_TProxyMode" json:"tproxy,omitempty"`
// ReceiveOriginalDestAddress is for enabling IP_RECVORIGDSTADDR socket
// option. This option is for UDP only.
ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
DomainStrategy DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
DialerProxy string `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"`
TcpKeepAliveInterval int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
TcpKeepAliveIdle int32 `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
TcpCongestion string `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"`
Interface string `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"`
V6Only bool `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"`
TcpWindowClamp int32 `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"`
TcpUserTimeout int32 `protobuf:"varint,16,opt,name=tcp_user_timeout,json=tcpUserTimeout,proto3" json:"tcp_user_timeout,omitempty"`
TcpMaxSeg int32 `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"`
Penetrate bool `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
ReceiveOriginalDestAddress bool `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
BindAddress []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
BindPort uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
AcceptProxyProtocol bool `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
DomainStrategy DomainStrategy `protobuf:"varint,8,opt,name=domain_strategy,json=domainStrategy,proto3,enum=xray.transport.internet.DomainStrategy" json:"domain_strategy,omitempty"`
DialerProxy string `protobuf:"bytes,9,opt,name=dialer_proxy,json=dialerProxy,proto3" json:"dialer_proxy,omitempty"`
TcpKeepAliveInterval int32 `protobuf:"varint,10,opt,name=tcp_keep_alive_interval,json=tcpKeepAliveInterval,proto3" json:"tcp_keep_alive_interval,omitempty"`
TcpKeepAliveIdle int32 `protobuf:"varint,11,opt,name=tcp_keep_alive_idle,json=tcpKeepAliveIdle,proto3" json:"tcp_keep_alive_idle,omitempty"`
TcpCongestion string `protobuf:"bytes,12,opt,name=tcp_congestion,json=tcpCongestion,proto3" json:"tcp_congestion,omitempty"`
Interface string `protobuf:"bytes,13,opt,name=interface,proto3" json:"interface,omitempty"`
V6Only bool `protobuf:"varint,14,opt,name=v6only,proto3" json:"v6only,omitempty"`
TcpWindowClamp int32 `protobuf:"varint,15,opt,name=tcp_window_clamp,json=tcpWindowClamp,proto3" json:"tcp_window_clamp,omitempty"`
TcpUserTimeout int32 `protobuf:"varint,16,opt,name=tcp_user_timeout,json=tcpUserTimeout,proto3" json:"tcp_user_timeout,omitempty"`
TcpMaxSeg int32 `protobuf:"varint,17,opt,name=tcp_max_seg,json=tcpMaxSeg,proto3" json:"tcp_max_seg,omitempty"`
Penetrate bool `protobuf:"varint,18,opt,name=penetrate,proto3" json:"penetrate,omitempty"`
TcpMptcp bool `protobuf:"varint,19,opt,name=tcp_mptcp,json=tcpMptcp,proto3" json:"tcp_mptcp,omitempty"`
CustomSockopt []*CustomSockopt `protobuf:"bytes,20,rep,name=customSockopt,proto3" json:"customSockopt,omitempty"`
AddressPortStrategy AddressPortStrategy `protobuf:"varint,21,opt,name=address_port_strategy,json=addressPortStrategy,proto3,enum=xray.transport.internet.AddressPortStrategy" json:"address_port_strategy,omitempty"`
}
func (x *SocketConfig) Reset() {
@@ -623,6 +685,13 @@ func (x *SocketConfig) GetCustomSockopt() []*CustomSockopt {
return nil
}
func (x *SocketConfig) GetAddressPortStrategy() AddressPortStrategy {
if x != nil {
return x.AddressPortStrategy
}
return AddressPortStrategy_None
}
var File_transport_internet_config_proto protoreflect.FileDescriptor
var file_transport_internet_config_proto_rawDesc = []byte{
@@ -678,7 +747,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
0x28, 0x09, 0x52, 0x03, 0x6f, 0x70, 0x74, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65,
0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x12, 0x0a,
0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x79, 0x70,
0x65, 0x22, 0x9b, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
0x65, 0x22, 0xfd, 0x07, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x43, 0x6f, 0x6e, 0x66,
0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x66, 0x6f, 0x18, 0x02, 0x20,
0x01, 0x28, 0x05, 0x52, 0x03, 0x74, 0x66, 0x6f, 0x12, 0x48, 0x0a, 0x06, 0x74, 0x70, 0x72, 0x6f,
@@ -732,28 +801,44 @@ var file_transport_internet_config_proto_rawDesc = []byte{
0x74, 0x18, 0x14, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x26, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74,
0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65,
0x74, 0x2e, 0x43, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x52,
0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x22, 0x2f,
0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03,
0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10,
0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a,
0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65,
0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12, 0x0a, 0x0a,
0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45,
0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50,
0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10,
0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x05, 0x12,
0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12, 0x0d, 0x0a,
0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d, 0x0a, 0x09,
0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a, 0x0a, 0x46,
0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a, 0x0a, 0x46,
0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x42, 0x67, 0x0a, 0x1b, 0x63,
0x6f, 0x6d, 0x2e, 0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72,
0x61, 0x79, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72,
0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61,
0x79, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65,
0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x6f, 0x63, 0x6b, 0x6f, 0x70, 0x74, 0x12, 0x60,
0x0a, 0x15, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x5f, 0x73,
0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x2c, 0x2e,
0x78, 0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50,
0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79, 0x52, 0x13, 0x61, 0x64, 0x64,
0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72, 0x61, 0x74, 0x65, 0x67, 0x79,
0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07,
0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78,
0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10,
0x02, 0x2a, 0xa9, 0x01, 0x0a, 0x0e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x74, 0x72, 0x61,
0x74, 0x65, 0x67, 0x79, 0x12, 0x09, 0x0a, 0x05, 0x41, 0x53, 0x5f, 0x49, 0x53, 0x10, 0x00, 0x12,
0x0a, 0x0a, 0x06, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x55,
0x53, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x02, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x53, 0x45, 0x5f,
0x49, 0x50, 0x36, 0x10, 0x03, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x34,
0x36, 0x10, 0x04, 0x12, 0x0c, 0x0a, 0x08, 0x55, 0x53, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10,
0x05, 0x12, 0x0c, 0x0a, 0x08, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x10, 0x06, 0x12,
0x0d, 0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x10, 0x07, 0x12, 0x0d,
0x0a, 0x09, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x10, 0x08, 0x12, 0x0e, 0x0a,
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x34, 0x36, 0x10, 0x09, 0x12, 0x0e, 0x0a,
0x0a, 0x46, 0x4f, 0x52, 0x43, 0x45, 0x5f, 0x49, 0x50, 0x36, 0x34, 0x10, 0x0a, 0x2a, 0x97, 0x01,
0x0a, 0x13, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x50, 0x6f, 0x72, 0x74, 0x53, 0x74, 0x72,
0x61, 0x74, 0x65, 0x67, 0x79, 0x12, 0x08, 0x0a, 0x04, 0x4e, 0x6f, 0x6e, 0x65, 0x10, 0x00, 0x12,
0x0f, 0x0a, 0x0b, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x01,
0x12, 0x12, 0x0a, 0x0e, 0x53, 0x72, 0x76, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e,
0x6c, 0x79, 0x10, 0x02, 0x12, 0x15, 0x0a, 0x11, 0x53, 0x72, 0x76, 0x50, 0x6f, 0x72, 0x74, 0x41,
0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x03, 0x12, 0x0f, 0x0a, 0x0b, 0x54,
0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x04, 0x12, 0x12, 0x0a, 0x0e,
0x54, 0x78, 0x74, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x4f, 0x6e, 0x6c, 0x79, 0x10, 0x05,
0x12, 0x15, 0x0a, 0x11, 0x54, 0x78, 0x74, 0x50, 0x6f, 0x72, 0x74, 0x41, 0x6e, 0x64, 0x41, 0x64,
0x64, 0x72, 0x65, 0x73, 0x73, 0x10, 0x06, 0x42, 0x67, 0x0a, 0x1b, 0x63, 0x6f, 0x6d, 0x2e, 0x78,
0x72, 0x61, 0x79, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x78, 0x74, 0x6c, 0x73, 0x2f, 0x78, 0x72, 0x61, 0x79, 0x2d, 0x63,
0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x17, 0x58, 0x72, 0x61, 0x79, 0x2e, 0x54, 0x72,
0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
@@ -768,33 +853,35 @@ func file_transport_internet_config_proto_rawDescGZIP() []byte {
return file_transport_internet_config_proto_rawDescData
}
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 2)
var file_transport_internet_config_proto_enumTypes = make([]protoimpl.EnumInfo, 3)
var file_transport_internet_config_proto_msgTypes = make([]protoimpl.MessageInfo, 5)
var file_transport_internet_config_proto_goTypes = []any{
(DomainStrategy)(0), // 0: xray.transport.internet.DomainStrategy
(SocketConfig_TProxyMode)(0), // 1: xray.transport.internet.SocketConfig.TProxyMode
(*TransportConfig)(nil), // 2: xray.transport.internet.TransportConfig
(*StreamConfig)(nil), // 3: xray.transport.internet.StreamConfig
(*ProxyConfig)(nil), // 4: xray.transport.internet.ProxyConfig
(*CustomSockopt)(nil), // 5: xray.transport.internet.CustomSockopt
(*SocketConfig)(nil), // 6: xray.transport.internet.SocketConfig
(*serial.TypedMessage)(nil), // 7: xray.common.serial.TypedMessage
(*net.IPOrDomain)(nil), // 8: xray.common.net.IPOrDomain
(AddressPortStrategy)(0), // 1: xray.transport.internet.AddressPortStrategy
(SocketConfig_TProxyMode)(0), // 2: xray.transport.internet.SocketConfig.TProxyMode
(*TransportConfig)(nil), // 3: xray.transport.internet.TransportConfig
(*StreamConfig)(nil), // 4: xray.transport.internet.StreamConfig
(*ProxyConfig)(nil), // 5: xray.transport.internet.ProxyConfig
(*CustomSockopt)(nil), // 6: xray.transport.internet.CustomSockopt
(*SocketConfig)(nil), // 7: xray.transport.internet.SocketConfig
(*serial.TypedMessage)(nil), // 8: xray.common.serial.TypedMessage
(*net.IPOrDomain)(nil), // 9: xray.common.net.IPOrDomain
}
var file_transport_internet_config_proto_depIdxs = []int32{
7, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
8, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
2, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
7, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
6, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
1, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
8, // 0: xray.transport.internet.TransportConfig.settings:type_name -> xray.common.serial.TypedMessage
9, // 1: xray.transport.internet.StreamConfig.address:type_name -> xray.common.net.IPOrDomain
3, // 2: xray.transport.internet.StreamConfig.transport_settings:type_name -> xray.transport.internet.TransportConfig
8, // 3: xray.transport.internet.StreamConfig.security_settings:type_name -> xray.common.serial.TypedMessage
7, // 4: xray.transport.internet.StreamConfig.socket_settings:type_name -> xray.transport.internet.SocketConfig
2, // 5: xray.transport.internet.SocketConfig.tproxy:type_name -> xray.transport.internet.SocketConfig.TProxyMode
0, // 6: xray.transport.internet.SocketConfig.domain_strategy:type_name -> xray.transport.internet.DomainStrategy
5, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
8, // [8:8] is the sub-list for method output_type
8, // [8:8] is the sub-list for method input_type
8, // [8:8] is the sub-list for extension type_name
8, // [8:8] is the sub-list for extension extendee
0, // [0:8] is the sub-list for field type_name
6, // 7: xray.transport.internet.SocketConfig.customSockopt:type_name -> xray.transport.internet.CustomSockopt
1, // 8: xray.transport.internet.SocketConfig.address_port_strategy:type_name -> xray.transport.internet.AddressPortStrategy
9, // [9:9] is the sub-list for method output_type
9, // [9:9] is the sub-list for method input_type
9, // [9:9] is the sub-list for extension type_name
9, // [9:9] is the sub-list for extension extendee
0, // [0:9] is the sub-list for field type_name
}
func init() { file_transport_internet_config_proto_init() }
@@ -807,7 +894,7 @@ func file_transport_internet_config_proto_init() {
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_transport_internet_config_proto_rawDesc,
NumEnums: 2,
NumEnums: 3,
NumMessages: 5,
NumExtensions: 0,
NumServices: 0,

Some files were not shown because too many files have changed in this diff Show More