Add site contacts/FAQ and accept AWG 2.0 / naive pastes in Add config.

ImportSubscription no longer requires only http(s): paste awg://, AWG .conf/JSON, or naive links; subscription parser also reads hosted AWG and Clash wireguard/naive. Site lists support emails and FAQ; Store listing texts/tools updated.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Navis
2026-08-02 15:40:13 +03:00
co-authored by Cursor
parent e416327fbf
commit 50f7f71393
39 changed files with 3128 additions and 112 deletions
+159
View File
@@ -3,6 +3,7 @@ package subscription
import (
"context"
"encoding/base64"
"io"
"net/http"
"net/http/httptest"
"strings"
@@ -193,3 +194,161 @@ proxies:
t.Fatalf("uri %s", items[0].URI)
}
}
func TestParseBodyAWGConf(t *testing.T) {
body := `[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
Address = 10.8.0.2/32
DNS = 1.1.1.1
Jc = 4
Jmin = 40
Jmax = 70
H1 = 1
H2 = 2
H3 = 3
H4 = 4
I1 = <r 128>
[Peer]
PublicKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
`
items, err := ParseBody(body)
if err != nil {
t.Fatal(err)
}
if len(items) != 1 {
t.Fatalf("got %d items: %+v", len(items), items)
}
if items[0].Protocol != "awg" {
t.Fatalf("proto %s", items[0].Protocol)
}
if !strings.Contains(items[0].URI, "Jc = 4") || !strings.Contains(items[0].URI, "203.0.113.10:51820") {
t.Fatalf("uri missing AWG2 fields: %s", items[0].URI)
}
}
func TestParseBodyAmneziaJSON(t *testing.T) {
body := `{
"client_priv_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=",
"server_pub_key": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=",
"client_ip": "10.8.0.2/32",
"hostName": "203.0.113.10",
"port": "51820",
"Jc": 4,
"Jmin": 10,
"Jmax": 50,
"H1": "1",
"H2": "2",
"H3": "3",
"H4": "4"
}`
items, err := ParseBody(body)
if err != nil {
t.Fatal(err)
}
if len(items) != 1 || items[0].Protocol != "awg" {
t.Fatalf("got %+v", items)
}
if !strings.Contains(items[0].URI, "203.0.113.10:51820") {
t.Fatalf("uri %s", items[0].URI)
}
}
func TestParseBodyClashWireguardAWG20(t *testing.T) {
body := `proxies:
- name: AWG-EU
type: wireguard
server: 203.0.113.20
port: 51820
ip: 10.8.0.2/32
private-key: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
public-key: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
allowed-ips: ['0.0.0.0/0']
dns: [1.1.1.1]
jc: 4
jmin: 40
jmax: 70
h1: 1
h2: 2
h3: 3
h4: 4
`
items, err := ParseBody(body)
if err != nil {
t.Fatal(err)
}
if len(items) != 1 {
t.Fatalf("got %d: %+v", len(items), items)
}
if items[0].Protocol != "awg" {
t.Fatalf("proto %s", items[0].Protocol)
}
if items[0].Name != "AWG-EU" {
t.Fatalf("name %q", items[0].Name)
}
if !strings.Contains(items[0].URI, "Jc = 4") || !strings.Contains(items[0].URI, "203.0.113.20:51820") {
t.Fatalf("uri %s", items[0].URI)
}
}
func TestParseBodyClashNaive(t *testing.T) {
body := `proxies:
- name: Naive-HTTPS
type: naive
server: naive.example.com
port: 443
username: user
password: secret
- name: Naive-QUIC
type: naive
server: quic.example.com
port: 443
username: u2
password: p2
udp: true
`
items, err := ParseBody(body)
if err != nil {
t.Fatal(err)
}
if len(items) != 2 {
t.Fatalf("got %d: %+v", len(items), items)
}
if items[0].Protocol != "naive" || items[1].Protocol != "naive" {
t.Fatalf("protos %s %s", items[0].Protocol, items[1].Protocol)
}
if !strings.Contains(items[0].URI, "https://") || !strings.Contains(items[0].URI, "user:secret@naive.example.com") {
t.Fatalf("https uri %s", items[0].URI)
}
if !strings.Contains(items[1].URI, "quic://") || !strings.Contains(items[1].URI, "u2:p2@quic.example.com") {
t.Fatalf("quic uri %s", items[1].URI)
}
}
func TestFetchAWGConfOverHTTP(t *testing.T) {
conf := `[Interface]
PrivateKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=
Address = 10.8.0.2/32
Jc = 3
[Peer]
PublicKey = AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEE=
Endpoint = 198.51.100.7:51820
AllowedIPs = 0.0.0.0/0
`
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, _ = io.WriteString(w, conf)
}))
defer srv.Close()
res, err := Fetch(context.Background(), srv.URL+"/awg.conf")
if err != nil {
t.Fatal(err)
}
if len(res.Items) != 1 || res.Items[0].Protocol != "awg" {
t.Fatalf("got %+v", res.Items)
}
}