mirror of
https://github.com/bestnite/sub2clash.git
synced 2025-07-04 20:02:34 +08:00
Compare commits
17 Commits
Author | SHA1 | Date | |
---|---|---|---|
300bb4931d | |||
8e53183685 | |||
1f38125e85 | |||
4e7d84499f | |||
282649aca0 | |||
fe2f47865e | |||
f6273947e7 | |||
0f2dba21eb | |||
72635ce0fc | |||
fd1ff69711 | |||
52e609298a | |||
d6539a0707 | |||
070d8b5eb5 | |||
5c71c54276 | |||
83ce5349c9 | |||
f1bdeddfba | |||
1866e6bf67 |
63
.github/workflows/docker.yml
vendored
Normal file
63
.github/workflows/docker.yml
vendored
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
name: Build and Push to GHCR
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- dev
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Check out code
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Login to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v1
|
||||||
|
with:
|
||||||
|
registry: ghcr.io
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set tag name
|
||||||
|
id: set_tag
|
||||||
|
run: |
|
||||||
|
if [[ $GITHUB_REF == refs/heads/* ]]; then
|
||||||
|
echo "::set-output name=tag::$(echo $GITHUB_REF | cut -d'/' -f3)"
|
||||||
|
else
|
||||||
|
echo "::set-output name=tag::${{ github.ref_name }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Check if triggered by tag
|
||||||
|
id: check_tag
|
||||||
|
run: |
|
||||||
|
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
|
||||||
|
echo "::set-output name=triggered_by_tag::true"
|
||||||
|
else
|
||||||
|
echo "::set-output name=triggered_by_tag::false"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Build and push Docker image for dev branch
|
||||||
|
if: steps.check_tag.outputs.triggered_by_tag == 'false'
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: ghcr.io/${{ github.repository }}:${{ steps.set_tag.outputs.tag }}
|
||||||
|
|
||||||
|
- name: Build and push Docker image for tags
|
||||||
|
if: steps.check_tag.outputs.triggered_by_tag == 'true'
|
||||||
|
uses: docker/build-push-action@v2
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: |
|
||||||
|
ghcr.io/${{ github.repository }}:${{ steps.set_tag.outputs.tag }}
|
||||||
|
ghcr.io/${{ github.repository }}:latest
|
||||||
|
|
48
.github/workflows/go.yml
vendored
48
.github/workflows/go.yml
vendored
@ -7,6 +7,7 @@ on:
|
|||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- '*'
|
- '*'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|
||||||
@ -25,17 +26,20 @@ jobs:
|
|||||||
LDFLAGS="-s -w"
|
LDFLAGS="-s -w"
|
||||||
|
|
||||||
# Linux
|
# Linux
|
||||||
GOOS=linux GOARCH=amd64 go build -ldflags="$LDFLAGS" -o output/sub2clash-linux-amd64 main.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags="$LDFLAGS" -o output/sub2clash-linux-386 main.go
|
||||||
GOOS=linux GOARCH=arm go build -ldflags="$LDFLAGS" -o output/sub2clash-linux-arm main.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="$LDFLAGS" -o output/sub2clash-linux-amd64 main.go
|
||||||
GOOS=linux GOARCH=arm64 go build -ldflags="$LDFLAGS" -o output/sub2clash-linux-arm64 main.go
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags="$LDFLAGS" -o output/sub2clash-linux-arm main.go
|
||||||
|
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="$LDFLAGS" -o output/sub2clash-linux-arm64 main.go
|
||||||
|
|
||||||
# Darwin
|
# Darwin
|
||||||
GOOS=darwin GOARCH=amd64 go build -ldflags="$LDFLAGS" -o output/sub2clash-darwin-amd64 main.go
|
CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags="$LDFLAGS" -o output/sub2clash-darwin-amd64 main.go
|
||||||
GOOS=darwin GOARCH=arm64 go build -ldflags="$LDFLAGS" -o output/sub2clash-darwin-arm64 main.go # For Apple Silicon
|
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags="$LDFLAGS" -o output/sub2clash-darwin-arm64 main.go
|
||||||
|
|
||||||
# Windows
|
# Windows
|
||||||
GOOS=windows GOARCH=amd64 go build -ldflags="$LDFLAGS" -o output/sub2clash-windows-amd64.exe main.go
|
CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags="$LDFLAGS" -o output/sub2clash-windows-386.exe main.go
|
||||||
GOOS=windows GOARCH=arm go build -ldflags="$LDFLAGS" -o output/sub2clash-windows-arm.exe main.go
|
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags="$LDFLAGS" -o output/sub2clash-windows-amd64.exe main.go
|
||||||
|
CGO_ENABLED=0 GOOS=windows GOARCH=arm go build -ldflags="$LDFLAGS" -o output/sub2clash-windows-arm.exe main.go
|
||||||
|
CGO_ENABLED=0 GOOS=windows GOARCH=arm64 go build -ldflags="$LDFLAGS" -o output/sub2clash-windows-arm64.exe main.go
|
||||||
|
|
||||||
- name: Create Release
|
- name: Create Release
|
||||||
id: create_release
|
id: create_release
|
||||||
@ -48,6 +52,16 @@ jobs:
|
|||||||
draft: false
|
draft: false
|
||||||
prerelease: false
|
prerelease: false
|
||||||
|
|
||||||
|
- name: Upload Release Asset (Linux 386)
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.upload_url }}
|
||||||
|
asset_path: ./output/sub2clash-linux-386
|
||||||
|
asset_name: sub2clash-linux-386
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
- name: Upload Release Asset (Linux amd64)
|
- name: Upload Release Asset (Linux amd64)
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@ -98,6 +112,16 @@ jobs:
|
|||||||
asset_name: sub2clash-darwin-arm64
|
asset_name: sub2clash-darwin-arm64
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Release Asset (Windows 386)
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.up
|
||||||
|
asset_path: ./output/sub2clash-windows-386.exe
|
||||||
|
asset_name: sub2clash-windows-386.exe
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
- name: Upload Release Asset (Windows amd64)
|
- name: Upload Release Asset (Windows amd64)
|
||||||
uses: actions/upload-release-asset@v1
|
uses: actions/upload-release-asset@v1
|
||||||
env:
|
env:
|
||||||
@ -118,3 +142,13 @@ jobs:
|
|||||||
asset_name: sub2clash-windows-arm.exe
|
asset_name: sub2clash-windows-arm.exe
|
||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
- name: Upload Release Asset (Windows arm64)
|
||||||
|
uses: actions/upload-release-asset@v1
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
with:
|
||||||
|
upload_url: ${{ steps.create_release.outputs.up
|
||||||
|
asset_path: ./output/sub2clash-windows-arm64.exe
|
||||||
|
asset_name: sub2clash-windows-arm64.exe
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
|
||||||
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ dist
|
|||||||
subs
|
subs
|
||||||
test
|
test
|
||||||
logs
|
logs
|
||||||
|
dist/
|
||||||
|
19
.goreleaser.yaml
Normal file
19
.goreleaser.yaml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
before:
|
||||||
|
hooks:
|
||||||
|
- go mod tidy
|
||||||
|
builds:
|
||||||
|
- env:
|
||||||
|
- CGO_ENABLED=0
|
||||||
|
goos:
|
||||||
|
- linux
|
||||||
|
- windows
|
||||||
|
- darwin
|
||||||
|
goarch:
|
||||||
|
- amd64
|
||||||
|
- arm
|
||||||
|
- arm64
|
||||||
|
- 386
|
||||||
|
ldflags:
|
||||||
|
- -s -w
|
||||||
|
no_unique_dist_dir: true
|
||||||
|
binary: "{{ .ProjectName }}-{{ .Os }}-{{ .Arch }}"
|
24
Dockerfile
Normal file
24
Dockerfile
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# 使用官方 Golang 镜像作为构建环境
|
||||||
|
FROM golang:1.21-alpine as builder
|
||||||
|
LABEL authors="nite07"
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 复制源代码到工作目录
|
||||||
|
COPY . .
|
||||||
|
RUN go mod download
|
||||||
|
|
||||||
|
# 使用 -ldflags 参数进行编译
|
||||||
|
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags="-s -w" -o sub2clash main.go
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 从 builder 镜像中复制出编译好的二进制文件
|
||||||
|
COPY --from=builder /app/sub2clash /app/sub2clash
|
||||||
|
|
||||||
|
# 设置容器的默认启动命令
|
||||||
|
ENTRYPOINT ["/app/sub2clash"]
|
60
README.md
60
README.md
@ -8,31 +8,50 @@
|
|||||||
- 自动根据节点名称按国家划分策略组
|
- 自动根据节点名称按国家划分策略组
|
||||||
- 支持多订阅合并
|
- 支持多订阅合并
|
||||||
- 支持多种协议
|
- 支持多种协议
|
||||||
- [x] Shadowsocks
|
- Shadowsocks
|
||||||
- [x] ShadowsocksR
|
- ShadowsocksR
|
||||||
- [x] Vmess
|
- Vmess
|
||||||
- [x] Vless
|
- Vless
|
||||||
- [x] Trojan
|
- Trojan
|
||||||
- [ ] Hysteria
|
|
||||||
- [ ] TUIC
|
|
||||||
- [ ] WireGuard
|
|
||||||
|
|
||||||
## API
|
## 使用
|
||||||
|
|
||||||
### `/clash`,`/meta`
|
### 运行
|
||||||
|
|
||||||
|
- [docker compose](./docker-compose.yml)
|
||||||
|
- 运行[二进制文件](https://github.com/nitezs/sub2clash/releases/latest)
|
||||||
|
|
||||||
|
### 配置
|
||||||
|
|
||||||
|
可以通过编辑 .env 文件来修改默认配置,docker 直接添加环境变量
|
||||||
|
|
||||||
|
| 变量名 | 说明 | 默认值 |
|
||||||
|
|-----------------------|----------------------------------------|-----------------------|
|
||||||
|
| PORT | 端口 | `8011` |
|
||||||
|
| META_TEMPLATE | meta 模板文件名 | `template_meta.yaml` |
|
||||||
|
| CLASH_TEMPLATE | clash 模板文件名 | `template_clash.yaml` |
|
||||||
|
| REQUEST_RETRY_TIMES | Get 请求重试次数 | `3` |
|
||||||
|
| REQUEST_MAX_FILE_SIZE | Get 请求订阅文件最大大小(byte) | `1048576` |
|
||||||
|
| CACHE_EXPIRE | 订阅缓存时间(秒) | `300` |
|
||||||
|
| LOG_LEVEL | 日志等级,可选值 `debug`,`info`,`warn`,`error` | `info` |
|
||||||
|
|
||||||
|
### API
|
||||||
|
|
||||||
|
#### `/clash`, `/meta`
|
||||||
|
|
||||||
获取 Clash/Clash.Meta 配置链接
|
获取 Clash/Clash.Meta 配置链接
|
||||||
|
|
||||||
| Query 参数 | 类型 | 是否必须 | 说明 |
|
| Query 参数 | 类型 | 是否必须 | 默认值 | 说明 |
|
||||||
|--------------|--------|-------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
|--------------|--------|-------------------|-----------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
||||||
| sub | string | sub/proxy 至少有一项存在 | 订阅链接(可以输入多个,用 `,` 分隔) |
|
| sub | string | sub/proxy 至少有一项存在 | - | 订阅链接(可以输入多个,用 `,` 分隔) |
|
||||||
| proxy | string | sub/proxy 至少有一项存在 | 节点分享链接(可以输入多个,用 `,` 分隔) |
|
| proxy | string | sub/proxy 至少有一项存在 | - | 节点分享链接(可以输入多个,用 `,` 分隔) |
|
||||||
| refresh | bool | 否(默认 `false`) | 强制刷新配置(默认缓存 5 分钟) |
|
| refresh | bool | 否 | `false` | 强制刷新配置(默认缓存 5 分钟) |
|
||||||
| template | string | 否 | 外部模板 |
|
| template | string | 否 | - | 外部模板链接或内部模板名称 |
|
||||||
| ruleProvider | string | 否 | 格式 `[Behavior,Url,Group,Prepend],[Behavior,Url,Group,Prepend],...`,其中 `Group` 是该规则集所走的策略组名,`Prepend` 为 bool 类型,如果为 `true` 规则将被添加到规则列表顶部,否则添加到规则列表底部(会调整到MATCH规则之前) |
|
| ruleProvider | string | 否 | - | 格式 `[Behavior,Url,Group,Prepend,Name],[Behavior,Url,Group,Prepend,Name]...`,其中 `Group` 是该规则集所走的策略组名,`Prepend` 为 bool 类型,如果为 `true` 规则将被添加到规则列表顶部,否则添加到规则列表底部(会调整到MATCH规则之前) |
|
||||||
| rule | string | 否 | 格式 `[Rule,Prepend],[Rule,Prepend]...`,其中 `Prepend` 为 bool 类型,如果为 `true` 规则将被添加到规则列表顶部,否则添加到规则列表底部(会调整到MATCH规则之前) |
|
| rule | string | 否 | - | 格式 `[Rule,Prepend],[Rule,Prepend]...`,其中 `Prepend` 为 bool 类型,如果为 `true` 规则将被添加到规则列表顶部,否则添加到规则列表底部(会调整到MATCH规则之前) |
|
||||||
| autoTest | bool | 否(默认 `false`) | 指定国家策略组是否自动测速 |
|
| autoTest | bool | 否 | `false` | 指定国家策略组是否自动测速 |
|
||||||
| lazy | bool | 否(默认 `false`) | 自动测速是否启用 lazy |
|
| lazy | bool | 否 | `false` | 自动测速是否启用 lazy |
|
||||||
|
| sort | string | 否 | `nameasc` | 国家策略组排序策略,可选值 `nameasc`、`namedesc`、`sizeasc`、`sizedesc` |
|
||||||
|
|
||||||
## 默认模板
|
## 默认模板
|
||||||
|
|
||||||
@ -45,3 +64,4 @@
|
|||||||
|
|
||||||
## TODO
|
## TODO
|
||||||
|
|
||||||
|
- [ ] 可视化面板
|
@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"gopkg.in/yaml.v3"
|
"gopkg.in/yaml.v3"
|
||||||
|
"net/url"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
"sub2clash/model"
|
"sub2clash/model"
|
||||||
@ -17,13 +18,16 @@ func BuildSub(query validator.SubQuery, template string) (
|
|||||||
*model.Subscription, error,
|
*model.Subscription, error,
|
||||||
) {
|
) {
|
||||||
// 定义变量
|
// 定义变量
|
||||||
var externalTemplate = query.Template != ""
|
|
||||||
var temp *model.Subscription
|
var temp *model.Subscription
|
||||||
var sub *model.Subscription
|
var sub *model.Subscription
|
||||||
var err error
|
var err error
|
||||||
var templateBytes []byte
|
var templateBytes []byte
|
||||||
// 加载模板
|
// 加载模板
|
||||||
if !externalTemplate {
|
if query.Template != "" {
|
||||||
|
template = query.Template
|
||||||
|
}
|
||||||
|
_, err = url.ParseRequestURI(template)
|
||||||
|
if err != nil {
|
||||||
templateBytes, err = utils.LoadTemplate(template)
|
templateBytes, err = utils.LoadTemplate(template)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.New("加载模板失败: " + err.Error())
|
return nil, errors.New("加载模板失败: " + err.Error())
|
||||||
@ -63,10 +67,14 @@ func BuildSub(query validator.SubQuery, template string) (
|
|||||||
} else {
|
} else {
|
||||||
proxyList = sub.Proxies
|
proxyList = sub.Proxies
|
||||||
}
|
}
|
||||||
utils.AddProxy(temp, query.AutoTest, query.Lazy, proxyList...)
|
utils.AddProxy(sub, query.AutoTest, query.Lazy, query.Sort, proxyList...)
|
||||||
}
|
}
|
||||||
// 处理自定义代理
|
// 处理自定义代理
|
||||||
utils.AddProxy(temp, query.AutoTest, query.Lazy, utils.ParseProxy(query.Proxies...)...)
|
utils.AddProxy(
|
||||||
|
sub, query.AutoTest, query.Lazy, query.Sort,
|
||||||
|
utils.ParseProxy(query.Proxies...)...,
|
||||||
|
)
|
||||||
|
MergeSubAndTemplate(temp, sub)
|
||||||
// 处理自定义规则
|
// 处理自定义规则
|
||||||
for _, v := range query.Rules {
|
for _, v := range query.Rules {
|
||||||
if v.Prepend {
|
if v.Prepend {
|
||||||
@ -88,13 +96,50 @@ func BuildSub(query validator.SubQuery, template string) (
|
|||||||
}
|
}
|
||||||
if v.Prepend {
|
if v.Prepend {
|
||||||
utils.PrependRuleProvider(
|
utils.PrependRuleProvider(
|
||||||
temp, name, v.Group, provider,
|
temp, v.Name, v.Group, provider,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
utils.AppenddRuleProvider(
|
utils.AppenddRuleProvider(
|
||||||
temp, name, v.Group, provider,
|
temp, v.Name, v.Group, provider,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return temp, nil
|
return temp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func MergeSubAndTemplate(temp *model.Subscription, sub *model.Subscription) {
|
||||||
|
// 只合并节点、策略组
|
||||||
|
// 统计所有国家策略组名称
|
||||||
|
var countryGroupNames []string
|
||||||
|
for _, proxyGroup := range sub.ProxyGroups {
|
||||||
|
if proxyGroup.IsCountryGrop {
|
||||||
|
countryGroupNames = append(
|
||||||
|
countryGroupNames, proxyGroup.Name,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 将订阅中的节点添加到模板中
|
||||||
|
temp.Proxies = append(temp.Proxies, sub.Proxies...)
|
||||||
|
// 将订阅中的策略组添加到模板中
|
||||||
|
skipGroups := []string{"全球直连", "广告拦截", "手动切换"}
|
||||||
|
for i := range temp.ProxyGroups {
|
||||||
|
skip := false
|
||||||
|
for _, v := range skipGroups {
|
||||||
|
if strings.Contains(temp.ProxyGroups[i].Name, v) {
|
||||||
|
if v == "手动切换" {
|
||||||
|
proxies := make([]string, 0, len(sub.Proxies))
|
||||||
|
for _, p := range sub.Proxies {
|
||||||
|
proxies = append(proxies, p.Name)
|
||||||
|
}
|
||||||
|
temp.ProxyGroups[i].Proxies = proxies
|
||||||
|
}
|
||||||
|
skip = true
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !skip {
|
||||||
|
temp.ProxyGroups[i].Proxies = append(temp.ProxyGroups[i].Proxies, countryGroupNames...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
temp.ProxyGroups = append(temp.ProxyGroups, sub.ProxyGroups...)
|
||||||
|
}
|
||||||
|
20
docker-compose.yml
Normal file
20
docker-compose.yml
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
version: '3'
|
||||||
|
|
||||||
|
services:
|
||||||
|
sub2clash:
|
||||||
|
container_name: sub2clash
|
||||||
|
restart: unless-stopped
|
||||||
|
image: ghcr.io/nitezs/sub2clash:latest
|
||||||
|
ports:
|
||||||
|
- "8011:8011"
|
||||||
|
volumes:
|
||||||
|
- ./templates:/app/templates
|
||||||
|
- ./logs:/app/logs
|
||||||
|
# environment:
|
||||||
|
# - PORT=8011
|
||||||
|
# - META_TEMPLATE=template_meta.yaml
|
||||||
|
# - PROXY_TEMPLATE=template_clash.yaml
|
||||||
|
# - REQUEST_RETRY_TIMES=3
|
||||||
|
# - REQUEST_MAX_FILE_SIZE=1048576
|
||||||
|
# - CACHE_EXPIRE=300
|
||||||
|
# - LOG_LEVEL=info
|
11
go.mod
11
go.mod
@ -2,18 +2,23 @@ module sub2clash
|
|||||||
|
|
||||||
go 1.21
|
go 1.21
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/gin-gonic/gin v1.9.1
|
||||||
|
github.com/joho/godotenv v1.5.1
|
||||||
|
go.uber.org/zap v1.25.0
|
||||||
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.10.1 // indirect
|
github.com/bytedance/sonic v1.10.1 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/chenzhuoyu/iasm v0.9.0 // indirect
|
github.com/chenzhuoyu/iasm v0.9.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.15.4 // indirect
|
github.com/go-playground/validator/v10 v10.15.4 // indirect
|
||||||
github.com/goccy/go-json v0.10.2 // indirect
|
github.com/goccy/go-json v0.10.2 // indirect
|
||||||
github.com/joho/godotenv v1.5.1 // indirect
|
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
github.com/leodido/go-urn v1.2.4 // indirect
|
||||||
@ -24,12 +29,10 @@ require (
|
|||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
go.uber.org/zap v1.25.0 // indirect
|
|
||||||
golang.org/x/arch v0.5.0 // indirect
|
golang.org/x/arch v0.5.0 // indirect
|
||||||
golang.org/x/crypto v0.13.0 // indirect
|
golang.org/x/crypto v0.13.0 // indirect
|
||||||
golang.org/x/net v0.15.0 // indirect
|
golang.org/x/net v0.15.0 // indirect
|
||||||
golang.org/x/sys v0.12.0 // indirect
|
golang.org/x/sys v0.12.0 // indirect
|
||||||
golang.org/x/text v0.13.0 // indirect
|
golang.org/x/text v0.13.0 // indirect
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
|
||||||
)
|
)
|
||||||
|
18
go.sum
18
go.sum
@ -1,7 +1,7 @@
|
|||||||
|
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
|
||||||
|
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||||
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM=
|
||||||
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM=
|
||||||
github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk=
|
|
||||||
github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
|
||||||
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
|
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
|
||||||
github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||||
@ -11,6 +11,7 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV
|
|||||||
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
|
github.com/chenzhuoyu/iasm v0.9.0 h1:9fhXjVzq5hUy2gkhhgHl95zG2cEAhw9OSGs8toWWAwo=
|
||||||
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||||
@ -18,17 +19,18 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
|
|||||||
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
|
||||||
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
|
||||||
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
||||||
|
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
||||||
github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo=
|
|
||||||
github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
|
||||||
github.com/go-playground/validator/v10 v10.15.4 h1:zMXza4EpOdooxPel5xDqXEdXG5r+WggpvnAKMsalBjs=
|
github.com/go-playground/validator/v10 v10.15.4 h1:zMXza4EpOdooxPel5xDqXEdXG5r+WggpvnAKMsalBjs=
|
||||||
github.com/go-playground/validator/v10 v10.15.4/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
github.com/go-playground/validator/v10 v10.15.4/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||||
|
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||||
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
||||||
@ -48,10 +50,9 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/natefinch/lumberjack v2.0.0+incompatible h1:4QJd3OLAMgj7ph+yZTuX13Ld4UpgHp07nNdFX7mqFfM=
|
|
||||||
github.com/natefinch/lumberjack v2.0.0+incompatible/go.mod h1:Wi9p2TTF5DG5oU+6YfsmYQpsTIOm0B1VNzQg9Mw6nPk=
|
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
@ -62,11 +63,14 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
|
|||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||||
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
|
||||||
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
|
||||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||||
|
go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk=
|
||||||
|
go.uber.org/goleak v1.2.0/go.mod h1:XJYK+MuIchqpmGmUSAzotztawfKvYLUIgg7guXrwVUo=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
|
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
|
||||||
@ -84,10 +88,12 @@ golang.org/x/sys v0.12.0 h1:CM0HF96J0hcLAwsHPJZjfdNzs0gftsLfgKt57wWHJ0o=
|
|||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k=
|
||||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||||
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||||
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=
|
||||||
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
58
model/proxy_group.go
Normal file
58
model/proxy_group.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package model
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/text/collate"
|
||||||
|
"golang.org/x/text/language"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProxyGroup struct {
|
||||||
|
Name string `yaml:"name,omitempty"`
|
||||||
|
Type string `yaml:"type,omitempty"`
|
||||||
|
Proxies []string `yaml:"proxies,omitempty"`
|
||||||
|
IsCountryGrop bool `yaml:"-"`
|
||||||
|
Url string `yaml:"url,omitempty"`
|
||||||
|
Interval int `yaml:"interval,omitempty"`
|
||||||
|
Tolerance int `yaml:"tolerance,omitempty"`
|
||||||
|
Lazy bool `yaml:"lazy"`
|
||||||
|
Size int `yaml:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyGroupsSortByName []ProxyGroup
|
||||||
|
type ProxyGroupsSortBySize []ProxyGroup
|
||||||
|
|
||||||
|
func (p ProxyGroupsSortByName) Len() int {
|
||||||
|
return len(p)
|
||||||
|
}
|
||||||
|
func (p ProxyGroupsSortBySize) Len() int {
|
||||||
|
return len(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ProxyGroupsSortByName) Less(i, j int) bool {
|
||||||
|
// 定义一组备选语言:首选英语,其次中文
|
||||||
|
tags := []language.Tag{
|
||||||
|
language.English,
|
||||||
|
language.Chinese,
|
||||||
|
}
|
||||||
|
matcher := language.NewMatcher(tags)
|
||||||
|
|
||||||
|
// 假设我们的请求语言是 "zh"(中文),则使用匹配器找到最佳匹配的语言
|
||||||
|
bestMatch, _, _ := matcher.Match(language.Make("zh"))
|
||||||
|
// 使用最佳匹配的语言进行排序
|
||||||
|
c := collate.New(bestMatch)
|
||||||
|
|
||||||
|
return c.CompareString(p[i].Name, p[j].Name) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ProxyGroupsSortBySize) Less(i, j int) bool {
|
||||||
|
if p[i].Size == p[j].Size {
|
||||||
|
return p[i].Name < p[j].Name
|
||||||
|
}
|
||||||
|
return p[i].Size < p[j].Size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p ProxyGroupsSortByName) Swap(i, j int) {
|
||||||
|
p[i], p[j] = p[j], p[i]
|
||||||
|
}
|
||||||
|
func (p ProxyGroupsSortBySize) Swap(i, j int) {
|
||||||
|
p[i], p[j] = p[j], p[i]
|
||||||
|
}
|
11
model/sub.go
11
model/sub.go
@ -13,17 +13,6 @@ type Subscription struct {
|
|||||||
RuleProviders map[string]RuleProvider `yaml:"rule-providers,omitempty,omitempty"`
|
RuleProviders map[string]RuleProvider `yaml:"rule-providers,omitempty,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ProxyGroup struct {
|
|
||||||
Name string `yaml:"name,omitempty"`
|
|
||||||
Type string `yaml:"type,omitempty"`
|
|
||||||
Proxies []string `yaml:"proxies,omitempty"`
|
|
||||||
IsCountryGrop bool `yaml:"-"`
|
|
||||||
Url string `yaml:"url,omitempty"`
|
|
||||||
Interval int `yaml:"interval,omitempty"`
|
|
||||||
Tolerance int `yaml:"tolerance,omitempty"`
|
|
||||||
Lazy bool `yaml:"lazy"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RuleProvider struct {
|
type RuleProvider struct {
|
||||||
Type string `yaml:"type,omitempty"`
|
Type string `yaml:"type,omitempty"`
|
||||||
Behavior string `yaml:"behavior,omitempty"`
|
Behavior string `yaml:"behavior,omitempty"`
|
||||||
|
@ -50,13 +50,12 @@ proxy-groups:
|
|||||||
rules:
|
rules:
|
||||||
- GEOSITE,private,全球直连
|
- GEOSITE,private,全球直连
|
||||||
- GEOIP,private,全球直连
|
- GEOIP,private,全球直连
|
||||||
|
- GEOSITE,category-ads-all,广告拦截
|
||||||
|
- GEOSITE,CN,全球直连
|
||||||
|
- GEOIP,CN,全球直连
|
||||||
- GEOSITE,biliintl,哔哩哔哩
|
- GEOSITE,biliintl,哔哩哔哩
|
||||||
- GEOSITE,bilibili,哔哩哔哩
|
- GEOSITE,bilibili,哔哩哔哩
|
||||||
- GEOSITE,bahamut,巴哈姆特
|
- GEOSITE,bahamut,巴哈姆特
|
||||||
- GEOSITE,CN,全球直连
|
|
||||||
- GEOIP,CN,全球直连
|
|
||||||
- GEOSITE,category-games,游戏平台
|
- GEOSITE,category-games,游戏平台
|
||||||
- GEOSITE,geolocation-!cn,节点选择
|
- GEOSITE,geolocation-!cn,节点选择
|
||||||
- GEOIP,ad,广告拦截
|
|
||||||
- GEOSITE,category-ads-all,广告拦截
|
|
||||||
- MATCH,漏网之鱼
|
- MATCH,漏网之鱼
|
@ -1,6 +1,7 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
"sub2clash/model"
|
"sub2clash/model"
|
||||||
"sub2clash/parser"
|
"sub2clash/parser"
|
||||||
@ -27,35 +28,30 @@ func GetContryName(proxy model.Proxy) string {
|
|||||||
return "其他地区"
|
return "其他地区"
|
||||||
}
|
}
|
||||||
|
|
||||||
var skipGroups = map[string]bool{
|
func AddProxy(
|
||||||
"手动切换": true,
|
sub *model.Subscription, autotest bool, lazy bool, sortStrategy string,
|
||||||
"全球直连": true,
|
proxies ...model.Proxy,
|
||||||
"广告拦截": true,
|
) {
|
||||||
"应用净化": true,
|
|
||||||
}
|
|
||||||
|
|
||||||
func AddProxy(sub *model.Subscription, autotest bool, lazy bool, proxies ...model.Proxy) {
|
|
||||||
newCountryGroupNames := make([]string, 0)
|
newCountryGroupNames := make([]string, 0)
|
||||||
|
// 添加节点
|
||||||
for _, proxy := range proxies {
|
for _, proxy := range proxies {
|
||||||
sub.Proxies = append(sub.Proxies, proxy)
|
sub.Proxies = append(sub.Proxies, proxy)
|
||||||
|
|
||||||
haveProxyGroup := false
|
haveProxyGroup := false
|
||||||
countryName := GetContryName(proxy)
|
countryName := GetContryName(proxy)
|
||||||
|
|
||||||
for i := range sub.ProxyGroups {
|
for i := range sub.ProxyGroups {
|
||||||
group := &sub.ProxyGroups[i]
|
group := &sub.ProxyGroups[i]
|
||||||
|
|
||||||
if group.Name == countryName {
|
if group.Name == countryName {
|
||||||
group.Proxies = append(group.Proxies, proxy.Name)
|
group.Proxies = append(group.Proxies, proxy.Name)
|
||||||
|
group.Size++
|
||||||
haveProxyGroup = true
|
haveProxyGroup = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if group.Name == "手动切换" {
|
if group.Name == "手动切换" {
|
||||||
group.Proxies = append(group.Proxies, proxy.Name)
|
group.Proxies = append(group.Proxies, proxy.Name)
|
||||||
|
group.Size++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if !haveProxyGroup {
|
if !haveProxyGroup {
|
||||||
var newGroup model.ProxyGroup
|
var newGroup model.ProxyGroup
|
||||||
if !autotest {
|
if !autotest {
|
||||||
@ -64,6 +60,7 @@ func AddProxy(sub *model.Subscription, autotest bool, lazy bool, proxies ...mode
|
|||||||
Type: "select",
|
Type: "select",
|
||||||
Proxies: []string{proxy.Name},
|
Proxies: []string{proxy.Name},
|
||||||
IsCountryGrop: true,
|
IsCountryGrop: true,
|
||||||
|
Size: 1,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
newGroup = model.ProxyGroup{
|
newGroup = model.ProxyGroup{
|
||||||
@ -75,23 +72,32 @@ func AddProxy(sub *model.Subscription, autotest bool, lazy bool, proxies ...mode
|
|||||||
Interval: 300,
|
Interval: 300,
|
||||||
Tolerance: 50,
|
Tolerance: 50,
|
||||||
Lazy: lazy,
|
Lazy: lazy,
|
||||||
|
Size: 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
sub.ProxyGroups = append(sub.ProxyGroups, newGroup)
|
sub.ProxyGroups = append(sub.ProxyGroups, newGroup)
|
||||||
newCountryGroupNames = append(newCountryGroupNames, countryName)
|
newCountryGroupNames = append(newCountryGroupNames, countryName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// 统计国家策略组数量
|
||||||
|
countryGroupCount := 0
|
||||||
for i := range sub.ProxyGroups {
|
for i := range sub.ProxyGroups {
|
||||||
if sub.ProxyGroups[i].IsCountryGrop {
|
if sub.ProxyGroups[i].IsCountryGrop {
|
||||||
continue
|
countryGroupCount++
|
||||||
}
|
}
|
||||||
if !skipGroups[sub.ProxyGroups[i].Name] {
|
|
||||||
combined := make([]string, len(newCountryGroupNames)+len(sub.ProxyGroups[i].Proxies))
|
|
||||||
copy(combined, newCountryGroupNames)
|
|
||||||
copy(combined[len(newCountryGroupNames):], sub.ProxyGroups[i].Proxies)
|
|
||||||
sub.ProxyGroups[i].Proxies = combined
|
|
||||||
}
|
}
|
||||||
|
// 对国家策略组进行排序
|
||||||
|
switch sortStrategy {
|
||||||
|
case "sizeasc":
|
||||||
|
sort.Sort(model.ProxyGroupsSortBySize(sub.ProxyGroups[:countryGroupCount]))
|
||||||
|
case "sizedesc":
|
||||||
|
sort.Sort(sort.Reverse(model.ProxyGroupsSortBySize(sub.ProxyGroups[:countryGroupCount])))
|
||||||
|
case "nameasc":
|
||||||
|
sort.Sort(model.ProxyGroupsSortByName(sub.ProxyGroups[:countryGroupCount]))
|
||||||
|
case "namedesc":
|
||||||
|
sort.Sort(sort.Reverse(model.ProxyGroupsSortByName(sub.ProxyGroups[:countryGroupCount])))
|
||||||
|
default:
|
||||||
|
sort.Sort(model.ProxyGroupsSortByName(sub.ProxyGroups[:countryGroupCount]))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,9 +1,12 @@
|
|||||||
package validator
|
package validator
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"github.com/gin-gonic/gin"
|
"github.com/gin-gonic/gin"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
@ -21,6 +24,7 @@ type SubQuery struct {
|
|||||||
Rules []RuleStruct `form:"-" binding:""`
|
Rules []RuleStruct `form:"-" binding:""`
|
||||||
AutoTest bool `form:"autoTest,default=false" binding:""`
|
AutoTest bool `form:"autoTest,default=false" binding:""`
|
||||||
Lazy bool `form:"lazy,default=false" binding:""`
|
Lazy bool `form:"lazy,default=false" binding:""`
|
||||||
|
Sort string `form:"sort" binding:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleProviderStruct struct {
|
type RuleProviderStruct struct {
|
||||||
@ -28,6 +32,7 @@ type RuleProviderStruct struct {
|
|||||||
Url string
|
Url string
|
||||||
Group string
|
Group string
|
||||||
Prepend bool
|
Prepend bool
|
||||||
|
Name string
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleStruct struct {
|
type RuleStruct struct {
|
||||||
@ -46,7 +51,6 @@ func ParseQuery(c *gin.Context) (SubQuery, error) {
|
|||||||
if query.Sub != "" {
|
if query.Sub != "" {
|
||||||
query.Subs = strings.Split(query.Sub, ",")
|
query.Subs = strings.Split(query.Sub, ",")
|
||||||
for i := range query.Subs {
|
for i := range query.Subs {
|
||||||
query.Subs[i], _ = url.QueryUnescape(query.Subs[i])
|
|
||||||
if _, err := url.ParseRequestURI(query.Subs[i]); err != nil {
|
if _, err := url.ParseRequestURI(query.Subs[i]); err != nil {
|
||||||
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
||||||
}
|
}
|
||||||
@ -56,49 +60,36 @@ func ParseQuery(c *gin.Context) (SubQuery, error) {
|
|||||||
}
|
}
|
||||||
if query.Proxy != "" {
|
if query.Proxy != "" {
|
||||||
query.Proxies = strings.Split(query.Proxy, ",")
|
query.Proxies = strings.Split(query.Proxy, ",")
|
||||||
for i := range query.Proxies {
|
|
||||||
query.Proxies[i], _ = url.QueryUnescape(query.Proxies[i])
|
|
||||||
if _, err := url.ParseRequestURI(query.Proxies[i]); err != nil {
|
|
||||||
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
query.Proxies = nil
|
query.Proxies = nil
|
||||||
}
|
}
|
||||||
if query.Template != "" {
|
if query.Template != "" {
|
||||||
unescape, err := url.QueryUnescape(query.Template)
|
uri, err := url.ParseRequestURI(query.Template)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
if strings.Contains(query.Template, string(os.PathSeparator)) {
|
||||||
|
return SubQuery{}, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
uri, err := url.ParseRequestURI(unescape)
|
|
||||||
query.Template = uri.String()
|
query.Template = uri.String()
|
||||||
if err != nil {
|
|
||||||
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if query.RuleProvider != "" {
|
if query.RuleProvider != "" {
|
||||||
var err error
|
|
||||||
query.RuleProvider, err = url.QueryUnescape(query.RuleProvider)
|
|
||||||
if err != nil {
|
|
||||||
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
|
||||||
}
|
|
||||||
reg := regexp.MustCompile(`\[(.*?)\]`)
|
reg := regexp.MustCompile(`\[(.*?)\]`)
|
||||||
ruleProviders := reg.FindAllStringSubmatch(query.RuleProvider, -1)
|
ruleProviders := reg.FindAllStringSubmatch(query.RuleProvider, -1)
|
||||||
for i := range ruleProviders {
|
for i := range ruleProviders {
|
||||||
length := len(ruleProviders)
|
length := len(ruleProviders)
|
||||||
parts := strings.Split(ruleProviders[length-i-1][1], ",")
|
parts := strings.Split(ruleProviders[length-i-1][1], ",")
|
||||||
if len(parts) != 4 {
|
if len(parts) < 4 {
|
||||||
return SubQuery{}, errors.New("参数错误: ruleProvider 格式错误")
|
return SubQuery{}, errors.New("参数错误: ruleProvider 格式错误")
|
||||||
}
|
}
|
||||||
u := parts[1]
|
u := parts[1]
|
||||||
u, err = url.QueryUnescape(u)
|
uri, err := url.ParseRequestURI(u)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
||||||
}
|
}
|
||||||
uri, err := url.ParseRequestURI(u)
|
|
||||||
u = uri.String()
|
u = uri.String()
|
||||||
if err != nil {
|
if len(parts) == 4 {
|
||||||
return SubQuery{}, errors.New("参数错误: " + err.Error())
|
hash := md5.Sum([]byte(u))
|
||||||
|
parts = append(parts, hex.EncodeToString(hash[:]))
|
||||||
}
|
}
|
||||||
query.RuleProviders = append(
|
query.RuleProviders = append(
|
||||||
query.RuleProviders, RuleProviderStruct{
|
query.RuleProviders, RuleProviderStruct{
|
||||||
@ -106,6 +97,7 @@ func ParseQuery(c *gin.Context) (SubQuery, error) {
|
|||||||
Url: u,
|
Url: u,
|
||||||
Group: parts[2],
|
Group: parts[2],
|
||||||
Prepend: parts[3] == "true",
|
Prepend: parts[3] == "true",
|
||||||
|
Name: parts[4],
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user