alroyso 1 рік тому
батько
коміт
8d3ff0311e

+ 12 - 0
build.yaml

@@ -0,0 +1,12 @@
+targets:
+  $default:
+    builders:
+      dart_json_mapper:
+        generate_for:
+          - lib/main.dart
+
+      # This part is needed to tell original reflectable builder to stay away
+      # it overrides default options for reflectable builder to an **empty** set of files
+      reflectable:
+        generate_for:
+          - no/files

BIN
core/dist/libclash.dylib


+ 86 - 0
core/dist/libclash.h

@@ -0,0 +1,86 @@
+/* Code generated by cmd/cgo; DO NOT EDIT. */
+
+/* package mapleafgo.cn/clash_for_flutter/core */
+
+
+#line 1 "cgo-builtin-export-prolog"
+
+#include <stddef.h>
+
+#ifndef GO_CGO_EXPORT_PROLOGUE_H
+#define GO_CGO_EXPORT_PROLOGUE_H
+
+#ifndef GO_CGO_GOSTRING_TYPEDEF
+typedef struct { const char *p; ptrdiff_t n; } _GoString_;
+#endif
+
+#endif
+
+/* Start of preamble from import "C" comments.  */
+
+
+
+
+/* End of preamble from import "C" comments.  */
+
+
+/* Start of boilerplate cgo prologue.  */
+#line 1 "cgo-gcc-export-header-prolog"
+
+#ifndef GO_CGO_PROLOGUE_H
+#define GO_CGO_PROLOGUE_H
+
+typedef signed char GoInt8;
+typedef unsigned char GoUint8;
+typedef short GoInt16;
+typedef unsigned short GoUint16;
+typedef int GoInt32;
+typedef unsigned int GoUint32;
+typedef long long GoInt64;
+typedef unsigned long long GoUint64;
+typedef GoInt64 GoInt;
+typedef GoUint64 GoUint;
+typedef size_t GoUintptr;
+typedef float GoFloat32;
+typedef double GoFloat64;
+#ifdef _MSC_VER
+#include <complex.h>
+typedef _Fcomplex GoComplex64;
+typedef _Dcomplex GoComplex128;
+#else
+typedef float _Complex GoComplex64;
+typedef double _Complex GoComplex128;
+#endif
+
+/*
+  static assertion to make sure the file is being used on architecture
+  at least with matching size of GoInt.
+*/
+typedef char _check_for_64_bit_pointer_matching_GoInt[sizeof(void*)==64/8 ? 1:-1];
+
+#ifndef GO_CGO_GOSTRING_TYPEDEF
+typedef _GoString_ GoString;
+#endif
+typedef void *GoMap;
+typedef void *GoChan;
+typedef struct { void *t; void *v; } GoInterface;
+typedef struct { void *data; GoInt len; GoInt cap; } GoSlice;
+
+#endif
+
+/* End of boilerplate cgo prologue.  */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern GoUint8 setHomeDir(char* homeStr);
+extern GoUint8 setConfig(char* configStr);
+extern void withExternalController(char* externalController);
+extern void withSecret(char* secret);
+extern GoUint8 mmdbVerify(char* path);
+extern GoUint8 startService();
+
+#ifdef __cplusplus
+}
+#endif

+ 44 - 0
core/go.mod

@@ -0,0 +1,44 @@
+module mapleafgo.cn/clash_for_flutter/core
+
+go 1.20
+
+require (
+	github.com/Dreamacro/clash v1.16.0
+	github.com/oschwald/geoip2-golang v1.8.0
+)
+
+require (
+	github.com/Dreamacro/protobytes v0.0.0-20230524072133-0b6ef2348cfa // indirect
+	github.com/ajg/form v1.5.1 // indirect
+	github.com/go-chi/chi/v5 v5.0.8 // indirect
+	github.com/go-chi/cors v1.2.1 // indirect
+	github.com/go-chi/render v1.0.2 // indirect
+	github.com/gofrs/uuid/v5 v5.0.0 // indirect
+	github.com/google/go-cmp v0.5.9 // indirect
+	github.com/gorilla/websocket v1.5.0 // indirect
+	github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb // indirect
+	github.com/josharian/native v1.1.0 // indirect
+	github.com/mdlayher/netlink v1.7.2 // indirect
+	github.com/mdlayher/socket v0.4.1 // indirect
+	github.com/miekg/dns v1.1.54 // indirect
+	github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
+	github.com/oschwald/maxminddb-golang v1.10.0 // indirect
+	github.com/pierrec/lz4/v4 v4.1.17 // indirect
+	github.com/samber/lo v1.38.1 // indirect
+	github.com/sirupsen/logrus v1.9.2 // indirect
+	github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
+	github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 // indirect
+	github.com/vishvananda/netns v0.0.4 // indirect
+	go.etcd.io/bbolt v1.3.7 // indirect
+	go.uber.org/atomic v1.11.0 // indirect
+	golang.org/x/crypto v0.9.0 // indirect
+	golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect
+	golang.org/x/mod v0.10.0 // indirect
+	golang.org/x/net v0.10.0 // indirect
+	golang.org/x/sync v0.2.0 // indirect
+	golang.org/x/sys v0.8.0 // indirect
+	golang.org/x/text v0.9.0 // indirect
+	golang.org/x/tools v0.9.1 // indirect
+	gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
+	gopkg.in/yaml.v3 v3.0.1 // indirect
+)

+ 90 - 0
core/go.sum

@@ -0,0 +1,90 @@
+github.com/Dreamacro/clash v1.16.0 h1:Ve+8mvtsCv7ySa6f53/GOW6Tho0AFp5RNFsgQZFKcZs=
+github.com/Dreamacro/clash v1.16.0/go.mod h1:ZM3UI2gqqUN7UL7L/F9aTHODTByya6sbC/WivQpaoJk=
+github.com/Dreamacro/protobytes v0.0.0-20230524072133-0b6ef2348cfa h1:1P5rBkdUX+VK0WNJaTAK0LvxVZ6SDPxCU4MLdcVX7R4=
+github.com/Dreamacro/protobytes v0.0.0-20230524072133-0b6ef2348cfa/go.mod h1:QvmEZ/h6KXszPOr2wUFl7Zn3hfFNYdfbXwPVDTyZs6k=
+github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=
+github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
+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/go-chi/chi/v5 v5.0.8 h1:lD+NLqFcAi1ovnVZpsnObHGW4xb4J8lNmoYVfECH1Y0=
+github.com/go-chi/chi/v5 v5.0.8/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
+github.com/go-chi/cors v1.2.1 h1:xEC8UT3Rlp2QuWNEr4Fs/c2EAGVKBwy/1vHx3bppil4=
+github.com/go-chi/cors v1.2.1/go.mod h1:sSbTewc+6wYHBBCW7ytsFSn836hqM7JxpglAy2Vzc58=
+github.com/go-chi/render v1.0.2 h1:4ER/udB0+fMWB2Jlf15RV3F4A2FDuYi/9f+lFttR/Lg=
+github.com/go-chi/render v1.0.2/go.mod h1:/gr3hVkmYR0YlEy3LxCuVRFzEu9Ruok+gFqbIofjao0=
+github.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=
+github.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=
+github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
+github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
+github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
+github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
+github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb h1:6fDKEAXwe3rsfS4khW3EZ8kEqmSiV9szhMPcDrD+Y7Q=
+github.com/insomniacslk/dhcp v0.0.0-20230516061539-49801966e6cb/go.mod h1:7474bZ1YNCvarT6WFKie4kEET6J0KYRDC4XJqqXzQW4=
+github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
+github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
+github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
+github.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=
+github.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=
+github.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=
+github.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=
+github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
+github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
+github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
+github.com/oschwald/geoip2-golang v1.8.0 h1:KfjYB8ojCEn/QLqsDU0AzrJ3R5Qa9vFlx3z6SLNcKTs=
+github.com/oschwald/geoip2-golang v1.8.0/go.mod h1:R7bRvYjOeaoenAp9sKRS8GX5bJWcZ0laWO5+DauEktw=
+github.com/oschwald/maxminddb-golang v1.10.0 h1:Xp1u0ZhqkSuopaKmk1WwHtjF0H9Hd9181uj2MQ5Vndg=
+github.com/oschwald/maxminddb-golang v1.10.0/go.mod h1:Y2ELenReaLAZ0b400URyGwvYxHV1dLIxBuyOsyYjHK0=
+github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+github.com/pierrec/lz4/v4 v4.1.17 h1:kV4Ip+/hUBC+8T6+2EgburRtkE9ef4nbY3f4dFhGjMc=
+github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
+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/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=
+github.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
+github.com/sirupsen/logrus v1.9.2 h1:oxx1eChJGI6Uks2ZC4W1zpLlVgqB8ner4EuQwV4Ik1Y=
+github.com/sirupsen/logrus v1.9.2/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
+github.com/stretchr/testify v1.8.3 h1:RP3t2pwF7cMEbC1dqtB6poj3niw/9gnV4Cjg5oW5gtY=
+github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
+github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01 h1:F9xjJm4IH8VjcqG4ujciOF+GIM4mjPkHhWLLzOghPtM=
+github.com/vishvananda/netlink v1.2.1-beta.2.0.20230420174744-55c8b9515a01/go.mod h1:cAAsePK2e15YDAMJNyOpGYEWNe4sIghTY7gpz4cX/Ik=
+github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
+github.com/vishvananda/netns v0.0.4 h1:Oeaw1EM2JMxD51g9uhtC0D7erkIjgmj8+JZc26m1YX8=
+github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
+go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ=
+go.etcd.io/bbolt v1.3.7/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
+go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
+go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
+golang.org/x/crypto v0.9.0 h1:LF6fAI+IutBocDJ2OT0Q1g8plpYljMZ4+lty+dsqw3g=
+golang.org/x/crypto v0.9.0/go.mod h1:yrmDGqONDYtNj3tH8X9dzUun2m2lzPa9ngI6/RUPGR0=
+golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
+golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
+golang.org/x/mod v0.10.0 h1:lFO9qtOdlre5W1jxS3r/4szv2/6iXxScdzjoBMXNhYk=
+golang.org/x/mod v0.10.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
+golang.org/x/net v0.10.0 h1:X2//UzNDwYmtCLn7To6G58Wr6f5ahEAQgKNzv9Y951M=
+golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
+golang.org/x/sync v0.2.0 h1:PUR+T4wwASmuSTYdKjYHI5TD22Wy5ogLU5qZCOLxBrI=
+golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.0.0-20220804214406-8e32c043e418/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
+golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
+golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
+golang.org/x/tools v0.9.1 h1:8WMNJAz3zrtPmnYC7ISf5dEn3MT0gY7jBJfw27yrrLo=
+golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
+gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
+gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

+ 84 - 0
core/lib.go

@@ -0,0 +1,84 @@
+package main
+
+import "C"
+import (
+	"github.com/Dreamacro/clash/config"
+	"github.com/Dreamacro/clash/constant"
+	"github.com/Dreamacro/clash/hub"
+	"github.com/oschwald/geoip2-golang"
+	"log"
+	"os"
+	"path/filepath"
+)
+
+var options []hub.Option
+
+func init() {
+	constant.Version = "v1.16.0"
+}
+
+//export setHomeDir
+func setHomeDir(homeStr *C.char) bool {
+	homeDir := C.GoString(homeStr)
+	info, err := os.Stat(homeDir)
+	if err != nil {
+		log.Printf("clash_lib [setHomeDir]: %s : %+v\n", homeDir, err)
+		return false
+	}
+	if !info.IsDir() {
+		log.Printf("clash_lib [setHomeDir]: Path is not directory %s\n", homeDir)
+		return false
+	}
+	constant.SetHomeDir(homeDir)
+	return true
+}
+
+//export setConfig
+func setConfig(configStr *C.char) bool {
+	configFile := C.GoString(configStr)
+	if configFile == "" {
+		return false
+	}
+	if !filepath.IsAbs(configFile) {
+		configFile = filepath.Join(constant.Path.HomeDir(), configFile)
+	}
+	constant.SetConfig(configFile)
+	return true
+}
+
+//export withExternalController
+func withExternalController(externalController *C.char) {
+	options = append(options, hub.WithExternalController(C.GoString(externalController)))
+}
+
+//export withSecret
+func withSecret(secret *C.char) {
+	options = append(options, hub.WithSecret(C.GoString(secret)))
+}
+
+//export mmdbVerify
+func mmdbVerify(path *C.char) bool {
+	instance, err := geoip2.Open(C.GoString(path))
+	if err == nil {
+		_ = instance.Close()
+	}
+	return err == nil
+}
+
+//export startService
+func startService() bool {
+	if err := config.Init(constant.Path.HomeDir()); err != nil {
+		log.Printf("clash_lib [startService]: Initial error: %+v\n", err)
+		return false
+	}
+	err := hub.Parse(options...)
+	if err != nil {
+		log.Printf("clash_lib [startService]: %+v\n", err)
+		return false
+	}
+	return true
+}
+
+func main() {
+	log.Println("hello clash")
+}

+ 0 - 0
lib/app/bean/config_bean.dart


+ 3 - 14
lib/app/data/model/LocalUser.dart

@@ -1,21 +1,10 @@
+import 'package:dart_json_mapper/dart_json_mapper.dart';
+
+@jsonSerializable
 class LocalUser {
   String? email;
   String? password;
   String? accessToken;
 
   LocalUser({this.email, this.password, this.accessToken});
-
-  LocalUser.fromJson(Map<String, dynamic> json) {
-    email = json['email'];
-    password = json['password'];
-    accessToken = json['access_token'];
-  }
-
-  Map<String, dynamic> toJson() {
-    final Map<String, dynamic> data = new Map<String, dynamic>();
-    data['email'] = this.email;
-    data['password'] = this.password;
-    data['accessToken'] = this.accessToken;
-    return data;
-  }
 }

+ 3 - 29
lib/app/data/model/LoginMode.dart

@@ -1,5 +1,7 @@
-import 'UserMode.dart';
+import 'package:dart_json_mapper/dart_json_mapper.dart';
 
+import 'UserMode.dart';
+@jsonSerializable
 class LoginMode {
   String? accessToken;
   User? user;
@@ -21,32 +23,4 @@ class LoginMode {
         this.userBuy,
         this.userTicket,
         this.clashConfig});
-
-  LoginMode.fromJson(Map<String, dynamic> json) {
-    accessToken = json['access_token'];
-    user = json['user'] != null ? User.fromJson(json['user']) : null;
-    tutorial = json['tutorial'];
-    swoftdownload = json['swoftdownload'];
-    affurl = json['affurl'];
-    userLoginUrl = json['user_login_url'];
-    userBuy = json['user_buy'];
-    userTicket = json['user_ticket'];
-    clashConfig = json['clash_config'];
-  }
-
-  Map<String, dynamic> toJson() {
-    final Map<String, dynamic> data = new Map<String, dynamic>();
-    data['access_token'] = this.accessToken;
-    if (this.user != null) {
-      data['user'] = this.user!.toJson();
-    }
-    data['tutorial'] = this.tutorial;
-    data['swoftdownload'] = this.swoftdownload;
-    data['affurl'] = this.affurl;
-    data['user_login_url'] = this.userLoginUrl;
-    data['user_buy'] = this.userBuy;
-    data['user_ticket'] = this.userTicket;
-    data['clash_config'] = this.clashConfig;
-    return data;
-  }
 }

+ 7 - 52
lib/app/data/model/NodeMode.dart

@@ -1,4 +1,7 @@
-class nodeMode {
+import 'package:dart_json_mapper/dart_json_mapper.dart';
+
+@jsonSerializable
+class NodeMode {
   int? id;
   String? name;
   String? host;
@@ -18,9 +21,11 @@ class nodeMode {
   int? vless;
   String? vlessPulkey;
   String? ip;
+  @JsonProperty(name: "online_users")
   int? onlineUsers;
+  @JsonProperty(name: "country_code")
   String? countryCode;
-  nodeMode(
+  NodeMode(
       {this.id,
         this.name,
         this.host,
@@ -43,54 +48,4 @@ class nodeMode {
         this.onlineUsers,
         this.countryCode,
       });
-
-  nodeMode.fromJson(Map<String, dynamic> json) {
-    id = json['id'];
-    name = json['name'];
-    host = json['host'];
-    group = json['group'];
-    type = json['type'];
-    port = json['port'];
-    uuid = json['uuid'];
-    method = json['method'];
-    v2AlterId = json['v2_alter_id'];
-    v2Net = json['v2_net'];
-    v2Type = json['v2_type'];
-    v2Host = json['v2_host'];
-    v2Path = json['v2_path'];
-    v2Tls = json['v2_tls'];
-    v2Sni = json['v2_sni'];
-    udp = json['udp'];
-    vless = json['vless'];
-    vlessPulkey = json['vless_pulkey'];
-    ip = json['ip'];
-    onlineUsers = json['online_users'];
-    countryCode = json['country_code'];
-  }
-
-  Map<String, dynamic> toJson() {
-    final Map<String, dynamic> data = new Map<String, dynamic>();
-    data['id'] = this.id;
-    data['name'] = this.name;
-    data['host'] = this.host;
-    data['group'] = this.group;
-    data['type'] = this.type;
-    data['port'] = this.port;
-    data['uuid'] = this.uuid;
-    data['method'] = this.method;
-    data['v2_alter_id'] = this.v2AlterId;
-    data['v2_net'] = this.v2Net;
-    data['v2_type'] = this.v2Type;
-    data['v2_host'] = this.v2Host;
-    data['v2_path'] = this.v2Path;
-    data['v2_tls'] = this.v2Tls;
-    data['v2_sni'] = this.v2Sni;
-    data['udp'] = this.udp;
-    data['vless'] = this.vless;
-    data['vless_pulkey'] = this.vlessPulkey;
-    data['ip'] = this.ip;
-    data['online_users'] = this.onlineUsers;
-    data['country_code'] = this.countryCode;
-    return data;
-  }
 }

+ 3 - 30
lib/app/data/model/SysConfig.dart

@@ -1,3 +1,6 @@
+import 'package:dart_json_mapper/dart_json_mapper.dart';
+
+@jsonSerializable
 class SysConfig {
   String? affurl;
   String? userLoginUrl;
@@ -23,34 +26,4 @@ class SysConfig {
         this.userRofile,
         this.userDownload,
         this.tag});
-
-  SysConfig.fromJson(Map<String, dynamic> json) {
-    affurl = json['affurl'];
-    userLoginUrl = json['user_login_url'];
-    userLoginUrlKe = json['user_login_url_ke'];
-    userBuy = json['user_buy'];
-    userTicket = json['user_ticket'];
-    userReg = json['user_reg'];
-    userReset = json['user_reset'];
-    userTutorial = json['user_tutorial'];
-    userRofile = json['user_rofile'];
-    userDownload = json['user_download'];
-    tag = json['tag'];
-  }
-
-  Map<String, dynamic> toJson() {
-    final Map<String, dynamic> data = new Map<String, dynamic>();
-    data['affurl'] = this.affurl;
-    data['user_login_url'] = this.userLoginUrl;
-    data['user_login_url_ke'] = this.userLoginUrlKe;
-    data['user_buy'] = this.userBuy;
-    data['user_ticket'] = this.userTicket;
-    data['user_reg'] = this.userReg;
-    data['user_reset'] = this.userReset;
-    data['user_tutorial'] = this.userTutorial;
-    data['user_rofile'] = this.userRofile;
-    data['user_download'] = this.userDownload;
-    data['tag'] = this.tag;
-    return data;
-  }
 }

+ 8 - 60
lib/app/data/model/UserMode.dart

@@ -1,3 +1,7 @@
+
+import 'package:dart_json_mapper/dart_json_mapper.dart';
+
+@jsonSerializable
 class User {
   int? id;
   String? nickname;
@@ -14,13 +18,13 @@ class User {
   int? speedLimit;
   int? credit;
   String? expiredAt;
-  Null? banTime;
+  String? banTime;
   String? level;
-  Null? group;
+  String? group;
   int? lastLogin;
-  Null? resetTime;
+  String? resetTime;
   int? inviteNum;
-  Null? userGroupId;
+  String? userGroupId;
   int? status;
   String? code;
 
@@ -49,60 +53,4 @@ class User {
         this.userGroupId,
         this.status,
         this.code});
-
-  User.fromJson(Map<String, dynamic> json) {
-    id = json['id'];
-    nickname = json['nickname'];
-    account = json['account'];
-    port = json['port'];
-    passwd = json['passwd'];
-    uuid = json['uuid'];
-    transferEnable = json['transfer_enable'];
-    unusedTraffic = json['unusedTraffic'];
-    u = json['u'];
-    d = json['d'];
-    t = json['t'];
-    enable = json['enable'];
-    speedLimit = json['speed_limit'];
-    credit = json['credit'];
-    expiredAt = json['expired_at'];
-    banTime = json['ban_time'];
-    level = json['level'];
-    group = json['group'];
-    lastLogin = json['last_login'];
-    resetTime = json['reset_time'];
-    inviteNum = json['invite_num'];
-    userGroupId = json['user_group_id'];
-    status = json['status'];
-    code = json['code'];
-  }
-
-  Map<String, dynamic> toJson() {
-    final Map<String, dynamic> data = new Map<String, dynamic>();
-    data['id'] = this.id;
-    data['nickname'] = this.nickname;
-    data['account'] = this.account;
-    data['port'] = this.port;
-    data['passwd'] = this.passwd;
-    data['uuid'] = this.uuid;
-    data['transfer_enable'] = this.transferEnable;
-    data['unusedTraffic'] = this.unusedTraffic;
-    data['u'] = this.u;
-    data['d'] = this.d;
-    data['t'] = this.t;
-    data['enable'] = this.enable;
-    data['speed_limit'] = this.speedLimit;
-    data['credit'] = this.credit;
-    data['expired_at'] = this.expiredAt;
-    data['ban_time'] = this.banTime;
-    data['level'] = this.level;
-    data['group'] = this.group;
-    data['last_login'] = this.lastLogin;
-    data['reset_time'] = this.resetTime;
-    data['invite_num'] = this.inviteNum;
-    data['user_group_id'] = this.userGroupId;
-    data['status'] = this.status;
-    data['code'] = this.code;
-    return data;
-  }
 }

+ 39 - 0
lib/app/global_controller/GlobalConfigFileController.dart

@@ -0,0 +1,39 @@
+import 'dart:io';
+
+import 'package:get/get.dart';
+import 'package:naiyouwl/clash_generated_bindings.dart';
+class GlobalConfigFileController extends GetxController{
+  static bool isStartClash = false;
+
+  late Directory configDir;
+  late final Clash clash;
+  late final String clashConfigPath;
+  late final String clashForMePath;
+  late final String profilesPath;
+
+  var systemProxy = false.obs;
+  // var clashConfig = Config.defaultConfig().obs;
+  // var clashForMe = ClashForMeConfig.defaultConfig().obs;
+  //
+  // Future<void> init() async {
+  //    await getApplicationSupportDirectory().then((dir) => configDir = dir);
+  //
+  //   profilesPath = "${configDir.path}${Constants.profilesPath}";
+  //   clashForMePath = "${configDir.path}${Constants.clashForMe}";
+  //   clashConfigPath = "${configDir.path}${Constants.clashConfig}";
+  //
+  //   await _initClash();
+  //   _initConfig();
+  //   _initAction();
+  // }
+  //
+  // _initConfig() async {
+  //   ClashForMeConfig? tempCfm = ClashForMeConfig.formYamlFile(clashForMePath);
+  //   tempCfm = await _profilesInitCheck(tempCfm);
+  //   if (tempCfm != null) {
+  //     clashForMe.value = tempCfm;
+  //   }
+  //
+  //   clashConfig.value = Config.formYamlFile(clashConfigPath);
+  // }
+}

+ 1 - 14
lib/app/global_controller/GlobalController.dart

@@ -5,23 +5,10 @@ import '../data/model/NodeMode.dart';
 import '../network/api_service.dart';
 
 class GlobalController extends GetxController {
-  var nodeModes = <nodeMode>[].obs;
+  var nodeModes = <NodeMode>[].obs;
   var isLoading = false.obs;
   var errorMsg = ''.obs;
   Future<void> fetchNodes() async {
     nodeModes.value = await ApiService().getNode("/api/client/v4/nodes?vless=1");
-
-    // try {
-    //   isLoading.value = true;
-    //   nodeModes.clear();
-    //   nodeModes.value = await ApiService().getNode("/api/client/v6/nodes?vless=1");
-    //   LogHelper().d(nodeModes.toList());
-    //   //loadSelectedNode();
-    // } catch (e) {
-    //   throw e.toString();
-    // } finally {
-    //   isLoading.value = false;
-    // }
   }
-
 }

+ 6 - 7
lib/app/modules/home/controllers/home_controller.dart

@@ -1,4 +1,5 @@
 
+import 'package:dart_json_mapper/dart_json_mapper.dart';
 import 'package:get/get.dart';
 import '../../../common/LogHelper.dart';
 import '../../../common/SharedPreferencesUtil.dart';
@@ -26,7 +27,7 @@ class HomeController extends GetxController {
   var userMode = User().obs;
   var errorMsg = ''.obs;
   var selectNode = '选择节点'.obs;
-  var nodeModes = <nodeMode>[];
+  var nodeModes = <NodeMode>[];
   late final GlobalController globalController ;
 
 
@@ -52,10 +53,9 @@ class HomeController extends GetxController {
 
   Future<void> fetchSysConfig() async {
     try {
-      Map<String, dynamic>? data  = await SharedPreferencesUtil().getObject("sysconfig");
+      String? data  = await SharedPreferencesUtil().getString("sysconfig");
       if(data  != null){
-        sysConfig.value = SysConfig.fromJson(data);
-        LogHelper().d(sysConfig.value.toJson());
+        sysConfig.value =JsonMapper.deserialize<SysConfig>(data)!;
       }
 
     } catch (e) {
@@ -91,10 +91,9 @@ class HomeController extends GetxController {
 
   Future<void> fetchLocalUser() async {
     try {
-      Map<String, dynamic>? userdata  = await SharedPreferencesUtil().getObject("localUser");
+      String? userdata  = await SharedPreferencesUtil().getString("localUser");
       if(userdata  != null){
-        localUsers.value = LocalUser.fromJson(userdata);
-        LogHelper().d(sysConfig.value.toJson());
+        localUsers.value = JsonMapper.deserialize<LocalUser>(userdata)!;
       }
     } catch (e) {
       errorMsg.value = e.toString();

+ 5 - 4
lib/app/modules/login/controllers/login_controller.dart

@@ -1,3 +1,4 @@
+import 'package:dart_json_mapper/dart_json_mapper.dart';
 import 'package:get/get.dart';
 import 'package:naiyouwl/app/common/constants.dart';
 import 'package:naiyouwl/app/data/model/LoginMode.dart';
@@ -24,7 +25,7 @@ class LoginController extends GetxController {
       if(loginModes.value.accessToken != null){
         await SharedPreferencesUtil().setString("token", loginModes.value.accessToken.toString());
         var userModes = LocalUser(email: username,password: password,accessToken: loginModes.value.accessToken.toString());
-        await SharedPreferencesUtil().setObject("localUser", userModes.toJson());
+        await SharedPreferencesUtil().setString("localUser", JsonMapper.serialize(userModes));
       }
       Get.offNamed(Routes.HOME,arguments: loginModes.value);
 
@@ -37,10 +38,10 @@ class LoginController extends GetxController {
 
   Future<void> fetchLocalUser() async {
     try {
-      Map<String, dynamic>? data  = await SharedPreferencesUtil().getObject("localUser");
+      String? data  = await SharedPreferencesUtil().getString("localUser");
       if(data  != null){
-        localUsers.value = LocalUser.fromJson(data);
-        LogHelper().d(localUsers.value.toJson());
+        localUsers.value = JsonMapper.deserialize<LocalUser>(data)!;
+        LogHelper().d(localUsers.value.email);
       }
     } catch (e) {
       errorMsg.value = e.toString();

+ 12 - 12
lib/app/modules/node/controllers/node_controller.dart

@@ -18,11 +18,11 @@ class NodeController extends GetxController {
 
   final count = 0.obs;
   var isLoading = false.obs;
-  var nodesToShow = <nodeMode>[].obs;
-  var nodeModes = <nodeMode>[];
+  var nodesToShow = <NodeMode>[].obs;
+  var nodeModes = <NodeMode>[];
   var selectedIndex = (-1).obs;
   var errorMsg = ''.obs;
-  final selectedNode = Rx<nodeMode?>(null);
+  final selectedNode = Rx<NodeMode?>(null);
   var pingResults = <int, String>{}.obs;
   //final selectedNodeId = (-1).obs;
   final isLoadingMap = <int, bool>{}.obs;
@@ -44,7 +44,7 @@ class NodeController extends GetxController {
   // }
 
 
-  Future<void> tcpPing(nodeMode node) async {
+  Future<void> tcpPing(NodeMode node) async {
     int? nodeId = node.id;
 
     if (nodeId != null) {
@@ -74,20 +74,20 @@ class NodeController extends GetxController {
     }
   }
 
-  Future<void> pingSingleNode(nodeMode node) async {
+  Future<void> pingSingleNode(NodeMode node) async {
 
     await tcpPing(node);
 
   }
 
 
-  void selectNode(nodeMode node) {
+  void selectNode(NodeMode node) {
     selectedNode.value = node;
     _storeSelectedNode(node);
     //selectedIndex.value = nodeModes.indexWhere((item) => item.id == node.id);
   }
 
-  Future<void> _storeSelectedNode(nodeMode node) async {
+  Future<void> _storeSelectedNode(NodeMode node) async {
     final prefs = await SharedPreferences.getInstance();
     // 为简化起见,我们只存储node的ID,但您可以根据需要存储更多信息
     prefs.setInt('selectedNodeId', node.id!);
@@ -103,16 +103,16 @@ class NodeController extends GetxController {
   }
 
   //自动选择人数最小的线路
-  nodeMode selectBestNode(List<nodeMode> nodes) {
+  NodeMode selectBestNode(List<NodeMode> nodes) {
     return nodes.where((node) => node.countryCode == 'HK') //筛选地区是HK的节点
         .reduce((value, element) =>
     value.onlineUsers! < element.onlineUsers! ? value : element); //选择人数最小的
   }
 
   void selectMinOnlineUsersNodeInRegion(String region) {
-    List<nodeMode> nodesInRegion = nodeModes.where((node) => node.countryCode == region).toList();
+    List<NodeMode> nodesInRegion = nodeModes.where((node) => node.countryCode == region).toList();
     if (nodesInRegion.isNotEmpty) {
-      nodeMode? minNode = nodesInRegion.reduce((curr, next) => curr.onlineUsers! <= next.onlineUsers! ? curr : next);
+      NodeMode? minNode = nodesInRegion.reduce((curr, next) => curr.onlineUsers! <= next.onlineUsers! ? curr : next);
       if (minNode != null) {
         selectNode(minNode);
       }
@@ -143,11 +143,11 @@ class NodeController extends GetxController {
   }
 
   void updateNodesToDisplay() {
-    List<nodeMode> nodes = List.from(nodeModes);
+    List<NodeMode> nodes = List.from(nodeModes);
 
     switch (displayStrategy.value) {
       case NodeDisplayStrategy.LeastUsers:
-        List<nodeMode> filteredNodes = nodeModes.where((node) => node.countryCode == "hk" && node.onlineUsers != null).toList();
+        List<NodeMode> filteredNodes = nodeModes.where((node) => node.countryCode == "hk" && node.onlineUsers != null).toList();
 
         if (filteredNodes.isNotEmpty) {
           int? minUsers = filteredNodes.map((node) => node.onlineUsers!).reduce((curr, next) => curr < next ? curr : next);

+ 2 - 1
lib/app/modules/welcome/controllers/welcome_controller.dart

@@ -1,3 +1,4 @@
+import 'package:dart_json_mapper/dart_json_mapper.dart';
 import 'package:get/get.dart';
 import 'package:naiyouwl/app/common/SharedPreferencesUtil.dart';
 import 'package:naiyouwl/app/common/constants.dart';
@@ -15,7 +16,7 @@ class WelcomeController extends GetxController {
     try {
       isLoading.value = true;
       sysConfig.value = await ApiService().fetchSysConfig(kSysConfig);
-      await SharedPreferencesUtil().setObject("sysconfig", sysConfig.value.toJson());
+      await SharedPreferencesUtil().setString("sysconfig", JsonMapper.serialize(sysConfig.value));
 
       var token = await SharedPreferencesUtil().getString("token");
       if(token != null){

+ 45 - 9
lib/app/network/api_service.dart

@@ -1,3 +1,4 @@
+import 'package:dart_json_mapper/dart_json_mapper.dart';
 import 'package:dio/dio.dart';
 import 'package:naiyouwl/app/data/model/LoginMode.dart';
 import 'package:naiyouwl/app/data/model/NodeMode.dart';
@@ -17,14 +18,23 @@ class ApiService {
 
   Future<SysConfig> fetchSysConfig(String path) async {
     final  data = await _requestWrapper(() => _dioClient.get(path));
-    return SysConfig.fromJson(data);
-
+    final result = JsonMapper.deserialize<SysConfig>(data);
+    if (result != null) {
+      return result;
+    } else {
+      throw Exception("Failed API response is NUll");
+    }
   }
 
   Future<LoginMode> login(String path, {Map<String, dynamic>? data}) async {
 
       final  retData =await _requestWrapper(() => _dioClient.post(path,data: data));
-      return LoginMode.fromJson(retData);
+      final result = JsonMapper.deserialize<LoginMode>(retData);
+      if (result != null) {
+        return result;
+      } else {
+        throw Exception("Failed API response is NUll");
+      }
 
   }
 
@@ -32,17 +42,27 @@ class ApiService {
   Future<User> userinfo(String path) async {
 
     final  retData =await _requestWrapper(() => _dioClient.get(path));
-    return User.fromJson(retData);
+    final result = JsonMapper.deserialize<User>(retData);
+    if (result != null) {
+      return result;
+    } else {
+      throw Exception("Failed API response is NUll");
+    }
 
   }
 
 
-  Future<List<nodeMode>> getNode(String path) async {
-    final  retData =await _requestWrapper(() => _dioClient.get(path));
+  Future<List<NodeMode>> getNode(String path) async {
+    final  retData = await _requestWrapper(() => _dioClient.get(path));
     if (retData is List) {
       // 遍历List并为每一项调用NodeMode.fromJson
-      return (retData as List).map((item) {
-        return nodeMode.fromJson(item);
+      return (retData).map((item) {
+        final result = JsonMapper.deserialize<NodeMode>(item);
+        if (result != null) {
+          return result;
+        } else {
+          throw Exception("Failed API response is NUll");
+        }
       }).toList();
 
     } else {
@@ -71,8 +91,23 @@ class ApiService {
 
   Future<dynamic> _requestWrapper(Future<dynamic> Function() apiCall) async {
     try {
-      return await apiCall();
+      final response = await apiCall();
+
+      // 如果你有返回的HTTP状态码, 检查它们
+      // if(response.statusCode < 200 || response.statusCode >= 300) {
+      //   throw Exception('API returned non-success status code: ${response.statusCode}');
+      // }
+
+      // 对于特定的请求, 检查返回值是否为null
+      if (response == null) {
+        throw Exception('API response is null');
+      }
+
+      return response;
     } catch (e) {
+      // 日志错误
+      print('API request error: $e');
+
       if (e is DioError && e.error is AppException) {
         throw e.error; // 抛出自定义的AppException
       } else {
@@ -81,4 +116,5 @@ class ApiService {
     }
   }
 
+
 }

+ 290 - 0
lib/clash_generated_bindings.dart

@@ -0,0 +1,290 @@
+// AUTO GENERATED FILE, DO NOT EDIT.
+//
+// Generated by `package:ffigen`.
+// ignore_for_file: type=lint
+import 'dart:ffi' as ffi;
+
+class Clash {
+  /// Holds the symbol lookup function.
+  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+      _lookup;
+
+  /// The symbols are looked up in [dynamicLibrary].
+  Clash(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup;
+
+  /// The symbols are looked up with [lookup].
+  Clash.fromLookup(
+      ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)
+          lookup)
+      : _lookup = lookup;
+
+  int setHomeDir(
+    ffi.Pointer<ffi.Char> homeStr,
+  ) {
+    return _setHomeDir(
+      homeStr,
+    );
+  }
+
+  late final _setHomeDirPtr =
+      _lookup<ffi.NativeFunction<GoUint8 Function(ffi.Pointer<ffi.Char>)>>(
+          'setHomeDir');
+  late final _setHomeDir =
+      _setHomeDirPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();
+
+  int setConfig(
+    ffi.Pointer<ffi.Char> configStr,
+  ) {
+    return _setConfig(
+      configStr,
+    );
+  }
+
+  late final _setConfigPtr =
+      _lookup<ffi.NativeFunction<GoUint8 Function(ffi.Pointer<ffi.Char>)>>(
+          'setConfig');
+  late final _setConfig =
+      _setConfigPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();
+
+  void withExternalController(
+    ffi.Pointer<ffi.Char> externalController,
+  ) {
+    return _withExternalController(
+      externalController,
+    );
+  }
+
+  late final _withExternalControllerPtr =
+      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
+          'withExternalController');
+  late final _withExternalController = _withExternalControllerPtr
+      .asFunction<void Function(ffi.Pointer<ffi.Char>)>();
+
+  void withSecret(
+    ffi.Pointer<ffi.Char> secret,
+  ) {
+    return _withSecret(
+      secret,
+    );
+  }
+
+  late final _withSecretPtr =
+      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(
+          'withSecret');
+  late final _withSecret =
+      _withSecretPtr.asFunction<void Function(ffi.Pointer<ffi.Char>)>();
+
+  int mmdbVerify(
+    ffi.Pointer<ffi.Char> path,
+  ) {
+    return _mmdbVerify(
+      path,
+    );
+  }
+
+  late final _mmdbVerifyPtr =
+      _lookup<ffi.NativeFunction<GoUint8 Function(ffi.Pointer<ffi.Char>)>>(
+          'mmdbVerify');
+  late final _mmdbVerify =
+      _mmdbVerifyPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();
+
+  int startService() {
+    return _startService();
+  }
+
+  late final _startServicePtr =
+      _lookup<ffi.NativeFunction<GoUint8 Function()>>('startService');
+  late final _startService = _startServicePtr.asFunction<int Function()>();
+}
+
+final class __mbstate_t extends ffi.Union {
+  @ffi.Array.multi([128])
+  external ffi.Array<ffi.Char> __mbstate8;
+
+  @ffi.LongLong()
+  external int _mbstateL;
+}
+
+final class __darwin_pthread_handler_rec extends ffi.Struct {
+  external ffi
+      .Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>
+      __routine;
+
+  external ffi.Pointer<ffi.Void> __arg;
+
+  external ffi.Pointer<__darwin_pthread_handler_rec> __next;
+}
+
+final class _opaque_pthread_attr_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([56])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_cond_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([40])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_condattr_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([8])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_mutex_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([56])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_mutexattr_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([8])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_once_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([8])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_rwlock_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([192])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_rwlockattr_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  @ffi.Array.multi([16])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _opaque_pthread_t extends ffi.Struct {
+  @ffi.Long()
+  external int __sig;
+
+  external ffi.Pointer<__darwin_pthread_handler_rec> __cleanup_stack;
+
+  @ffi.Array.multi([8176])
+  external ffi.Array<ffi.Char> __opaque;
+}
+
+final class _GoString_ extends ffi.Struct {
+  external ffi.Pointer<ffi.Char> p;
+
+  @ptrdiff_t()
+  external int n;
+}
+
+typedef ptrdiff_t = __darwin_ptrdiff_t;
+typedef __darwin_ptrdiff_t = ffi.Long;
+
+final class GoInterface extends ffi.Struct {
+  external ffi.Pointer<ffi.Void> t;
+
+  external ffi.Pointer<ffi.Void> v;
+}
+
+final class GoSlice extends ffi.Struct {
+  external ffi.Pointer<ffi.Void> data;
+
+  @GoInt()
+  external int len;
+
+  @GoInt()
+  external int cap;
+}
+
+typedef GoInt = GoInt64;
+typedef GoInt64 = ffi.LongLong;
+typedef GoUint8 = ffi.UnsignedChar;
+
+const int __DARWIN_ONLY_64_BIT_INO_T = 1;
+
+const int __DARWIN_ONLY_UNIX_CONFORMANCE = 1;
+
+const int __DARWIN_ONLY_VERS_1050 = 1;
+
+const int __DARWIN_UNIX03 = 1;
+
+const int __DARWIN_64_BIT_INO_T = 1;
+
+const int __DARWIN_VERS_1050 = 1;
+
+const int __DARWIN_NON_CANCELABLE = 0;
+
+const String __DARWIN_SUF_EXTSN = '\$DARWIN_EXTSN';
+
+const int __DARWIN_C_ANSI = 4096;
+
+const int __DARWIN_C_FULL = 900000;
+
+const int __DARWIN_C_LEVEL = 900000;
+
+const int __STDC_WANT_LIB_EXT1__ = 1;
+
+const int __DARWIN_NO_LONG_LONG = 0;
+
+const int _DARWIN_FEATURE_64_BIT_INODE = 1;
+
+const int _DARWIN_FEATURE_ONLY_64_BIT_INODE = 1;
+
+const int _DARWIN_FEATURE_ONLY_VERS_1050 = 1;
+
+const int _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE = 1;
+
+const int _DARWIN_FEATURE_UNIX_CONFORMANCE = 3;
+
+const int __has_ptrcheck = 0;
+
+const int __DARWIN_NULL = 0;
+
+const int __PTHREAD_SIZE__ = 8176;
+
+const int __PTHREAD_ATTR_SIZE__ = 56;
+
+const int __PTHREAD_MUTEXATTR_SIZE__ = 8;
+
+const int __PTHREAD_MUTEX_SIZE__ = 56;
+
+const int __PTHREAD_CONDATTR_SIZE__ = 8;
+
+const int __PTHREAD_COND_SIZE__ = 40;
+
+const int __PTHREAD_ONCE_SIZE__ = 8;
+
+const int __PTHREAD_RWLOCK_SIZE__ = 192;
+
+const int __PTHREAD_RWLOCKATTR_SIZE__ = 16;
+
+const int __DARWIN_WCHAR_MAX = 2147483647;
+
+const int __DARWIN_WCHAR_MIN = -2147483648;
+
+const int __DARWIN_WEOF = -1;
+
+const int _FORTIFY_SOURCE = 2;
+
+const int NULL = 0;
+
+const int USER_ADDR_NULL = 0;

+ 9 - 2
lib/main.dart

@@ -1,17 +1,24 @@
 import 'dart:io';
 import 'dart:ui';
 
+
+import 'package:dart_json_mapper/dart_json_mapper.dart';
 import 'package:flutter/material.dart';
 
 import 'package:get/get.dart';
 import 'package:window_manager/window_manager.dart';
 
 import 'app/routes/app_pages.dart';
-
+import 'main.mapper.g.dart' show initializeJsonMapper;
 void main() async {
   const width = 375.0;
   const height = 736.0;
-
+  initializeJsonMapper();
+  JsonMapper().useAdapter(JsonMapperAdapter(valueDecorators: {
+    typeOf<Map<String, String>>(): (value) {
+      return Map.castFrom<dynamic, dynamic, String, String>(value);
+    },
+  }));
 
   if(Platform.isWindows || Platform.isMacOS){
     WidgetsFlutterBinding.ensureInitialized();

+ 4 - 0
linux/flutter/generated_plugin_registrant.cc

@@ -8,6 +8,7 @@
 
 #include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
 #include <screen_retriever/screen_retriever_plugin.h>
+#include <url_launcher_linux/url_launcher_plugin.h>
 #include <window_manager/window_manager_plugin.h>
 
 void fl_register_plugins(FlPluginRegistry* registry) {
@@ -17,6 +18,9 @@ void fl_register_plugins(FlPluginRegistry* registry) {
   g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
   screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
+  g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
+      fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
+  url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
   g_autoptr(FlPluginRegistrar) window_manager_registrar =
       fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
   window_manager_plugin_register_with_registrar(window_manager_registrar);

+ 1 - 0
linux/flutter/generated_plugins.cmake

@@ -5,6 +5,7 @@
 list(APPEND FLUTTER_PLUGIN_LIST
   flutter_secure_storage_linux
   screen_retriever
+  url_launcher_linux
   window_manager
 )
 

+ 4 - 0
macos/Flutter/GeneratedPluginRegistrant.swift

@@ -7,14 +7,18 @@ import Foundation
 
 import connectivity_plus_macos
 import flutter_secure_storage_macos
+import path_provider_foundation
 import screen_retriever
 import shared_preferences_foundation
+import url_launcher_macos
 import window_manager
 
 func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
   ConnectivityPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlugin"))
   FlutterSecureStorageMacosPlugin.register(with: registry.registrar(forPlugin: "FlutterSecureStorageMacosPlugin"))
+  PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
   ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin"))
   SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
+  UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
   WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin"))
 }

+ 13 - 0
macos/Podfile.lock

@@ -5,12 +5,17 @@ PODS:
   - flutter_secure_storage_macos (3.3.1):
     - FlutterMacOS
   - FlutterMacOS (1.0.0)
+  - path_provider_foundation (0.0.1):
+    - Flutter
+    - FlutterMacOS
   - ReachabilitySwift (5.0.0)
   - screen_retriever (0.0.1):
     - FlutterMacOS
   - shared_preferences_foundation (0.0.1):
     - Flutter
     - FlutterMacOS
+  - url_launcher_macos (0.0.1):
+    - FlutterMacOS
   - window_manager (0.2.0):
     - FlutterMacOS
 
@@ -18,8 +23,10 @@ DEPENDENCIES:
   - connectivity_plus_macos (from `Flutter/ephemeral/.symlinks/plugins/connectivity_plus_macos/macos`)
   - flutter_secure_storage_macos (from `Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos`)
   - FlutterMacOS (from `Flutter/ephemeral`)
+  - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`)
   - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`)
   - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`)
+  - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
   - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`)
 
 SPEC REPOS:
@@ -33,10 +40,14 @@ EXTERNAL SOURCES:
     :path: Flutter/ephemeral/.symlinks/plugins/flutter_secure_storage_macos/macos
   FlutterMacOS:
     :path: Flutter/ephemeral
+  path_provider_foundation:
+    :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin
   screen_retriever:
     :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos
   shared_preferences_foundation:
     :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin
+  url_launcher_macos:
+    :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
   window_manager:
     :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos
 
@@ -44,9 +55,11 @@ SPEC CHECKSUMS:
   connectivity_plus_macos: f6e86fd000e971d361e54b5afcadc8c8fa773308
   flutter_secure_storage_macos: 6ceee8fbc7f484553ad17f79361b556259df89aa
   FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24
+  path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943
   ReachabilitySwift: 985039c6f7b23a1da463388634119492ff86c825
   screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38
   shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126
+  url_launcher_macos: d2691c7dd33ed713bf3544850a623080ec693d95
   window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8
 
 PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367

+ 16 - 0
macos/Runner.xcodeproj/project.pbxproj

@@ -27,6 +27,7 @@
 		33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
 		33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
 		33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+		3B5D091F2AD913BB0057E569 /* libclash.dylib in Resources */ = {isa = PBXBuildFile; fileRef = 3B5D091E2AD913BB0057E569 /* libclash.dylib */; };
 		8444AE800248F37BB8ABF637 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F397D2CB6412D720D523E30C /* Pods_Runner.framework */; };
 		CC6EB3AF67BFAAA84BB25D80 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 0AC0DF046605705B6C85E2E2 /* Pods_RunnerTests.framework */; };
 /* End PBXBuildFile section */
@@ -81,6 +82,7 @@
 		33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; };
 		33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; };
 		33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; };
+		3B5D091E2AD913BB0057E569 /* libclash.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libclash.dylib; path = Frameworks/libclash.dylib; sourceTree = "<group>"; };
 		75F24307BAD49042829F2A60 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
 		7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; };
 		9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; };
@@ -132,6 +134,7 @@
 		33CC10E42044A3C60003C045 = {
 			isa = PBXGroup;
 			children = (
+				3B5D091E2AD913BB0057E569 /* libclash.dylib */,
 				33FAB671232836740065AC1E /* Runner */,
 				33CEB47122A05771004F2AC0 /* Flutter */,
 				331C80D6294CF71000263BE5 /* RunnerTests */,
@@ -315,6 +318,7 @@
 			files = (
 				33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
 				33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+				3B5D091F2AD913BB0057E569 /* libclash.dylib in Resources */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -575,6 +579,10 @@
 					"$(inherited)",
 					"@executable_path/../Frameworks",
 				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Frameworks",
+				);
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_VERSION = 5.0;
 			};
@@ -702,6 +710,10 @@
 					"$(inherited)",
 					"@executable_path/../Frameworks",
 				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Frameworks",
+				);
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_OPTIMIZATION_LEVEL = "-Onone";
 				SWIFT_VERSION = 5.0;
@@ -723,6 +735,10 @@
 					"$(inherited)",
 					"@executable_path/../Frameworks",
 				);
+				LIBRARY_SEARCH_PATHS = (
+					"$(inherited)",
+					"$(PROJECT_DIR)/Frameworks",
+				);
 				PROVISIONING_PROFILE_SPECIFIER = "";
 				SWIFT_VERSION = 5.0;
 			};

+ 15 - 1
pubspec.yaml

@@ -19,12 +19,18 @@ dependencies:
   dio: ^4.0.6
   connectivity_plus: ^2.3.6
   flutter_secure_storage: ^5.0.2
+  ffi: ^2.0.1
+  dart_json_mapper: ^2.2.7
+  url_launcher: ^6.1.11
+  path_provider: ^2.0.11
+
 
 dev_dependencies: 
   flutter_lints: ^2.0.0
   flutter_test: 
     sdk: flutter
-
+  ffigen: any
+  build_runner: ^2.4.4
 flutter: 
   assets: 
     - images/login/login.png
@@ -43,3 +49,11 @@ flutter:
     - images/node/nodetablebg.png
   uses-material-design: true
 
+ffigen:
+  name: "Clash"
+  output: 'lib/clash_generated_bindings.dart'
+  headers:
+    entry-points:
+      - 'core/dist/libclash.h'
+#  llvm-path:
+#    - 'C:/Users/Casbin/Scoop/apps/llvm/current'

+ 3 - 0
windows/flutter/generated_plugin_registrant.cc

@@ -9,6 +9,7 @@
 #include <connectivity_plus_windows/connectivity_plus_windows_plugin.h>
 #include <flutter_secure_storage_windows/flutter_secure_storage_windows_plugin.h>
 #include <screen_retriever/screen_retriever_plugin.h>
+#include <url_launcher_windows/url_launcher_windows.h>
 #include <window_manager/window_manager_plugin.h>
 
 void RegisterPlugins(flutter::PluginRegistry* registry) {
@@ -18,6 +19,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
       registry->GetRegistrarForPlugin("FlutterSecureStorageWindowsPlugin"));
   ScreenRetrieverPluginRegisterWithRegistrar(
       registry->GetRegistrarForPlugin("ScreenRetrieverPlugin"));
+  UrlLauncherWindowsRegisterWithRegistrar(
+      registry->GetRegistrarForPlugin("UrlLauncherWindows"));
   WindowManagerPluginRegisterWithRegistrar(
       registry->GetRegistrarForPlugin("WindowManagerPlugin"));
 }

+ 1 - 0
windows/flutter/generated_plugins.cmake

@@ -6,6 +6,7 @@ list(APPEND FLUTTER_PLUGIN_LIST
   connectivity_plus_windows
   flutter_secure_storage_windows
   screen_retriever
+  url_launcher_windows
   window_manager
 )