Update On Sat Mar 21 19:50:15 CET 2026

This commit is contained in:
github-action[bot]
2026-03-21 19:50:15 +01:00
parent 6f0b8b4a85
commit f2b058a18d
132 changed files with 2099 additions and 970 deletions
+1
View File
@@ -1305,3 +1305,4 @@ Update On Tue Mar 17 20:16:34 CET 2026
Update On Wed Mar 18 20:13:21 CET 2026
Update On Thu Mar 19 20:11:34 CET 2026
Update On Fri Mar 20 20:03:22 CET 2026
Update On Sat Mar 21 19:50:06 CET 2026
+9 -9
View File
@@ -12,19 +12,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Checkout submodules
run: git submodule update --init --recursive --force
- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
- name: Setup Go
uses: actions/setup-go@v6
@@ -37,7 +37,7 @@ jobs:
cd $(go env GOROOT)
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
~/.cache/go-build
@@ -71,7 +71,7 @@ jobs:
run: ./gradlew --no-daemon app:assembleAlphaRelease
- name: Upload Aritfact (universal)
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: ${{ success() }}
with:
name: CMFA Debug Unsigned APK (universal)
@@ -79,7 +79,7 @@ jobs:
app/build/outputs/apk/alpha/release/*-universal-*.apk
- name: Upload Aritfact (arm64-v8a)
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: ${{ success() }}
with:
name: CMFA Debug Unsigned APK (arm64-v8a)
@@ -87,7 +87,7 @@ jobs:
app/build/outputs/apk/alpha/release/*-arm64-v8a-*.apk
- name: Upload Aritfact (armeabi-v7a)
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: ${{ success() }}
with:
name: CMFA Debug Unsigned APK (armeabi-v7a)
@@ -95,7 +95,7 @@ jobs:
app/build/outputs/apk/alpha/release/*-armeabi-v7a-*.apk
- name: Upload Aritfact (x86_64)
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: ${{ success() }}
with:
name: CMFA Debug Unsigned APK (x86_64)
@@ -103,7 +103,7 @@ jobs:
app/build/outputs/apk/alpha/release/*-x86_64-*.apk
- name: Upload Aritfact (x86)
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
if: ${{ success() }}
with:
name: CMFA Debug Unsigned APK (x86)
@@ -10,19 +10,19 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Checkout submodules
run: git submodule update --init --recursive --force
- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
- name: Setup Go
uses: actions/setup-go@v6
@@ -35,7 +35,7 @@ jobs:
cd $(go env GOROOT)
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
~/.cache/go-build
+4 -4
View File
@@ -12,7 +12,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
fetch-depth: 0
@@ -20,13 +20,13 @@ jobs:
run: git submodule update --init --recursive --force
- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: 21
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
uses: gradle/actions/setup-gradle@v5
- name: Setup Go
uses: actions/setup-go@v6
@@ -39,7 +39,7 @@ jobs:
cd $(go env GOROOT)
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
~/.cache/go-build
@@ -10,13 +10,13 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout Repository
uses: actions/checkout@v4
uses: actions/checkout@v6
- name: Checkout and Update submodules
run: git submodule update --init --recursive --remote --force
- name: Setup Java
uses: actions/setup-java@v4
uses: actions/setup-java@v5
with:
distribution: "temurin"
java-version: 21
@@ -32,7 +32,7 @@ jobs:
cd $(go env GOROOT)
for p in $GITHUB_WORKSPACE/.github/patch/*.patch; do patch --verbose -p 1 < "$p"; done
- uses: actions/cache@v4
- uses: actions/cache@v5
with:
path: |
~/.cache/go-build
+7 -7
View File
@@ -155,7 +155,7 @@ jobs:
- { goos: linux, goarch: amd64, goamd64: v3, output: amd64-v3-go120, goversion: '1.20' }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Go
if: ${{ matrix.jobs.goversion == '' }}
@@ -386,7 +386,7 @@ jobs:
shell: bash
- name: Archive production artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}"
path: |
@@ -405,7 +405,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
path: bin/
merge-multiple: true
@@ -463,7 +463,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: Meta
fetch-depth: '0'
@@ -498,7 +498,7 @@ jobs:
bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}
rm ./genReleaseNote.sh
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
path: bin/
merge-multiple: true
@@ -522,11 +522,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
path: bin/
merge-multiple: true
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
# Fix mingw trying to be smart and converting paths https://github.com/moby/moby/issues/24029#issuecomment-250412919
MSYS_NO_PATHCONV: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
+3 -10
View File
@@ -165,11 +165,10 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
q := m.Question[0]
domain := msgToDomain(m)
cacheM, expireTime, hit := r.cache.GetWithExpire(q.String())
msg, expireTime, hit := getMsgFromCache(r.cache, q)
if hit {
log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(cacheM), expireTime.Format("2006-01-02 15:04:05"))
log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(msg), expireTime.Format("2006-01-02 15:04:05"))
now := time.Now()
msg = cacheM.Copy()
if expireTime.Before(now) {
setMsgTTL(msg, uint32(1)) // Continue fetch
continueFetch = true
@@ -201,14 +200,8 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
return
}
msg := result
if cache {
// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.
msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {
return rr.Header().Rrtype != D.TypeOPT
})
putMsgToCache(r.cache, q.String(), q, msg)
putMsgToCache(r.cache, q, result)
}
}()
+23 -3
View File
@@ -45,7 +45,19 @@ func updateTTL(records []D.RR, ttl uint32) {
}
}
func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) {
// getMsgFromCache returns a cached dns message if it exists, otherwise returns nil.
// the returned msg is a copy of the original msg, so it can be modified without affecting the original msg.
func getMsgFromCache(c dnsCache, q D.Question) (*D.Msg, time.Time, bool) {
msg, expireTime, hit := c.GetWithExpire(q.String())
if msg != nil {
msg = msg.Copy() // never modify the original msg
}
return msg, expireTime, hit
}
// putMsgToCache puts a dns message into the cache.
// the msg is copied before being stored in the cache, so it can be modified without affecting the original msg.
func putMsgToCache(c dnsCache, q D.Question, msg *D.Msg) {
// skip dns cache for acme challenge
if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") {
log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name)
@@ -58,12 +70,20 @@ func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) {
// If it does so it MUST NOT cache it for longer than five (5) minutes [...]
ttl = serverFailureCacheTTL
} else {
ttl = minimalTTL(append(append(msg.Answer, msg.Ns...), msg.Extra...))
ttl = minimalTTL(lo.Concat(msg.Answer, msg.Ns, msg.Extra))
}
if ttl == 0 {
return
}
c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(ttl)*time.Second))
msg = msg.Copy() // never modify the original msg
// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.
msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {
return rr.Header().Rrtype != D.TypeOPT
})
c.SetWithExpire(q.String(), msg, time.Now().Add(time.Duration(ttl)*time.Second))
}
func setMsgTTL(msg *D.Msg, ttl uint32) {
+3 -3
View File
@@ -60,10 +60,10 @@ body:
attributes:
label: 应用日志 / App logs
description: |
请确保您已移除所有敏感信息,并确保你的日志等级为 `Trace` 或 `Debug`。请参考 [FAQ - 日志目录](https://nyanpasu.elaina.moe/zh-CN/others/faq.html#_2-clash-nyanpasu-%E5%BA%94%E7%94%A8-%E6%97%A5%E8%AE%B0%E7%9B%AE%E5%BD%95%E5%9C%A8%E5%93%AA%E9%87%8C)
请确保您已移除所有敏感信息,并确保你的日志等级为 `Trace` 或 `Debug`。请参考 [FAQ - 日志目录](https://nyanpasu.majokeiko.com/zh-CN/others/faq.html#_2-clash-nyanpasu-%E5%BA%94%E7%94%A8-%E6%97%A5%E8%AE%B0%E7%9B%AE%E5%BD%95%E5%9C%A8%E5%93%AA%E9%87%8C)
如果您可以打开主界面,可以在设置页的“日志目录”旁找到“收集日志”按钮,点击即可收集日志。(1.5.0 不可用)
如果日志过长,请使用 [Gist](https://gist.github.com/) 或 [Hastebin](https://hastebin.com/) 并附上链接。
Please make sure you have removed all sensitive information and your log level is `Trace` or `Debug`. Please refer to [FAQ - Logs directory](https://nyanpasu.elaina.moe/others/faq.html#_2-where-is-the-clash-nyanpasu-application-logs-directory)
Please make sure you have removed all sensitive information and your log level is `Trace` or `Debug`. Please refer to [FAQ - Logs directory](https://nyanpasu.majokeiko.com/others/faq.html#_2-where-is-the-clash-nyanpasu-application-logs-directory)
If you can open the main interface, you can find the "Collect logs" button next to the "Open Logs Dir" in the settings page, click to collect the logs. (Not available in 1.5.0)
If the log is too long, please use [Gist](https://gist.github.com/) or [Hastebin](https://hastebin.com/) and provide the link.
placeholder: |
@@ -149,7 +149,7 @@ body:
required: false
- label: 您已知悉如果没有提供正确的系统信息,以及日志,您的 Issue 会直接被关闭 / You have known that if you don't provide correct system information and logs, your issue will be closed directly
required: true
- label: 您已仔细查看并知情 [Q&A](https://nyanpasu.elaina.moe/zh-CN/others/issues) 和 [FAQ](https://nyanpasu.elaina.moe/zh-CN/others/faq) 中的内容 / You have read and understood the contents of [Q&A](https://nyanpasu.elaina.moe/others/issues) and [FAQ](https://nyanpasu.elaina.moe/others/faq)
- label: 您已仔细查看并知情 [Q&A](https://nyanpasu.majokeiko.com/zh-CN/others/issues) 和 [FAQ](https://nyanpasu.majokeiko.com/zh-CN/others/faq) 中的内容 / You have read and understood the contents of [Q&A](https://nyanpasu.majokeiko.com/others/issues) and [FAQ](https://nyanpasu.majokeiko.com/others/faq)
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues), No duplicate or related open issue has been found
+1 -1
View File
@@ -86,7 +86,7 @@ body:
- label: 如果您有足够的时间和能力,并愿意为此提交 PR,请勾上此复选框 / Pull request is welcome. Check this if you want to start a pull request
required: false
- label: 您已仔细查看并知情 [Q&A](https://nyanpasu.elaina.moe/zh-CN/others/issues) 中的内容 / You have checked [Q&A](https://nyanpasu.elaina.moe/others/issues) carefully
- label: 您已仔细查看并知情 [Q&A](https://nyanpasu.majokeiko.com/zh-CN/others/issues) 中的内容 / You have checked [Q&A](https://nyanpasu.majokeiko.com/others/issues) carefully
required: true
- label: 您已搜索过 [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues),没有找到类似内容 / I have searched on [Issue Tracker](https://github.com/libnyanpasu/clash-nyanpasu/issues), No duplicate or related open issue has been found
+144 -47
View File
@@ -9,11 +9,15 @@ on:
type: boolean
default: false
fixed-webview:
description: 'Fixed WebView'
bundle-type:
description: 'Bundle variant'
required: true
type: boolean
default: false
type: choice
default: 'standard'
options:
- standard
- fixed-webview
- both
nightly:
description: 'Nightly prepare'
@@ -44,11 +48,11 @@ on:
type: boolean
default: false
fixed-webview:
description: 'Fixed WebView'
required: true
type: boolean
default: false
bundle-type:
description: 'Bundle variant: standard | fixed-webview | both'
required: false
type: string
default: 'standard'
nightly:
description: 'Nightly prepare'
@@ -137,8 +141,114 @@ jobs:
}
}
- name: Build UI
run: |
pnpm -F ui build
- name: Set file server folder path
if: ${{ inputs.nightly == true }}
shell: bash
run: |
GIT_HASH=$(git rev-parse --short HEAD)
echo "FOLDER_PATH=nightly/${GIT_HASH}" >> $GITHUB_ENV
- name: Prepare (Windows NSIS and Portable)
if: ${{ inputs.bundle-type != 'fixed-webview' }}
run: ${{ inputs.nightly == true && 'pnpm prepare:nightly --nsis' || 'pnpm prepare:release --nsis' }}
- name: Tauri build x86_64
if: ${{ inputs.arch == 'x86_64' && inputs.bundle-type != 'fixed-webview' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }}
run: |
pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json' || '-f default-meta' }}
- name: Tauri build i686
if: ${{ inputs.arch == 'i686' && inputs.bundle-type != 'fixed-webview' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }}
run: |
pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target i686-pc-windows-msvc' || '-f default-meta --target i686-pc-windows-msvc' }}
- name: Tauri build arm64
if: ${{ inputs.arch == 'aarch64' && inputs.bundle-type != 'fixed-webview' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }}
run: |
pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target aarch64-pc-windows-msvc' || '-f default-meta --target aarch64-pc-windows-msvc' }}
- name: Portable Bundle
if: ${{ inputs.portable == true && inputs.bundle-type != 'fixed-webview' }}
run: |
pnpm portable
env:
RUST_ARCH: ${{ inputs.arch }}
TAG_NAME: ${{ inputs.tag }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }}
VITE_WIN_PORTABLE: 1
- name: Upload NSIS Installer
if: ${{ inputs.bundle-type != 'fixed-webview' }}
uses: actions/upload-artifact@v7
with:
name: Clash.Nyanpasu-windows-${{ inputs.arch }}-nsis-installer
path: |
./backend/target/**/bundle/**/*.exe
./backend/target/**/bundle/**/*.zip
./backend/target/**/bundle/**/*.zip.sig
- name: Upload portable
if: ${{ inputs.portable == true && inputs.bundle-type != 'fixed-webview' }}
uses: actions/upload-artifact@v7
with:
name: Clash.Nyanpasu-windows-${{ inputs.arch }}-portable
path: |
./*_portable.zip
- name: Upload to file server
if: ${{ inputs.nightly == true && inputs.bundle-type != 'fixed-webview' }}
shell: bash
continue-on-error: true
run: |
deno run -A scripts/deno/upload-build-artifacts.ts \
"backend/target/**/bundle/nsis/*.exe" \
"*_portable.zip"
env:
FILE_SERVER_TOKEN: ${{ secrets.FILE_SERVER_TOKEN }}
FOLDER_PATH: ${{ env.FOLDER_PATH }}
- name: Upload file server results
if: ${{ inputs.nightly == true && inputs.bundle-type != 'fixed-webview' }}
uses: actions/upload-artifact@v7
with:
name: upload-results-windows-${{ inputs.arch }}
path: ./upload-results.json
if-no-files-found: ignore
- name: Clean standard build outputs
if: ${{ inputs.bundle-type == 'both' }}
run: |
Get-ChildItem -Path "./backend/target" -Recurse -Include "*.exe", "*.zip", "*.zip.sig" |
Where-Object { $_.FullName -like "*\bundle\*" } |
Remove-Item -Force -ErrorAction SilentlyContinue
Get-ChildItem -Path "." -Filter "*_portable.zip" -ErrorAction SilentlyContinue |
Remove-Item -Force
Remove-Item -Path "./upload-results.json" -Force -ErrorAction SilentlyContinue
- name: Download fixed WebView
if: ${{ inputs.fixed-webview == true }}
if: ${{ inputs.bundle-type != 'standard' }}
run: |
$condition = '${{ inputs.arch }}'
switch ($condition) {
@@ -162,20 +272,12 @@ jobs:
expand.exe $outfile -F:* ./backend/tauri
echo "Extraction finished"
- name: Prepare (Windows NSIS and Portable)
if: ${{ inputs.fixed-webview == false }}
run: ${{ inputs.nightly == true && 'pnpm prepare:nightly --nsis' || 'pnpm prepare:release --nsis' }}
- name: Prepare (Windows NSIS and Portable) with fixed WebView
if: ${{ inputs.fixed-webview == true }}
if: ${{ inputs.bundle-type != 'standard' }}
run: ${{ inputs.nightly == true && 'pnpm prepare:nightly --nsis --fixed-webview' || 'pnpm prepare:release --nsis --fixed-webview' }}
- name: Build UI
run: |
pnpm -F ui build
# TODO: optimize strategy
- name: Tauri build x86_64
if: ${{ inputs.arch == 'x86_64' }}
- name: Tauri build x86_64 with fixed WebView
if: ${{ inputs.arch == 'x86_64' && inputs.bundle-type != 'standard' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
@@ -184,8 +286,8 @@ jobs:
run: |
pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json' || '-f default-meta' }}
- name: Tauri build i686
if: ${{ inputs.arch == 'i686' }}
- name: Tauri build i686 with fixed WebView
if: ${{ inputs.arch == 'i686' && inputs.bundle-type != 'standard' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
@@ -193,8 +295,9 @@ jobs:
NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }}
run: |
pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target i686-pc-windows-msvc' || '-f default-meta --target i686-pc-windows-msvc' }}
- name: Tauri build arm64
if: ${{ inputs.arch == 'aarch64' }}
- name: Tauri build arm64 with fixed WebView
if: ${{ inputs.arch == 'aarch64' && inputs.bundle-type != 'standard' }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
@@ -204,7 +307,7 @@ jobs:
pnpm tauri build ${{ inputs.nightly == true && '-f nightly -c ./backend/tauri/tauri.nightly.conf.json --target aarch64-pc-windows-msvc' || '-f default-meta --target aarch64-pc-windows-msvc' }}
- name: Rename fixed webview bundle name
if: ${{ inputs.fixed-webview == true }}
if: ${{ inputs.bundle-type != 'standard' }}
run: |
$files = Get-ChildItem -Path "./backend/target" -Recurse -Include "*.exe", "*.zip", "*.zip.sig" | Where-Object { $_.FullName -like "*\bundle\*" }
$condition = '${{ inputs.arch }}'
@@ -226,10 +329,10 @@ jobs:
Rename-Item -Path $file -NewName $newname
}
- name: Portable Bundle
if: ${{ inputs.portable == true }}
- name: Portable Bundle with fixed WebView
if: ${{ inputs.portable == true && inputs.bundle-type != 'standard' }}
run: |
pnpm portable ${{ inputs.fixed-webview == true && '--fixed-webview' || '' }}
pnpm portable --fixed-webview
env:
RUST_ARCH: ${{ inputs.arch }}
TAG_NAME: ${{ inputs.tag }}
@@ -239,46 +342,40 @@ jobs:
NIGHTLY: ${{ inputs.nightly == true && 'true' || 'false' }}
VITE_WIN_PORTABLE: 1
- name: Upload NSIS Installer
- name: Upload NSIS Installer with fixed WebView
if: ${{ inputs.bundle-type != 'standard' }}
uses: actions/upload-artifact@v7
with:
name: Clash.Nyanpasu-windows-${{ inputs.arch }}${{ inputs.fixed-webview == true && '-fixed-webview' || '' }}-nsis-installer
name: Clash.Nyanpasu-windows-${{ inputs.arch }}-fixed-webview-nsis-installer
path: |
./backend/target/**/bundle/**/*.exe
./backend/target/**/bundle/**/*.zip
./backend/target/**/bundle/**/*.zip.sig
- name: Upload portable
if: ${{ inputs.portable == true }}
- name: Upload portable with fixed WebView
if: ${{ inputs.portable == true && inputs.bundle-type != 'standard' }}
uses: actions/upload-artifact@v7
with:
name: Clash.Nyanpasu-windows-${{ inputs.arch }}${{ inputs.fixed-webview == true && '-fixed-webview' || '' }}-portable
name: Clash.Nyanpasu-windows-${{ inputs.arch }}-fixed-webview-portable
path: |
./*_portable.zip
- name: Set file server folder path
if: ${{ inputs.nightly == true }}
shell: bash
run: |
GIT_HASH=$(git rev-parse --short HEAD)
echo "FOLDER_PATH=nightly/${GIT_HASH}" >> $GITHUB_ENV
- name: Upload to file server
if: ${{ inputs.nightly == true }}
- name: Upload to file server with fixed WebView
if: ${{ inputs.nightly == true && inputs.bundle-type != 'standard' }}
shell: bash
continue-on-error: true
run: |
deno run -A scripts/deno/upload-build-artifacts.ts \
"backend/target/**/bundle/**/*.exe" \
"backend/target/**/bundle/nsis/*.exe" \
"*_portable.zip"
env:
FILE_SERVER_TOKEN: ${{ secrets.FILE_SERVER_TOKEN }}
FOLDER_PATH: ${{ env.FOLDER_PATH }}
- name: Upload file server results
if: ${{ inputs.nightly == true }}
- name: Upload file server results with fixed WebView
if: ${{ inputs.nightly == true && inputs.bundle-type != 'standard' }}
uses: actions/upload-artifact@v7
with:
name: upload-results-windows-${{ inputs.arch }}${{ inputs.fixed-webview == true && '-fixed-webview' || '' }}
name: upload-results-windows-${{ inputs.arch }}-fixed-webview
path: ./upload-results.json
if-no-files-found: ignore
@@ -91,7 +91,6 @@ jobs:
- name: Upload updater to surge.sh
run: |
pnpm i -g surge
surge manifest/site surge.elaina.moe
surge manifest/site nyanpasu.surge.sh
env:
SURGE_TOKEN: ${{ secrets.SURGE_TOKEN }}
+3 -42
View File
@@ -21,7 +21,7 @@ jobs:
with:
portable: true
nightly: true
fixed-webview: false
bundle-type: 'both'
arch: 'x86_64'
tag: 'pre-release'
secrets: inherit
@@ -33,7 +33,7 @@ jobs:
with:
portable: true
nightly: true
fixed-webview: false
bundle-type: 'both'
arch: 'aarch64'
tag: 'pre-release'
secrets: inherit
@@ -45,47 +45,11 @@ jobs:
with:
portable: true
nightly: true
fixed-webview: false
bundle-type: 'both'
arch: 'i686'
tag: 'pre-release'
secrets: inherit
windows_amd64_build_fixed_webview:
name: Windows x86_64 Build with Fixed WebView
if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }}
uses: ./.github/workflows/deps-build-windows-nsis.yaml
with:
portable: true
nightly: true
arch: 'x86_64'
fixed-webview: true
tag: 'pre-release'
secrets: inherit
windows_aarch64_build_fixed_webview:
name: Windows aarch64 Build with Fixed WebView
if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }}
uses: ./.github/workflows/deps-build-windows-nsis.yaml
with:
portable: true
nightly: true
arch: 'aarch64'
fixed-webview: true
tag: 'pre-release'
secrets: inherit
windows_i686_build_fixed_webview:
name: Windows i686 Build with Fixed WebView
if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }}
uses: ./.github/workflows/deps-build-windows-nsis.yaml
with:
portable: true
nightly: true
arch: 'i686'
fixed-webview: true
tag: 'pre-release'
secrets: inherit
linux_amd64_build:
name: Linux amd64 Build
if: ${{ github.event_name == 'workflow_dispatch' || startsWith(github.repository, 'libnyanpasu') }}
@@ -163,9 +127,6 @@ jobs:
windows_amd64_build,
windows_i686_build,
windows_aarch64_build,
windows_amd64_build_fixed_webview,
windows_i686_build_fixed_webview,
windows_aarch64_build_fixed_webview,
linux_amd64_build,
linux_i686_build,
linux_aarch64_build,
@@ -15,6 +15,7 @@ jobs:
with:
portable: true
nightly: false
bundle-type: 'both'
tag: ${{ github.event.release.tag_name }}
secrets: inherit
+1 -1
View File
@@ -60,7 +60,7 @@ representative at an online or offline event.
Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
i@elaina.moe.
i@majokeiko.com.
All complaints will be reviewed and investigated promptly and fairly.
All community leaders are obligated to respect the privacy and security of the
+8 -8
View File
@@ -1,5 +1,5 @@
<h1 align="center">
<img src="https://nyanpasu.elaina.moe/images/banner/nyanpasu_banner.png" alt="Clash Nyanpasu Banner" />
<img src="https://nyanpasu.majokeiko.com/images/banner/nyanpasu_banner.png" alt="Clash Nyanpasu Banner" />
</h1>
<h3>Clash Nyanpasu</h3>
@@ -21,22 +21,22 @@
## Features
- Built-in support [Clash Premium](https://github.com/Dreamacro/clash), [Mihomo](https://github.com/MetaCubeX/mihomo) & [Clash Rust](https://github.com/Watfaq/clash-rs).
- Profiles management and enhancement (by YAML, JavaScript & Lua). [Doc](https://nyanpasu.elaina.moe/tutorial/proxy-chain)
- Profiles management and enhancement (by YAML, JavaScript & Lua). [Doc](https://nyanpasu.majokeiko.com/tutorial/proxy-chain)
- Provider management support.
- Google Material You Design UI and animation support.
## Preview
![preview-light](https://nyanpasu.elaina.moe/images/screenshot/app-dashboard-light.png)
![preview-light](https://nyanpasu.majokeiko.com/images/screenshot/app-dashboard-light.png)
![preview-dark](https://nyanpasu.elaina.moe/images/screenshot/app-dashboard-dark.png)
![preview-dark](https://nyanpasu.majokeiko.com/images/screenshot/app-dashboard-dark.png)
## Links
- [Install](https://nyanpasu.elaina.moe/tutorial/install)
- [FAQ](https://nyanpasu.elaina.moe/others/faq)
- [Q&A Convention](https://nyanpasu.elaina.moe/others/issues)
- [How To Ask Questions](https://nyanpasu.elaina.moe/others/how-to-ask)
- [Install](https://nyanpasu.majokeiko.com/tutorial/install)
- [FAQ](https://nyanpasu.majokeiko.com/others/faq)
- [Q&A Convention](https://nyanpasu.majokeiko.com/others/issues)
- [How To Ask Questions](https://nyanpasu.majokeiko.com/others/how-to-ask)
## Development
@@ -11,7 +11,6 @@
"plugins": {
"updater": {
"endpoints": [
"https://deno.elaina.moe/updater/update-fixed-webview-proxy.json",
"https://nyanpasu.surge.sh/updater/update-fixed-webview-proxy.json",
"https://gh-proxy.com/https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-fixed-webview-proxy.json",
"https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-fixed-webview.json"
@@ -5,7 +5,6 @@
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDlBMUM0NjMxREZCNDRGMjYKUldRbVQ3VGZNVVljbW43N0FlWjA4UkNrbTgxSWxSSXJQcExXNkZjUTlTQkIyYkJzL0tsSWF2d0cK",
"endpoints": [
"https://deno.elaina.moe/updater/update-nightly-proxy.json",
"https://nyanpasu.surge.sh/updater/update-nightly-proxy.json",
"https://gh-proxy.com/https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-nightly-proxy.json",
"https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-nightly.json"
@@ -72,7 +72,6 @@
"updater": {
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDlBMUM0NjMxREZCNDRGMjYKUldRbVQ3VGZNVVljbW43N0FlWjA4UkNrbTgxSWxSSXJQcExXNkZjUTlTQkIyYkJzL0tsSWF2d0cK",
"endpoints": [
"https://deno.elaina.moe/updater/update-proxy.json",
"https://nyanpasu.surge.sh/updater/update-proxy.json",
"https://gh-proxy.com/https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update-proxy.json",
"https://github.com/libnyanpasu/clash-nyanpasu/releases/download/updater/update.json"
@@ -1,6 +1,6 @@
// nyanpasu merge profile chain template
const merge = `# Clash Nyanpasu Merge Template (YAML)
# Documentation on https://nyanpasu.elaina.moe/
# Documentation on https://nyanpasu.majokeiko.com/
# Set the default merge strategy to recursive merge.
# Enable the old mode with the override__ prefix.
# Use the filter__ prefix to filter lists (removing unwanted content).
@@ -9,7 +9,7 @@ const merge = `# Clash Nyanpasu Merge Template (YAML)
// nyanpasu javascript profile chain template
const javascript = `// Clash Nyanpasu JavaScript Template
// Documentation on https://nyanpasu.elaina.moe/
// Documentation on https://nyanpasu.majokeiko.com/
/** @type {config} */
export default function (profile) {
@@ -19,14 +19,14 @@ export default function (profile) {
// nyanpasu lua profile chain template
const luascript = `-- Clash Nyanpasu Lua Script Template
-- Documentation on https://nyanpasu.elaina.moe/
-- Documentation on https://nyanpasu.majokeiko.com/
return config;
`
// clash profile template example
const profile = `# Clash Nyanpasu Profile Template
# Documentation on https://nyanpasu.elaina.moe/
# Documentation on https://nyanpasu.majokeiko.com/
proxies:
@@ -17,7 +17,7 @@
"@dnd-kit/utilities": "3.2.2",
"@emotion/styled": "11.14.1",
"@hookform/resolvers": "5.2.2",
"@inlang/paraglide-js": "2.15.0",
"@inlang/paraglide-js": "2.15.1",
"@juggle/resize-observer": "3.4.0",
"@material/material-color-utilities": "0.4.0",
"@mui/icons-material": "7.3.9",
@@ -43,7 +43,7 @@
"country-emoji": "1.5.6",
"dayjs": "1.11.20",
"framer-motion": "12.38.0",
"i18next": "25.8.20",
"i18next": "25.9.0",
"jotai": "2.18.1",
"json-schema": "0.4.0",
"material-react-table": "3.2.1",
@@ -69,7 +69,7 @@
"@csstools/normalize.css": "12.1.1",
"@emotion/babel-plugin": "11.13.5",
"@emotion/react": "11.14.0",
"@iconify/json": "2.2.452",
"@iconify/json": "2.2.453",
"@monaco-editor/react": "4.7.0",
"@tanstack/react-query": "5.91.3",
"@tanstack/react-router": "1.167.5",
@@ -1,55 +1,55 @@
{
"default": {
"proxies": "https://nyanpasu.elaina.moe/others/field.html#proxies",
"proxy-groups": "https://nyanpasu.elaina.moe/others/field.html#proxy-groups",
"proxy-providers": "https://nyanpasu.elaina.moe/others/field.html#proxy-providers",
"rules": "https://nyanpasu.elaina.moe/others/field.html#rules",
"rule-providers": "https://nyanpasu.elaina.moe/others/field.html#rule-providers"
"proxies": "https://nyanpasu.majokeiko.com/others/field.html#proxies",
"proxy-groups": "https://nyanpasu.majokeiko.com/others/field.html#proxy-groups",
"proxy-providers": "https://nyanpasu.majokeiko.com/others/field.html#proxy-providers",
"rules": "https://nyanpasu.majokeiko.com/others/field.html#rules",
"rule-providers": "https://nyanpasu.majokeiko.com/others/field.html#rule-providers"
},
"handle": {
"mode": "https://nyanpasu.elaina.moe/others/field.html#mode",
"port": "https://nyanpasu.elaina.moe/others/field.html#port",
"socks-port": "https://nyanpasu.elaina.moe/others/field.html#socks-port",
"mixed-port": "https://nyanpasu.elaina.moe/others/field.html#mixed-port",
"allow-lan": "https://nyanpasu.elaina.moe/others/field.html#allow-lan",
"log-level": "https://nyanpasu.elaina.moe/others/field.html#log-level",
"ipv6": "https://nyanpasu.elaina.moe/others/field.html#ipv6",
"secret": "https://nyanpasu.elaina.moe/others/field.html#secret",
"external-controller": "https://nyanpasu.elaina.moe/others/field.html#external-controller"
"mode": "https://nyanpasu.majokeiko.com/others/field.html#mode",
"port": "https://nyanpasu.majokeiko.com/others/field.html#port",
"socks-port": "https://nyanpasu.majokeiko.com/others/field.html#socks-port",
"mixed-port": "https://nyanpasu.majokeiko.com/others/field.html#mixed-port",
"allow-lan": "https://nyanpasu.majokeiko.com/others/field.html#allow-lan",
"log-level": "https://nyanpasu.majokeiko.com/others/field.html#log-level",
"ipv6": "https://nyanpasu.majokeiko.com/others/field.html#ipv6",
"secret": "https://nyanpasu.majokeiko.com/others/field.html#secret",
"external-controller": "https://nyanpasu.majokeiko.com/others/field.html#external-controller"
},
"other": {
"dns": "https://nyanpasu.elaina.moe/others/field.html#dns",
"tun": "https://nyanpasu.elaina.moe/others/field.html#tun",
"ebpf": "https://nyanpasu.elaina.moe/others/field.html#ebpf",
"hosts": "https://nyanpasu.elaina.moe/others/field.html#hosts",
"script": "https://nyanpasu.elaina.moe/others/field.html#script",
"profile": "https://nyanpasu.elaina.moe/others/field.html#profile",
"payload": "https://nyanpasu.elaina.moe/others/field.html#payload",
"tunnels": "https://nyanpasu.elaina.moe/others/field.html#tunnels",
"auto-redir": "https://nyanpasu.elaina.moe/others/field.html#auto-redir",
"experimental": "https://nyanpasu.elaina.moe/others/field.html#experimental",
"interface-name": "https://nyanpasu.elaina.moe/others/field.html#interface-name",
"routing-mark": "https://nyanpasu.elaina.moe/others/field.html#routing-mark",
"redir-port": "https://nyanpasu.elaina.moe/others/field.html#redir-port",
"tproxy-port": "https://nyanpasu.elaina.moe/others/field.html#tproxy-port",
"iptables": "https://nyanpasu.elaina.moe/others/field.html#iptables",
"external-ui": "https://nyanpasu.elaina.moe/others/field.html#external-ui",
"bind-address": "https://nyanpasu.elaina.moe/others/field.html#bind-address",
"authentication": "https://nyanpasu.elaina.moe/others/field.html#authentication"
"dns": "https://nyanpasu.majokeiko.com/others/field.html#dns",
"tun": "https://nyanpasu.majokeiko.com/others/field.html#tun",
"ebpf": "https://nyanpasu.majokeiko.com/others/field.html#ebpf",
"hosts": "https://nyanpasu.majokeiko.com/others/field.html#hosts",
"script": "https://nyanpasu.majokeiko.com/others/field.html#script",
"profile": "https://nyanpasu.majokeiko.com/others/field.html#profile",
"payload": "https://nyanpasu.majokeiko.com/others/field.html#payload",
"tunnels": "https://nyanpasu.majokeiko.com/others/field.html#tunnels",
"auto-redir": "https://nyanpasu.majokeiko.com/others/field.html#auto-redir",
"experimental": "https://nyanpasu.majokeiko.com/others/field.html#experimental",
"interface-name": "https://nyanpasu.majokeiko.com/others/field.html#interface-name",
"routing-mark": "https://nyanpasu.majokeiko.com/others/field.html#routing-mark",
"redir-port": "https://nyanpasu.majokeiko.com/others/field.html#redir-port",
"tproxy-port": "https://nyanpasu.majokeiko.com/others/field.html#tproxy-port",
"iptables": "https://nyanpasu.majokeiko.com/others/field.html#iptables",
"external-ui": "https://nyanpasu.majokeiko.com/others/field.html#external-ui",
"bind-address": "https://nyanpasu.majokeiko.com/others/field.html#bind-address",
"authentication": "https://nyanpasu.majokeiko.com/others/field.html#authentication"
},
"meta": {
"tls": "https://nyanpasu.elaina.moe/others/field.html#tls",
"sniffer": "https://nyanpasu.elaina.moe/others/field.html#sniffer",
"geox-url": "https://nyanpasu.elaina.moe/others/field.html#geox-url",
"listeners": "https://nyanpasu.elaina.moe/others/field.html#listeners",
"sub-rules": "https://nyanpasu.elaina.moe/others/field.html#sub-rules",
"geodata-mode": "https://nyanpasu.elaina.moe/others/field.html#geodata-mode",
"unified-delay": "https://nyanpasu.elaina.moe/others/field.html#unified-delay",
"tcp-concurrent": "https://nyanpasu.elaina.moe/others/field.html#tcp-concurrent",
"enable-process": "https://nyanpasu.elaina.moe/others/field.html#enable-process",
"find-process-mode": "https://nyanpasu.elaina.moe/others/field.html#find-process-mode",
"skip-auth-prefixes": "https://nyanpasu.elaina.moe/others/field.html#skip-auth-prefixes",
"external-controller-tls": "https://nyanpasu.elaina.moe/others/field.html#external-controller-tls",
"global-client-fingerprint": "https://nyanpasu.elaina.moe/others/field.html#global-client-fingerprint"
"tls": "https://nyanpasu.majokeiko.com/others/field.html#tls",
"sniffer": "https://nyanpasu.majokeiko.com/others/field.html#sniffer",
"geox-url": "https://nyanpasu.majokeiko.com/others/field.html#geox-url",
"listeners": "https://nyanpasu.majokeiko.com/others/field.html#listeners",
"sub-rules": "https://nyanpasu.majokeiko.com/others/field.html#sub-rules",
"geodata-mode": "https://nyanpasu.majokeiko.com/others/field.html#geodata-mode",
"unified-delay": "https://nyanpasu.majokeiko.com/others/field.html#unified-delay",
"tcp-concurrent": "https://nyanpasu.majokeiko.com/others/field.html#tcp-concurrent",
"enable-process": "https://nyanpasu.majokeiko.com/others/field.html#enable-process",
"find-process-mode": "https://nyanpasu.majokeiko.com/others/field.html#find-process-mode",
"skip-auth-prefixes": "https://nyanpasu.majokeiko.com/others/field.html#skip-auth-prefixes",
"external-controller-tls": "https://nyanpasu.majokeiko.com/others/field.html#external-controller-tls",
"global-client-fingerprint": "https://nyanpasu.majokeiko.com/others/field.html#global-client-fingerprint"
}
}
@@ -0,0 +1,6 @@
declare const classNames: {
readonly item: 'item'
readonly shiki: 'shiki'
readonly dark: 'dark'
}
export default classNames
@@ -13,7 +13,7 @@ import { Link } from '@tanstack/react-router'
const WikiItem = () => {
const handleClick = useLockFn(async () => {
await commands.openThat('https://nyanpasu.elaina.moe')
await commands.openThat('https://nyanpasu.majokeiko.com')
})
return (
+2 -2
View File
@@ -2,7 +2,7 @@
"manifest_version": 1,
"latest": {
"mihomo": "v1.19.21",
"mihomo_alpha": "alpha-dd4eb63",
"mihomo_alpha": "alpha-ae374f6",
"clash_rs": "v0.9.6",
"clash_premium": "2023-09-05-gdcc8d87",
"clash_rs_alpha": "0.9.6-alpha+sha.b17ba0a"
@@ -69,5 +69,5 @@
"linux-armv7hf": "clash-rs-armv7-unknown-linux-gnueabihf"
}
},
"updated_at": "2026-03-19T22:22:44.922Z"
"updated_at": "2026-03-20T22:22:49.611Z"
}
+1 -1
View File
@@ -68,7 +68,7 @@
"cross-env": "10.1.0",
"dedent": "1.7.2",
"globals": "17.4.0",
"knip": "5.88.1",
"knip": "6.0.1",
"lint-staged": "16.4.0",
"npm-run-all2": "8.0.4",
"oxlint": "1.56.0",
+276 -49
View File
@@ -59,8 +59,8 @@ importers:
specifier: 17.4.0
version: 17.4.0
knip:
specifier: 5.88.1
version: 5.88.1(@types/node@24.11.0)(typescript@5.9.3)
specifier: 6.0.1
version: 6.0.1
lint-staged:
specifier: 16.4.0
version: 16.4.0
@@ -183,8 +183,8 @@ importers:
specifier: 5.2.2
version: 5.2.2(react-hook-form@7.71.2(react@19.2.4))
'@inlang/paraglide-js':
specifier: 2.15.0
version: 2.15.0(babel-plugin-macros@3.1.0)
specifier: 2.15.1
version: 2.15.1(babel-plugin-macros@3.1.0)
'@juggle/resize-observer':
specifier: 3.4.0
version: 3.4.0
@@ -261,8 +261,8 @@ importers:
specifier: 12.38.0
version: 12.38.0(@emotion/is-prop-valid@1.3.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
i18next:
specifier: 25.8.20
version: 25.8.20(typescript@5.9.3)
specifier: 25.9.0
version: 25.9.0(typescript@5.9.3)
jotai:
specifier: 2.18.1
version: 2.18.1(@babel/core@7.29.0)(@babel/template@7.28.6)(@types/react@19.2.14)(react@19.2.4)
@@ -301,7 +301,7 @@ importers:
version: 8.2.0(e309558cb1df3652b39c2e76ceb9cee4)
react-i18next:
specifier: 15.7.4
version: 15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
version: 15.7.4(i18next@25.9.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
react-markdown:
specifier: 10.1.0
version: 10.1.0(@types/react@19.2.14)(react@19.2.4)
@@ -334,8 +334,8 @@ importers:
specifier: 11.14.0
version: 11.14.0(@types/react@19.2.14)(react@19.2.4)
'@iconify/json':
specifier: 2.2.452
version: 2.2.452
specifier: 2.2.453
version: 2.2.453
'@monaco-editor/react':
specifier: 4.7.0
version: 4.7.0(monaco-editor@0.55.1)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -500,7 +500,7 @@ importers:
version: 6.0.0(react@19.2.4)
react-i18next:
specifier: 15.7.4
version: 15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
version: 15.7.4(i18next@25.9.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3)
react-use:
specifier: 17.6.0
version: 17.6.0(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
@@ -1704,8 +1704,8 @@ packages:
prettier-plugin-ember-template-tag:
optional: true
'@iconify/json@2.2.452':
resolution: {integrity: sha512-3gUSKSQ3UHRpuvk0GL5WdD3n+WR9MhvxjJ3cFjoBb44kD8yKC/esyxeOZfXdilRmJFvs1y8+j5DM6SIwCtMbXw==}
'@iconify/json@2.2.453':
resolution: {integrity: sha512-LL3avJyZUFtB/Ts1WbpunFZ58XnodcQn0xMLQbm9gxuuZNpnt2D89n+CstNHpqheynDz+nZVSSvKko1bFXBOTA==}
'@iconify/types@2.0.0':
resolution: {integrity: sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==}
@@ -1713,16 +1713,16 @@ packages:
'@iconify/utils@3.1.0':
resolution: {integrity: sha512-Zlzem1ZXhI1iHeeERabLNzBHdOa4VhQbqAcOQaMKuTuyZCpwKbC2R4Dd0Zo3g9EAc+Y4fiarO8HIHRAth7+skw==}
'@inlang/paraglide-js@2.15.0':
resolution: {integrity: sha512-2ZOa9nssVn4tjkKskqb88KP5A7cTIjo8AiM9xnPvH+vBhRIRenO+ftAbVOHhHcHjcFxy2QFcOfBAH/Cw1LIsUg==}
'@inlang/paraglide-js@2.15.1':
resolution: {integrity: sha512-7wWKbLWwLx1dkkYz55TnVp+39atKXf7rnlHnL8adSmM73UaAdB9fXDzo24GHSY/6FPGFKSkgHdT2qyJv2whWsA==}
hasBin: true
'@inlang/recommend-sherlock@0.2.1':
resolution: {integrity: sha512-ckv8HvHy/iTqaVAEKrr+gnl+p3XFNwe5D2+6w6wJk2ORV2XkcRkKOJ/XsTUJbPSiyi4PI+p+T3bqbmNx/rDUlg==}
'@inlang/sdk@2.8.0':
resolution: {integrity: sha512-w1jysvUDTMgCaONklIgOJAp9dUDl0UhLbsdqfWEwY/GIqoc9IwpuHsrP3pzC+h3DfOpkMMDnDkTpPv8kIZ98iA==}
engines: {node: '>=18.0.0'}
'@inlang/sdk@2.9.1':
resolution: {integrity: sha512-y0C3xaKo6pSGDr3p5OdreRVT3THJpgKVe1lLvG3BE4v9lskp3UfI9cPCbN8X2dpfLt/4ljtehMb5SykpMfJrMg==}
engines: {node: '>=20.0.0'}
'@isaacs/fs-minipass@4.0.1':
resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
@@ -1762,8 +1762,8 @@ packages:
'@keyv/serialize@1.1.1':
resolution: {integrity: sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==}
'@lix-js/sdk@0.4.7':
resolution: {integrity: sha512-pRbW+joG12L0ULfMiWYosIW0plmW4AsUdiPCp+Z8rAsElJ+wJ6in58zhD3UwUcd4BNcpldEGjg6PdA7e0RgsDQ==}
'@lix-js/sdk@0.4.9':
resolution: {integrity: sha512-30mDkXpx704359oRrJI42bjfCspCiaMItngVBbPkiTGypS7xX4jYbHWQkXI8XuJ7VDB69D0MsVU6xfrBAIrM4A==}
engines: {node: '>=18'}
'@lix-js/server-protocol-schema@0.1.1':
@@ -2232,42 +2232,91 @@ packages:
resolution: {integrity: sha512-16TtZXNOfH8RaRsV+iag5dTYeJvdOdZDBcpEPCULdKS3eTRJqAYxBNZPFaDJ3cx3WNyvbaQ0IxsPpnaR/tgGFA==}
engines: {node: '>= 20'}
'@oxc-parser/binding-android-arm-eabi@0.120.0':
resolution: {integrity: sha512-WU3qtINx802wOl8RxAF1v0VvmC2O4D9M8Sv486nLeQ7iPHVmncYZrtBhB4SYyX+XZxj2PNnCcN+PW21jHgiOxg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [android]
'@oxc-parser/binding-android-arm64@0.120.0':
resolution: {integrity: sha512-SEf80EHdhlbjZEgzeWm0ZA/br4GKMenDW3QB/gtyeTV1gStvvZeFi40ioHDZvds2m4Z9J1bUAUL8yn1/+A6iGg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@oxc-parser/binding-android-arm64@0.99.0':
resolution: {integrity: sha512-V4jhmKXgQQdRnm73F+r3ZY4pUEsijQeSraFeaCGng7abSNJGs76X6l82wHnmjLGFAeY00LWtjcELs7ZmbJ9+lA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [android]
'@oxc-parser/binding-darwin-arm64@0.120.0':
resolution: {integrity: sha512-xVrrbCai8R8CUIBu3CjryutQnEYhZqs1maIqDvtUCFZb8vY33H7uh9mHpL3a0JBIKoBUKjPH8+rzyAeXnS2d6A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@oxc-parser/binding-darwin-arm64@0.99.0':
resolution: {integrity: sha512-Rp41nf9zD5FyLZciS9l1GfK8PhYqrD5kEGxyTOA2esTLeAy37rZxetG2E3xteEolAkeb2WDkVrlxPtibeAncMg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [darwin]
'@oxc-parser/binding-darwin-x64@0.120.0':
resolution: {integrity: sha512-xyHBbnJ6mydnQUH7MAcafOkkrNzQC6T+LXgDH/3InEq2BWl/g424IMRiJVSpVqGjB+p2bd0h0WRR8iIwzjU7rw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@oxc-parser/binding-darwin-x64@0.99.0':
resolution: {integrity: sha512-WVonp40fPPxo5Gs0POTI57iEFv485TvNKOHMwZRhigwZRhZY2accEAkYIhei9eswF4HN5B44Wybkz7Gd1Qr/5Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [darwin]
'@oxc-parser/binding-freebsd-x64@0.120.0':
resolution: {integrity: sha512-UMnVRllquXUYTeNfFKmxTTEdZ/ix1nLl0ducDzMSREoWYGVIHnOOxoKMWlCOvRr9Wk/HZqo2rh1jeumbPGPV9A==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@oxc-parser/binding-freebsd-x64@0.99.0':
resolution: {integrity: sha512-H30bjOOttPmG54gAqu6+HzbLEzuNOYO2jZYrIq4At+NtLJwvNhXz28Hf5iEAFZIH/4hMpLkM4VN7uc+5UlNW3Q==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [freebsd]
'@oxc-parser/binding-linux-arm-gnueabihf@0.120.0':
resolution: {integrity: sha512-tkvn2CQ7QdcsMnpfiX3fd3wA3EFsWKYlcQzq9cFw/xc89Al7W6Y4O0FgLVkVQpo0Tnq/qtE1XfkJOnRRA9S/NA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@oxc-parser/binding-linux-arm-gnueabihf@0.99.0':
resolution: {integrity: sha512-0Z/Th0SYqzSRDPs6tk5lQdW0i73UCupnim3dgq2oW0//UdLonV/5wIZCArfKGC7w9y4h8TxgXpgtIyD1kKzzlQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@oxc-parser/binding-linux-arm-musleabihf@0.120.0':
resolution: {integrity: sha512-WN5y135Ic42gQDk9grbwY9++fDhqf8knN6fnP+0WALlAUh4odY/BDK1nfTJRSfpJD9P3r1BwU0m3pW2DU89whQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@oxc-parser/binding-linux-arm-musleabihf@0.99.0':
resolution: {integrity: sha512-xo0wqNd5bpbzQVNpAIFbHk1xa+SaS/FGBABCd942SRTnrpxl6GeDj/s1BFaGcTl8MlwlKVMwOcyKrw/2Kdfquw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm]
os: [linux]
'@oxc-parser/binding-linux-arm64-gnu@0.120.0':
resolution: {integrity: sha512-1GgQBCcXvFMw99EPdMy+4NZ3aYyXsxjf9kbUUg8HuAy3ZBXzOry5KfFEzT9nqmgZI1cuetvApkiJBZLAPo8uaw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-arm64-gnu@0.99.0':
resolution: {integrity: sha512-u26I6LKoLTPTd4Fcpr0aoAtjnGf5/ulMllo+QUiBhupgbVCAlaj4RyXH/mvcjcsl2bVBv9E/gYJZz2JjxQWXBA==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2275,6 +2324,13 @@ packages:
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-arm64-musl@0.120.0':
resolution: {integrity: sha512-gmMQ70gsPdDBgpcErvJEoWNBr7bJooSLlvOBVBSGfOzlP5NvJ3bFvnUeZZ9d+dPrqSngtonf7nyzWUTUj/U+lw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [linux]
libc: [musl]
'@oxc-parser/binding-linux-arm64-musl@0.99.0':
resolution: {integrity: sha512-qhftDo2D37SqCEl3ZTa367NqWSZNb1Ddp34CTmShLKFrnKdNiUn55RdokLnHtf1AL5ssaQlYDwBECX7XiBWOhw==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2282,6 +2338,20 @@ packages:
os: [linux]
libc: [musl]
'@oxc-parser/binding-linux-ppc64-gnu@0.120.0':
resolution: {integrity: sha512-T/kZuU0ajop0xhzVMwH5r3srC9Nqup5HaIo+3uFjIN5uPxa0LvSxC1ZqP4aQGJVW5G0z8/nCkjIfSMS91P/wzw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ppc64]
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-riscv64-gnu@0.120.0':
resolution: {integrity: sha512-vn21KXLAXzaI3N5CZWlBr1iWeXLl9QFIMor7S1hUjUGTeUuWCoE6JZB040/ZNDwf+JXPX8Ao9KbmJq9FMC2iGw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-riscv64-gnu@0.99.0':
resolution: {integrity: sha512-zxn/xkf519f12FKkpL5XwJipsylfSSnm36h6c1zBDTz4fbIDMGyIhHfWfwM7uUmHo9Aqw1pLxFpY39Etv398+Q==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2289,6 +2359,20 @@ packages:
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-riscv64-musl@0.120.0':
resolution: {integrity: sha512-SUbUxlar007LTGmSLGIC5x/WJvwhdX+PwNzFJ9f/nOzZOrCFbOT4ikt7pJIRg1tXVsEfzk5mWpGO1NFiSs4PIw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [riscv64]
os: [linux]
libc: [musl]
'@oxc-parser/binding-linux-s390x-gnu@0.120.0':
resolution: {integrity: sha512-hYiPJTxyfJY2+lMBFk3p2bo0R9GN+TtpPFlRqVchL1qvLG+pznstramHNvJlw9AjaoRUHwp9IKR7UZQnRPGjgQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [s390x]
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-s390x-gnu@0.99.0':
resolution: {integrity: sha512-Y1eSDKDS5E4IVC7Oxw+NbYAKRmJPMJTIjW+9xOWwteDHkFqpocKe0USxog+Q1uhzalD9M0p9eXWEWdGQCMDBMQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2296,6 +2380,13 @@ packages:
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-x64-gnu@0.120.0':
resolution: {integrity: sha512-q+5jSVZkprJCIy3dzJpApat0InJaoxQLsJuD6DkX8hrUS61z2lHQ1Fe9L2+TYbKHXCLWbL0zXe7ovkIdopBGMQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-x64-gnu@0.99.0':
resolution: {integrity: sha512-YVJMfk5cFWB8i2/nIrbk6n15bFkMHqWnMIWkVx7r2KwpTxHyFMfu2IpeVKo1ITDSmt5nBrGdLHD36QRlu2nDLg==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2303,6 +2394,13 @@ packages:
os: [linux]
libc: [glibc]
'@oxc-parser/binding-linux-x64-musl@0.120.0':
resolution: {integrity: sha512-D9QDDZNnH24e7X4ftSa6ar/2hCavETfW3uk0zgcMIrZNy459O5deTbWrjGzZiVrSWigGtlQwzs2McBP0QsfV1w==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [linux]
libc: [musl]
'@oxc-parser/binding-linux-x64-musl@0.99.0':
resolution: {integrity: sha512-2+SDPrie5f90A1b9EirtVggOgsqtsYU5raZwkDYKyS1uvJzjqHCDhG/f4TwQxHmIc5YkczdQfwvN91lwmjsKYQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -2310,23 +2408,55 @@ packages:
os: [linux]
libc: [musl]
'@oxc-parser/binding-openharmony-arm64@0.120.0':
resolution: {integrity: sha512-TBU8ZwOUWAOUWVfmI16CYWbvh4uQb9zHnGBHsw5Cp2JUVG044OIY1CSHODLifqzQIMTXvDvLzcL89GGdUIqNrA==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [openharmony]
'@oxc-parser/binding-wasm32-wasi@0.120.0':
resolution: {integrity: sha512-WG/FOZgDJCpJnuF3ToG/K28rcOmSY7FmFmfBKYb2fmLyhDzPpUldFGV7/Fz4ru0Iz/v4KPmf8xVgO8N3lO4KHA==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@oxc-parser/binding-wasm32-wasi@0.99.0':
resolution: {integrity: sha512-DKA4j0QerUWSMADziLM5sAyM7V53Fj95CV9SjP77bPfEfT7MnvFKnneaRMqPK1cpzjAGiQF52OBUIKyk0dwOQA==}
engines: {node: '>=14.0.0'}
cpu: [wasm32]
'@oxc-parser/binding-win32-arm64-msvc@0.120.0':
resolution: {integrity: sha512-1T0HKGcsz/BKo77t7+89L8Qvu4f9DoleKWHp3C5sJEcbCjDOLx3m9m722bWZTY+hANlUEs+yjlK+lBFsA+vrVQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@oxc-parser/binding-win32-arm64-msvc@0.99.0':
resolution: {integrity: sha512-EaB3AvsxqdNUhh9FOoAxRZ2L4PCRwDlDb//QXItwyOJrX7XS+uGK9B1KEUV4FZ/7rDhHsWieLt5e07wl2Ti5AQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [arm64]
os: [win32]
'@oxc-parser/binding-win32-ia32-msvc@0.120.0':
resolution: {integrity: sha512-L7vfLzbOXsjBXV0rv/6Y3Jd9BRjPeCivINZAqrSyAOZN3moCopDN+Psq9ZrGNZtJzP8946MtlRFZ0Als0wBCOw==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [ia32]
os: [win32]
'@oxc-parser/binding-win32-x64-msvc@0.120.0':
resolution: {integrity: sha512-ys+upfqNtSu58huAhJMBKl3XCkGzyVFBlMlGPzHeFKgpFF/OdgNs1MMf8oaJIbgMH8ZxgGF7qfue39eJohmKIg==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@oxc-parser/binding-win32-x64-msvc@0.99.0':
resolution: {integrity: sha512-sJN1Q8h7ggFOyDn0zsHaXbP/MklAVUvhrbq0LA46Qum686P3SZQHjbATqJn9yaVEvaSKXCshgl0vQ1gWkGgpcQ==}
engines: {node: ^20.19.0 || >=22.12.0}
cpu: [x64]
os: [win32]
'@oxc-project/types@0.120.0':
resolution: {integrity: sha512-k1YNu55DuvAip/MGE1FTsIuU3FUCn6v/ujG9V7Nq5Df/kX2CWb13hhwD0lmJGMGqE+bE1MXvv9SZVnMzEXlWcg==}
'@oxc-project/types@0.99.0':
resolution: {integrity: sha512-LLDEhXB7g1m5J+woRSgfKsFPS3LhR9xRhTeIoEBm5WrkwMxn6eZ0Ld0c0K5eHB57ChZX6I3uSmmLjZ8pcjlRcw==}
@@ -5538,6 +5668,9 @@ packages:
get-tsconfig@4.10.1:
resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
get-tsconfig@4.13.6:
resolution: {integrity: sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw==}
git-raw-commits@5.0.1:
resolution: {integrity: sha512-Y+csSm2GD/PCSh6Isd/WiMjNAydu0VBiG9J7EdQsNA5P9uXvLayqjmTsNlK5Gs9IhblFZqOU0yid5Il5JPoLiQ==}
engines: {node: '>=18'}
@@ -5664,8 +5797,8 @@ packages:
hyphenate-style-name@1.1.0:
resolution: {integrity: sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==}
i18next@25.8.20:
resolution: {integrity: sha512-xjo9+lbX/P1tQt3xpO2rfJiBppNfUnNIPKgCvNsTKsvTOCro1Qr/geXVg1N47j5ScOSaXAPq8ET93raK3Rr06A==}
i18next@25.9.0:
resolution: {integrity: sha512-mJ4rVRNWOTkqh5xnaGR6iMFT5vEw3Y2MTJhcjinR/7u8cRv6dAfC0ofuePh5fVPxoh395p6JdrJTStCcNW66gg==}
peerDependencies:
typescript: ^5
peerDependenciesMeta:
@@ -5946,13 +6079,10 @@ packages:
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
engines: {node: '>=0.10.0'}
knip@5.88.1:
resolution: {integrity: sha512-tpy5o7zu1MjawVkLPuahymVJekYY3kYjvzcoInhIchgePxTlo+api90tBv2KfhAIe5uXh+mez1tAfmbv8/TiZg==}
engines: {node: '>=18.18.0'}
knip@6.0.1:
resolution: {integrity: sha512-qk5m+w6IYEqfRG5546DXZJYl5AXsgFfDD6ULaDvkubqNtLye79sokBg3usURrWFjASMeQtvX19TfldU3jHkMNA==}
engines: {node: ^20.19.0 || >=22.12.0}
hasBin: true
peerDependencies:
'@types/node': '>=18'
typescript: '>=5.0.4 <7'
known-css-properties@0.37.0:
resolution: {integrity: sha512-JCDrsP4Z1Sb9JwG0aJ8Eo2r7k4Ou5MwmThS/6lcIe1ICyb7UBJKGRIUUdqc2ASdE/42lgz6zFUnzAIhtXnBVrQ==}
@@ -5960,9 +6090,9 @@ packages:
kolorist@1.8.0:
resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
kysely@0.27.6:
resolution: {integrity: sha512-FIyV/64EkKhJmjgC0g2hygpBv5RNWVPyNCqSAD7eTCv6eFWNIi4PN1UvdSJGicN/o35bnevgis4Y0UDC0qi8jQ==}
engines: {node: '>=14.0.0'}
kysely@0.28.14:
resolution: {integrity: sha512-SU3lgh0rPvq7upc6vvdVrCsSMUG1h3ChvHVOY7wJ2fw4C9QEB7X3d5eyYEyULUX7UQtxZJtZXGuT6U2US72UYA==}
engines: {node: '>=20.0.0'}
less@4.2.0:
resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==}
@@ -6454,6 +6584,10 @@ packages:
resolution: {integrity: sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==}
engines: {node: '>=12'}
oxc-parser@0.120.0:
resolution: {integrity: sha512-WyPWZlcIm+Fkte63FGfgFB8mAAk33aH9h5N9lphXVOHSXEBFFsmYdOBedVKly363aWABjZdaj/m9lBfEY4wt+w==}
engines: {node: ^20.19.0 || >=22.12.0}
oxc-parser@0.99.0:
resolution: {integrity: sha512-MpS1lbd2vR0NZn1v0drpgu7RUFu3x9Rd0kxExObZc2+F+DIrV0BOMval/RO3BYGwssIOerII6iS8EbbpCCZQpQ==}
engines: {node: ^20.19.0 || >=22.12.0}
@@ -9356,7 +9490,7 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@iconify/json@2.2.452':
'@iconify/json@2.2.453':
dependencies:
'@iconify/types': 2.0.0
pathe: 2.0.3
@@ -9369,10 +9503,10 @@ snapshots:
'@iconify/types': 2.0.0
mlly: 1.8.0
'@inlang/paraglide-js@2.15.0(babel-plugin-macros@3.1.0)':
'@inlang/paraglide-js@2.15.1(babel-plugin-macros@3.1.0)':
dependencies:
'@inlang/recommend-sherlock': 0.2.1
'@inlang/sdk': 2.8.0(babel-plugin-macros@3.1.0)
'@inlang/sdk': 2.9.1(babel-plugin-macros@3.1.0)
commander: 11.1.0
consola: 3.4.0
json5: 2.2.3
@@ -9385,12 +9519,12 @@ snapshots:
dependencies:
comment-json: 4.5.0
'@inlang/sdk@2.8.0(babel-plugin-macros@3.1.0)':
'@inlang/sdk@2.9.1(babel-plugin-macros@3.1.0)':
dependencies:
'@lix-js/sdk': 0.4.7(babel-plugin-macros@3.1.0)
'@lix-js/sdk': 0.4.9(babel-plugin-macros@3.1.0)
'@sinclair/typebox': 0.31.28
kysely: 0.27.6
sqlite-wasm-kysely: 0.3.0(kysely@0.27.6)
kysely: 0.28.14
sqlite-wasm-kysely: 0.3.0(kysely@0.28.14)
uuid: 13.0.0
transitivePeerDependencies:
- babel-plugin-macros
@@ -9435,14 +9569,14 @@ snapshots:
'@keyv/serialize@1.1.1': {}
'@lix-js/sdk@0.4.7(babel-plugin-macros@3.1.0)':
'@lix-js/sdk@0.4.9(babel-plugin-macros@3.1.0)':
dependencies:
'@lix-js/server-protocol-schema': 0.1.1
dedent: 1.5.1(babel-plugin-macros@3.1.0)
human-id: 4.1.3
js-sha256: 0.11.1
kysely: 0.27.6
sqlite-wasm-kysely: 0.3.0(kysely@0.27.6)
kysely: 0.28.14
sqlite-wasm-kysely: 0.3.0(kysely@0.28.14)
uuid: 10.0.0
transitivePeerDependencies:
- babel-plugin-macros
@@ -9989,53 +10123,117 @@ snapshots:
'@octokit/request-error': 7.0.2
'@octokit/webhooks-methods': 6.0.0
'@oxc-parser/binding-android-arm-eabi@0.120.0':
optional: true
'@oxc-parser/binding-android-arm64@0.120.0':
optional: true
'@oxc-parser/binding-android-arm64@0.99.0':
optional: true
'@oxc-parser/binding-darwin-arm64@0.120.0':
optional: true
'@oxc-parser/binding-darwin-arm64@0.99.0':
optional: true
'@oxc-parser/binding-darwin-x64@0.120.0':
optional: true
'@oxc-parser/binding-darwin-x64@0.99.0':
optional: true
'@oxc-parser/binding-freebsd-x64@0.120.0':
optional: true
'@oxc-parser/binding-freebsd-x64@0.99.0':
optional: true
'@oxc-parser/binding-linux-arm-gnueabihf@0.120.0':
optional: true
'@oxc-parser/binding-linux-arm-gnueabihf@0.99.0':
optional: true
'@oxc-parser/binding-linux-arm-musleabihf@0.120.0':
optional: true
'@oxc-parser/binding-linux-arm-musleabihf@0.99.0':
optional: true
'@oxc-parser/binding-linux-arm64-gnu@0.120.0':
optional: true
'@oxc-parser/binding-linux-arm64-gnu@0.99.0':
optional: true
'@oxc-parser/binding-linux-arm64-musl@0.120.0':
optional: true
'@oxc-parser/binding-linux-arm64-musl@0.99.0':
optional: true
'@oxc-parser/binding-linux-ppc64-gnu@0.120.0':
optional: true
'@oxc-parser/binding-linux-riscv64-gnu@0.120.0':
optional: true
'@oxc-parser/binding-linux-riscv64-gnu@0.99.0':
optional: true
'@oxc-parser/binding-linux-riscv64-musl@0.120.0':
optional: true
'@oxc-parser/binding-linux-s390x-gnu@0.120.0':
optional: true
'@oxc-parser/binding-linux-s390x-gnu@0.99.0':
optional: true
'@oxc-parser/binding-linux-x64-gnu@0.120.0':
optional: true
'@oxc-parser/binding-linux-x64-gnu@0.99.0':
optional: true
'@oxc-parser/binding-linux-x64-musl@0.120.0':
optional: true
'@oxc-parser/binding-linux-x64-musl@0.99.0':
optional: true
'@oxc-parser/binding-openharmony-arm64@0.120.0':
optional: true
'@oxc-parser/binding-wasm32-wasi@0.120.0':
dependencies:
'@napi-rs/wasm-runtime': 1.1.1
optional: true
'@oxc-parser/binding-wasm32-wasi@0.99.0':
dependencies:
'@napi-rs/wasm-runtime': 1.0.7
optional: true
'@oxc-parser/binding-win32-arm64-msvc@0.120.0':
optional: true
'@oxc-parser/binding-win32-arm64-msvc@0.99.0':
optional: true
'@oxc-parser/binding-win32-ia32-msvc@0.120.0':
optional: true
'@oxc-parser/binding-win32-x64-msvc@0.120.0':
optional: true
'@oxc-parser/binding-win32-x64-msvc@0.99.0':
optional: true
'@oxc-project/types@0.120.0': {}
'@oxc-project/types@0.99.0': {}
'@oxc-resolver/binding-android-arm-eabi@11.19.1':
@@ -13155,6 +13353,10 @@ snapshots:
dependencies:
resolve-pkg-maps: 1.0.0
get-tsconfig@4.13.6:
dependencies:
resolve-pkg-maps: 1.0.0
git-raw-commits@5.0.1(conventional-commits-parser@6.3.0):
dependencies:
'@conventional-changelog/git-client': 2.6.0(conventional-commits-parser@6.3.0)
@@ -13315,7 +13517,7 @@ snapshots:
hyphenate-style-name@1.1.0: {}
i18next@25.8.20(typescript@5.9.3):
i18next@25.9.0(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.28.6
optionalDependencies:
@@ -13519,20 +13721,20 @@ snapshots:
kind-of@6.0.3: {}
knip@5.88.1(@types/node@24.11.0)(typescript@5.9.3):
knip@6.0.1:
dependencies:
'@nodelib/fs.walk': 1.2.8
'@types/node': 24.11.0
fast-glob: 3.3.3
formatly: 0.3.0
get-tsconfig: 4.13.6
jiti: 2.6.1
minimist: 1.2.8
oxc-parser: 0.120.0
oxc-resolver: 11.19.1
picocolors: 1.1.1
picomatch: 4.0.3
smol-toml: 1.6.0
strip-json-comments: 5.0.3
typescript: 5.9.3
unbash: 2.2.0
yaml: 2.8.2
zod: 4.3.6
@@ -13541,7 +13743,7 @@ snapshots:
kolorist@1.8.0: {}
kysely@0.27.6: {}
kysely@0.28.14: {}
less@4.2.0:
dependencies:
@@ -14170,6 +14372,31 @@ snapshots:
is-docker: 2.2.1
is-wsl: 2.2.0
oxc-parser@0.120.0:
dependencies:
'@oxc-project/types': 0.120.0
optionalDependencies:
'@oxc-parser/binding-android-arm-eabi': 0.120.0
'@oxc-parser/binding-android-arm64': 0.120.0
'@oxc-parser/binding-darwin-arm64': 0.120.0
'@oxc-parser/binding-darwin-x64': 0.120.0
'@oxc-parser/binding-freebsd-x64': 0.120.0
'@oxc-parser/binding-linux-arm-gnueabihf': 0.120.0
'@oxc-parser/binding-linux-arm-musleabihf': 0.120.0
'@oxc-parser/binding-linux-arm64-gnu': 0.120.0
'@oxc-parser/binding-linux-arm64-musl': 0.120.0
'@oxc-parser/binding-linux-ppc64-gnu': 0.120.0
'@oxc-parser/binding-linux-riscv64-gnu': 0.120.0
'@oxc-parser/binding-linux-riscv64-musl': 0.120.0
'@oxc-parser/binding-linux-s390x-gnu': 0.120.0
'@oxc-parser/binding-linux-x64-gnu': 0.120.0
'@oxc-parser/binding-linux-x64-musl': 0.120.0
'@oxc-parser/binding-openharmony-arm64': 0.120.0
'@oxc-parser/binding-wasm32-wasi': 0.120.0
'@oxc-parser/binding-win32-arm64-msvc': 0.120.0
'@oxc-parser/binding-win32-ia32-msvc': 0.120.0
'@oxc-parser/binding-win32-x64-msvc': 0.120.0
oxc-parser@0.99.0:
dependencies:
'@oxc-project/types': 0.99.0
@@ -14546,11 +14773,11 @@ snapshots:
dependencies:
react: 19.2.4
react-i18next@15.7.4(i18next@25.8.20(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
react-i18next@15.7.4(i18next@25.9.0(typescript@5.9.3))(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3):
dependencies:
'@babel/runtime': 7.28.3
html-parse-stringify: 3.0.1
i18next: 25.8.20(typescript@5.9.3)
i18next: 25.9.0(typescript@5.9.3)
react: 19.2.4
optionalDependencies:
react-dom: 19.2.4(react@19.2.4)
@@ -15031,10 +15258,10 @@ snapshots:
sprintf-js@1.1.3: {}
sqlite-wasm-kysely@0.3.0(kysely@0.27.6):
sqlite-wasm-kysely@0.3.0(kysely@0.28.14):
dependencies:
'@sqlite.org/sqlite-wasm': 3.48.0-build4
kysely: 0.27.6
kysely: 0.28.14
stack-generator@2.0.10:
dependencies:
@@ -3,7 +3,7 @@ import { writeAll } from 'jsr:@std/io@0.225/write-all'
import { CHUNK_MULTIPLIER, performChunkedUpload } from './file-server.ts'
import { consola } from './logger.ts'
const CACHE_BASE_URL = 'https://file-server.elaina.moe/cache'
const CACHE_BASE_URL = 'https://file-server.majokeiko.com/cache'
// --- cache chunked upload types ---
@@ -5,8 +5,8 @@ import { consola } from './logger.ts'
// --- constants ---
export const FILE_SERVER_UPLOAD_URL = 'https://file-server.elaina.moe/upload'
export const FILE_SERVER_BIN_URL = 'https://file-server.elaina.moe/bin'
export const FILE_SERVER_UPLOAD_URL = 'https://file-server.majokeiko.com/upload'
export const FILE_SERVER_BIN_URL = 'https://file-server.majokeiko.com/bin'
export const UPLOAD_CONCURRENCY = 3
export const CHUNK_RETRY_ATTEMPTS = 5
+5 -5
View File
@@ -14,7 +14,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v5
with:
package_json_file: "frontend/package.json"
- uses: actions/setup-node@v6
@@ -59,7 +59,7 @@ jobs:
- uses: actions/setup-go@v6
with:
go-version: '1.26'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v5
with:
package_json_file: "frontend/package.json"
- uses: actions/setup-node@v6
@@ -68,7 +68,7 @@ jobs:
cache: "pnpm"
cache-dependency-path: "frontend/pnpm-lock.yaml"
- name: Install Task
uses: go-task/setup-task@v1
uses: go-task/setup-task@v2
- run: task build
release:
@@ -83,7 +83,7 @@ jobs:
- uses: actions/setup-go@v6
with:
go-version: '1.26'
- uses: pnpm/action-setup@v4
- uses: pnpm/action-setup@v5
with:
package_json_file: "frontend/package.json"
- uses: actions/setup-node@v6
@@ -96,7 +96,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Install Task
uses: go-task/setup-task@v1
uses: go-task/setup-task@v2
- run: task build:frontend
- name: Login to Docker Hub
uses: docker/login-action@v4
+2 -2
View File
@@ -20,7 +20,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Install Task
uses: go-task/setup-task@v1
uses: go-task/setup-task@v2
- name: Build site
run: task docs
@@ -41,7 +41,7 @@ jobs:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v4
- name: Install Task
uses: go-task/setup-task@v1
uses: go-task/setup-task@v2
- name: Build site
run: task docs
- name: Upload static files as artifact
+7 -7
View File
@@ -155,7 +155,7 @@ jobs:
- { goos: linux, goarch: amd64, goamd64: v3, output: amd64-v3-go120, goversion: '1.20' }
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Set up Go
if: ${{ matrix.jobs.goversion == '' }}
@@ -386,7 +386,7 @@ jobs:
shell: bash
- name: Archive production artifacts
uses: actions/upload-artifact@v4
uses: actions/upload-artifact@v7
with:
name: "${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}"
path: |
@@ -405,7 +405,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Download all workflow run artifacts
uses: actions/download-artifact@v4
uses: actions/download-artifact@v8
with:
path: bin/
merge-multiple: true
@@ -463,7 +463,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
ref: Meta
fetch-depth: '0'
@@ -498,7 +498,7 @@ jobs:
bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}
rm ./genReleaseNote.sh
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
path: bin/
merge-multiple: true
@@ -522,11 +522,11 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/download-artifact@v4
- uses: actions/download-artifact@v8
with:
path: bin/
merge-multiple: true
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
# Fix mingw trying to be smart and converting paths https://github.com/moby/moby/issues/24029#issuecomment-250412919
MSYS_NO_PATHCONV: true
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup Go
uses: actions/setup-go@v6
+3 -10
View File
@@ -165,11 +165,10 @@ func (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, e
q := m.Question[0]
domain := msgToDomain(m)
cacheM, expireTime, hit := r.cache.GetWithExpire(q.String())
msg, expireTime, hit := getMsgFromCache(r.cache, q)
if hit {
log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(cacheM), expireTime.Format("2006-01-02 15:04:05"))
log.Debugln("[DNS] cache hit %s --> %s, expire at %s", domain, msgToLogString(msg), expireTime.Format("2006-01-02 15:04:05"))
now := time.Now()
msg = cacheM.Copy()
if expireTime.Before(now) {
setMsgTTL(msg, uint32(1)) // Continue fetch
continueFetch = true
@@ -201,14 +200,8 @@ func (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.M
return
}
msg := result
if cache {
// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.
msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {
return rr.Header().Rrtype != D.TypeOPT
})
putMsgToCache(r.cache, q.String(), q, msg)
putMsgToCache(r.cache, q, result)
}
}()
+23 -3
View File
@@ -45,7 +45,19 @@ func updateTTL(records []D.RR, ttl uint32) {
}
}
func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) {
// getMsgFromCache returns a cached dns message if it exists, otherwise returns nil.
// the returned msg is a copy of the original msg, so it can be modified without affecting the original msg.
func getMsgFromCache(c dnsCache, q D.Question) (*D.Msg, time.Time, bool) {
msg, expireTime, hit := c.GetWithExpire(q.String())
if msg != nil {
msg = msg.Copy() // never modify the original msg
}
return msg, expireTime, hit
}
// putMsgToCache puts a dns message into the cache.
// the msg is copied before being stored in the cache, so it can be modified without affecting the original msg.
func putMsgToCache(c dnsCache, q D.Question, msg *D.Msg) {
// skip dns cache for acme challenge
if q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, "_acme-challenge.") {
log.Debugln("[DNS] dns cache ignored because of acme challenge for: %s", q.Name)
@@ -58,12 +70,20 @@ func putMsgToCache(c dnsCache, key string, q D.Question, msg *D.Msg) {
// If it does so it MUST NOT cache it for longer than five (5) minutes [...]
ttl = serverFailureCacheTTL
} else {
ttl = minimalTTL(append(append(msg.Answer, msg.Ns...), msg.Extra...))
ttl = minimalTTL(lo.Concat(msg.Answer, msg.Ns, msg.Extra))
}
if ttl == 0 {
return
}
c.SetWithExpire(key, msg.Copy(), time.Now().Add(time.Duration(ttl)*time.Second))
msg = msg.Copy() // never modify the original msg
// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.
msg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {
return rr.Header().Rrtype != D.TypeOPT
})
c.SetWithExpire(q.String(), msg, time.Now().Add(time.Duration(ttl)*time.Second))
}
func setMsgTTL(msg *D.Msg, ttl uint32) {
+2 -3
View File
@@ -5,12 +5,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=pdnsd
PKG_VERSION:=1.3.1
PKG_VERSION:=1.3.2
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/pdnsd/tar.gz/$(PKG_VERSION)?
PKG_HASH:=28929bfd7874fd86872dc1bf4cd01103d7ebbdad6a457ccd9a8947a2819440be
PKG_HASH:=ce76cecc5238ab48c0a78baa8225ce896f204fe9bda61845eeaf70f551b17478
PKG_BUILD_PARALLEL:=1
PKG_INSTALL:=1
@@ -54,4 +54,3 @@ endef
$(eval $(call BuildPackage,pdnsd-alt))
+2 -2
View File
@@ -1,12 +1,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=shadowsocksr-libev
PKG_VERSION:=2.5.9
PKG_VERSION:=2.5.10
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/shadowsocksr-libev/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=142d62bf4a1937c96c3f0970eef7391ce8df4efb830e295338a594ddf6493630
PKG_HASH:=7699d420bea841f6314a7b9d5c5fa996e02139812c79d39bbc4f786c65c4fffc
PKG_LICENSE:=GPL-3.0
PKG_LICENSE_FILES:=LICENSE
+2 -2
View File
@@ -6,12 +6,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=simple-obfs
PKG_VERSION:=0.0.10
PKG_VERSION:=0.0.11
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/simple-obfs/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=f64eb9f9013fe0798e5159e3cd108909eb4ac949d78d51804d96945af82d363e
PKG_HASH:=b5f1de228bbc3fcb7b2e1160c4736e9eb47978bd92b7bb3f0948f6a06bc979f0
PKG_LICENSE:=GPL-3.0-or-later
PKG_LICENSE_FILES:=LICENSE
+2 -2
View File
@@ -7,12 +7,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=trojan
PKG_VERSION:=1.16.9
PKG_VERSION:=1.17.0
PKG_RELEASE:=1
PKG_SOURCE:=Trojan-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/trojan/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=c8c65f8227dbffc2853fad9da759391ea9c709e12cee71b78392a4cdb8aeb89c
PKG_HASH:=e6772fed325301760e7150ddd483faa77af9046a2aeedbdb51278b4d6d9ae33f
PKG_BUILD_PARALLEL:=1
PKG_BUILD_DEPENDS:=openssl
+2 -2
View File
@@ -7,12 +7,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=v2dat
PKG_VERSION:=0.1.2
PKG_VERSION:=0.1.3
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://codeload.github.com/kenzok8/v2dat/tar.gz/v$(PKG_VERSION)?
PKG_HASH:=a85229fa6b0c9fbb4cb14c3e4734f5c6b743a779e6337bba09e144720451e1ac
PKG_HASH:=301e6c96266a512d5f0caa143c1128ac8b5b9c4bfbf96ec0c7027cc7aa5ce865
PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)
PKG_LICENSE:=GPL-3.0
@@ -13,6 +13,7 @@ public enum EConfigType
WireGuard = 9,
HTTP = 10,
Anytls = 11,
Naive = 12,
PolicyGroup = 101,
ProxyChain = 102,
}
+26 -2
View File
@@ -193,6 +193,10 @@ public class Global
public const string Hysteria2ProtocolShare = "hy2://";
public const string NaiveHttpsProtocolShare = "naive+https://";
public const string NaiveQuicProtocolShare = "naive+quic://";
public static readonly Dictionary<EConfigType, string> ProtocolShares = new()
{
{ EConfigType.VMess, "vmess://" },
@@ -203,7 +207,8 @@ public class Global
{ EConfigType.Hysteria2, "hysteria2://" },
{ EConfigType.TUIC, "tuic://" },
{ EConfigType.WireGuard, "wireguard://" },
{ EConfigType.Anytls, "anytls://" }
{ EConfigType.Anytls, "anytls://" },
{ EConfigType.Naive, "naive://" }
};
public static readonly Dictionary<EConfigType, string> ProtocolTypes = new()
@@ -217,7 +222,8 @@ public class Global
{ EConfigType.Hysteria2, "hysteria2" },
{ EConfigType.TUIC, "tuic" },
{ EConfigType.WireGuard, "wireguard" },
{ EConfigType.Anytls, "anytls" }
{ EConfigType.Anytls, "anytls" },
{ EConfigType.Naive, "naive" }
};
public static readonly List<string> VmessSecurities =
@@ -342,6 +348,7 @@ public class Global
EConfigType.Hysteria2,
EConfigType.TUIC,
EConfigType.Anytls,
EConfigType.Naive,
EConfigType.WireGuard,
EConfigType.SOCKS,
EConfigType.HTTP,
@@ -558,6 +565,14 @@ public class Global
"bbr"
];
public static readonly List<string> NaiveCongestionControls =
[
"bbr",
"bbr2",
"cubic",
"reno"
];
public static readonly List<string> allowSelectType =
[
"selector",
@@ -660,5 +675,14 @@ public class Global
""
];
public static readonly List<string> TunIcmpRoutingPolicies =
[
"rule",
"direct",
"unreachable",
"drop",
"reply",
];
#endregion const
}
@@ -322,6 +322,7 @@ public class CoreConfigContextBuilder
context.ProtectDomainList.Add(address);
}
// ech query server name protect
if (!node.EchConfigList.IsNullOrEmpty())
{
var echQuerySni = node.Sni;
@@ -338,6 +339,20 @@ public class CoreConfigContextBuilder
}
}
// xhttp downloadSettings address protect
if (!string.IsNullOrEmpty(node.Extra)
&& JsonUtils.ParseJson(node.Extra) is JsonObject extra
&& extra.TryGetPropertyValue("downloadSettings", out var dsNode)
&& dsNode is JsonObject downloadSettings
&& downloadSettings.TryGetPropertyValue("address", out var dAddrNode)
&& dAddrNode is JsonValue dAddrValue
&& dAddrValue.TryGetValue(out string? dAddr)
&& !string.IsNullOrEmpty(dAddr)
&& Utils.IsDomain(dAddr))
{
context.ProtectDomainList.Add(dAddr);
}
return nodeValidatorResult;
}
@@ -91,6 +91,7 @@ public static class ConfigHandler
{
EnableTun = false,
Mtu = 9000,
IcmpRouting = Global.TunIcmpRoutingPolicies.First(),
};
config.GuiItem ??= new();
config.MsgUIItem ??= new();
@@ -269,6 +270,7 @@ public static class ConfigHandler
EConfigType.TUIC => await AddTuicServer(config, item),
EConfigType.WireGuard => await AddWireguardServer(config, item),
EConfigType.Anytls => await AddAnytlsServer(config, item),
EConfigType.Naive => await AddNaiveServer(config, item),
_ => -1,
};
return ret;
@@ -804,7 +806,7 @@ public static class ConfigHandler
}
/// <summary>
/// Add or edit a Anytls server
/// Add or edit an Anytls server
/// Validates and processes Anytls-specific settings
/// </summary>
/// <param name="config">Current configuration</param>
@@ -831,6 +833,36 @@ public static class ConfigHandler
return 0;
}
/// <summary>
/// Add or edit a Naive server
/// Validates and processes Naive-specific settings
/// </summary>
/// <param name="config">Current configuration</param>
/// <param name="profileItem">Naive profile to add</param>
/// <param name="toFile">Whether to save to file</param>
/// <returns>0 if successful, -1 if failed</returns>
public static async Task<int> AddNaiveServer(Config config, ProfileItem profileItem, bool toFile = true)
{
profileItem.ConfigType = EConfigType.Naive;
profileItem.CoreType = ECoreType.sing_box;
profileItem.Address = profileItem.Address.TrimEx();
profileItem.Username = profileItem.Username.TrimEx();
profileItem.Password = profileItem.Password.TrimEx();
profileItem.Alpn = string.Empty;
profileItem.Network = string.Empty;
if (profileItem.StreamSecurity.IsNullOrEmpty())
{
profileItem.StreamSecurity = Global.StreamSecurity;
}
if (profileItem.Password.IsNullOrEmpty())
{
return -1;
}
await AddServerCommon(config, profileItem, toFile);
return 0;
}
/// <summary>
/// Sort the server list by the specified column
/// Updates the sort order in the profile extension data
@@ -1077,7 +1109,8 @@ public static class ConfigHandler
if (toFile)
{
profileItem.SetProtocolExtra();
//profileItem.SetProtocolExtra();
profileItem.SetProtocolExtra(profileItem.GetProtocolExtra());
await SQLiteHelper.Instance.ReplaceAsync(profileItem);
}
return 0;
@@ -1105,6 +1138,7 @@ public static class ConfigHandler
&& AreEqual(o.Address, n.Address)
&& o.Port == n.Port
&& AreEqual(o.Password, n.Password)
&& AreEqual(o.Username, n.Username)
&& AreEqual(oProtocolExtra.VlessEncryption, nProtocolExtra.VlessEncryption)
&& AreEqual(oProtocolExtra.SsMethod, nProtocolExtra.SsMethod)
&& AreEqual(oProtocolExtra.VmessSecurity, nProtocolExtra.VmessSecurity)
@@ -1496,6 +1530,7 @@ public static class ConfigHandler
EConfigType.TUIC => await AddTuicServer(config, profileItem, false),
EConfigType.WireGuard => await AddWireguardServer(config, profileItem, false),
EConfigType.Anytls => await AddAnytlsServer(config, profileItem, false),
EConfigType.Naive => await AddNaiveServer(config, profileItem, false),
_ => -1,
};
@@ -19,6 +19,7 @@ public class FmtHandler
EConfigType.TUIC => TuicFmt.ToUri(item),
EConfigType.WireGuard => WireguardFmt.ToUri(item),
EConfigType.Anytls => AnytlsFmt.ToUri(item),
EConfigType.Naive => NaiveFmt.ToUri(item),
_ => null,
};
@@ -80,6 +81,12 @@ public class FmtHandler
{
return AnytlsFmt.Resolve(str, out msg);
}
else if (str.StartsWith(Global.ProtocolShares[EConfigType.Naive])
|| str.StartsWith(Global.NaiveHttpsProtocolShare)
|| str.StartsWith(Global.NaiveQuicProtocolShare))
{
return NaiveFmt.Resolve(str, out msg);
}
else
{
msg = ResUI.NonvmessOrssProtocol;
@@ -0,0 +1,91 @@
namespace ServiceLib.Handler.Fmt;
public class NaiveFmt : BaseFmt
{
public static ProfileItem? Resolve(string str, out string msg)
{
msg = ResUI.ConfigurationFormatIncorrect;
var parsedUrl = Utils.TryUri(str);
if (parsedUrl == null)
{
return null;
}
ProfileItem item = new()
{
ConfigType = EConfigType.Naive,
Remarks = parsedUrl.GetComponents(UriComponents.Fragment, UriFormat.Unescaped),
Address = parsedUrl.IdnHost,
Port = parsedUrl.Port,
};
var protocolExtra = item.GetProtocolExtra();
if (parsedUrl.Scheme.Contains("quic"))
{
protocolExtra = protocolExtra with
{
NaiveQuic = true,
};
}
var rawUserInfo = Utils.UrlDecode(parsedUrl.UserInfo);
if (rawUserInfo.Contains(':'))
{
var split = rawUserInfo.Split(':', 2);
item.Username = split[0];
item.Password = split[1];
}
else
{
item.Password = rawUserInfo;
}
var query = Utils.ParseQueryString(parsedUrl.Query);
ResolveUriQuery(query, ref item);
var insecureConcurrency = int.TryParse(GetQueryValue(query, "insecure-concurrency"), out var ic) ? ic : 0;
if (insecureConcurrency > 0)
{
protocolExtra = protocolExtra with
{
InsecureConcurrency = insecureConcurrency,
};
}
item.SetProtocolExtra(protocolExtra);
return item;
}
public static string? ToUri(ProfileItem? item)
{
if (item == null)
{
return null;
}
var remark = string.Empty;
if (item.Remarks.IsNotEmpty())
{
remark = "#" + Utils.UrlEncode(item.Remarks);
}
var userInfo = item.Username.IsNotEmpty() ? $"{Utils.UrlEncode(item.Username)}:{Utils.UrlEncode(item.Password)}" : Utils.UrlEncode(item.Password);
var dicQuery = new Dictionary<string, string>();
ToUriQuery(item, Global.None, ref dicQuery);
var protocolExtra = item.GetProtocolExtra();
if (protocolExtra.InsecureConcurrency > 0)
{
dicQuery.Add("insecure-concurrency", protocolExtra?.InsecureConcurrency.ToString());
}
var query = dicQuery.Count > 0
? ("?" + string.Join("&", dicQuery.Select(x => x.Key + "=" + x.Value).ToArray()))
: string.Empty;
var url = $"{userInfo}@{GetIpv6(item.Address)}:{item.Port}";
if (protocolExtra.NaiveQuic == true)
{
return $"{Global.NaiveQuicProtocolShare}{url}{query}{remark}";
}
else
{
return $"{Global.NaiveHttpsProtocolShare}{url}{query}{remark}";
}
}
}
@@ -30,7 +30,10 @@ public class TuicFmt : BaseFmt
var query = Utils.ParseQueryString(url.Query);
ResolveUriQuery(query, ref item);
item.HeaderType = GetQueryValue(query, "congestion_control");
item.SetProtocolExtra(item.GetProtocolExtra() with
{
CongestionControl = GetQueryValue(query, "congestion_control")
});
return item;
}
@@ -51,7 +54,10 @@ public class TuicFmt : BaseFmt
var dicQuery = new Dictionary<string, string>();
ToUriQueryLite(item, ref dicQuery);
dicQuery.Add("congestion_control", item.HeaderType);
if (!item.GetProtocolExtra().CongestionControl.IsNullOrEmpty())
{
dicQuery.Add("congestion_control", item.GetProtocolExtra().CongestionControl);
}
return ToUri(EConfigType.TUIC, item.Address, item.Port, $"{item.Username ?? ""}:{item.Password}", dicQuery, remark);
}
@@ -305,12 +305,11 @@ public sealed class AppManager
return await SQLiteHelper.Instance.TableAsync<FullConfigTemplateItem>().FirstOrDefaultAsync(it => it.CoreType == eCoreType);
}
#pragma warning disable CS0618
public async Task MigrateProfileExtra()
{
await MigrateProfileExtraGroup();
#pragma warning disable CS0618
const int pageSize = 100;
var offset = 0;
@@ -334,7 +333,6 @@ public sealed class AppManager
}
//await ProfileGroupItemManager.Instance.ClearAll();
#pragma warning restore CS0618
}
private async Task<int> MigrateProfileExtraSub(List<ProfileItem> batch)
@@ -380,6 +378,7 @@ public sealed class AppManager
break;
case EConfigType.TUIC:
extra = extra with { CongestionControl = item.HeaderType.NullIfEmpty(), };
item.Username = item.Id;
item.Id = item.Security;
item.Password = item.Security;
@@ -436,7 +435,6 @@ public sealed class AppManager
private async Task<bool> MigrateProfileExtraGroup()
{
#pragma warning disable CS0618
var list = await SQLiteHelper.Instance.TableAsync<ProfileGroupItem>().ToListAsync();
var groupItems = new ConcurrentDictionary<string, ProfileGroupItem>(list.Where(t => !string.IsNullOrEmpty(t.IndexId)).ToDictionary(t => t.IndexId!));
@@ -501,8 +499,8 @@ public sealed class AppManager
return true;
//await ProfileGroupItemManager.Instance.ClearAll();
#pragma warning restore CS0618
}
#pragma warning restore CS0618
#endregion SqliteHelper
@@ -144,6 +144,7 @@ public class TunModeItem
public string Stack { get; set; }
public int Mtu { get; set; }
public bool EnableIPv6Address { get; set; }
public string IcmpRouting { get; set; }
}
[Serializable]
@@ -2,6 +2,9 @@ namespace ServiceLib.Models;
public record ProtocolExtraItem
{
public bool? Uot { get; init; }
public string? CongestionControl { get; init; }
// vmess
public string? AlterId { get; init; }
public string? VmessSecurity { get; init; }
@@ -29,6 +32,10 @@ public record ProtocolExtraItem
public string? Ports { get; init; }
public string? HopInterval { get; init; }
// naiveproxy
public int? InsecureConcurrency { get; init; }
public bool? NaiveQuic { get; init; }
// group profile
public string? GroupType { get; init; }
public string? ChildItems { get; init; }
@@ -134,10 +134,14 @@ public class Outbound4Sbox : BaseServer4Sbox
public int? recv_window_conn { get; set; }
public int? recv_window { get; set; }
public bool? disable_mtu_discovery { get; set; }
public int? insecure_concurrency { get; set; }
public bool? udp_over_tcp { get; set; }
public string? method { get; set; }
public string? username { get; set; }
public string? password { get; set; }
public string? congestion_control { get; set; }
public bool? quic { get; set; }
public string? quic_congestion_control { get; set; }
public string? version { get; set; }
public string? network { get; set; }
public string? packet_encoding { get; set; }
@@ -179,6 +179,8 @@ public class ServersItem4Ray
public string flow { get; set; }
public bool? uot { get; set; }
public List<SocksUsersItem4Ray> users { get; set; }
}
+55 -10
View File
@@ -691,7 +691,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add Child 的本地化字符串。
/// 查找类似 Add Child 的本地化字符串。
/// </summary>
public static string menuAddChildServer {
get {
@@ -718,7 +718,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [Hysteria2] 的本地化字符串。
/// 查找类似 Add [Hysteria2] 的本地化字符串。
/// </summary>
public static string menuAddHysteria2Server {
get {
@@ -727,7 +727,16 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add Policy Group 的本地化字符串。
/// 查找类似 Add [NaïveProxy] 的本地化字符串。
/// </summary>
public static string menuAddNaiveServer {
get {
return ResourceManager.GetString("menuAddNaiveServer", resourceCulture);
}
}
/// <summary>
/// 查找类似 Add Policy Group 的本地化字符串。
/// </summary>
public static string menuAddPolicyGroupServer {
get {
@@ -772,7 +781,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [Shadowsocks] 的本地化字符串。
/// 查找类似 Add [Shadowsocks] 的本地化字符串。
/// </summary>
public static string menuAddShadowsocksServer {
get {
@@ -781,7 +790,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [SOCKS] 的本地化字符串。
/// 查找类似 Add [SOCKS] 的本地化字符串。
/// </summary>
public static string menuAddSocksServer {
get {
@@ -790,7 +799,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [Trojan] 的本地化字符串。
/// 查找类似 Add [Trojan] 的本地化字符串。
/// </summary>
public static string menuAddTrojanServer {
get {
@@ -799,7 +808,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [TUIC] 的本地化字符串。
/// 查找类似 Add [TUIC] 的本地化字符串。
/// </summary>
public static string menuAddTuicServer {
get {
@@ -808,7 +817,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [VLESS] 的本地化字符串。
/// 查找类似 Add [VLESS] 的本地化字符串。
/// </summary>
public static string menuAddVlessServer {
get {
@@ -817,7 +826,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [VMess] 的本地化字符串。
/// 查找类似 Add [VMess] 的本地化字符串。
/// </summary>
public static string menuAddVmessServer {
get {
@@ -826,7 +835,7 @@ namespace ServiceLib.Resx {
}
/// <summary>
/// 查找类似 Add [WireGuard] 的本地化字符串。
/// 查找类似 Add [WireGuard] 的本地化字符串。
/// </summary>
public static string menuAddWireguardServer {
get {
@@ -3069,6 +3078,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 ICMP routing policy 的本地化字符串。
/// </summary>
public static string TbIcmpRoutingPolicy {
get {
return ResourceManager.GetString("TbIcmpRoutingPolicy", resourceCulture);
}
}
/// <summary>
/// 查找类似 UUID(id) 的本地化字符串。
/// </summary>
@@ -3105,6 +3123,15 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 Insecure Concurrency 的本地化字符串。
/// </summary>
public static string TbInsecureConcurrency {
get {
return ResourceManager.GetString("TbInsecureConcurrency", resourceCulture);
}
}
/// <summary>
/// 查找类似 Most Stable 的本地化字符串。
/// </summary>
@@ -4491,6 +4518,24 @@ namespace ServiceLib.Resx {
}
}
/// <summary>
/// 查找类似 UDP over TCP 的本地化字符串。
/// </summary>
public static string TbUot {
get {
return ResourceManager.GetString("TbUot", resourceCulture);
}
}
/// <summary>
/// 查找类似 Username 的本地化字符串。
/// </summary>
public static string TbUsername {
get {
return ResourceManager.GetString("TbUsername", resourceCulture);
}
}
/// <summary>
/// 查找类似 Validate Regional Domain IPs 的本地化字符串。
/// </summary>
@@ -1680,4 +1680,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Add NaïveProxy</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
</root>
@@ -1677,4 +1677,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Ajouter [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
</root>
@@ -1680,4 +1680,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>[NaïveProxy] konfiguráció hozzáadása</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
</root>
+25 -10
View File
@@ -514,19 +514,19 @@
<value>Add a custom configuration</value>
</data>
<data name="menuAddShadowsocksServer" xml:space="preserve">
<value>Add [Shadowsocks] </value>
<value>Add [Shadowsocks]</value>
</data>
<data name="menuAddSocksServer" xml:space="preserve">
<value>Add [SOCKS] </value>
<value>Add [SOCKS]</value>
</data>
<data name="menuAddTrojanServer" xml:space="preserve">
<value>Add [Trojan] </value>
<value>Add [Trojan]</value>
</data>
<data name="menuAddVlessServer" xml:space="preserve">
<value>Add [VLESS] </value>
<value>Add [VLESS]</value>
</data>
<data name="menuAddVmessServer" xml:space="preserve">
<value>Add [VMess] </value>
<value>Add [VMess]</value>
</data>
<data name="menuSelectAll" xml:space="preserve">
<value>Select all</value>
@@ -1036,7 +1036,7 @@
<value>Domain</value>
</data>
<data name="menuAddHysteria2Server" xml:space="preserve">
<value>Add [Hysteria2] </value>
<value>Add [Hysteria2]</value>
</data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria Max bandwidth (Up/Down)</value>
@@ -1045,7 +1045,7 @@
<value>Use System Hosts</value>
</data>
<data name="menuAddTuicServer" xml:space="preserve">
<value>Add [TUIC] </value>
<value>Add [TUIC]</value>
</data>
<data name="TbHeaderType8" xml:space="preserve">
<value>Congestion control</value>
@@ -1075,7 +1075,7 @@
<value>Enable IPv6 Address</value>
</data>
<data name="menuAddWireguardServer" xml:space="preserve">
<value>Add [WireGuard] </value>
<value>Add [WireGuard]</value>
</data>
<data name="TbPrivateKey" xml:space="preserve">
<value>Private Key</value>
@@ -1495,13 +1495,13 @@
<value>Policy Group Type</value>
</data>
<data name="menuAddPolicyGroupServer" xml:space="preserve">
<value>Add Policy Group </value>
<value>Add Policy Group</value>
</data>
<data name="menuAddProxyChainServer" xml:space="preserve">
<value>Add Proxy Chain</value>
</data>
<data name="menuAddChildServer" xml:space="preserve">
<value>Add Child </value>
<value>Add Child</value>
</data>
<data name="menuRemoveChildServer" xml:space="preserve">
<value>Remove Child </value>
@@ -1680,4 +1680,19 @@ The "Get Certificate" action may fail if a self-signed certificate is used or if
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Add [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
</root>
@@ -1680,4 +1680,19 @@
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>Добавить сервер [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
</root>
@@ -517,16 +517,16 @@
<value>添加 [Shadowsocks]</value>
</data>
<data name="menuAddSocksServer" xml:space="preserve">
<value>添加 [SOCKS] </value>
<value>添加 [SOCKS]</value>
</data>
<data name="menuAddTrojanServer" xml:space="preserve">
<value>添加 [Trojan] </value>
<value>添加 [Trojan]</value>
</data>
<data name="menuAddVlessServer" xml:space="preserve">
<value>添加 [VLESS] </value>
<value>添加 [VLESS]</value>
</data>
<data name="menuAddVmessServer" xml:space="preserve">
<value>添加 [VMess] </value>
<value>添加 [VMess]</value>
</data>
<data name="menuSelectAll" xml:space="preserve">
<value>全选</value>
@@ -1033,7 +1033,7 @@
<value>Domain</value>
</data>
<data name="menuAddHysteria2Server" xml:space="preserve">
<value>添加 [Hysteria2] </value>
<value>添加 [Hysteria2]</value>
</data>
<data name="TbSettingsHysteriaBandwidth" xml:space="preserve">
<value>Hysteria 最大带宽 (Up/Dw)</value>
@@ -1042,7 +1042,7 @@
<value>使用系统 hosts</value>
</data>
<data name="menuAddTuicServer" xml:space="preserve">
<value>添加 [TUIC] </value>
<value>添加 [TUIC]</value>
</data>
<data name="TbHeaderType8" xml:space="preserve">
<value>拥塞控制算法</value>
@@ -1072,7 +1072,7 @@
<value>启用 IPv6</value>
</data>
<data name="menuAddWireguardServer" xml:space="preserve">
<value>添加 [WireGuard] </value>
<value>添加 [WireGuard]</value>
</data>
<data name="TbPrivateKey" xml:space="preserve">
<value>PrivateKey</value>
@@ -1102,7 +1102,7 @@
<value>*grpc Authority</value>
</data>
<data name="menuAddHttpServer" xml:space="preserve">
<value>添加 [HTTP] </value>
<value>添加 [HTTP]</value>
</data>
<data name="TbSettingsEnableFragment" xml:space="preserve">
<value>启用分片 (Fragment)</value>
@@ -1381,7 +1381,7 @@
<value>Mldsa65Verify</value>
</data>
<data name="menuAddAnytlsServer" xml:space="preserve">
<value>添加 [Anytls] </value>
<value>添加 [Anytls]</value>
</data>
<data name="TbRemoteDNS" xml:space="preserve">
<value>远程 DNS</value>
@@ -1677,4 +1677,19 @@
<data name="menuEditFormat" xml:space="preserve">
<value>格式化</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>添加 [NaïveProxy]</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>不安全并发</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>用户名</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP 路由策略</value>
</data>
</root>
@@ -1677,4 +1677,19 @@
<data name="menuEditFormat" xml:space="preserve">
<value>Format</value>
</data>
<data name="TbUot" xml:space="preserve">
<value>UDP over TCP</value>
</data>
<data name="menuAddNaiveServer" xml:space="preserve">
<value>新增 [NaïveProxy] 節點</value>
</data>
<data name="TbInsecureConcurrency" xml:space="preserve">
<value>Insecure Concurrency</value>
</data>
<data name="TbUsername" xml:space="preserve">
<value>Username</value>
</data>
<data name="TbIcmpRoutingPolicy" xml:space="preserve">
<value>ICMP routing policy</value>
</data>
</root>
@@ -112,6 +112,7 @@ public partial class CoreConfigSingboxService
outbound.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod)
? protocolExtra.SsMethod : Global.None;
outbound.password = _node.Password;
outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null;
if (_node.Network == nameof(ETransport.tcp) && _node.HeaderType == Global.TcpHeaderHttp)
{
@@ -269,7 +270,7 @@ public partial class CoreConfigSingboxService
{
outbound.uuid = _node.Username;
outbound.password = _node.Password;
outbound.congestion_control = _node.HeaderType;
outbound.congestion_control = protocolExtra.CongestionControl;
break;
}
case EConfigType.Anytls:
@@ -277,6 +278,22 @@ public partial class CoreConfigSingboxService
outbound.password = _node.Password;
break;
}
case EConfigType.Naive:
{
outbound.username = _node.Username;
outbound.password = _node.Password;
if (protocolExtra.NaiveQuic == true)
{
outbound.quic = true;
outbound.quic_congestion_control = protocolExtra.CongestionControl.NullIfEmpty();
}
if (protocolExtra.InsecureConcurrency > 0)
{
outbound.insecure_concurrency = protocolExtra.InsecureConcurrency;
}
outbound.udp_over_tcp = protocolExtra.Uot == true ? true : null;
break;
}
}
FillOutboundTls(outbound);
@@ -727,13 +744,12 @@ public partial class CoreConfigSingboxService
}, null);
}
var idx = echConfig.IndexOf('+');
// NOTE: query_server_name, since sing-box 1.13.0
//var queryServerName = idx > 0 ? echConfig[..idx] : null;
var queryServerName = idx > 0 ? echConfig[..idx] : null;
var echDnsServer = idx > 0 ? echConfig[(idx + 1)..] : echConfig;
return (new Ech4Sbox()
{
enabled = true,
query_server_name = null,
query_server_name = queryServerName,
}, ParseDnsAddress(echDnsServer));
}
}
@@ -47,6 +47,36 @@ public partial class CoreConfigSingboxService
outbound = Global.DirectTag,
process_name = lstDirectExe
});
// ICMP Routing
var icmpRouting = _config.TunModeItem.IcmpRouting ?? "";
if (!Global.TunIcmpRoutingPolicies.Contains(icmpRouting))
{
icmpRouting = Global.TunIcmpRoutingPolicies.First();
}
if (icmpRouting == "direct")
{
_coreConfig.route.rules.Add(new()
{
network = ["icmp"],
outbound = Global.DirectTag,
});
}
else if (icmpRouting != "rule")
{
var rejectMethod = icmpRouting switch
{
"unreachable" => "default",
"drop" => "drop",
_ => "reply",
};
_coreConfig.route.rules.Add(new()
{
network = ["icmp"],
action = "reject",
method = rejectMethod,
});
}
}
if (_config.Inbound.First().SniffingEnabled)
@@ -153,6 +153,7 @@ public partial class CoreConfigV2rayService
serversItem.password = _node.Password;
serversItem.method = AppManager.Instance.GetShadowsocksSecurities(_node).Contains(protocolExtra.SsMethod)
? protocolExtra.SsMethod : "none";
serversItem.uot = protocolExtra.Uot == true ? true : null;
serversItem.ota = false;
serversItem.level = 1;
@@ -61,6 +61,18 @@ public class AddServerViewModel : MyReactiveObject
[Reactive]
public int WgMtu { get; set; }
[Reactive]
public bool Uot { get; set; }
[Reactive]
public string CongestionControl { get; set; }
[Reactive]
public int? InsecureConcurrency { get; set; }
[Reactive]
public bool NaiveQuic { get; set; }
public ReactiveCommand<Unit, Unit> FetchCertCmd { get; }
public ReactiveCommand<Unit, Unit> FetchCertChainCmd { get; }
public ReactiveCommand<Unit, Unit> SaveCmd { get; }
@@ -123,6 +135,10 @@ public class AddServerViewModel : MyReactiveObject
WgInterfaceAddress = protocolExtra?.WgInterfaceAddress ?? string.Empty;
WgReserved = protocolExtra?.WgReserved ?? string.Empty;
WgMtu = protocolExtra?.WgMtu ?? 1280;
Uot = protocolExtra?.Uot ?? false;
CongestionControl = protocolExtra?.CongestionControl ?? string.Empty;
InsecureConcurrency = protocolExtra?.InsecureConcurrency > 0 ? protocolExtra.InsecureConcurrency : null;
NaiveQuic = protocolExtra?.NaiveQuic ?? false;
}
private async Task SaveServerAsync()
@@ -185,6 +201,10 @@ public class AddServerViewModel : MyReactiveObject
WgInterfaceAddress = WgInterfaceAddress.NullIfEmpty(),
WgReserved = WgReserved.NullIfEmpty(),
WgMtu = WgMtu >= 576 ? WgMtu : null,
Uot = Uot ? true : null,
CongestionControl = CongestionControl.NullIfEmpty(),
InsecureConcurrency = InsecureConcurrency > 0 ? InsecureConcurrency : null,
NaiveQuic = NaiveQuic ? true : null,
});
if (await ConfigHandler.AddServer(_config, SelectedSource) == 0)
@@ -18,6 +18,7 @@ public class MainWindowViewModel : MyReactiveObject
public ReactiveCommand<Unit, Unit> AddTuicServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddWireguardServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddAnytlsServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddNaiveServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddCustomServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddPolicyGroupServerCmd { get; }
public ReactiveCommand<Unit, Unit> AddProxyChainServerCmd { get; }
@@ -117,6 +118,10 @@ public class MainWindowViewModel : MyReactiveObject
{
await AddServerAsync(EConfigType.Anytls);
});
AddNaiveServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerAsync(EConfigType.Naive);
});
AddCustomServerCmd = ReactiveCommand.CreateFromTask(async () =>
{
await AddServerAsync(EConfigType.Custom);
@@ -95,6 +95,7 @@ public class OptionSettingViewModel : MyReactiveObject
[Reactive] public string TunStack { get; set; }
[Reactive] public int TunMtu { get; set; }
[Reactive] public bool TunEnableIPv6Address { get; set; }
[Reactive] public string TunIcmpRouting { get; set; }
#endregion Tun mode
@@ -218,6 +219,7 @@ public class OptionSettingViewModel : MyReactiveObject
TunStack = _config.TunModeItem.Stack;
TunMtu = _config.TunModeItem.Mtu;
TunEnableIPv6Address = _config.TunModeItem.EnableIPv6Address;
TunIcmpRouting = _config.TunModeItem.IcmpRouting;
#endregion Tun mode
@@ -376,6 +378,7 @@ public class OptionSettingViewModel : MyReactiveObject
_config.TunModeItem.Stack = TunStack;
_config.TunModeItem.Mtu = TunMtu;
_config.TunModeItem.EnableIPv6Address = TunEnableIPv6Address;
_config.TunModeItem.IcmpRouting = TunIcmpRouting;
//coreType
await SaveCoreType();
@@ -198,6 +198,19 @@
Width="300"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleSwitch
x:Name="togUotEnabled3"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
@@ -484,7 +497,7 @@
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbHeaderType8}" />
<ComboBox
x:Name="cmbHeaderType8"
x:Name="cmbCongestionControl8"
Grid.Row="3"
Grid.Column="1"
Width="200"
@@ -581,12 +594,93 @@
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtId10"
x:Name="txtId11"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
</Grid>
<Grid
x:Name="gridNaive"
Grid.Row="2"
ColumnDefinitions="300,Auto"
IsVisible="False"
RowDefinitions="Auto,Auto,Auto,Auto,Auto,Auto">
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUsername}" />
<TextBox
x:Name="txtId12"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtSecurity12"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="QUIC" />
<StackPanel
Grid.Row="3"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleSwitch
x:Name="togNaiveQuic12"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<ComboBox
x:Name="cmbCongestionControl12"
Width="200"
Margin="{StaticResource Margin4}"
PlaceholderText="{x:Static resx:ResUI.TbHeaderType8}" />
</StackPanel>
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbInsecureConcurrency}" />
<TextBox
x:Name="txtInsecureConcurrency12"
Grid.Row="4"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleSwitch
x:Name="togUotEnabled12"
Grid.Row="5"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</Grid>
<Separator
x:Name="sepa2"
@@ -80,7 +80,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
cmbFingerprint.SelectedValue = string.Empty;
gridFinalmask.IsVisible = false;
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
cmbCongestionControl8.ItemsSource = Global.TuicCongestionControls;
break;
case EConfigType.WireGuard:
@@ -94,10 +94,28 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.Anytls:
gridAnytls.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
lstStreamSecurity.Add(Global.StreamSecurityReality);
cmbCoreType.IsEnabled = false;
gridFinalmask.IsVisible = false;
break;
case EConfigType.Naive:
gridNaive.IsVisible = true;
sepa2.IsVisible = false;
gridTransport.IsVisible = false;
cmbCoreType.IsEnabled = false;
gridFinalmask.IsVisible = false;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.SelectedValue = string.Empty;
cmbAlpn.IsEnabled = false;
cmbAlpn.SelectedValue = string.Empty;
cmbAllowInsecure.IsEnabled = false;
cmbAllowInsecure.SelectedValue = string.Empty;
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
break;
}
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
@@ -122,6 +140,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.Shadowsocks:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
break;
@@ -156,7 +175,7 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.SelectedValue).DisposeWith(disposables);
break;
case EConfigType.WireGuard:
@@ -168,7 +187,17 @@ public partial class AddServerWindow : WindowBase<AddServerViewModel>
break;
case EConfigType.Anytls:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables);
break;
case EConfigType.Naive:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.cmbCongestionControl12.IsEnabled).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.SelectedValue).DisposeWith(disposables);
@@ -50,6 +50,7 @@
<Separator />
<MenuItem x:Name="menuAddTuicServer" Header="{x:Static resx:ResUI.menuAddTuicServer}" />
<MenuItem x:Name="menuAddAnytlsServer" Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
<MenuItem x:Name="menuAddNaiveServer" Header="{x:Static resx:ResUI.menuAddNaiveServer}" />
</MenuItem>
<MenuItem Header="{x:Static resx:ResUI.menuSubscription}">
@@ -72,6 +72,7 @@ public partial class MainWindow : WindowBase<MainWindowViewModel>
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables);
@@ -824,6 +824,20 @@
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
<ComboBox
x:Name="cmbIcmpRoutingPolicy"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
@@ -34,6 +34,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs;
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbCoreType1.ItemsSource = Global.CoreTypes;
cmbCoreType2.ItemsSource = Global.CoreTypes;
@@ -114,6 +115,7 @@ public partial class OptionSettingWindow : WindowBase<OptionSettingViewModel>
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.SelectedValue).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.SelectedValue).DisposeWith(disposables);
+119 -2
View File
@@ -277,6 +277,20 @@
Margin="{StaticResource Margin4}"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleButton
x:Name="togUotEnabled3"
Grid.Row="3"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<TextBlock
Grid.Row="4"
Grid.Column="0"
@@ -646,7 +660,7 @@
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbHeaderType8}" />
<ComboBox
x:Name="cmbHeaderType8"
x:Name="cmbCongestionControl8"
Grid.Row="3"
Grid.Column="1"
Width="200"
@@ -772,13 +786,116 @@
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtId10"
x:Name="txtId11"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
</Grid>
<Grid
x:Name="gridNaive"
Grid.Row="2"
Visibility="Hidden">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="300" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="1"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbUsername}" />
<TextBox
x:Name="txtId12"
Grid.Row="1"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="2"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbId3}" />
<TextBox
x:Name="txtSecurity12"
Grid.Row="2"
Grid.Column="1"
Width="400"
Margin="{StaticResource Margin4}"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="3"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="QUIC" />
<StackPanel
Grid.Row="3"
Grid.Column="1"
VerticalAlignment="Center"
Orientation="Horizontal">
<ToggleButton
x:Name="togNaiveQuic12"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
<ComboBox
x:Name="cmbCongestionControl12"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Right"
materialDesign:HintAssist.Hint="{x:Static resx:ResUI.TbHeaderType8}"
Style="{StaticResource DefComboBox}" />
</StackPanel>
<TextBlock
Grid.Row="4"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbInsecureConcurrency}" />
<TextBox
x:Name="txtInsecureConcurrency12"
Grid.Row="4"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left"
Style="{StaticResource DefTextBox}" />
<TextBlock
Grid.Row="5"
Grid.Column="0"
Margin="{StaticResource Margin4}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbUot}" />
<ToggleButton
x:Name="togUotEnabled12"
Grid.Row="5"
Grid.Column="1"
Margin="{StaticResource Margin4}"
HorizontalAlignment="Left" />
</Grid>
<Separator
x:Name="sepa2"
@@ -75,7 +75,7 @@ public partial class AddServerWindow
cmbFingerprint.Text = string.Empty;
gridFinalmask.Visibility = Visibility.Collapsed;
cmbHeaderType8.ItemsSource = Global.TuicCongestionControls;
cmbCongestionControl8.ItemsSource = Global.TuicCongestionControls;
break;
case EConfigType.WireGuard:
@@ -89,10 +89,28 @@ public partial class AddServerWindow
case EConfigType.Anytls:
gridAnytls.Visibility = Visibility.Visible;
sepa2.Visibility = Visibility.Collapsed;
gridTransport.Visibility = Visibility.Collapsed;
cmbCoreType.IsEnabled = false;
lstStreamSecurity.Add(Global.StreamSecurityReality);
gridFinalmask.Visibility = Visibility.Collapsed;
break;
case EConfigType.Naive:
gridNaive.Visibility = Visibility.Visible;
sepa2.Visibility = Visibility.Collapsed;
gridTransport.Visibility = Visibility.Collapsed;
cmbCoreType.IsEnabled = false;
gridFinalmask.Visibility = Visibility.Collapsed;
cmbFingerprint.IsEnabled = false;
cmbFingerprint.Text = string.Empty;
cmbAlpn.IsEnabled = false;
cmbAlpn.Text = string.Empty;
cmbAllowInsecure.IsEnabled = false;
cmbAllowInsecure.Text = string.Empty;
cmbCongestionControl12.ItemsSource = Global.NaiveCongestionControls;
break;
}
cmbStreamSecurity.ItemsSource = lstStreamSecurity;
@@ -117,6 +135,7 @@ public partial class AddServerWindow
case EConfigType.Shadowsocks:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SsMethod, v => v.cmbSecurity3.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled3.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.MuxEnabled, v => v.togmuxEnabled3.IsChecked).DisposeWith(disposables);
break;
@@ -151,7 +170,7 @@ public partial class AddServerWindow
case EConfigType.TUIC:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.HeaderType, v => v.cmbHeaderType8.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl8.Text).DisposeWith(disposables);
break;
case EConfigType.WireGuard:
@@ -163,7 +182,16 @@ public partial class AddServerWindow
break;
case EConfigType.Anytls:
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId10.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtId11.Text).DisposeWith(disposables);
break;
case EConfigType.Naive:
this.Bind(ViewModel, vm => vm.SelectedSource.Username, v => v.txtId12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.SelectedSource.Password, v => v.txtSecurity12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.NaiveQuic, v => v.togNaiveQuic12.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CongestionControl, v => v.cmbCongestionControl12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.InsecureConcurrency, v => v.txtInsecureConcurrency12.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.Uot, v => v.togUotEnabled12.IsChecked).DisposeWith(disposables);
break;
}
this.Bind(ViewModel, vm => vm.SelectedSource.Network, v => v.cmbNetwork.Text).DisposeWith(disposables);
@@ -124,6 +124,10 @@
x:Name="menuAddAnytlsServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddAnytlsServer}" />
<MenuItem
x:Name="menuAddNaiveServer"
Height="{StaticResource MenuItemHeight}"
Header="{x:Static resx:ResUI.menuAddNaiveServer}" />
</MenuItem>
</Menu>
<Separator />
@@ -71,6 +71,7 @@ public partial class MainWindow
this.BindCommand(ViewModel, vm => vm.AddTuicServerCmd, v => v.menuAddTuicServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddWireguardServerCmd, v => v.menuAddWireguardServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddAnytlsServerCmd, v => v.menuAddAnytlsServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddNaiveServerCmd, v => v.menuAddNaiveServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddCustomServerCmd, v => v.menuAddCustomServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddPolicyGroupServerCmd, v => v.menuAddPolicyGroupServer).DisposeWith(disposables);
this.BindCommand(ViewModel, vm => vm.AddProxyChainServerCmd, v => v.menuAddProxyChainServer).DisposeWith(disposables);
@@ -1076,6 +1076,22 @@
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="6"
Grid.Column="0"
Margin="{StaticResource Margin8}"
VerticalAlignment="Center"
Style="{StaticResource ToolbarTextBlock}"
Text="{x:Static resx:ResUI.TbIcmpRoutingPolicy}" />
<ComboBox
x:Name="cmbIcmpRoutingPolicy"
Grid.Row="6"
Grid.Column="1"
Width="200"
Margin="{StaticResource Margin8}"
HorizontalAlignment="Left"
Style="{StaticResource DefComboBox}" />
<TextBlock
Grid.Row="7"
Grid.Column="0"
@@ -31,6 +31,7 @@ public partial class OptionSettingWindow
cmbmux4SboxProtocol.ItemsSource = Global.SingboxMuxs;
cmbMtu.ItemsSource = Global.TunMtus;
cmbStack.ItemsSource = Global.TunStacks;
cmbIcmpRoutingPolicy.ItemsSource = Global.TunIcmpRoutingPolicies;
cmbCoreType1.ItemsSource = Global.CoreTypes;
cmbCoreType2.ItemsSource = Global.CoreTypes;
@@ -119,6 +120,7 @@ public partial class OptionSettingWindow
this.Bind(ViewModel, vm => vm.TunStack, v => v.cmbStack.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunMtu, v => v.cmbMtu.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunEnableIPv6Address, v => v.togEnableIPv6Address.IsChecked).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.TunIcmpRouting, v => v.cmbIcmpRoutingPolicy.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType1, v => v.cmbCoreType1.Text).DisposeWith(disposables);
this.Bind(ViewModel, vm => vm.CoreType2, v => v.cmbCoreType2.Text).DisposeWith(disposables);
@@ -189,4 +189,15 @@ fun String.concatUrl(vararg paths: String): String {
}
return builder.toString()
}
/**
* Helper function to match text either by Regex or literal string.
*/
fun String.matchesPattern(regex: Regex?, keyword: String?, ignoreCase: Boolean = true): Boolean {
if (keyword.isNullOrEmpty()) {
return true
}
return regex?.containsMatchIn(this)
?: this.contains(keyword, ignoreCase = ignoreCase)
}
@@ -71,6 +71,8 @@ class GroupServerFragment : BaseFragment<FragmentGroupServerBinding>(),
itemTouchHelper?.attachToRecyclerView(binding.recyclerView)
binding.refreshLayout.setOnRefreshListener(this)
// Set the distance to trigger sync to 160dp
binding.refreshLayout.setDistanceToTriggerSync((160 * resources.displayMetrics.density).toInt())
mainViewModel.updateListAction.observe(viewLifecycleOwner) { index ->
if (mainViewModel.subscriptionId != subId) {
@@ -19,6 +19,7 @@ import com.v2ray.ang.dto.ServersCache
import com.v2ray.ang.dto.SubscriptionCache
import com.v2ray.ang.dto.SubscriptionUpdateResult
import com.v2ray.ang.dto.TestServiceMessage
import com.v2ray.ang.extension.matchesPattern
import com.v2ray.ang.extension.serializable
import com.v2ray.ang.extension.toastError
import com.v2ray.ang.extension.toastSuccess
@@ -35,6 +36,7 @@ import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.util.Collections
import java.util.regex.PatternSyntaxException
class MainViewModel(application: Application) : AndroidViewModel(application) {
private var serverList = mutableListOf<String>() // MmkvManager.decodeServerList()
@@ -117,7 +119,12 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
@Synchronized
fun updateCache() {
serversCache.clear()
val kw = keywordFilter.trim().lowercase()
val kw = keywordFilter.trim()
val searchRegex = try {
if (kw.isNotEmpty()) Regex(kw, setOf(RegexOption.IGNORE_CASE)) else null
} catch (e: PatternSyntaxException) {
null // Fallback to literal search if regex is invalid
}
for (guid in serverList) {
val profile = MmkvManager.decodeServerConfig(guid) ?: continue
if (kw.isEmpty()) {
@@ -125,11 +132,15 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
continue
}
val remarks = profile.remarks.lowercase()
val description = profile.description.orEmpty().lowercase()
val server = profile.server.orEmpty().lowercase()
if (remarks.contains(kw) || description.contains(kw) || server.contains(kw)) {
val remarks = profile.remarks
val description = profile.description.orEmpty()
val server = profile.server.orEmpty()
val protocol = profile.configType.name
if (remarks.matchesPattern(searchRegex, kw)
|| description.matchesPattern(searchRegex, kw)
|| server.matchesPattern(searchRegex, kw)
|| protocol.matchesPattern(searchRegex, kw)
) {
serversCache.add(ServersCache(guid, profile))
}
}
@@ -479,4 +490,4 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
}
}
}
}
}
@@ -297,8 +297,8 @@
<string name="title_update_config_count">Обновлено профилей: %d</string>
<string name="title_update_subscription_result">Обновлено профилей: %1$d (успешно: %2$d, ошибок: %3$d, пропущено: %4$d)</string>
<string name="title_update_subscription_no_subscription">Нет подписок</string>
<string name="toast_server_not_found_in_group">Selected server not found in current group</string>
<string name="toast_fragment_not_available">Unable to locate current view</string>
<string name="toast_server_not_found_in_group">Выбранный профиль не найден в текущей группе</string>
<string name="toast_fragment_not_available">Фрагмент недоступен</string>
<string name="tasker_start_service">Запуск службы</string>
<string name="tasker_setting_confirm">Подтвердить</string>
+2
View File
@@ -10,6 +10,8 @@
[![Happ](https://github.com/user-attachments/assets/14055dab-e8bb-48bd-89e8-962709e4098e)](https://happ.su)
[![BlancVPN](https://github.com/user-attachments/assets/9145ea7d-5da3-446e-8143-710dba4292c3)](https://blanc.link/VMTSDqW)
[**Sponsor Xray-core**](https://github.com/XTLS/Xray-core/issues/3668)
## Donation & NFTs
+1 -1
View File
@@ -214,7 +214,7 @@ func (s *DoHNameServer) dohHTTPSContext(ctx context.Context, b []byte) ([]byte,
req.Header.Add("Accept", "application/dns-message")
req.Header.Add("Content-Type", "application/dns-message")
req.Header.Set("User-Agent", utils.ChromeUA)
utils.TryDefaultHeadersWith(req.Header, "fetch")
req.Header.Set("X-Padding", utils.H2Base62Pad(crypto.RandBetween(100, 1000)))
hc := s.httpClient
@@ -32,6 +32,10 @@ func (o *Observer) GetObservation(ctx context.Context) (proto.Message, error) {
return &observatory.ObservationResult{Status: o.createResult()}, nil
}
func (o *Observer) Check(tag []string) {
o.hp.Check(tag)
}
func (o *Observer) createResult() []*observatory.OutboundStatus {
var result []*observatory.OutboundStatus
o.hp.access.Lock()
+1 -1
View File
@@ -62,7 +62,7 @@ func (s *pingClient) MeasureDelay(httpMethod string) (time.Duration, error) {
if err != nil {
return rttFailed, err
}
req.Header.Set("User-Agent", utils.ChromeUA)
utils.TryDefaultHeadersWith(req.Header, "nav")
start := time.Now()
resp, err := s.httpClient.Do(req)
+1 -1
View File
@@ -164,7 +164,7 @@ func (o *Observer) probe(outbound string) ProbeResult {
probeURL = o.config.ProbeUrl
}
req, _ := http.NewRequest(http.MethodGet, probeURL, nil)
req.Header.Set("User-Agent", utils.ChromeUA)
utils.TryDefaultHeadersWith(req.Header, "nav")
response, err := httpClient.Do(req)
if err != nil {
return errors.New("outbound failed to relay connection").Base(err)
+20
View File
@@ -4,8 +4,11 @@ package crypto // import "github.com/xtls/xray-core/common/crypto"
import (
"crypto/rand"
"math/big"
"github.com/xtls/xray-core/common"
)
// [,)
func RandBetween(from int64, to int64) int64 {
if from == to {
return from
@@ -16,3 +19,20 @@ func RandBetween(from int64, to int64) int64 {
bigInt, _ := rand.Int(rand.Reader, big.NewInt(to-from))
return from + bigInt.Int64()
}
// [,]
func RandBytesBetween(b []byte, from, to byte) {
common.Must2(rand.Read(b))
if from > to {
from, to = to, from
}
if to-from == 255 {
return
}
for i := range b {
b[i] = from + b[i]%(to-from+1)
}
}
+165 -2
View File
@@ -4,6 +4,8 @@ import (
"math/rand"
"strconv"
"time"
"net/http"
"strings"
"github.com/klauspost/cpuid/v2"
)
@@ -24,5 +26,166 @@ func ChromeVersion() int {
return version - 1
}
// ChromeUA provides default browser User-Agent based on CPU-seeded PRNG.
var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(ChromeVersion()) + ".0.0.0 Safari/537.36"
// The full Chromium brand GREASE implementation
var clientHintGreaseNA = []string{" ", "(", ":", "-", ".", "/", ")", ";", "=", "?", "_"}
var clientHintVersionNA = []string{"8", "99", "24"}
var clientHintShuffle3 = [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}
var clientHintShuffle4 = [][4]int{
{0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1},
{1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0},
{2, 0, 1, 3}, {2, 0, 3, 1}, {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0},
{3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}}
func getGreasedChInvalidBrand(seed int) string {
return "\"Not" + clientHintGreaseNA[seed % len(clientHintGreaseNA)] + "A" + clientHintGreaseNA[(seed + 1) % len(clientHintGreaseNA)] + "Brand\";v=\"" + clientHintVersionNA[seed % len(clientHintVersionNA)] + "\"";
}
func getGreasedChOrder(brandLength int, seed int) []int {
switch brandLength {
case 1:
return []int{0}
case 2:
return []int{seed % brandLength, (seed + 1) % brandLength}
case 3:
return clientHintShuffle3[seed % len(clientHintShuffle3)][:]
default:
return clientHintShuffle4[seed % len(clientHintShuffle4)][:]
}
return []int{}
}
func getUngreasedChUa(majorVersion int, forkName string) []string {
// Set the capacity to 4, the maximum allowed brand size, so Go will never allocate memory twice
baseChUa := make([]string, 0, 4)
baseChUa = append(baseChUa, getGreasedChInvalidBrand(majorVersion),
"\"Chromium\";v=\"" + strconv.Itoa(majorVersion) + "\"")
switch forkName {
case "chrome":
baseChUa = append(baseChUa, "\"Google Chrome\";v=\"" + strconv.Itoa(majorVersion) + "\"")
case "edge":
baseChUa = append(baseChUa, "\"Microsoft Edge\";v=\"" + strconv.Itoa(majorVersion) + "\"")
}
return baseChUa
}
func getGreasedChUa(majorVersion int, forkName string) string {
ungreasedCh := getUngreasedChUa(majorVersion, forkName)
shuffleMap := getGreasedChOrder(len(ungreasedCh), majorVersion)
shuffledCh := make([]string, len(ungreasedCh))
for i, e := range shuffleMap {
shuffledCh[e] = ungreasedCh[i]
}
return strings.Join(shuffledCh, ", ")
}
// It's better to pin on Firefox ESR releases, and there could be a Firefox ESR version generator later.
// However, if the Firefox fingerprint in uTLS doesn't have its update cadence match that of Firefox ESR, then it's better to update the Firefox version manually instead every time a new major ESR release is available.
var FirefoxUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:140.0) Gecko/20100101 Firefox/140.0"
// The code below provides a coherent default browser user agent string based on a CPU-seeded PRNG.
var AnchoredChromeVersion = ChromeVersion()
var ChromeUA = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0 Safari/537.36"
var ChromeUACH = getGreasedChUa(AnchoredChromeVersion, "chrome")
var MSEdgeUA = ChromeUA + "Edg/" + strconv.Itoa(AnchoredChromeVersion) + ".0.0.0"
var MSEdgeUACH = getGreasedChUa(AnchoredChromeVersion, "edge")
func applyMasqueradedHeaders(header http.Header, browser string, variant string) {
// Browser-specific.
switch browser {
case "chrome":
header["Sec-CH-UA"] = []string{ChromeUACH}
header["Sec-CH-UA-Mobile"] = []string{"?0"}
header["Sec-CH-UA-Platform"] = []string{"\"Windows\""}
header["DNT"] = []string{"1"}
header.Set("User-Agent", ChromeUA)
header.Set("Accept-Language", "en-US,en;q=0.9")
case "edge":
header["Sec-CH-UA"] = []string{MSEdgeUACH}
header["Sec-CH-UA-Mobile"] = []string{"?0"}
header["Sec-CH-UA-Platform"] = []string{"\"Windows\""}
header["DNT"] = []string{"1"}
header.Set("User-Agent", MSEdgeUA)
header.Set("Accept-Language", "en-US,en;q=0.9")
case "firefox":
header.Set("User-Agent", FirefoxUA)
header["DNT"] = []string{"1"}
header.Set("Accept-Language", "en-US,en;q=0.5")
case "golang":
// Expose the default net/http header.
header.Del("User-Agent")
return
}
// Context-specific.
switch variant {
case "nav":
if header.Get("Cache-Control") == "" {
switch browser {
case "chrome", "edge":
header.Set("Cache-Control", "max-age=0")
}
}
header.Set("Upgrade-Insecure-Requests", "1")
if header.Get("Accept") == "" {
switch browser {
case "chrome", "edge":
header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7")
case "firefox":
header.Set("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8")
}
}
header.Set("Sec-Fetch-Site", "none")
header.Set("Sec-Fetch-Mode", "navigate")
header.Set("Sec-Fetch-User", "?1")
header.Set("Sec-Fetch-Dest", "document")
header.Set("Priority", "u=0, i")
case "ws":
header.Set("Sec-Fetch-Mode", "websocket")
header.Set("Sec-Fetch-Dest", "empty")
header.Set("Sec-Fetch-Site", "same-origin")
if header.Get("Cache-Control") == "" {
header.Set("Cache-Control", "no-cache")
}
if header.Get("Pragma") == "" {
header.Set("Pragma", "no-cache")
}
if header.Get("Accept") == "" {
header.Set("Accept", "*/*")
}
case "fetch":
header.Set("Sec-Fetch-Mode", "cors")
header.Set("Sec-Fetch-Dest", "empty")
header.Set("Sec-Fetch-Site", "same-origin")
if header.Get("Priority") == "" {
switch browser {
case "chrome", "edge":
header.Set("Priority", "u=1, i")
case "firefox":
header.Set("Priority", "u=4")
}
}
if header.Get("Cache-Control") == "" {
header.Set("Cache-Control", "no-cache")
}
if header.Get("Pragma") == "" {
header.Set("Pragma", "no-cache")
}
if header.Get("Accept") == "" {
header.Set("Accept", "*/*")
}
}
}
func TryDefaultHeadersWith(header http.Header, variant string) {
// The global UA special value handler for transports. Used to be called HandleTransportUASettings.
// Just a FYI to whoever needing to fix this piece of code after some spontaneous event, I tried to make the two methods separate to let the code be cleaner and more organized.
if len(header.Values("User-Agent")) < 1 {
applyMasqueradedHeaders(header, "chrome", variant)
} else {
switch header.Get("User-Agent") {
case "chrome":
applyMasqueradedHeaders(header, "chrome", variant)
case "firefox":
applyMasqueradedHeaders(header, "firefox", variant)
case "edge":
applyMasqueradedHeaders(header, "edge", variant)
case "golang":
applyMasqueradedHeaders(header, "golang", variant)
}
}
}
@@ -13,6 +13,11 @@ type Observatory interface {
GetObservation(ctx context.Context) (proto.Message, error)
}
type BurstObservatory interface {
Observatory
Check(tag []string)
}
func ObservatoryType() interface{} {
return (*Observatory)(nil)
}
@@ -44,6 +44,34 @@ func (v *AuthenticatorRequest) Build() (*http.RequestConfig, error) {
Name: "User-Agent",
Value: []string{utils.ChromeUA},
},
{
Name: "Sec-CH-UA",
Value: []string{utils.ChromeUACH},
},
{
Name: "Sec-CH-UA-Mobile",
Value: []string{"?0"},
},
{
Name: "Sec-CH-UA-Platform",
Value: []string{"Windows"},
},
{
Name: "Sec-Fetch-Mode",
Value: []string{"no-cors", "cors", "same-origin"},
},
{
Name: "Sec-Fetch-Dest",
Value: []string{"empty"},
},
{
Name: "Sec-Fetch-Site",
Value: []string{"none"},
},
{
Name: "Sec-Fetch-User",
Value: []string{"?1"},
},
{
Name: "Accept-Encoding",
Value: []string{"gzip, deflate"},
+61 -11
View File
@@ -909,6 +909,12 @@ func (c *REALITYConfig) Build() (proto.Message, error) {
}
}
for _, sn := range config.ServerNames {
if strings.Contains(sn, "apple") || strings.Contains(sn, "icloud") {
errors.LogWarning(context.Background(), `REALITY: Choosing apple, icloud, etc. as the target may get your IP blocked by the GFW`)
}
}
config.LimitFallbackUpload = new(reality.LimitFallback)
config.LimitFallbackUpload.AfterBytes = c.LimitFallbackUpload.AfterBytes
config.LimitFallbackUpload.BytesPerSec = c.LimitFallbackUpload.BytesPerSec
@@ -1254,10 +1260,11 @@ var (
)
type TCPItem struct {
Delay Int32Range `json:"delay"`
Rand int32 `json:"rand"`
Type string `json:"type"`
Packet json.RawMessage `json:"packet"`
Delay Int32Range `json:"delay"`
Rand int32 `json:"rand"`
RandRange *Int32Range `json:"randRange"`
Type string `json:"type"`
Packet json.RawMessage `json:"packet"`
}
type HeaderCustomTCP struct {
@@ -1289,10 +1296,18 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) {
}
}
errInvalidRange := errors.New("invalid randRange")
clients := make([]*custom.TCPSequence, len(c.Clients))
for i, value := range c.Clients {
clients[i] = &custom.TCPSequence{}
for _, item := range value {
if item.RandRange == nil {
item.RandRange = &Int32Range{From: 0, To: 255}
}
if item.RandRange.From < 0 || item.RandRange.To > 255 {
return nil, errInvalidRange
}
var err error
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
return nil, err
@@ -1301,6 +1316,8 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) {
DelayMin: int64(item.Delay.From),
DelayMax: int64(item.Delay.To),
Rand: item.Rand,
RandMin: item.RandRange.From,
RandMax: item.RandRange.To,
Packet: item.Packet,
})
}
@@ -1310,6 +1327,12 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) {
for i, value := range c.Servers {
servers[i] = &custom.TCPSequence{}
for _, item := range value {
if item.RandRange == nil {
item.RandRange = &Int32Range{From: 0, To: 255}
}
if item.RandRange.From < 0 || item.RandRange.To > 255 {
return nil, errInvalidRange
}
var err error
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
return nil, err
@@ -1318,6 +1341,8 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) {
DelayMin: int64(item.Delay.From),
DelayMax: int64(item.Delay.To),
Rand: item.Rand,
RandMin: item.RandRange.From,
RandMax: item.RandRange.To,
Packet: item.Packet,
})
}
@@ -1327,6 +1352,12 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) {
for i, value := range c.Errors {
errors[i] = &custom.TCPSequence{}
for _, item := range value {
if item.RandRange == nil {
item.RandRange = &Int32Range{From: 0, To: 255}
}
if item.RandRange.From < 0 || item.RandRange.To > 255 {
return nil, errInvalidRange
}
var err error
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
return nil, err
@@ -1335,6 +1366,8 @@ func (c *HeaderCustomTCP) Build() (proto.Message, error) {
DelayMin: int64(item.Delay.From),
DelayMax: int64(item.Delay.To),
Rand: item.Rand,
RandMin: item.RandRange.From,
RandMax: item.RandRange.To,
Packet: item.Packet,
})
}
@@ -1433,9 +1466,10 @@ func (c *NoiseMask) Build() (proto.Message, error) {
}
type UDPItem struct {
Rand int32 `json:"rand"`
Type string `json:"type"`
Packet json.RawMessage `json:"packet"`
Rand int32 `json:"rand"`
RandRange *Int32Range `json:"randRange"`
Type string `json:"type"`
Packet json.RawMessage `json:"packet"`
}
type HeaderCustomUDP struct {
@@ -1457,25 +1491,41 @@ func (c *HeaderCustomUDP) Build() (proto.Message, error) {
client := make([]*custom.UDPItem, 0, len(c.Client))
for _, item := range c.Client {
if item.RandRange == nil {
item.RandRange = &Int32Range{From: 0, To: 255}
}
if item.RandRange.From < 0 || item.RandRange.To > 255 {
return nil, errors.New("invalid randRange")
}
var err error
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
return nil, err
}
client = append(client, &custom.UDPItem{
Rand: item.Rand,
Packet: item.Packet,
Rand: item.Rand,
RandMin: item.RandRange.From,
RandMax: item.RandRange.To,
Packet: item.Packet,
})
}
server := make([]*custom.UDPItem, 0, len(c.Server))
for _, item := range c.Server {
if item.RandRange == nil {
item.RandRange = &Int32Range{From: 0, To: 255}
}
if item.RandRange.From < 0 || item.RandRange.To > 255 {
return nil, errors.New("invalid randRange")
}
var err error
if item.Packet, err = PraseByteSlice(item.Packet, item.Type); err != nil {
return nil, err
}
server = append(server, &custom.UDPItem{
Rand: item.Rand,
Packet: item.Packet,
Rand: item.Rand,
RandMin: item.RandRange.From,
RandMax: item.RandRange.To,
Packet: item.Packet,
})
}
+4
View File
@@ -174,6 +174,10 @@ func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
return nil, err
}
receiverSettings.StreamSettings = ss
if strings.Contains(ss.SecurityType, "reality") && (receiverSettings.PortList == nil ||
len(receiverSettings.PortList.Ports()) != 1 || receiverSettings.PortList.Ports()[0] != 443) {
errors.LogWarning(context.Background(), `REALITY: Listening on non-443 ports may get your IP blocked by the GFW`)
}
}
if c.SniffingConfig != nil {
s, err := c.SniffingConfig.Build()
+1 -3
View File
@@ -220,9 +220,7 @@ func setUpHTTPTunnel(ctx context.Context, dest net.Destination, target string, u
for _, h := range header {
req.Header.Set(h.Key, h.Value)
}
if req.Header.Get("User-Agent") == "" {
req.Header.Set("User-Agent", utils.ChromeUA)
}
utils.TryDefaultHeadersWith(req.Header, "nav")
connectHTTP1 := func(rawConn net.Conn) (net.Conn, error) {
req.Header.Set("Proxy-Connection", "Keep-Alive")
+9 -2
View File
@@ -27,7 +27,9 @@ import (
"github.com/xtls/xray-core/common/signal"
"github.com/xtls/xray-core/common/task"
"github.com/xtls/xray-core/core"
"github.com/xtls/xray-core/features"
"github.com/xtls/xray-core/features/dns"
"github.com/xtls/xray-core/features/extension"
feature_inbound "github.com/xtls/xray-core/features/inbound"
"github.com/xtls/xray-core/features/outbound"
"github.com/xtls/xray-core/features/policy"
@@ -78,6 +80,7 @@ type Handler struct {
validator vless.Validator
decryption *encryption.ServerInstance
outboundHandlerManager outbound.Manager
observer features.Feature
defaultDispatcher routing.Dispatcher
ctx context.Context
fallbacks map[string]map[string]map[string]*Fallback // or nil
@@ -93,6 +96,7 @@ func New(ctx context.Context, config *Config, dc dns.Client, validator vless.Val
stats: v.GetFeature(stats.ManagerType()).(stats.Manager),
validator: validator,
outboundHandlerManager: v.GetFeature(outbound.ManagerType()).(outbound.Manager),
observer: v.GetFeature(extension.ObservatoryType()),
defaultDispatcher: v.GetFeature(routing.DispatcherType()).(routing.Dispatcher),
ctx: ctx,
}
@@ -623,7 +627,7 @@ func (h *Handler) Process(ctx context.Context, network net.Network, connection s
if err != nil {
return err
}
return r.NewMux(ctx, dispatcher.WrapLink(ctx, h.policyManager, h.stats, &transport.Link{Reader: clientReader, Writer: clientWriter}))
return r.NewMux(ctx, dispatcher.WrapLink(ctx, h.policyManager, h.stats, &transport.Link{Reader: clientReader, Writer: clientWriter}), h.observer)
}
if err := dispatch.DispatchLink(ctx, request.Destination(), &transport.Link{
@@ -645,7 +649,7 @@ func (r *Reverse) Tag() string {
return r.tag
}
func (r *Reverse) NewMux(ctx context.Context, link *transport.Link) error {
func (r *Reverse) NewMux(ctx context.Context, link *transport.Link, observer features.Feature) error {
muxClient, err := mux.NewClientWorker(*link, mux.ClientStrategy{})
if err != nil {
return errors.New("failed to create mux client worker").Base(err).AtWarning()
@@ -655,6 +659,9 @@ func (r *Reverse) NewMux(ctx context.Context, link *transport.Link) error {
return errors.New("failed to create portal worker").Base(err).AtWarning()
}
r.picker.AddWorker(worker)
if burstObs, ok := observer.(extension.BurstObservatory); ok {
go burstObs.Check([]string{r.Tag()})
}
select {
case <-ctx.Done():
case <-muxClient.WaitClosed():
@@ -3,14 +3,11 @@ package finalmask
import (
"context"
"net"
"sync"
"github.com/xtls/xray-core/common/errors"
)
const (
UDPSize = 4096 + 123
)
type Udpmask interface {
UDP()
@@ -29,27 +26,165 @@ func NewUdpmaskManager(udpmasks []Udpmask) *UdpmaskManager {
}
func (m *UdpmaskManager) WrapPacketConnClient(raw net.PacketConn) (net.PacketConn, error) {
var err error
var sizes []int
var conns []net.PacketConn
for i, mask := range m.udpmasks {
raw, err = mask.WrapPacketConnClient(raw, i, len(m.udpmasks)-1)
if err != nil {
return nil, err
if _, ok := mask.(headerConn); ok {
conn, err := mask.WrapPacketConnClient(nil, i, len(m.udpmasks)-1)
if err != nil {
return nil, err
}
sizes = append(sizes, conn.(headerSize).Size())
conns = append(conns, conn)
} else {
if len(conns) > 0 {
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
sizes = nil
conns = nil
}
var err error
raw, err = mask.WrapPacketConnClient(raw, i, len(m.udpmasks)-1)
if err != nil {
return nil, err
}
}
}
if len(conns) > 0 {
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
sizes = nil
conns = nil
}
return raw, nil
}
func (m *UdpmaskManager) WrapPacketConnServer(raw net.PacketConn) (net.PacketConn, error) {
var err error
var sizes []int
var conns []net.PacketConn
for i, mask := range m.udpmasks {
raw, err = mask.WrapPacketConnServer(raw, i, len(m.udpmasks)-1)
if err != nil {
return nil, err
if _, ok := mask.(headerConn); ok {
conn, err := mask.WrapPacketConnServer(nil, i, len(m.udpmasks)-1)
if err != nil {
return nil, err
}
sizes = append(sizes, conn.(headerSize).Size())
conns = append(conns, conn)
} else {
if len(conns) > 0 {
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
sizes = nil
conns = nil
}
var err error
raw, err = mask.WrapPacketConnServer(raw, i, len(m.udpmasks)-1)
if err != nil {
return nil, err
}
}
}
if len(conns) > 0 {
raw = &headerManagerConn{sizes: sizes, conns: conns, PacketConn: raw}
sizes = nil
conns = nil
}
return raw, nil
}
const (
UDPSize = 4096
)
type headerConn interface {
HeaderConn()
}
type headerSize interface {
Size() int
}
type headerManagerConn struct {
sizes []int
conns []net.PacketConn
net.PacketConn
m sync.Mutex
writeBuf [UDPSize]byte
}
func (c *headerManagerConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
buf := p
if len(buf) < UDPSize {
buf = make([]byte, UDPSize)
}
n, addr, err = c.PacketConn.ReadFrom(buf)
if n == 0 || err != nil {
return 0, addr, err
}
newBuf := buf[:n]
sum := 0
for _, size := range c.sizes {
sum += size
}
if n < sum {
errors.LogDebug(context.Background(), addr, " mask read err short length")
return 0, addr, nil
}
for i := range c.conns {
n, _, err = c.conns[i].ReadFrom(newBuf)
if n == 0 || err != nil {
errors.LogDebug(context.Background(), addr, " mask read err ", err)
return 0, addr, nil
}
newBuf = newBuf[c.sizes[i] : n+c.sizes[i]]
}
if len(p) < n {
errors.LogDebug(context.Background(), addr, " mask read err short buffer")
return 0, addr, nil
}
copy(p, buf[sum:sum+n])
return n, addr, nil
}
func (c *headerManagerConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {
c.m.Lock()
defer c.m.Unlock()
sum := 0
for _, size := range c.sizes {
sum += size
}
if sum+len(p) > UDPSize {
errors.LogDebug(context.Background(), addr, " mask write err short write")
return 0, nil
}
n = copy(c.writeBuf[sum:], p)
for i := len(c.conns) - 1; i >= 0; i-- {
n, err = c.conns[i].WriteTo(c.writeBuf[sum-c.sizes[i]:n+sum], nil)
if n == 0 || err != nil {
errors.LogDebug(context.Background(), addr, " mask write err ", err)
return 0, nil
}
sum -= c.sizes[i]
}
n, err = c.PacketConn.WriteTo(c.writeBuf[:n], addr)
if n == 0 || err != nil {
return n, err
}
return len(p), nil
}
type Tcpmask interface {
TCP()
@@ -33,9 +33,6 @@ func NewConnServer(c *Config, raw net.Conn, server bool) (net.Conn, error) {
func (c *fragmentConn) TcpMaskConn() {}
func (c *fragmentConn) RawConn() net.Conn {
if c.server {
return c
}
return c.Conn
}
@@ -25,3 +25,6 @@ func (c *UDPConfig) WrapPacketConnClient(raw net.PacketConn, level int, levelCou
func (c *UDPConfig) WrapPacketConnServer(raw net.PacketConn, level int, levelCount int) (net.PacketConn, error) {
return NewConnServerUDP(c, raw)
}
func (c *UDPConfig) HeaderConn() {
}
@@ -26,7 +26,9 @@ type TCPItem struct {
DelayMin int64 `protobuf:"varint,1,opt,name=delay_min,json=delayMin,proto3" json:"delay_min,omitempty"`
DelayMax int64 `protobuf:"varint,2,opt,name=delay_max,json=delayMax,proto3" json:"delay_max,omitempty"`
Rand int32 `protobuf:"varint,3,opt,name=rand,proto3" json:"rand,omitempty"`
Packet []byte `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"`
RandMin int32 `protobuf:"varint,4,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
RandMax int32 `protobuf:"varint,5,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
Packet []byte `protobuf:"bytes,6,opt,name=packet,proto3" json:"packet,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -82,6 +84,20 @@ func (x *TCPItem) GetRand() int32 {
return 0
}
func (x *TCPItem) GetRandMin() int32 {
if x != nil {
return x.RandMin
}
return 0
}
func (x *TCPItem) GetRandMax() int32 {
if x != nil {
return x.RandMax
}
return 0
}
func (x *TCPItem) GetPacket() []byte {
if x != nil {
return x.Packet
@@ -196,7 +212,9 @@ func (x *TCPConfig) GetErrors() []*TCPSequence {
type UDPItem struct {
state protoimpl.MessageState `protogen:"open.v1"`
Rand int32 `protobuf:"varint,1,opt,name=rand,proto3" json:"rand,omitempty"`
Packet []byte `protobuf:"bytes,2,opt,name=packet,proto3" json:"packet,omitempty"`
RandMin int32 `protobuf:"varint,2,opt,name=rand_min,json=randMin,proto3" json:"rand_min,omitempty"`
RandMax int32 `protobuf:"varint,3,opt,name=rand_max,json=randMax,proto3" json:"rand_max,omitempty"`
Packet []byte `protobuf:"bytes,4,opt,name=packet,proto3" json:"packet,omitempty"`
unknownFields protoimpl.UnknownFields
sizeCache protoimpl.SizeCache
}
@@ -238,6 +256,20 @@ func (x *UDPItem) GetRand() int32 {
return 0
}
func (x *UDPItem) GetRandMin() int32 {
if x != nil {
return x.RandMin
}
return 0
}
func (x *UDPItem) GetRandMax() int32 {
if x != nil {
return x.RandMax
}
return 0
}
func (x *UDPItem) GetPacket() []byte {
if x != nil {
return x.Packet
@@ -301,21 +333,25 @@ var File_transport_internet_finalmask_header_custom_config_proto protoreflect.Fi
const file_transport_internet_finalmask_header_custom_config_proto_rawDesc = "" +
"\n" +
"7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"o\n" +
"7transport/internet/finalmask/header/custom/config.proto\x12/xray.transport.internet.finalmask.header.custom\"\xa5\x01\n" +
"\aTCPItem\x12\x1b\n" +
"\tdelay_min\x18\x01 \x01(\x03R\bdelayMin\x12\x1b\n" +
"\tdelay_max\x18\x02 \x01(\x03R\bdelayMax\x12\x12\n" +
"\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x16\n" +
"\x06packet\x18\x04 \x01(\fR\x06packet\"c\n" +
"\x04rand\x18\x03 \x01(\x05R\x04rand\x12\x19\n" +
"\brand_min\x18\x04 \x01(\x05R\arandMin\x12\x19\n" +
"\brand_max\x18\x05 \x01(\x05R\arandMax\x12\x16\n" +
"\x06packet\x18\x06 \x01(\fR\x06packet\"c\n" +
"\vTCPSequence\x12T\n" +
"\bsequence\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.TCPItemR\bsequence\"\x91\x02\n" +
"\tTCPConfig\x12V\n" +
"\aclients\x18\x01 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aclients\x12V\n" +
"\aservers\x18\x02 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\aservers\x12T\n" +
"\x06errors\x18\x03 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\x06errors\"5\n" +
"\x06errors\x18\x03 \x03(\v2<.xray.transport.internet.finalmask.header.custom.TCPSequenceR\x06errors\"k\n" +
"\aUDPItem\x12\x12\n" +
"\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x16\n" +
"\x06packet\x18\x02 \x01(\fR\x06packet\"\xaf\x01\n" +
"\x04rand\x18\x01 \x01(\x05R\x04rand\x12\x19\n" +
"\brand_min\x18\x02 \x01(\x05R\arandMin\x12\x19\n" +
"\brand_max\x18\x03 \x01(\x05R\arandMax\x12\x16\n" +
"\x06packet\x18\x04 \x01(\fR\x06packet\"\xaf\x01\n" +
"\tUDPConfig\x12P\n" +
"\x06client\x18\x01 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06client\x12P\n" +
"\x06server\x18\x02 \x03(\v28.xray.transport.internet.finalmask.header.custom.UDPItemR\x06serverB\xaf\x01\n" +

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