├── .github └── workflows │ └── build.yml ├── .gitignore ├── LICENSE ├── README.md ├── client ├── .goreleaser.yaml ├── Dockerfile ├── LICENSE ├── cli │ ├── config.go │ ├── main.go │ ├── service.go │ ├── service_option │ │ ├── option.go │ │ ├── option_linux.go │ │ └── option_windows.go │ └── tunnel.go ├── go.mod ├── go.sum ├── gui │ ├── global │ │ ├── config.go │ │ └── global.go │ ├── main.go │ ├── registry │ │ └── registry.go │ ├── rsrc_windows_386.syso │ ├── rsrc_windows_amd64.syso │ ├── state.go │ ├── static │ │ └── logo.ico │ ├── window │ │ └── window.go │ └── winres │ │ ├── icon.png │ │ ├── icon16.png │ │ └── winres.json ├── internal │ ├── common │ │ ├── dialer.go │ │ ├── dns.go │ │ ├── get_tunnel_config.go │ │ ├── log_writer.go │ │ ├── logger.go │ │ ├── message.go │ │ ├── register.go │ │ ├── state.go │ │ ├── url.go │ │ └── version.go │ ├── engine │ │ ├── event.go │ │ ├── ws_config.go │ │ ├── ws_domain.go │ │ ├── ws_forward_config.go │ │ ├── ws_host_config.go │ │ ├── ws_p2p_config.go │ │ ├── ws_port_check.go │ │ ├── ws_proxy_config.go │ │ ├── ws_remove_config.go │ │ └── ws_tunnel_config.go │ └── service │ │ ├── impl.client.go │ │ ├── impl.p2p.go │ │ ├── impl.server.go │ │ ├── impl.tunnel.go │ │ └── interface.go ├── lib │ ├── build.bat │ └── lib.go ├── pkg │ ├── bean │ │ ├── page.go │ │ └── response.go │ ├── env │ │ └── get.go │ ├── fs │ │ └── fs.go │ ├── gost-x │ │ ├── LICENSE │ │ ├── README.md │ │ ├── admission │ │ │ ├── admission.go │ │ │ ├── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ │ └── wrapper │ │ │ │ ├── conn.go │ │ │ │ └── listener.go │ │ ├── api │ │ │ ├── api.go │ │ │ ├── config.go │ │ │ ├── config_admission.go │ │ │ ├── config_auther.go │ │ │ ├── config_bypass.go │ │ │ ├── config_chain.go │ │ │ ├── config_conn_limiter.go │ │ │ ├── config_hop.go │ │ │ ├── config_hosts.go │ │ │ ├── config_ingress.go │ │ │ ├── config_limiter.go │ │ │ ├── config_observer.go │ │ │ ├── config_rate_limiter.go │ │ │ ├── config_recorder.go │ │ │ ├── config_reload.go │ │ │ ├── config_resolver.go │ │ │ ├── config_router.go │ │ │ ├── config_sd.go │ │ │ ├── config_service.go │ │ │ ├── doc.go │ │ │ ├── error.go │ │ │ ├── middleware.go │ │ │ ├── service │ │ │ │ └── service.go │ │ │ └── swagger.yaml │ │ ├── auth │ │ │ ├── auth.go │ │ │ └── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ ├── bypass │ │ │ ├── bypass.go │ │ │ └── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ ├── chain │ │ │ ├── chain.go │ │ │ ├── route.go │ │ │ ├── router.go │ │ │ └── transport.go │ │ ├── config │ │ │ ├── cmd │ │ │ │ └── cmd.go │ │ │ ├── config.go │ │ │ ├── loader │ │ │ │ └── loader.go │ │ │ └── parsing │ │ │ │ ├── admission │ │ │ │ └── parse.go │ │ │ │ ├── auth │ │ │ │ └── parse.go │ │ │ │ ├── bypass │ │ │ │ └── parse.go │ │ │ │ ├── chain │ │ │ │ └── parse.go │ │ │ │ ├── hop │ │ │ │ └── parse.go │ │ │ │ ├── hosts │ │ │ │ └── parse.go │ │ │ │ ├── ingress │ │ │ │ └── parse.go │ │ │ │ ├── limiter │ │ │ │ └── parse.go │ │ │ │ ├── logger │ │ │ │ └── parse.go │ │ │ │ ├── node │ │ │ │ └── parse.go │ │ │ │ ├── observer │ │ │ │ └── parse.go │ │ │ │ ├── parse.go │ │ │ │ ├── parser │ │ │ │ └── parser.go │ │ │ │ ├── recorder │ │ │ │ └── parse.go │ │ │ │ ├── resolver │ │ │ │ └── parse.go │ │ │ │ ├── router │ │ │ │ └── parse.go │ │ │ │ ├── sd │ │ │ │ └── parse.go │ │ │ │ ├── selector │ │ │ │ └── parse.go │ │ │ │ ├── service │ │ │ │ └── parse.go │ │ │ │ └── tls.go │ │ ├── connector │ │ │ ├── direct │ │ │ │ ├── conn.go │ │ │ │ ├── connector.go │ │ │ │ └── metadata.go │ │ │ ├── forward │ │ │ │ └── connector.go │ │ │ ├── http │ │ │ │ ├── connector.go │ │ │ │ └── metadata.go │ │ │ ├── http2 │ │ │ │ ├── conn.go │ │ │ │ ├── connector.go │ │ │ │ └── metadata.go │ │ │ ├── relay │ │ │ │ ├── bind.go │ │ │ │ ├── conn.go │ │ │ │ ├── connector.go │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── router │ │ │ │ ├── conn.go │ │ │ │ ├── connector.go │ │ │ │ └── metadata.go │ │ │ ├── serial │ │ │ │ └── connector.go │ │ │ ├── sni │ │ │ │ ├── conn.go │ │ │ │ ├── connector.go │ │ │ │ └── metadata.go │ │ │ ├── socks │ │ │ │ ├── v4 │ │ │ │ │ ├── connector.go │ │ │ │ │ └── metadata.go │ │ │ │ └── v5 │ │ │ │ │ ├── bind.go │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── connector.go │ │ │ │ │ ├── listener.go │ │ │ │ │ ├── metadata.go │ │ │ │ │ └── selector.go │ │ │ ├── ss │ │ │ │ ├── connector.go │ │ │ │ ├── metadata.go │ │ │ │ └── udp │ │ │ │ │ ├── connector.go │ │ │ │ │ └── metadata.go │ │ │ ├── sshd │ │ │ │ └── connector.go │ │ │ ├── tcp │ │ │ │ └── connector.go │ │ │ ├── tunnel │ │ │ │ ├── bind.go │ │ │ │ ├── conn.go │ │ │ │ ├── connector.go │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ └── unix │ │ │ │ └── connector.go │ │ ├── ctx │ │ │ └── value.go │ │ ├── dialer │ │ │ ├── direct │ │ │ │ ├── conn.go │ │ │ │ └── dialer.go │ │ │ ├── dtls │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── ftcp │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── grpc │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── http2 │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ ├── h2 │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── dialer.go │ │ │ │ │ └── metadata.go │ │ │ │ └── metadata.go │ │ │ ├── http3 │ │ │ │ ├── dialer.go │ │ │ │ ├── metadata.go │ │ │ │ └── wt │ │ │ │ │ ├── client.go │ │ │ │ │ ├── dialer.go │ │ │ │ │ └── metadata.go │ │ │ ├── icmp │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── kcp │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── mtcp │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── mtls │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── mws │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── obfs │ │ │ │ ├── http │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── dialer.go │ │ │ │ │ └── metadata.go │ │ │ │ └── tls │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── dialer.go │ │ │ │ │ └── metadata.go │ │ │ ├── pht │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── quic │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── serial │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── ssh │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── sshd │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── tcp │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── tls │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── udp │ │ │ │ ├── conn.go │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── unix │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ ├── wg │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ │ └── ws │ │ │ │ ├── dialer.go │ │ │ │ └── metadata.go │ │ ├── go.mod │ │ ├── go.sum │ │ ├── handler │ │ │ ├── api │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── auto │ │ │ │ └── handler.go │ │ │ ├── dns │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── file │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── forward │ │ │ │ ├── local │ │ │ │ │ ├── handler.go │ │ │ │ │ └── metadata.go │ │ │ │ └── remote │ │ │ │ │ ├── handler.go │ │ │ │ │ └── metadata.go │ │ │ ├── http │ │ │ │ ├── handler.go │ │ │ │ ├── metadata.go │ │ │ │ └── udp.go │ │ │ ├── http2 │ │ │ │ ├── conn.go │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── http3 │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── metrics │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── redirect │ │ │ │ ├── tcp │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── handler_darwin.go │ │ │ │ │ ├── handler_linux.go │ │ │ │ │ ├── handler_other.go │ │ │ │ │ └── metadata.go │ │ │ │ └── udp │ │ │ │ │ ├── handler.go │ │ │ │ │ └── metadata.go │ │ │ ├── relay │ │ │ │ ├── bind.go │ │ │ │ ├── conn.go │ │ │ │ ├── connect.go │ │ │ │ ├── entrypoint.go │ │ │ │ ├── forward.go │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── router │ │ │ │ ├── associate.go │ │ │ │ ├── entrypoint.go │ │ │ │ ├── handler.go │ │ │ │ ├── metadata.go │ │ │ │ └── router.go │ │ │ ├── serial │ │ │ │ ├── conn.go │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── sni │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── socks │ │ │ │ ├── v4 │ │ │ │ │ ├── handler.go │ │ │ │ │ └── metadata.go │ │ │ │ └── v5 │ │ │ │ │ ├── bind.go │ │ │ │ │ ├── connect.go │ │ │ │ │ ├── handler.go │ │ │ │ │ ├── mbind.go │ │ │ │ │ ├── metadata.go │ │ │ │ │ ├── selector.go │ │ │ │ │ ├── udp.go │ │ │ │ │ └── udp_tun.go │ │ │ ├── ss │ │ │ │ ├── handler.go │ │ │ │ ├── metadata.go │ │ │ │ └── udp │ │ │ │ │ ├── handler.go │ │ │ │ │ └── metadata.go │ │ │ ├── sshd │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── tap │ │ │ │ ├── handler.go │ │ │ │ └── metadata.go │ │ │ ├── tun │ │ │ │ ├── client.go │ │ │ │ ├── handler.go │ │ │ │ ├── metadata.go │ │ │ │ └── server.go │ │ │ ├── tunnel │ │ │ │ ├── bind.go │ │ │ │ ├── connect.go │ │ │ │ ├── dialer.go │ │ │ │ ├── entrypoint.go │ │ │ │ ├── handler.go │ │ │ │ ├── metadata.go │ │ │ │ └── tunnel.go │ │ │ └── unix │ │ │ │ ├── handler.go │ │ │ │ ├── metadata.go │ │ │ │ └── sniffing.go │ │ ├── hop │ │ │ ├── hop.go │ │ │ └── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ ├── hosts │ │ │ ├── hosts.go │ │ │ └── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ ├── ingress │ │ │ ├── ingress.go │ │ │ └── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ ├── internal │ │ │ ├── ctx │ │ │ │ └── value.go │ │ │ ├── io │ │ │ │ └── io.go │ │ │ ├── loader │ │ │ │ ├── file.go │ │ │ │ ├── http.go │ │ │ │ ├── loader.go │ │ │ │ └── redis.go │ │ │ ├── matcher │ │ │ │ └── matcher.go │ │ │ ├── net │ │ │ │ ├── addr.go │ │ │ │ ├── dialer │ │ │ │ │ ├── dialer.go │ │ │ │ │ ├── dialer_linux.go │ │ │ │ │ └── dialer_other.go │ │ │ │ ├── http │ │ │ │ │ └── http.go │ │ │ │ ├── ip │ │ │ │ │ └── ip.go │ │ │ │ ├── net.go │ │ │ │ ├── proxyproto │ │ │ │ │ ├── conn.go │ │ │ │ │ └── listener.go │ │ │ │ ├── resovle.go │ │ │ │ ├── transport.go │ │ │ │ └── udp │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── listener.go │ │ │ │ │ ├── pool.go │ │ │ │ │ └── relay.go │ │ │ ├── plugin │ │ │ │ └── plugin.go │ │ │ └── util │ │ │ │ ├── cache │ │ │ │ └── cache.go │ │ │ │ ├── dtls │ │ │ │ └── conn.go │ │ │ │ ├── forwarder │ │ │ │ └── sniffer.go │ │ │ │ ├── grpc │ │ │ │ └── proto │ │ │ │ │ ├── gost.pb.go │ │ │ │ │ ├── gost.proto │ │ │ │ │ ├── gost_grpc.pb.go │ │ │ │ │ ├── gost_grpcx.go │ │ │ │ │ └── protoc.sh │ │ │ │ ├── icmp │ │ │ │ └── conn.go │ │ │ │ ├── kcp │ │ │ │ ├── config.go │ │ │ │ └── kcp.go │ │ │ │ ├── mux │ │ │ │ └── mux.go │ │ │ │ ├── pht │ │ │ │ ├── client.go │ │ │ │ ├── conn.go │ │ │ │ └── server.go │ │ │ │ ├── quic │ │ │ │ └── conn.go │ │ │ │ ├── relay │ │ │ │ └── conn.go │ │ │ │ ├── resolver │ │ │ │ ├── cache.go │ │ │ │ └── resolver.go │ │ │ │ ├── serial │ │ │ │ ├── conn.go │ │ │ │ ├── serial.go │ │ │ │ ├── serial_linux.go │ │ │ │ ├── serial_other.go │ │ │ │ ├── serial_windows.go │ │ │ │ └── util.go │ │ │ │ ├── sniffing │ │ │ │ ├── sniff.go │ │ │ │ └── sniffer.go │ │ │ │ ├── socks │ │ │ │ ├── conn.go │ │ │ │ └── socks.go │ │ │ │ ├── ss │ │ │ │ ├── conn.go │ │ │ │ └── ss.go │ │ │ │ ├── ssh │ │ │ │ ├── conn.go │ │ │ │ ├── session.go │ │ │ │ └── ssh.go │ │ │ │ ├── sshd │ │ │ │ └── conn.go │ │ │ │ ├── stats │ │ │ │ └── stats.go │ │ │ │ ├── tap │ │ │ │ └── config.go │ │ │ │ ├── tls │ │ │ │ └── tls.go │ │ │ │ ├── tun │ │ │ │ └── config.go │ │ │ │ ├── ws │ │ │ │ ├── frame.go │ │ │ │ └── ws.go │ │ │ │ └── wt │ │ │ │ └── conn.go │ │ ├── limiter │ │ │ ├── conn │ │ │ │ ├── conn.go │ │ │ │ ├── generator.go │ │ │ │ ├── limiter.go │ │ │ │ └── wrapper │ │ │ │ │ ├── conn.go │ │ │ │ │ └── listener.go │ │ │ ├── rate │ │ │ │ ├── generator.go │ │ │ │ ├── limiter.go │ │ │ │ └── rate.go │ │ │ └── traffic │ │ │ │ ├── cache │ │ │ │ └── limiter.go │ │ │ │ ├── generator.go │ │ │ │ ├── limiter.go │ │ │ │ ├── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ │ │ ├── traffic.go │ │ │ │ └── wrapper │ │ │ │ ├── conn.go │ │ │ │ ├── io.go │ │ │ │ └── listener.go │ │ ├── listener │ │ │ ├── dns │ │ │ │ ├── listener.go │ │ │ │ ├── metadata.go │ │ │ │ └── server.go │ │ │ ├── dtls │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── ftcp │ │ │ │ ├── conn.go │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── grpc │ │ │ │ ├── listener.go │ │ │ │ ├── metadata.go │ │ │ │ └── server.go │ │ │ ├── http2 │ │ │ │ ├── conn.go │ │ │ │ ├── h2 │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── listener.go │ │ │ │ │ └── metadata.go │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── http3 │ │ │ │ ├── conn.go │ │ │ │ ├── h3 │ │ │ │ │ ├── listener.go │ │ │ │ │ └── metadata.go │ │ │ │ ├── listener.go │ │ │ │ ├── metadata.go │ │ │ │ └── wt │ │ │ │ │ ├── listener.go │ │ │ │ │ └── metadata.go │ │ │ ├── icmp │ │ │ │ ├── conn.go │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── kcp │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── mtcp │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── mtls │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── mws │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── obfs │ │ │ │ ├── http │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── listener.go │ │ │ │ │ └── metadata.go │ │ │ │ └── tls │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── listener.go │ │ │ │ │ └── metadata.go │ │ │ ├── pht │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── quic │ │ │ │ ├── conn.go │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── redirect │ │ │ │ ├── tcp │ │ │ │ │ ├── listener.go │ │ │ │ │ ├── listener_darwin.go │ │ │ │ │ ├── listener_linux.go │ │ │ │ │ ├── listener_other.go │ │ │ │ │ └── metadata.go │ │ │ │ └── udp │ │ │ │ │ ├── conn.go │ │ │ │ │ ├── listener.go │ │ │ │ │ ├── listener_linux.go │ │ │ │ │ ├── listener_other.go │ │ │ │ │ └── metadata.go │ │ │ ├── rtcp │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── rudp │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── serial │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── ssh │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── sshd │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── tap │ │ │ │ ├── conn.go │ │ │ │ ├── listener.go │ │ │ │ ├── metadata.go │ │ │ │ ├── tap_darwin.go │ │ │ │ ├── tap_linux.go │ │ │ │ ├── tap_unix.go │ │ │ │ └── tap_windows.go │ │ │ ├── tcp │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── tls │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── tun │ │ │ │ ├── conn.go │ │ │ │ ├── listener.go │ │ │ │ ├── metadata.go │ │ │ │ ├── tun.go │ │ │ │ ├── tun_darwin.go │ │ │ │ ├── tun_linux.go │ │ │ │ ├── tun_unix.go │ │ │ │ └── tun_windows.go │ │ │ ├── udp │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ ├── unix │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ │ └── ws │ │ │ │ ├── listener.go │ │ │ │ └── metadata.go │ │ ├── logger │ │ │ ├── logger.go │ │ │ └── nop_logger.go │ │ ├── metadata │ │ │ ├── metadata.go │ │ │ └── util │ │ │ │ └── util.go │ │ ├── metrics │ │ │ ├── metrics.go │ │ │ ├── noop.go │ │ │ ├── prom.go │ │ │ ├── service │ │ │ │ └── service.go │ │ │ └── wrapper │ │ │ │ ├── conn.go │ │ │ │ └── listener.go │ │ ├── observer │ │ │ ├── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ │ └── stats │ │ │ │ ├── stats.go │ │ │ │ └── wrapper │ │ │ │ ├── conn.go │ │ │ │ ├── io.go │ │ │ │ └── listener.go │ │ ├── recorder │ │ │ ├── file.go │ │ │ ├── http.go │ │ │ ├── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ │ ├── recorder.go │ │ │ ├── redis.go │ │ │ └── tcp.go │ │ ├── registry │ │ │ ├── admission.go │ │ │ ├── auther.go │ │ │ ├── bypass.go │ │ │ ├── chain.go │ │ │ ├── connector.go │ │ │ ├── dialer.go │ │ │ ├── handler.go │ │ │ ├── hop.go │ │ │ ├── hosts.go │ │ │ ├── ingress.go │ │ │ ├── limiter.go │ │ │ ├── listener.go │ │ │ ├── logger.go │ │ │ ├── observer.go │ │ │ ├── recorder.go │ │ │ ├── registry.go │ │ │ ├── resolver.go │ │ │ ├── router.go │ │ │ ├── sd.go │ │ │ └── service.go │ │ ├── resolver │ │ │ ├── exchanger │ │ │ │ └── exchanger.go │ │ │ ├── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ │ └── resolver.go │ │ ├── router │ │ │ ├── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ │ ├── router.go │ │ │ ├── router_linux.go │ │ │ └── router_other.go │ │ ├── routing │ │ │ ├── matcher.go │ │ │ └── rules │ │ │ │ ├── parser.go │ │ │ │ └── parser_test.go │ │ ├── sd │ │ │ └── plugin │ │ │ │ ├── grpc.go │ │ │ │ └── http.go │ │ ├── selector │ │ │ ├── filter.go │ │ │ ├── selector.go │ │ │ ├── strategy.go │ │ │ └── weighted.go │ │ └── service │ │ │ ├── service.go │ │ │ └── status.go │ ├── logger │ │ └── logger.go │ ├── p2p │ │ ├── client │ │ │ ├── connector.go │ │ │ ├── control.go │ │ │ ├── event │ │ │ │ └── event.go │ │ │ ├── proxy │ │ │ │ ├── general_tcp.go │ │ │ │ ├── proxy.go │ │ │ │ ├── proxy_manager.go │ │ │ │ ├── proxy_wrapper.go │ │ │ │ └── xtcp.go │ │ │ ├── service.go │ │ │ └── visitor │ │ │ │ ├── stcp.go │ │ │ │ ├── visitor.go │ │ │ │ ├── visitor_manager.go │ │ │ │ └── xtcp.go │ │ ├── conf │ │ │ ├── frpc.toml │ │ │ ├── frpc_full_example.toml │ │ │ ├── frps.toml │ │ │ ├── frps_full_example.toml │ │ │ └── vfrpc.toml │ │ ├── example │ │ │ ├── client.go │ │ │ ├── main.go │ │ │ ├── server.go │ │ │ └── visit.go │ │ ├── frpc │ │ │ └── core.go │ │ ├── frps │ │ │ └── core.go │ │ ├── pkg │ │ │ ├── auth │ │ │ │ ├── auth.go │ │ │ │ ├── oidc.go │ │ │ │ ├── pass.go │ │ │ │ └── token.go │ │ │ ├── config │ │ │ │ ├── load.go │ │ │ │ ├── types │ │ │ │ │ └── types.go │ │ │ │ └── v1 │ │ │ │ │ ├── client.go │ │ │ │ │ ├── common.go │ │ │ │ │ ├── proxy.go │ │ │ │ │ ├── server.go │ │ │ │ │ └── visitor.go │ │ │ ├── errors │ │ │ │ └── errors.go │ │ │ ├── msg │ │ │ │ ├── ctl.go │ │ │ │ ├── handler.go │ │ │ │ └── msg.go │ │ │ ├── nathole │ │ │ │ ├── analysis.go │ │ │ │ ├── classify.go │ │ │ │ ├── controller.go │ │ │ │ ├── discovery.go │ │ │ │ ├── nathole.go │ │ │ │ └── utils.go │ │ │ ├── plugin │ │ │ │ └── server │ │ │ │ │ ├── http.go │ │ │ │ │ ├── manager.go │ │ │ │ │ ├── plugin.go │ │ │ │ │ ├── tracer.go │ │ │ │ │ └── types.go │ │ │ ├── transport │ │ │ │ ├── message.go │ │ │ │ └── tls.go │ │ │ └── util │ │ │ │ ├── limit │ │ │ │ ├── reader.go │ │ │ │ └── writer.go │ │ │ │ ├── log │ │ │ │ └── log.go │ │ │ │ ├── net │ │ │ │ ├── conn.go │ │ │ │ ├── dial.go │ │ │ │ ├── dns.go │ │ │ │ ├── kcp.go │ │ │ │ ├── listener.go │ │ │ │ ├── tls.go │ │ │ │ ├── udp.go │ │ │ │ └── websocket.go │ │ │ │ ├── util │ │ │ │ ├── types.go │ │ │ │ └── util.go │ │ │ │ ├── version │ │ │ │ └── version.go │ │ │ │ ├── wait │ │ │ │ └── backoff.go │ │ │ │ └── xlog │ │ │ │ ├── ctx.go │ │ │ │ └── xlog.go │ │ ├── registry │ │ │ └── register.go │ │ └── server │ │ │ ├── control.go │ │ │ ├── controller │ │ │ └── resource.go │ │ │ ├── metrics │ │ │ └── metrics.go │ │ │ ├── proxy │ │ │ ├── proxy.go │ │ │ ├── stcp.go │ │ │ └── xtcp.go │ │ │ ├── service.go │ │ │ └── visitor │ │ │ └── visitor.go │ ├── signal │ │ └── free.go │ └── utils │ │ ├── calc.go │ │ ├── port.go │ │ └── regexp.go └── webui │ ├── backend │ ├── bootstrap │ │ ├── fs.go │ │ ├── logger.go │ │ ├── release.go │ │ ├── router.go │ │ ├── server.go │ │ ├── task.go │ │ └── todo.go │ ├── controller │ │ ├── client │ │ │ └── api.go │ │ ├── config │ │ │ └── api.go │ │ ├── logger │ │ │ └── api.go │ │ ├── p2p │ │ │ └── api.go │ │ └── tunnel │ │ │ └── api.go │ ├── global │ │ └── global.go │ ├── model │ │ ├── client.go │ │ ├── p2p.go │ │ └── tunnel.go │ ├── router │ │ ├── api.go │ │ ├── router.go │ │ └── static.go │ ├── service │ │ ├── client │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.status.go │ │ │ └── service.update.go │ │ ├── config │ │ │ ├── service.go │ │ │ └── service.query.go │ │ ├── logger │ │ │ ├── service.go │ │ │ └── service.list.go │ │ ├── p2p │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.status.go │ │ │ └── service.update.go │ │ └── tunnel │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.status.go │ │ │ └── service.update.go │ ├── todo │ │ └── todo.go │ └── web │ │ ├── dist.zip │ │ └── static.go │ └── frontend │ ├── index.html │ ├── package-lock.json │ ├── package.json │ ├── src │ ├── App.vue │ ├── api │ │ ├── client.js │ │ ├── config.js │ │ ├── logger.js │ │ ├── p2p.js │ │ └── tunnel.js │ ├── components │ │ ├── DialogApi.vue │ │ ├── Drawer.vue │ │ ├── Empty.vue │ │ ├── Loading.vue │ │ ├── MessageApi.vue │ │ ├── Modal.vue │ │ └── ModalApi.vue │ ├── icon │ │ ├── alert.vue │ │ └── online.vue │ ├── layout │ │ ├── components │ │ │ ├── AppCard.vue │ │ │ ├── Header.vue │ │ │ └── Layout.vue │ │ ├── empty │ │ │ └── index.vue │ │ ├── mobile │ │ │ └── index.vue │ │ └── simple │ │ │ └── index.vue │ ├── main.js │ ├── request │ │ └── index.js │ ├── router │ │ ├── index.js │ │ └── routers │ │ │ └── base.js │ ├── setting.js │ ├── store │ │ ├── app.js │ │ ├── index.js │ │ └── local.js │ ├── style.css │ ├── utils │ │ ├── browser.js │ │ ├── copy.js │ │ ├── flow.js │ │ ├── formDataRule.js │ │ ├── func.js │ │ ├── menu.js │ │ ├── random.js │ │ ├── regexp.js │ │ └── routerToMenu.js │ └── views │ │ └── home │ │ ├── about.vue │ │ ├── client.vue │ │ ├── index.vue │ │ ├── logger.vue │ │ ├── p2p.vue │ │ └── tunnel.vue │ └── vite.config.js ├── images ├── config.png ├── dashboard.png ├── node.png ├── wx.png └── zfb.png ├── install.sh ├── proxy ├── .goreleaser.yaml ├── Dockerfile ├── LICENSE ├── bootstrap │ ├── api.go │ ├── config.go │ ├── logger.go │ ├── release.go │ └── server.go ├── cmd │ ├── program │ │ ├── service.go │ │ └── service_option │ │ │ ├── option.go │ │ │ ├── option_linux.go │ │ │ └── option_windows.go │ ├── root.go │ ├── service.go │ └── version.go ├── configs │ └── config.go ├── global │ ├── env.go │ └── global.go ├── go.mod ├── go.sum ├── main.go └── pkg │ ├── logger │ └── logger.go │ ├── middleware │ └── logger.go │ ├── proxy │ ├── config.go │ ├── proxy.go │ └── tls.go │ └── signal │ └── free.go ├── server ├── .goreleaser.yaml ├── Dockerfile ├── LICENSE ├── bootstrap │ ├── config.go │ ├── jwt.go │ ├── logger.go │ ├── memory.go │ ├── persistence.go │ ├── release.go │ ├── router.go │ ├── server.go │ ├── task.go │ └── todo.go ├── cmd │ ├── close_otp.go │ ├── migrate.go │ ├── program │ │ ├── service.go │ │ └── service_option │ │ │ ├── option.go │ │ │ ├── option_linux.go │ │ │ └── option_windows.go │ ├── reset_passwd.go │ ├── root.go │ ├── service.go │ └── version.go ├── configs │ ├── config.go │ ├── mysql.go │ └── sqlite.go ├── controller │ ├── admin │ │ ├── dashboard │ │ │ └── api.go │ │ ├── gost_client │ │ │ └── api.go │ │ ├── gost_client_forward │ │ │ └── api.go │ │ ├── gost_client_host │ │ │ └── api.go │ │ ├── gost_client_logger │ │ │ └── api.go │ │ ├── gost_client_p2p │ │ │ └── api.go │ │ ├── gost_client_proxy │ │ │ └── api.go │ │ ├── gost_client_tunnel │ │ │ └── api.go │ │ ├── gost_node │ │ │ └── api.go │ │ ├── gost_node_bind │ │ │ └── api.go │ │ ├── gost_node_config │ │ │ └── api.go │ │ ├── gost_node_logger │ │ │ └── api.go │ │ ├── gost_node_rule │ │ │ └── api.go │ │ ├── system_config │ │ │ └── api.go │ │ ├── system_notice │ │ │ └── api.go │ │ └── system_user │ │ │ └── api.go │ ├── auth │ │ └── api.go │ ├── normal │ │ ├── gost │ │ │ └── api.go │ │ ├── gost_client │ │ │ └── api.go │ │ ├── gost_client_forward │ │ │ └── api.go │ │ ├── gost_client_host │ │ │ └── api.go │ │ ├── gost_client_logger │ │ │ └── api.go │ │ ├── gost_client_p2p │ │ │ └── api.go │ │ ├── gost_client_proxy │ │ │ └── api.go │ │ ├── gost_client_tunnel │ │ │ └── api.go │ │ ├── gost_node │ │ │ └── api.go │ │ ├── gost_obs │ │ │ └── api.go │ │ └── system_notice │ │ │ └── api.go │ └── public │ │ ├── gost │ │ └── api.go │ │ ├── p2p │ │ └── api.go │ │ └── system_config │ │ └── api.go ├── generate.bat ├── generate.sh ├── generate │ └── main.go ├── global │ ├── env.go │ └── global.go ├── go.mod ├── go.sum ├── main.go ├── model │ ├── base.go │ ├── gost_auth.go │ ├── gost_client.go │ ├── gost_client_admission.go │ ├── gost_client_config.go │ ├── gost_client_forward.go │ ├── gost_client_host.go │ ├── gost_client_host_domain.go │ ├── gost_client_logger.go │ ├── gost_client_p2p.go │ ├── gost_client_proxy.go │ ├── gost_client_tunnel.go │ ├── gost_node.go │ ├── gost_node_bind.go │ ├── gost_node_config.go │ ├── gost_node_domain.go │ ├── gost_node_logger.go │ ├── gost_node_port.go │ ├── gost_obs.go │ ├── system_config.go │ ├── system_notice.go │ ├── system_user.go │ ├── system_user_checkin.go │ └── system_user_email.go ├── pkg │ ├── bean │ │ ├── page.go │ │ └── response.go │ ├── captcha │ │ ├── default.ttf │ │ └── generate.go │ ├── email │ │ └── email.go │ ├── env │ │ └── get.go │ ├── jwt │ │ └── tool.go │ ├── logger │ │ └── logger.go │ ├── memory │ │ ├── interface.go │ │ ├── local │ │ │ └── local.go │ │ └── redis │ │ │ └── redis.go │ ├── middleware │ │ └── logger.go │ ├── orm │ │ ├── interface.go │ │ ├── mysql │ │ │ └── mysql.go │ │ └── sqlite │ │ │ └── sqlite.go │ ├── p2p_cfg │ │ └── v1 │ │ │ ├── client.go │ │ │ ├── common.go │ │ │ ├── proxy.go │ │ │ ├── server.go │ │ │ └── visitor.go │ ├── signal │ │ └── free.go │ └── utils │ │ ├── calc.go │ │ ├── date.go │ │ ├── md5.go │ │ ├── other.go │ │ ├── random.go │ │ ├── regexp.go │ │ ├── timeout.go │ │ └── to_type.go ├── repository │ ├── query │ │ ├── gen.go │ │ ├── gost_auth.gen.go │ │ ├── gost_client.gen.go │ │ ├── gost_client_admission.gen.go │ │ ├── gost_client_config.gen.go │ │ ├── gost_client_forward.gen.go │ │ ├── gost_client_host.gen.go │ │ ├── gost_client_host_domain.gen.go │ │ ├── gost_client_logger.gen.go │ │ ├── gost_client_p2_p.gen.go │ │ ├── gost_client_proxy.gen.go │ │ ├── gost_client_tunnel.gen.go │ │ ├── gost_node.gen.go │ │ ├── gost_node_bind.gen.go │ │ ├── gost_node_config.gen.go │ │ ├── gost_node_domain.gen.go │ │ ├── gost_node_logger.gen.go │ │ ├── gost_node_port.gen.go │ │ ├── gost_obs.gen.go │ │ ├── system_config.gen.go │ │ ├── system_notice.gen.go │ │ ├── system_user.gen.go │ │ ├── system_user_checkin.gen.go │ │ └── system_user_email.gen.go │ └── repository.go ├── router │ ├── admin │ │ ├── dashboard.go │ │ ├── gost_client.go │ │ ├── gost_client_forward.go │ │ ├── gost_client_host.go │ │ ├── gost_client_logger.go │ │ ├── gost_client_p2p.go │ │ ├── gost_client_proxy.go │ │ ├── gost_client_tunnel.go │ │ ├── gost_config.go │ │ ├── gost_node.go │ │ ├── gost_node_bind.go │ │ ├── gost_node_logger.go │ │ ├── gost_node_rule.go │ │ ├── system_config.go │ │ ├── system_notice.go │ │ └── system_user.go │ ├── auth │ │ └── auth.go │ ├── middleware │ │ ├── auth.go │ │ ├── auth_admin.go │ │ └── limiter.go │ ├── normal │ │ ├── gost.go │ │ ├── gost_client.go │ │ ├── gost_client_forward.go │ │ ├── gost_client_host.go │ │ ├── gost_client_logger.go │ │ ├── gost_client_p2p.go │ │ ├── gost_client_proxy.go │ │ ├── gost_client_tunnel.go │ │ ├── gost_node.go │ │ ├── gost_obs.go │ │ └── system_notice.go │ ├── public │ │ ├── gost.go │ │ ├── p2p.go │ │ └── system_config.go │ ├── router.go │ └── static.go ├── service │ ├── admin │ │ ├── dashboard │ │ │ ├── service.count.go │ │ │ ├── service.go │ │ │ ├── service.node.obs.date.go │ │ │ ├── service.node.obs.go │ │ │ ├── service.user.obs.date.go │ │ │ └── service.user.obs.go │ │ ├── gost_client │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ └── service.page.go │ │ ├── gost_client_forward │ │ │ ├── service.config.go │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_client_host │ │ │ ├── service.config.go │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_client_logger │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_client_p2p │ │ │ ├── service.config.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_client_proxy │ │ │ ├── service.config.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_client_tunnel │ │ │ ├── service.config.go │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_node │ │ │ ├── service.clean.port.go │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.page.go │ │ │ ├── service.query.go │ │ │ └── service.update.go │ │ ├── gost_node_bind │ │ │ ├── service.go │ │ │ └── service.update.go │ │ ├── gost_node_config │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.page.go │ │ │ └── service.update.go │ │ ├── gost_node_logger │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_node_rule │ │ │ ├── service.go │ │ │ └── service.list.go │ │ ├── system_config │ │ │ ├── service.base.go │ │ │ ├── service.email.go │ │ │ ├── service.email.verify.go │ │ │ ├── service.go │ │ │ ├── service.gost.go │ │ │ └── service.query.go │ │ ├── system_notice │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.page.go │ │ │ └── service.update.go │ │ └── system_user │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.page.go │ │ │ └── service.update.go │ ├── auth │ │ ├── service.bind.email.go │ │ ├── service.captcha.go │ │ ├── service.checkin.go │ │ ├── service.close.otp.go │ │ ├── service.gen.bind.email.code.go │ │ ├── service.gen.otp.go │ │ ├── service.gen.reset.pwd.email.code.go │ │ ├── service.go │ │ ├── service.login.go │ │ ├── service.login.otp.go │ │ ├── service.open.otp.go │ │ ├── service.password.go │ │ ├── service.register.go │ │ ├── service.renew.go │ │ ├── service.reset.pwd.go │ │ ├── service.unbind.email.go │ │ └── service.userinfo.go │ ├── common │ │ ├── cache │ │ │ ├── auth.go │ │ │ ├── bind.email.go │ │ │ ├── bind.opt.go │ │ │ ├── captcha.go │ │ │ ├── client.go │ │ │ ├── config.go │ │ │ ├── ingress.go │ │ │ ├── ip.security.go │ │ │ ├── login.opt.go │ │ │ ├── node.go │ │ │ ├── obs.go │ │ │ └── tunnel.go │ │ ├── node_port │ │ │ └── port.go │ │ ├── node_rule │ │ │ ├── default.rule.go │ │ │ ├── interface.go │ │ │ ├── user.level.rule.go │ │ │ └── user.qq.group.rule.go │ │ └── warn_msg │ │ │ └── warn_msg.go │ ├── gost_engine │ │ ├── cmd.client.go │ │ ├── cmd.node.go │ │ ├── event.client.go │ │ ├── event.node.go │ │ ├── ws.engine.go │ │ └── ws.message.go │ ├── normal │ │ ├── gost │ │ │ ├── service.go │ │ │ └── service.info.go │ │ ├── gost_client │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.page.go │ │ │ └── service.update.go │ │ ├── gost_client_forward │ │ │ ├── service.admission.go │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.enable.go │ │ │ ├── service.go │ │ │ ├── service.matcher.go │ │ │ ├── service.page.go │ │ │ ├── service.renew.go │ │ │ └── service.update.go │ │ ├── gost_client_host │ │ │ ├── service.admission.go │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.domain.go │ │ │ ├── service.enable.go │ │ │ ├── service.go │ │ │ ├── service.page.go │ │ │ ├── service.renew.go │ │ │ └── service.update.go │ │ ├── gost_client_logger │ │ │ ├── service.go │ │ │ └── service.page.go │ │ ├── gost_client_p2p │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.enable.go │ │ │ ├── service.go │ │ │ ├── service.page.go │ │ │ ├── service.renew.go │ │ │ └── service.update.go │ │ ├── gost_client_proxy │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.enable.go │ │ │ ├── service.go │ │ │ ├── service.page.go │ │ │ ├── service.renew.go │ │ │ └── service.update.go │ │ ├── gost_client_tunnel │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.enable.go │ │ │ ├── service.go │ │ │ ├── service.page.go │ │ │ ├── service.renew.go │ │ │ └── service.update.go │ │ ├── gost_node │ │ │ ├── service.clean.port.go │ │ │ ├── service.create.go │ │ │ ├── service.delete.go │ │ │ ├── service.go │ │ │ ├── service.list.go │ │ │ ├── service.page.go │ │ │ └── service.update.go │ │ ├── gost_obs │ │ │ ├── service.client.month.go │ │ │ ├── service.go │ │ │ ├── service.node.month.go │ │ │ ├── service.tunnel.month.go │ │ │ └── service.user.month.go │ │ └── system_notice │ │ │ ├── service.go │ │ │ └── service.list.go │ └── public │ │ ├── gost │ │ ├── service.admission.go │ │ ├── service.auther.go │ │ ├── service.client.port.go │ │ ├── service.client.ws.go │ │ ├── service.go │ │ ├── service.ingress.go │ │ ├── service.limiter.go │ │ ├── service.node.port.go │ │ ├── service.node.ws.go │ │ ├── service.obs.go │ │ └── service.visit.go │ │ ├── p2p │ │ ├── service.go │ │ ├── service.new.go │ │ └── service.visit.go │ │ └── system_config │ │ ├── service.go │ │ └── service.query.go ├── task │ ├── gost_client_logger.go │ ├── gost_node_logger.go │ ├── gost_obs.go │ └── task.go ├── todo │ ├── fix.go │ ├── gost_client.go │ ├── gost_client_logger.go │ ├── gost_node_logger.go │ ├── gost_node_port.go │ ├── gost_obs.go │ ├── gost_obs_write_back.go │ ├── system_config.go │ ├── system_user.go │ └── todo.go └── web │ ├── dist.zip │ └── static.go └── web ├── index.html ├── package-lock.json ├── package.json ├── src ├── App.vue ├── api │ ├── admin │ │ ├── dashboard.js │ │ ├── gost_client.js │ │ ├── gost_client_forward.js │ │ ├── gost_client_host.js │ │ ├── gost_client_logger.js │ │ ├── gost_client_p2p.js │ │ ├── gost_client_proxy.js │ │ ├── gost_client_tunnel.js │ │ ├── gost_node.js │ │ ├── gost_node_bind.js │ │ ├── gost_node_config.js │ │ ├── gost_node_logger.js │ │ ├── gost_node_rule.js │ │ ├── system_config.js │ │ ├── system_notice.js │ │ └── system_user.js │ ├── auth │ │ └── index.js │ ├── normal │ │ ├── gost.js │ │ ├── gost_client.js │ │ ├── gost_client_forward.js │ │ ├── gost_client_host.js │ │ ├── gost_client_logger.js │ │ ├── gost_client_p2p.js │ │ ├── gost_client_proxy.js │ │ ├── gost_client_tunnel.js │ │ ├── gost_node.js │ │ ├── gost_node_config.js │ │ ├── gost_obs.js │ │ └── system_notice.js │ └── public │ │ └── index.js ├── components │ ├── DatePicker.vue │ ├── DialogApi.vue │ ├── Drawer.vue │ ├── Empty.vue │ ├── Loading.vue │ ├── MessageApi.vue │ ├── Modal.vue │ ├── ModalApi.vue │ ├── Obs.vue │ └── Slide.vue ├── icon │ ├── alert.vue │ └── online.vue ├── layout │ ├── components │ │ ├── AppCard.vue │ │ ├── Header.vue │ │ ├── Layout.vue │ │ ├── Menu.vue │ │ ├── MenuCollapsed.vue │ │ ├── MenuIconSvg.vue │ │ ├── SearchCard.vue │ │ ├── SearchItem.vue │ │ └── SideMenu.vue │ ├── empty │ │ └── index.vue │ ├── mobile │ │ └── index.vue │ └── simple │ │ └── index.vue ├── main.js ├── request │ └── index.js ├── router │ ├── index.js │ └── routers │ │ ├── admin.js │ │ ├── base.js │ │ └── normal.js ├── setting.js ├── store │ ├── app.js │ ├── index.js │ └── local.js ├── style.css ├── utils │ ├── browser.js │ ├── copy.js │ ├── flow.js │ ├── formDataRule.js │ ├── func.js │ ├── menu.js │ ├── random.js │ ├── regexp.js │ └── routerToMenu.js └── views │ ├── admin │ ├── dashboard │ │ └── index.vue │ ├── gost_client │ │ └── index.vue │ ├── gost_client_forward │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_host │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_logger │ │ ├── index.vue │ │ └── tableColumns.js │ ├── gost_client_p2p │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_proxy │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_tunnel │ │ ├── index.js │ │ └── index.vue │ ├── gost_node │ │ ├── index.vue │ │ └── protocols.js │ ├── gost_node_config │ │ ├── index.js │ │ └── index.vue │ ├── gost_node_logger │ │ ├── index.vue │ │ └── tableColumns.js │ ├── system_config_base │ │ └── index.vue │ ├── system_config_email │ │ └── index.vue │ ├── system_config_gost │ │ └── index.vue │ ├── system_notice │ │ ├── index.vue │ │ └── tableColumns.js │ └── system_user │ │ ├── index.vue │ │ └── tableColumns.js │ ├── dashboard │ └── index.vue │ ├── login │ └── index.vue │ ├── normal │ ├── gost_client │ │ └── index.vue │ ├── gost_client_forward │ │ ├── create.vue │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_host │ │ ├── create.vue │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_logger │ │ ├── index.vue │ │ └── tableColumns.js │ ├── gost_client_p2p │ │ ├── create.vue │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_proxy │ │ ├── create.vue │ │ ├── index.js │ │ └── index.vue │ ├── gost_client_tunnel │ │ ├── create.vue │ │ ├── index.js │ │ └── index.vue │ ├── gost_node │ │ ├── index.vue │ │ └── protocols.js │ ├── gost_node_config │ │ ├── index.js │ │ └── index.vue │ └── gost_obs │ │ ├── client_index.vue │ │ ├── node_index.vue │ │ └── tunnel_index.vue │ ├── public │ ├── 403.vue │ └── 404.vue │ └── resetPassword │ └── index.vue └── vite.config.js /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | /server/data 3 | /server/dist 4 | /client/dist 5 | /proxy/dist 6 | /proxy/data 7 | /web/dist 8 | /web/node_modules 9 | *.aar 10 | *.jar 11 | /client/webui/node_modules 12 | /client/webui/dist 13 | /client/webui/dist.zip -------------------------------------------------------------------------------- /client/cli/service_option/option.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !windows 2 | 3 | package service_option 4 | 5 | import ( 6 | "github.com/kardianos/service" 7 | ) 8 | 9 | func MakeOptions() service.KeyValue { 10 | return service.KeyValue{ 11 | "OnFailureDelayDuration": "10", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/cli/service_option/option_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package service_option 4 | 5 | import ( 6 | "github.com/kardianos/service" 7 | ) 8 | 9 | func MakeOptions() service.KeyValue { 10 | return service.KeyValue{ 11 | "OnFailureDelayDuration": "10", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/gui/global/config.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "errors" 5 | "gopkg.in/yaml.v2" 6 | "os" 7 | ) 8 | 9 | var Config config 10 | 11 | type config struct { 12 | Address string `yaml:"address"` 13 | } 14 | 15 | func LoadConfig(path string) error { 16 | stat, err := os.Stat(path) 17 | if err != nil { 18 | Config = config{ 19 | Address: "0.0.0.0:18080", 20 | } 21 | marshal, _ := yaml.Marshal(Config) 22 | if err := os.WriteFile(path, marshal, 0644); err != nil { 23 | return err 24 | } 25 | return nil 26 | } 27 | if stat.IsDir() { 28 | return errors.New(path + " is not file") 29 | } 30 | file, err := os.ReadFile(path) 31 | if err != nil { 32 | return err 33 | } 34 | return yaml.Unmarshal(file, &Config) 35 | } 36 | 37 | func SaveConfig(path string) error { 38 | marshal, _ := yaml.Marshal(Config) 39 | return os.WriteFile(path, marshal, 0644) 40 | } 41 | -------------------------------------------------------------------------------- /client/gui/global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "os" 5 | "path/filepath" 6 | ) 7 | 8 | var ( 9 | BasePath, _ = os.Executable() 10 | ) 11 | 12 | func init() { 13 | BasePath, _ = os.Executable() 14 | BasePath = filepath.Dir(BasePath) 15 | } 16 | -------------------------------------------------------------------------------- /client/gui/rsrc_windows_386.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/client/gui/rsrc_windows_386.syso -------------------------------------------------------------------------------- /client/gui/rsrc_windows_amd64.syso: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/client/gui/rsrc_windows_amd64.syso -------------------------------------------------------------------------------- /client/gui/state.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | var WindowShow bool 4 | -------------------------------------------------------------------------------- /client/gui/static/logo.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/client/gui/static/logo.ico -------------------------------------------------------------------------------- /client/gui/winres/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/client/gui/winres/icon.png -------------------------------------------------------------------------------- /client/gui/winres/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/client/gui/winres/icon16.png -------------------------------------------------------------------------------- /client/internal/common/dns.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "context" 5 | "net" 6 | "time" 7 | ) 8 | 9 | func init() { 10 | fixDNSResolver() 11 | } 12 | 13 | func fixDNSResolver() { 14 | if net.DefaultResolver != nil { 15 | timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second) 16 | defer cancel() 17 | _, err := net.DefaultResolver.LookupHost(timeoutCtx, "google.com") 18 | if err == nil { 19 | return 20 | } 21 | } 22 | net.DefaultResolver = &net.Resolver{ 23 | PreferGo: true, 24 | Dial: func(ctx context.Context, network, addr string) (net.Conn, error) { 25 | if addr == "127.0.0.1:53" || addr == "[::1]:53" { 26 | addr = "8.8.8.8:53" 27 | } 28 | var d net.Dialer 29 | return d.DialContext(ctx, network, addr) 30 | }, 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/internal/common/log_writer.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | type LogWriter struct { 4 | } 5 | 6 | func NewLogWriter() *LogWriter { 7 | return &LogWriter{} 8 | } 9 | 10 | func (l *LogWriter) Write(p []byte) (n int, err error) { 11 | Logger.AddLog("detail", string(p)) 12 | return len(p), nil 13 | } 14 | -------------------------------------------------------------------------------- /client/internal/common/state.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | import ( 4 | "sync" 5 | ) 6 | 7 | var State = RunningState{ 8 | mu: &sync.RWMutex{}, 9 | data: make(map[string]bool), 10 | } 11 | 12 | type RunningState struct { 13 | mu *sync.RWMutex 14 | data map[string]bool 15 | } 16 | 17 | func (state *RunningState) Set(key string, running bool) { 18 | state.mu.Lock() 19 | defer state.mu.Unlock() 20 | state.data[key] = running 21 | } 22 | 23 | func (state *RunningState) Get(key string) bool { 24 | state.mu.RLock() 25 | defer state.mu.RUnlock() 26 | return state.data[key] 27 | } 28 | -------------------------------------------------------------------------------- /client/internal/common/url.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | func GenerateWsUrl(tls bool, address string) string { 4 | var scheme string 5 | if tls { 6 | scheme = "wss" 7 | } else { 8 | scheme = "ws" 9 | } 10 | return scheme + "://" + address 11 | } 12 | 13 | func GenerateHttpUrl(tls bool, address string) string { 14 | var scheme string 15 | if tls { 16 | scheme = "https" 17 | } else { 18 | scheme = "http" 19 | } 20 | return scheme + "://" + address 21 | } 22 | -------------------------------------------------------------------------------- /client/internal/common/version.go: -------------------------------------------------------------------------------- 1 | package common 2 | 3 | var ( 4 | VERSION = "v1.2.8" // 服务端版本 5 | ) 6 | -------------------------------------------------------------------------------- /client/internal/engine/ws_p2p_config.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "gostc-sub/pkg/p2p/frpc" 5 | v1 "gostc-sub/pkg/p2p/pkg/config/v1" 6 | "gostc-sub/pkg/p2p/registry" 7 | ) 8 | 9 | type ClientP2PConfigData struct { 10 | Code string 11 | Common v1.ClientCommonConfig 12 | STCPCfg v1.STCPProxyConfig 13 | XTCPCfg v1.XTCPProxyConfig 14 | } 15 | 16 | func (e *Event) WsP2PConfig(data ClientP2PConfigData) { 17 | registry.Del(data.Code) 18 | var proxys []v1.ProxyConfigurer 19 | if data.STCPCfg.Name != "" { 20 | proxys = append(proxys, &data.STCPCfg) 21 | } 22 | if data.XTCPCfg.Name != "" { 23 | proxys = append(proxys, &data.XTCPCfg) 24 | } 25 | svc := frpc.NewService(data.Common, proxys, nil) 26 | if err := svc.Start(); err == nil { 27 | _ = registry.Set(data.Code, svc) 28 | e.svcMap.Store(data.Code, true) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /client/internal/engine/ws_port_check.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "gostc-sub/pkg/utils" 5 | "strconv" 6 | ) 7 | 8 | type PortCheckResp struct { 9 | Code string `json:"code"` // 节点编号 10 | Use bool `json:"use"` // 是否被占用 11 | Port string `json:"port"` // 端口 12 | } 13 | 14 | func (e *Event) WsPortCheck(data map[string]string) PortCheckResp { 15 | port, _ := strconv.Atoi(data["port"]) 16 | var result = PortCheckResp{ 17 | Code: data["code"], 18 | Use: true, 19 | Port: data["port"], 20 | } 21 | if port != 0 { 22 | result = PortCheckResp{ 23 | Code: data["code"], 24 | Use: utils.TrinaryOperation(utils.IsUse("0.0.0.0", port) == nil, false, true), 25 | Port: data["port"], 26 | } 27 | } 28 | return result 29 | } 30 | -------------------------------------------------------------------------------- /client/internal/engine/ws_remove_config.go: -------------------------------------------------------------------------------- 1 | package engine 2 | 3 | import ( 4 | "github.com/go-gost/x/registry" 5 | registry2 "gostc-sub/pkg/p2p/registry" 6 | ) 7 | 8 | func (e *Event) WsRemoveConfig(names []string) { 9 | for _, name := range names { 10 | if svc := registry.ServiceRegistry().Get(name); svc != nil { 11 | _ = svc.Close() 12 | registry.ServiceRegistry().Unregister(name) 13 | } 14 | registry2.Del(name) 15 | e.svcMap.Store(name, true) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /client/internal/service/interface.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type Service interface { 4 | Start() error 5 | Stop() 6 | IsRunning() bool 7 | } 8 | -------------------------------------------------------------------------------- /client/lib/build.bat: -------------------------------------------------------------------------------- 1 | @REM set ANDROID_HOME=C:/Sdk/android-sdk 2 | set ANDROID_HOME=D:/sdk 3 | @REM set ANDROID_NDK_HOME=C:/Sdk/android-sdk/ndk/25.1.8937393 4 | set ANDROID_NDK_HOME=D:/sdk/ndk/ndk-bundle 5 | gomobile bind -ldflags "-s -w" -v -androidapi 21 "gostc-sub/lib" -------------------------------------------------------------------------------- /client/pkg/env/get.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | ) 7 | 8 | // GetEnv 获取环境变量 9 | func GetEnv[T any](key string, defaultValue T) T { 10 | var result T 11 | strValue, exists := os.LookupEnv(key) 12 | if !exists { 13 | return defaultValue 14 | } 15 | 16 | switch any(result).(type) { 17 | case string: 18 | return any(strValue).(T) 19 | case int: 20 | if v, err := strconv.Atoi(strValue); err == nil { 21 | return any(v).(T) 22 | } 23 | case bool: 24 | if v, err := strconv.ParseBool(strValue); err == nil { 25 | return any(v).(T) 26 | } 27 | case float64: 28 | if v, err := strconv.ParseFloat(strValue, 64); err == nil { 29 | return any(v).(T) 30 | } 31 | } 32 | 33 | return defaultValue 34 | } 35 | -------------------------------------------------------------------------------- /client/pkg/gost-x/README.md: -------------------------------------------------------------------------------- 1 | # The extended (may be experimental) features outside the main gost tree. -------------------------------------------------------------------------------- /client/pkg/gost-x/api/doc.go: -------------------------------------------------------------------------------- 1 | // Documentation of Web API. 2 | // 3 | // Schemes: https, http 4 | // BasePath: / 5 | // Version: 1.0.0 6 | // 7 | // Consumes: 8 | // - application/json 9 | // 10 | // Produces: 11 | // - application/json 12 | // 13 | // SecurityDefinitions: 14 | // basicAuth: 15 | // type: basic 16 | // 17 | // swagger:meta 18 | package api 19 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/direct/conn.go: -------------------------------------------------------------------------------- 1 | package direct 2 | 3 | import ( 4 | "io" 5 | "net" 6 | "time" 7 | ) 8 | 9 | type conn struct{} 10 | 11 | func (c *conn) Close() error { 12 | return nil 13 | } 14 | 15 | func (c *conn) Read(b []byte) (n int, err error) { 16 | return 0, io.EOF 17 | } 18 | 19 | func (c *conn) Write(b []byte) (n int, err error) { 20 | return 0, io.ErrClosedPipe 21 | } 22 | 23 | func (c *conn) LocalAddr() net.Addr { 24 | return &net.TCPAddr{} 25 | } 26 | 27 | func (c *conn) RemoteAddr() net.Addr { 28 | return &net.TCPAddr{} 29 | } 30 | 31 | func (c *conn) SetDeadline(t time.Time) error { 32 | return nil 33 | } 34 | 35 | func (c *conn) SetReadDeadline(t time.Time) error { 36 | return nil 37 | } 38 | 39 | func (c *conn) SetWriteDeadline(t time.Time) error { 40 | return nil 41 | } 42 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/direct/metadata.go: -------------------------------------------------------------------------------- 1 | package direct 2 | 3 | import ( 4 | "strings" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | action string 12 | } 13 | 14 | func (c *directConnector) parseMetadata(md mdata.Metadata) (err error) { 15 | c.md.action = strings.ToLower(mdutil.GetString(md, "action")) 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/http/metadata.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | mdata "github.com/go-gost/core/metadata" 8 | mdutil "github.com/go-gost/x/metadata/util" 9 | ) 10 | 11 | type metadata struct { 12 | connectTimeout time.Duration 13 | header http.Header 14 | } 15 | 16 | func (c *httpConnector) parseMetadata(md mdata.Metadata) (err error) { 17 | const ( 18 | connectTimeout = "timeout" 19 | header = "header" 20 | ) 21 | 22 | c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout) 23 | 24 | if mm := mdutil.GetStringMapString(md, header); len(mm) > 0 { 25 | hd := http.Header{} 26 | for k, v := range mm { 27 | hd.Add(k, v) 28 | } 29 | c.md.header = hd 30 | } 31 | 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/http2/metadata.go: -------------------------------------------------------------------------------- 1 | package http2 2 | 3 | import ( 4 | "net/http" 5 | "time" 6 | 7 | mdata "github.com/go-gost/core/metadata" 8 | mdutil "github.com/go-gost/x/metadata/util" 9 | ) 10 | 11 | type metadata struct { 12 | connectTimeout time.Duration 13 | header http.Header 14 | } 15 | 16 | func (c *http2Connector) parseMetadata(md mdata.Metadata) (err error) { 17 | const ( 18 | connectTimeout = "timeout" 19 | header = "header" 20 | ) 21 | 22 | c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout) 23 | if mm := mdutil.GetStringMapString(md, header); len(mm) > 0 { 24 | hd := http.Header{} 25 | for k, v := range mm { 26 | hd.Add(k, v) 27 | } 28 | c.md.header = hd 29 | } 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/router/metadata.go: -------------------------------------------------------------------------------- 1 | package tunnel 2 | 3 | import ( 4 | "errors" 5 | "time" 6 | 7 | mdata "github.com/go-gost/core/metadata" 8 | "github.com/go-gost/relay" 9 | mdutil "github.com/go-gost/x/metadata/util" 10 | "github.com/google/uuid" 11 | ) 12 | 13 | var ( 14 | ErrInvalidRouterID = errors.New("router: invalid router ID") 15 | ) 16 | 17 | type metadata struct { 18 | connectTimeout time.Duration 19 | routerID relay.TunnelID 20 | } 21 | 22 | func (c *routerConnector) parseMetadata(md mdata.Metadata) (err error) { 23 | c.md.connectTimeout = mdutil.GetDuration(md, "connectTimeout") 24 | 25 | if s := mdutil.GetString(md, "router.id"); s != "" { 26 | uuid, err := uuid.Parse(s) 27 | if err != nil { 28 | return err 29 | } 30 | c.md.routerID = relay.NewTunnelID(uuid[:]) 31 | } 32 | 33 | return 34 | } 35 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/sni/metadata.go: -------------------------------------------------------------------------------- 1 | package sni 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | host string 12 | connectTimeout time.Duration 13 | } 14 | 15 | func (c *sniConnector) parseMetadata(md mdata.Metadata) (err error) { 16 | const ( 17 | host = "host" 18 | connectTimeout = "timeout" 19 | ) 20 | 21 | c.md.host = mdutil.GetString(md, host) 22 | c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout) 23 | 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/socks/v4/metadata.go: -------------------------------------------------------------------------------- 1 | package v4 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | connectTimeout time.Duration 12 | disable4a bool 13 | } 14 | 15 | func (c *socks4Connector) parseMetadata(md mdata.Metadata) (err error) { 16 | const ( 17 | connectTimeout = "timeout" 18 | disable4a = "disable4a" 19 | ) 20 | 21 | c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout) 22 | c.md.disable4a = mdutil.GetBool(md, disable4a) 23 | 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/ss/metadata.go: -------------------------------------------------------------------------------- 1 | package ss 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | key string 12 | connectTimeout time.Duration 13 | noDelay bool 14 | } 15 | 16 | func (c *ssConnector) parseMetadata(md mdata.Metadata) (err error) { 17 | const ( 18 | key = "key" 19 | connectTimeout = "timeout" 20 | noDelay = "nodelay" 21 | ) 22 | 23 | c.md.key = mdutil.GetString(md, key) 24 | c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout) 25 | c.md.noDelay = mdutil.GetBool(md, noDelay) 26 | 27 | return 28 | } 29 | -------------------------------------------------------------------------------- /client/pkg/gost-x/connector/ss/udp/metadata.go: -------------------------------------------------------------------------------- 1 | package ss 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | key string 12 | connectTimeout time.Duration 13 | } 14 | 15 | func (c *ssuConnector) parseMetadata(md mdata.Metadata) (err error) { 16 | const ( 17 | key = "key" 18 | connectTimeout = "timeout" 19 | ) 20 | 21 | c.md.key = mdutil.GetString(md, key) 22 | c.md.connectTimeout = mdutil.GetDuration(md, connectTimeout) 23 | 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/dtls/metadata.go: -------------------------------------------------------------------------------- 1 | package dtls 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | const ( 11 | defaultBufferSize = 1200 12 | ) 13 | 14 | type metadata struct { 15 | mtu int 16 | bufferSize int 17 | flightInterval time.Duration 18 | } 19 | 20 | func (d *dtlsDialer) parseMetadata(md mdata.Metadata) (err error) { 21 | d.md.mtu = mdutil.GetInt(md, "dtls.mtu", "mtu") 22 | d.md.bufferSize = mdutil.GetInt(md, "dtls.bufferSize", "bufferSize") 23 | if d.md.bufferSize <= 0 { 24 | d.md.bufferSize = defaultBufferSize 25 | } 26 | d.md.flightInterval = mdutil.GetDuration(md, "dtls.flightInterval", "flightInterval") 27 | return 28 | } 29 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/ftcp/metadata.go: -------------------------------------------------------------------------------- 1 | package ftcp 2 | 3 | import ( 4 | md "github.com/go-gost/core/metadata" 5 | ) 6 | 7 | type metadata struct{} 8 | 9 | func (d *ftcpDialer) parseMetadata(md md.Metadata) (err error) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/http2/h2/metadata.go: -------------------------------------------------------------------------------- 1 | package h2 2 | 3 | import ( 4 | "net/http" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | host string 12 | path string 13 | header http.Header 14 | } 15 | 16 | func (d *h2Dialer) parseMetadata(md mdata.Metadata) (err error) { 17 | const ( 18 | host = "host" 19 | path = "path" 20 | header = "header" 21 | ) 22 | 23 | d.md.host = mdutil.GetString(md, host) 24 | d.md.path = mdutil.GetString(md, path) 25 | if m := mdutil.GetStringMapString(md, header); len(m) > 0 { 26 | h := http.Header{} 27 | for k, v := range m { 28 | h.Add(k, v) 29 | } 30 | d.md.header = h 31 | } 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/http2/metadata.go: -------------------------------------------------------------------------------- 1 | package http2 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | ) 6 | 7 | type metadata struct{} 8 | 9 | func (d *http2Dialer) parseMetadata(md mdata.Metadata) (err error) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/icmp/metadata.go: -------------------------------------------------------------------------------- 1 | package quic 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | keepAlivePeriod time.Duration 12 | maxIdleTimeout time.Duration 13 | handshakeTimeout time.Duration 14 | } 15 | 16 | func (d *icmpDialer) parseMetadata(md mdata.Metadata) (err error) { 17 | if mdutil.GetBool(md, "keepalive") { 18 | d.md.keepAlivePeriod = mdutil.GetDuration(md, "ttl") 19 | if d.md.keepAlivePeriod <= 0 { 20 | d.md.keepAlivePeriod = 10 * time.Second 21 | } 22 | } 23 | d.md.handshakeTimeout = mdutil.GetDuration(md, "handshakeTimeout") 24 | d.md.maxIdleTimeout = mdutil.GetDuration(md, "maxIdleTimeout") 25 | 26 | return 27 | } 28 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/mtcp/conn.go: -------------------------------------------------------------------------------- 1 | package mtcp 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/go-gost/x/internal/util/mux" 7 | ) 8 | 9 | type muxSession struct { 10 | conn net.Conn 11 | session *mux.Session 12 | } 13 | 14 | func (session *muxSession) GetConn() (net.Conn, error) { 15 | return session.session.GetConn() 16 | } 17 | 18 | func (session *muxSession) Accept() (net.Conn, error) { 19 | return session.session.Accept() 20 | } 21 | 22 | func (session *muxSession) Close() error { 23 | if session.session == nil { 24 | return nil 25 | } 26 | return session.session.Close() 27 | } 28 | 29 | func (session *muxSession) IsClosed() bool { 30 | if session.session == nil { 31 | return true 32 | } 33 | return session.session.IsClosed() 34 | } 35 | 36 | func (session *muxSession) NumStreams() int { 37 | return session.session.NumStreams() 38 | } 39 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/mtls/conn.go: -------------------------------------------------------------------------------- 1 | package mtls 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/go-gost/x/internal/util/mux" 7 | ) 8 | 9 | type muxSession struct { 10 | conn net.Conn 11 | session *mux.Session 12 | } 13 | 14 | func (session *muxSession) GetConn() (net.Conn, error) { 15 | return session.session.GetConn() 16 | } 17 | 18 | func (session *muxSession) Accept() (net.Conn, error) { 19 | return session.session.Accept() 20 | } 21 | 22 | func (session *muxSession) Close() error { 23 | if session.session == nil { 24 | return nil 25 | } 26 | return session.session.Close() 27 | } 28 | 29 | func (session *muxSession) IsClosed() bool { 30 | if session.session == nil { 31 | return true 32 | } 33 | return session.session.IsClosed() 34 | } 35 | 36 | func (session *muxSession) NumStreams() int { 37 | return session.session.NumStreams() 38 | } 39 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/mws/conn.go: -------------------------------------------------------------------------------- 1 | package mws 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/go-gost/x/internal/util/mux" 7 | ) 8 | 9 | type muxSession struct { 10 | conn net.Conn 11 | session *mux.Session 12 | } 13 | 14 | func (session *muxSession) GetConn() (net.Conn, error) { 15 | return session.session.GetConn() 16 | } 17 | 18 | func (session *muxSession) Accept() (net.Conn, error) { 19 | return session.session.Accept() 20 | } 21 | 22 | func (session *muxSession) Close() error { 23 | if session.session == nil { 24 | return nil 25 | } 26 | return session.session.Close() 27 | } 28 | 29 | func (session *muxSession) IsClosed() bool { 30 | if session.session == nil { 31 | return true 32 | } 33 | return session.session.IsClosed() 34 | } 35 | 36 | func (session *muxSession) NumStreams() int { 37 | return session.session.NumStreams() 38 | } 39 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/obfs/http/metadata.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | const ( 11 | defaultPath = "/" 12 | ) 13 | 14 | type metadata struct { 15 | host string 16 | path string 17 | header http.Header 18 | } 19 | 20 | func (d *obfsHTTPDialer) parseMetadata(md mdata.Metadata) (err error) { 21 | d.md.host = mdutil.GetString(md, "obfs.host", "host") 22 | d.md.path = mdutil.GetString(md, "obfs.path", "path") 23 | if d.md.path == "" { 24 | d.md.path = defaultPath 25 | } 26 | 27 | if m := mdutil.GetStringMapString(md, "obfs.header", "header"); len(m) > 0 { 28 | h := http.Header{} 29 | for k, v := range m { 30 | h.Add(k, v) 31 | } 32 | d.md.header = h 33 | } 34 | return 35 | } 36 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/obfs/tls/metadata.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | type metadata struct { 9 | host string 10 | } 11 | 12 | func (d *obfsTLSDialer) parseMetadata(md mdata.Metadata) (err error) { 13 | const ( 14 | host = "host" 15 | ) 16 | 17 | d.md.host = mdutil.GetString(md, host) 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/serial/metadata.go: -------------------------------------------------------------------------------- 1 | package serial 2 | 3 | import ( 4 | md "github.com/go-gost/core/metadata" 5 | ) 6 | 7 | type metadata struct { 8 | } 9 | 10 | func (d *serialDialer) parseMetadata(md md.Metadata) (err error) { 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/tcp/metadata.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "time" 5 | 6 | md "github.com/go-gost/core/metadata" 7 | ) 8 | 9 | const ( 10 | dialTimeout = "dialTimeout" 11 | ) 12 | 13 | const ( 14 | defaultDialTimeout = 5 * time.Second 15 | ) 16 | 17 | type metadata struct { 18 | dialTimeout time.Duration 19 | } 20 | 21 | func (d *tcpDialer) parseMetadata(md md.Metadata) (err error) { 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/tls/metadata.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | handshakeTimeout time.Duration 12 | } 13 | 14 | func (d *tlsDialer) parseMetadata(md mdata.Metadata) (err error) { 15 | const ( 16 | handshakeTimeout = "handshakeTimeout" 17 | ) 18 | 19 | d.md.handshakeTimeout = mdutil.GetDuration(md, handshakeTimeout) 20 | 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/udp/conn.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import "net" 4 | 5 | type conn struct { 6 | *net.UDPConn 7 | } 8 | 9 | func (c *conn) WriteTo(b []byte, addr net.Addr) (int, error) { 10 | return c.UDPConn.Write(b) 11 | } 12 | 13 | func (c *conn) ReadFrom(b []byte) (n int, addr net.Addr, err error) { 14 | n, err = c.UDPConn.Read(b) 15 | addr = c.RemoteAddr() 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/udp/metadata.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import ( 4 | "time" 5 | 6 | md "github.com/go-gost/core/metadata" 7 | ) 8 | 9 | const ( 10 | dialTimeout = "dialTimeout" 11 | ) 12 | 13 | const ( 14 | defaultDialTimeout = 5 * time.Second 15 | ) 16 | 17 | type metadata struct { 18 | dialTimeout time.Duration 19 | } 20 | 21 | func (d *udpDialer) parseMetadata(md md.Metadata) (err error) { 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/unix/metadata.go: -------------------------------------------------------------------------------- 1 | package unix 2 | 3 | import ( 4 | md "github.com/go-gost/core/metadata" 5 | ) 6 | 7 | type metadata struct { 8 | } 9 | 10 | func (d *unixDialer) parseMetadata(md md.Metadata) (err error) { 11 | return 12 | } 13 | -------------------------------------------------------------------------------- /client/pkg/gost-x/dialer/wg/metadata.go: -------------------------------------------------------------------------------- 1 | package wg 2 | 3 | import ( 4 | "time" 5 | 6 | md "github.com/go-gost/core/metadata" 7 | ) 8 | 9 | const ( 10 | dialTimeout = "dialTimeout" 11 | ) 12 | 13 | const ( 14 | defaultDialTimeout = 5 * time.Second 15 | ) 16 | 17 | type metadata struct { 18 | dialTimeout time.Duration 19 | } 20 | 21 | func (d *wgDialer) parseMetadata(md md.Metadata) (err error) { 22 | return 23 | } 24 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/api/metadata.go: -------------------------------------------------------------------------------- 1 | package api 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | type metadata struct { 9 | accesslog bool 10 | pathPrefix string 11 | } 12 | 13 | func (h *apiHandler) parseMetadata(md mdata.Metadata) (err error) { 14 | h.md.accesslog = mdutil.GetBool(md, "api.accessLog", "accessLog") 15 | h.md.pathPrefix = mdutil.GetString(md, "api.pathPrefix", "pathPrefix") 16 | return 17 | } 18 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/file/metadata.go: -------------------------------------------------------------------------------- 1 | package file 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | type metadata struct { 9 | dir string 10 | } 11 | 12 | func (h *fileHandler) parseMetadata(md mdata.Metadata) (err error) { 13 | h.md.dir = mdutil.GetString(md, "file.dir", "dir") 14 | return 15 | } 16 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/http2/conn.go: -------------------------------------------------------------------------------- 1 | package http2 2 | 3 | import ( 4 | "errors" 5 | "io" 6 | "net/http" 7 | ) 8 | 9 | type flushWriter struct { 10 | w io.Writer 11 | } 12 | 13 | func (fw flushWriter) Write(p []byte) (n int, err error) { 14 | defer func() { 15 | if r := recover(); r != nil { 16 | if s, ok := r.(string); ok { 17 | err = errors.New(s) 18 | return 19 | } 20 | err = r.(error) 21 | } 22 | }() 23 | 24 | n, err = fw.w.Write(p) 25 | if err != nil { 26 | // log.Log("flush writer:", err) 27 | return 28 | } 29 | if f, ok := fw.w.(http.Flusher); ok { 30 | f.Flush() 31 | } 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/metrics/metadata.go: -------------------------------------------------------------------------------- 1 | package metrics 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | const ( 9 | DefaultPath = "/metrics" 10 | ) 11 | 12 | type metadata struct { 13 | path string 14 | } 15 | 16 | func (h *metricsHandler) parseMetadata(md mdata.Metadata) (err error) { 17 | h.md.path = mdutil.GetString(md, "metrics.path", "path") 18 | if h.md.path == "" { 19 | h.md.path = DefaultPath 20 | } 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/redirect/tcp/handler_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !darwin 2 | 3 | package redirect 4 | 5 | import ( 6 | "errors" 7 | "net" 8 | ) 9 | 10 | func (h *redirectHandler) getOriginalDstAddr(_ net.Conn) (addr net.Addr, err error) { 11 | err = errors.New("TCP redirect is not available on non-linux platform") 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/redirect/udp/metadata.go: -------------------------------------------------------------------------------- 1 | package redirect 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | ) 6 | 7 | type metadata struct{} 8 | 9 | func (h *redirectHandler) parseMetadata(md mdata.Metadata) (err error) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/serial/metadata.go: -------------------------------------------------------------------------------- 1 | package serial 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | const ( 11 | defaultPort = "COM1" 12 | defaultBaudRate = 9600 13 | ) 14 | 15 | type metadata struct { 16 | timeout time.Duration 17 | } 18 | 19 | func (h *serialHandler) parseMetadata(md mdata.Metadata) (err error) { 20 | h.md.timeout = mdutil.GetDuration(md, "timeout", "serial.timeout", "handler.serial.timeout") 21 | return 22 | } 23 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/ss/udp/metadata.go: -------------------------------------------------------------------------------- 1 | package ss 2 | 3 | import ( 4 | "math" 5 | "time" 6 | 7 | mdata "github.com/go-gost/core/metadata" 8 | mdutil "github.com/go-gost/x/metadata/util" 9 | ) 10 | 11 | const ( 12 | MaxMessageSize = math.MaxUint16 13 | ) 14 | 15 | type metadata struct { 16 | key string 17 | readTimeout time.Duration 18 | } 19 | 20 | func (h *ssuHandler) parseMetadata(md mdata.Metadata) (err error) { 21 | const ( 22 | key = "key" 23 | readTimeout = "readTimeout" 24 | ) 25 | 26 | h.md.key = mdutil.GetString(md, key) 27 | h.md.readTimeout = mdutil.GetDuration(md, readTimeout) 28 | 29 | return 30 | } 31 | -------------------------------------------------------------------------------- /client/pkg/gost-x/handler/tap/metadata.go: -------------------------------------------------------------------------------- 1 | package tap 2 | 3 | import ( 4 | "math" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | const ( 11 | MaxMessageSize = math.MaxUint16 12 | ) 13 | 14 | type metadata struct { 15 | key string 16 | } 17 | 18 | func (h *tapHandler) parseMetadata(md mdata.Metadata) (err error) { 19 | const ( 20 | key = "key" 21 | ) 22 | 23 | h.md.key = mdutil.GetString(md, key) 24 | return 25 | } 26 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/ctx/value.go: -------------------------------------------------------------------------------- 1 | package ctx 2 | 3 | import ( 4 | "context" 5 | 6 | xrecorder "github.com/go-gost/x/recorder" 7 | ) 8 | 9 | type recorderObjectCtxKey struct{} 10 | 11 | var ( 12 | ctxKeyRecorderObject = &recorderObjectCtxKey{} 13 | ) 14 | 15 | func ContextWithRecorderObject(ctx context.Context, ro *xrecorder.HandlerRecorderObject) context.Context { 16 | return context.WithValue(ctx, ctxKeyRecorderObject, ro) 17 | } 18 | 19 | func RecorderObjectFromContext(ctx context.Context) *xrecorder.HandlerRecorderObject { 20 | v, _ := ctx.Value(ctxKeyRecorderObject).(*xrecorder.HandlerRecorderObject) 21 | return v 22 | } 23 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/io/io.go: -------------------------------------------------------------------------------- 1 | package io 2 | 3 | import ( 4 | "io" 5 | "time" 6 | ) 7 | 8 | type readWriter struct { 9 | io.Reader 10 | io.Writer 11 | } 12 | 13 | func NewReadWriter(r io.Reader, w io.Writer) io.ReadWriter { 14 | return &readWriter{ 15 | Reader: r, 16 | Writer: w, 17 | } 18 | } 19 | 20 | type readWriteCloser struct { 21 | io.Reader 22 | io.Writer 23 | io.Closer 24 | } 25 | 26 | func NewReadWriteCloser(r io.Reader, w io.Writer, c io.Closer) io.ReadWriteCloser { 27 | return &readWriteCloser{ 28 | Reader: r, 29 | Writer: w, 30 | Closer: c, 31 | } 32 | } 33 | 34 | type setReadDeadline interface { 35 | SetReadDeadline(t time.Time) error 36 | } 37 | 38 | func SetReadDeadline(rw io.ReadWriter, t time.Time) error { 39 | if v, _ := rw.(setReadDeadline); v != nil { 40 | return v.SetReadDeadline(t) 41 | } 42 | return nil 43 | } 44 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/loader/loader.go: -------------------------------------------------------------------------------- 1 | package loader 2 | 3 | import ( 4 | "context" 5 | "io" 6 | ) 7 | 8 | type Loader interface { 9 | Load(context.Context) (io.Reader, error) 10 | Close() error 11 | } 12 | 13 | type Lister interface { 14 | List(ctx context.Context) ([]string, error) 15 | } 16 | 17 | type Mapper interface { 18 | Map(ctx context.Context) (map[string]string, error) 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/net/dialer/dialer_linux.go: -------------------------------------------------------------------------------- 1 | package dialer 2 | 3 | import ( 4 | "golang.org/x/sys/unix" 5 | ) 6 | 7 | func bindDevice(fd uintptr, ifceName string) error { 8 | if ifceName == "" { 9 | return nil 10 | } 11 | return unix.BindToDevice(int(fd), ifceName) 12 | } 13 | 14 | func setMark(fd uintptr, mark int) error { 15 | if mark == 0 { 16 | return nil 17 | } 18 | return unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_MARK, mark) 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/net/dialer/dialer_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package dialer 4 | 5 | func bindDevice(fd uintptr, ifceName string) error { 6 | return nil 7 | } 8 | 9 | func setMark(fd uintptr, mark int) error { 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/net/ip/ip.go: -------------------------------------------------------------------------------- 1 | package ip 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/songgao/water/waterutil" 7 | ) 8 | 9 | var prots = map[waterutil.IPProtocol]string{ 10 | waterutil.HOPOPT: "HOPOPT", 11 | waterutil.ICMP: "ICMP", 12 | waterutil.IGMP: "IGMP", 13 | waterutil.GGP: "GGP", 14 | waterutil.TCP: "TCP", 15 | waterutil.UDP: "UDP", 16 | waterutil.IPv6_Route: "IPv6-Route", 17 | waterutil.IPv6_Frag: "IPv6-Frag", 18 | waterutil.IPv6_ICMP: "IPv6-ICMP", 19 | } 20 | 21 | func Protocol(p waterutil.IPProtocol) string { 22 | if v, ok := prots[p]; ok { 23 | return v 24 | } 25 | return fmt.Sprintf("unknown(%d)", p) 26 | } 27 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/net/proxyproto/conn.go: -------------------------------------------------------------------------------- 1 | package proxyproto 2 | 3 | import ( 4 | "net" 5 | 6 | proxyproto "github.com/pires/go-proxyproto" 7 | ) 8 | 9 | func WrapClientConn(ppv int, src, dst net.Addr, c net.Conn) net.Conn { 10 | if ppv <= 0 { 11 | return c 12 | } 13 | 14 | header := proxyproto.HeaderProxyFromAddrs(byte(ppv), src, dst) 15 | header.WriteTo(c) 16 | return c 17 | } 18 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/net/proxyproto/listener.go: -------------------------------------------------------------------------------- 1 | package proxyproto 2 | 3 | import ( 4 | "net" 5 | "time" 6 | 7 | proxyproto "github.com/pires/go-proxyproto" 8 | ) 9 | 10 | func WrapListener(ppv int, ln net.Listener, readHeaderTimeout time.Duration) net.Listener { 11 | if ppv <= 0 { 12 | return ln 13 | } 14 | 15 | return &proxyproto.Listener{ 16 | Listener: ln, 17 | ReadHeaderTimeout: readHeaderTimeout, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/net/transport.go: -------------------------------------------------------------------------------- 1 | package net 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/go-gost/core/common/bufpool" 7 | ) 8 | 9 | const ( 10 | bufferSize = 64 * 1024 11 | ) 12 | 13 | func Transport(rw1, rw2 io.ReadWriter) error { 14 | errc := make(chan error, 1) 15 | go func() { 16 | errc <- CopyBuffer(rw1, rw2, bufferSize) 17 | }() 18 | 19 | go func() { 20 | errc <- CopyBuffer(rw2, rw1, bufferSize) 21 | }() 22 | 23 | if err := <-errc; err != nil && err != io.EOF { 24 | return err 25 | } 26 | 27 | return nil 28 | } 29 | 30 | func CopyBuffer(dst io.Writer, src io.Reader, bufSize int) error { 31 | buf := bufpool.Get(bufSize) 32 | defer bufpool.Put(buf) 33 | 34 | _, err := io.CopyBuffer(dst, src, buf) 35 | return err 36 | } 37 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/net/udp/conn.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import ( 4 | "io" 5 | "net" 6 | 7 | xnet "github.com/go-gost/x/internal/net" 8 | ) 9 | 10 | type Conn interface { 11 | net.PacketConn 12 | io.Reader 13 | io.Writer 14 | ReadUDP 15 | WriteUDP 16 | xnet.SetBuffer 17 | xnet.SyscallConn 18 | xnet.RemoteAddr 19 | } 20 | 21 | type ReadUDP interface { 22 | ReadFromUDP(b []byte) (n int, addr *net.UDPAddr, err error) 23 | ReadMsgUDP(b, oob []byte) (n, oobn, flags int, addr *net.UDPAddr, err error) 24 | } 25 | 26 | type WriteUDP interface { 27 | WriteToUDP(b []byte, addr *net.UDPAddr) (int, error) 28 | WriteMsgUDP(b, oob []byte, addr *net.UDPAddr) (n, oobn int, err error) 29 | } 30 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/grpc/proto/gost.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | option go_package = "github.com/go-gost/core/common/util/grpc/proto"; 3 | 4 | message Chunk { 5 | bytes data = 1; 6 | } 7 | 8 | service GostTunel { 9 | rpc Tunnel (stream Chunk) returns (stream Chunk); 10 | } -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/grpc/proto/protoc.sh: -------------------------------------------------------------------------------- 1 | protoc --go_out=. --go_opt=paths=source_relative \ 2 | --go-grpc_out=. --go-grpc_opt=paths=source_relative \ 3 | gost.proto -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/kcp/kcp.go: -------------------------------------------------------------------------------- 1 | package kcp 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/golang/snappy" 7 | ) 8 | 9 | type kcpCompStreamConn struct { 10 | net.Conn 11 | w *snappy.Writer 12 | r *snappy.Reader 13 | } 14 | 15 | func CompStreamConn(conn net.Conn) net.Conn { 16 | return &kcpCompStreamConn{ 17 | Conn: conn, 18 | w: snappy.NewBufferedWriter(conn), 19 | r: snappy.NewReader(conn), 20 | } 21 | } 22 | 23 | func (c *kcpCompStreamConn) Read(b []byte) (n int, err error) { 24 | return c.r.Read(b) 25 | } 26 | 27 | func (c *kcpCompStreamConn) Write(b []byte) (n int, err error) { 28 | n, err = c.w.Write(b) 29 | if err != nil { 30 | return 31 | } 32 | err = c.w.Flush() 33 | return n, err 34 | } 35 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/resolver/resolver.go: -------------------------------------------------------------------------------- 1 | package resolver 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/miekg/dns" 7 | ) 8 | 9 | func AddSubnetOpt(m *dns.Msg, ip net.IP) { 10 | if m == nil || ip == nil { 11 | return 12 | } 13 | 14 | opt := new(dns.OPT) 15 | opt.Hdr.Name = "." 16 | opt.Hdr.Rrtype = dns.TypeOPT 17 | e := new(dns.EDNS0_SUBNET) 18 | e.Code = dns.EDNS0SUBNET 19 | if ip := ip.To4(); ip != nil { 20 | e.Family = 1 21 | e.SourceNetmask = 24 22 | e.Address = ip 23 | } else { 24 | e.Family = 2 25 | e.SourceNetmask = 128 26 | e.Address = ip.To16() 27 | } 28 | opt.Option = append(opt.Option, e) 29 | m.Extra = append(m.Extra, opt) 30 | } 31 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/serial/serial_other.go: -------------------------------------------------------------------------------- 1 | //go:build !windows && !linux 2 | // +build !windows,!linux 3 | 4 | package serial 5 | 6 | import ( 7 | "errors" 8 | "time" 9 | ) 10 | 11 | func openPort(name string, baud int, databits byte, parity Parity, stopbits StopBits, readTimeout time.Duration) (p *Port, err error) { 12 | return nil, errors.New("unsupported platform") 13 | } 14 | 15 | type Port struct { 16 | } 17 | 18 | func (p *Port) Read(b []byte) (n int, err error) { 19 | return 20 | } 21 | 22 | func (p *Port) Write(b []byte) (n int, err error) { 23 | return 24 | } 25 | 26 | func (p *Port) Close() (err error) { 27 | return 28 | } 29 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/socks/socks.go: -------------------------------------------------------------------------------- 1 | package socks 2 | 3 | const ( 4 | // MethodTLS is an extended SOCKS5 method with tls encryption support. 5 | MethodTLS uint8 = 0x80 6 | // MethodTLSAuth is an extended SOCKS5 method with tls encryption and authentication support. 7 | MethodTLSAuth uint8 = 0x82 8 | // MethodMux is an extended SOCKS5 method for stream multiplexing. 9 | MethodMux = 0x88 10 | ) 11 | 12 | const ( 13 | // CmdMuxBind is an extended SOCKS5 request CMD for 14 | // multiplexing transport with the binding server. 15 | CmdMuxBind uint8 = 0xF2 16 | // CmdUDPTun is an extended SOCKS5 request CMD for UDP over TCP. 17 | CmdUDPTun uint8 = 0xF3 18 | ) 19 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/tap/config.go: -------------------------------------------------------------------------------- 1 | package tap 2 | 3 | import "net" 4 | 5 | // Route is an IP routing entry 6 | type Route struct { 7 | Net net.IPNet 8 | Gateway net.IP 9 | } 10 | type Config struct { 11 | Name string 12 | Net string 13 | MTU int 14 | Gateway string 15 | Routes []Route 16 | } 17 | -------------------------------------------------------------------------------- /client/pkg/gost-x/internal/util/tun/config.go: -------------------------------------------------------------------------------- 1 | package tun 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/go-gost/core/router" 7 | ) 8 | 9 | type Config struct { 10 | Name string 11 | Net []net.IPNet 12 | // peer addr of point-to-point on MacOS 13 | Peer string 14 | MTU int 15 | Gateway net.IP 16 | Router router.Router 17 | } 18 | -------------------------------------------------------------------------------- /client/pkg/gost-x/limiter/conn/wrapper/listener.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "net" 5 | 6 | limiter "github.com/go-gost/core/limiter/conn" 7 | ) 8 | 9 | type listener struct { 10 | net.Listener 11 | limiter limiter.ConnLimiter 12 | } 13 | 14 | func WrapListener(limiter limiter.ConnLimiter, ln net.Listener) net.Listener { 15 | if limiter == nil { 16 | return ln 17 | } 18 | 19 | return &listener{ 20 | limiter: limiter, 21 | Listener: ln, 22 | } 23 | } 24 | 25 | func (ln *listener) Accept() (net.Conn, error) { 26 | c, err := ln.Listener.Accept() 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | host, _, _ := net.SplitHostPort(c.RemoteAddr().String()) 32 | if lim := ln.limiter.Limiter(host); lim != nil { 33 | if lim.Allow(1) { 34 | return WrapConn(lim, c), nil 35 | } 36 | c.Close() 37 | } 38 | 39 | return c, nil 40 | } 41 | -------------------------------------------------------------------------------- /client/pkg/gost-x/limiter/traffic/generator.go: -------------------------------------------------------------------------------- 1 | package traffic 2 | 3 | import ( 4 | limiter "github.com/go-gost/core/limiter/traffic" 5 | ) 6 | 7 | type limitGenerator struct { 8 | in int 9 | out int 10 | } 11 | 12 | func newLimitGenerator(in, out int) *limitGenerator { 13 | return &limitGenerator{ 14 | in: in, 15 | out: out, 16 | } 17 | } 18 | 19 | func (p *limitGenerator) In() limiter.Limiter { 20 | if p == nil || p.in <= 0 { 21 | return nil 22 | } 23 | return NewLimiter(p.in) 24 | } 25 | 26 | func (p *limitGenerator) Out() limiter.Limiter { 27 | if p == nil || p.out <= 0 { 28 | return nil 29 | } 30 | return NewLimiter(p.out) 31 | } 32 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/dtls/metadata.go: -------------------------------------------------------------------------------- 1 | package dtls 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | const ( 11 | defaultBufferSize = 1200 12 | ) 13 | 14 | type metadata struct { 15 | mtu int 16 | bufferSize int 17 | flightInterval time.Duration 18 | } 19 | 20 | func (l *dtlsListener) parseMetadata(md mdata.Metadata) (err error) { 21 | l.md.mtu = mdutil.GetInt(md, "dtls.mtu", "mtu") 22 | l.md.bufferSize = mdutil.GetInt(md, "dtls.bufferSize", "bufferSize") 23 | if l.md.bufferSize <= 0 { 24 | l.md.bufferSize = defaultBufferSize 25 | } 26 | 27 | l.md.flightInterval = mdutil.GetDuration(md, "dtls.flightInterval", "flightInterval") 28 | 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/http2/h2/metadata.go: -------------------------------------------------------------------------------- 1 | package h2 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | const ( 9 | defaultBacklog = 128 10 | ) 11 | 12 | type metadata struct { 13 | path string 14 | backlog int 15 | mptcp bool 16 | } 17 | 18 | func (l *h2Listener) parseMetadata(md mdata.Metadata) (err error) { 19 | const ( 20 | path = "path" 21 | backlog = "backlog" 22 | ) 23 | 24 | l.md.backlog = mdutil.GetInt(md, backlog) 25 | if l.md.backlog <= 0 { 26 | l.md.backlog = defaultBacklog 27 | } 28 | 29 | l.md.path = mdutil.GetString(md, path) 30 | l.md.mptcp = mdutil.GetBool(md, "mptcp") 31 | 32 | return 33 | } 34 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/http2/metadata.go: -------------------------------------------------------------------------------- 1 | package http2 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | const ( 9 | defaultBacklog = 128 10 | ) 11 | 12 | type metadata struct { 13 | backlog int 14 | mptcp bool 15 | } 16 | 17 | func (l *http2Listener) parseMetadata(md mdata.Metadata) (err error) { 18 | const ( 19 | backlog = "backlog" 20 | ) 21 | 22 | l.md.backlog = mdutil.GetInt(md, backlog) 23 | if l.md.backlog <= 0 { 24 | l.md.backlog = defaultBacklog 25 | } 26 | l.md.mptcp = mdutil.GetBool(md, "mptcp") 27 | 28 | return 29 | } 30 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/icmp/conn.go: -------------------------------------------------------------------------------- 1 | package quic 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/quic-go/quic-go" 7 | ) 8 | 9 | type quicConn struct { 10 | quic.Stream 11 | laddr net.Addr 12 | raddr net.Addr 13 | } 14 | 15 | func (c *quicConn) LocalAddr() net.Addr { 16 | return c.laddr 17 | } 18 | 19 | func (c *quicConn) RemoteAddr() net.Addr { 20 | return c.raddr 21 | } 22 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/obfs/http/metadata.go: -------------------------------------------------------------------------------- 1 | package http 2 | 3 | import ( 4 | "net/http" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | header http.Header 12 | mptcp bool 13 | } 14 | 15 | func (l *obfsListener) parseMetadata(md mdata.Metadata) (err error) { 16 | const ( 17 | header = "header" 18 | ) 19 | 20 | if mm := mdutil.GetStringMapString(md, header); len(mm) > 0 { 21 | hd := http.Header{} 22 | for k, v := range mm { 23 | hd.Add(k, v) 24 | } 25 | l.md.header = hd 26 | } 27 | 28 | l.md.mptcp = mdutil.GetBool(md, "mptcp") 29 | 30 | return 31 | } 32 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/obfs/tls/metadata.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import ( 4 | md "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | type metadata struct { 9 | mptcp bool 10 | } 11 | 12 | func (l *obfsListener) parseMetadata(md md.Metadata) (err error) { 13 | l.md.mptcp = mdutil.GetBool(md, "mptcp") 14 | 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/quic/conn.go: -------------------------------------------------------------------------------- 1 | package quic 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/quic-go/quic-go" 7 | ) 8 | 9 | type quicConn struct { 10 | quic.Stream 11 | laddr net.Addr 12 | raddr net.Addr 13 | } 14 | 15 | func (c *quicConn) LocalAddr() net.Addr { 16 | return c.laddr 17 | } 18 | 19 | func (c *quicConn) RemoteAddr() net.Addr { 20 | return c.raddr 21 | } 22 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/redirect/tcp/listener_darwin.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "syscall" 5 | ) 6 | 7 | func (l *redirectListener) control(network, address string, c syscall.RawConn) error { 8 | return nil 9 | } 10 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/redirect/tcp/listener_linux.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | "syscall" 5 | 6 | "golang.org/x/sys/unix" 7 | ) 8 | 9 | func (l *redirectListener) control(network, address string, c syscall.RawConn) error { 10 | return c.Control(func(fd uintptr) { 11 | if err := unix.SetsockoptInt(int(fd), unix.SOL_IP, unix.IP_TRANSPARENT, 1); err != nil { 12 | l.logger.Errorf("SetsockoptInt(SOL_IP, IP_TRANSPARENT, 1): %v", err) 13 | } 14 | }) 15 | } 16 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/redirect/tcp/listener_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !darwin 2 | 3 | package tcp 4 | 5 | import ( 6 | "errors" 7 | "syscall" 8 | ) 9 | 10 | func (l *redirectListener) control(network, address string, c syscall.RawConn) error { 11 | return errors.New("TProxy is not available on non-linux platform") 12 | } 13 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/redirect/tcp/metadata.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | type metadata struct { 9 | tproxy bool 10 | mptcp bool 11 | } 12 | 13 | func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) { 14 | l.md.tproxy = mdutil.GetBool(md, "tproxy") 15 | l.md.mptcp = mdutil.GetBool(md, "mptcp") 16 | 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/redirect/udp/conn.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import ( 4 | "net" 5 | "sync" 6 | "time" 7 | 8 | "github.com/go-gost/core/common/bufpool" 9 | ) 10 | 11 | type redirConn struct { 12 | net.Conn 13 | buf []byte 14 | ttl time.Duration 15 | once sync.Once 16 | } 17 | 18 | func (c *redirConn) Read(b []byte) (n int, err error) { 19 | c.once.Do(func() { 20 | n = copy(b, c.buf) 21 | bufpool.Put(c.buf) 22 | }) 23 | 24 | if c.ttl > 0 { 25 | c.SetReadDeadline(time.Now().Add(c.ttl)) 26 | defer c.SetReadDeadline(time.Time{}) 27 | } 28 | 29 | if n == 0 { 30 | n, err = c.Conn.Read(b) 31 | } 32 | 33 | return 34 | } 35 | 36 | func (c *redirConn) Write(b []byte) (n int, err error) { 37 | if c.ttl > 0 { 38 | c.SetWriteDeadline(time.Now().Add(c.ttl)) 39 | defer c.SetWriteDeadline(time.Time{}) 40 | } 41 | return c.Conn.Write(b) 42 | } 43 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/redirect/udp/listener_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package udp 4 | 5 | import ( 6 | "errors" 7 | "net" 8 | ) 9 | 10 | func (l *redirectListener) listenUDP(addr string) (*net.UDPConn, error) { 11 | return nil, errors.New("UDP redirect is not available on non-linux platform") 12 | } 13 | 14 | func (l *redirectListener) accept() (conn net.Conn, err error) { 15 | return nil, errors.New("UDP redirect is not available on non-linux platform") 16 | } 17 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/redirect/udp/metadata.go: -------------------------------------------------------------------------------- 1 | package udp 2 | 3 | import ( 4 | "time" 5 | 6 | mdata "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | const ( 11 | defaultTTL = 30 * time.Second 12 | defaultReadBufferSize = 4096 13 | ) 14 | 15 | type metadata struct { 16 | ttl time.Duration 17 | readBufferSize int 18 | } 19 | 20 | func (l *redirectListener) parseMetadata(md mdata.Metadata) (err error) { 21 | const ( 22 | ttl = "ttl" 23 | readBufferSize = "readBufferSize" 24 | ) 25 | 26 | l.md.ttl = mdutil.GetDuration(md, ttl) 27 | if l.md.ttl <= 0 { 28 | l.md.ttl = defaultTTL 29 | } 30 | 31 | l.md.readBufferSize = mdutil.GetInt(md, readBufferSize) 32 | if l.md.readBufferSize <= 0 { 33 | l.md.readBufferSize = defaultReadBufferSize 34 | } 35 | 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/rtcp/metadata.go: -------------------------------------------------------------------------------- 1 | package rtcp 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | ) 6 | 7 | type metadata struct{} 8 | 9 | func (l *rtcpListener) parseMetadata(md mdata.Metadata) (err error) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/serial/metadata.go: -------------------------------------------------------------------------------- 1 | package serial 2 | 3 | import ( 4 | "time" 5 | 6 | md "github.com/go-gost/core/metadata" 7 | mdutil "github.com/go-gost/x/metadata/util" 8 | ) 9 | 10 | type metadata struct { 11 | timeout time.Duration 12 | } 13 | 14 | func (l *serialListener) parseMetadata(md md.Metadata) (err error) { 15 | l.md.timeout = mdutil.GetDuration(md, "timeout", "serial.timeout", "listener.serial.timeout") 16 | 17 | return 18 | } 19 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/tap/tap_darwin.go: -------------------------------------------------------------------------------- 1 | package tap 2 | 3 | import ( 4 | "errors" 5 | "net" 6 | 7 | "github.com/songgao/water" 8 | ) 9 | 10 | func (l *tapListener) createTap() (ifce *water.Interface, name string, ip net.IP, err error) { 11 | err = errors.New("tap is not supported on darwin") 12 | return 13 | } 14 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/tcp/metadata.go: -------------------------------------------------------------------------------- 1 | package tcp 2 | 3 | import ( 4 | md "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | type metadata struct { 9 | mptcp bool 10 | } 11 | 12 | func (l *tcpListener) parseMetadata(md md.Metadata) (err error) { 13 | l.md.mptcp = mdutil.GetBool(md, "mptcp") 14 | 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/tls/metadata.go: -------------------------------------------------------------------------------- 1 | package tls 2 | 3 | import ( 4 | mdata "github.com/go-gost/core/metadata" 5 | mdutil "github.com/go-gost/x/metadata/util" 6 | ) 7 | 8 | type metadata struct { 9 | mptcp bool 10 | } 11 | 12 | func (l *tlsListener) parseMetadata(md mdata.Metadata) (err error) { 13 | l.md.mptcp = mdutil.GetBool(md, "mptcp") 14 | 15 | return 16 | } 17 | -------------------------------------------------------------------------------- /client/pkg/gost-x/listener/unix/metadata.go: -------------------------------------------------------------------------------- 1 | package unix 2 | 3 | import ( 4 | md "github.com/go-gost/core/metadata" 5 | ) 6 | 7 | type metadata struct{} 8 | 9 | func (l *unixListener) parseMetadata(md md.Metadata) (err error) { 10 | return 11 | } 12 | -------------------------------------------------------------------------------- /client/pkg/gost-x/metadata/metadata.go: -------------------------------------------------------------------------------- 1 | package metadata 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/go-gost/core/metadata" 7 | ) 8 | 9 | type mapMetadata map[string]any 10 | 11 | func NewMetadata(m map[string]any) metadata.Metadata { 12 | if len(m) == 0 { 13 | return nil 14 | } 15 | md := make(map[string]any) 16 | for k, v := range m { 17 | md[strings.ToLower(k)] = v 18 | } 19 | return mapMetadata(md) 20 | } 21 | 22 | func (m mapMetadata) IsExists(key string) bool { 23 | _, ok := m[strings.ToLower(key)] 24 | return ok 25 | } 26 | 27 | func (m mapMetadata) Set(key string, value any) { 28 | m[strings.ToLower(key)] = value 29 | } 30 | 31 | func (m mapMetadata) Get(key string) any { 32 | if m != nil { 33 | return m[strings.ToLower(key)] 34 | } 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /client/pkg/gost-x/metrics/wrapper/listener.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "net" 5 | 6 | xmetrics "github.com/go-gost/x/metrics" 7 | ) 8 | 9 | type listener struct { 10 | service string 11 | net.Listener 12 | } 13 | 14 | func WrapListener(service string, ln net.Listener) net.Listener { 15 | if !xmetrics.IsEnabled() { 16 | return ln 17 | } 18 | 19 | return &listener{ 20 | service: service, 21 | Listener: ln, 22 | } 23 | } 24 | 25 | func (ln *listener) Accept() (net.Conn, error) { 26 | c, err := ln.Listener.Accept() 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return WrapConn(ln.service, c), nil 32 | } 33 | -------------------------------------------------------------------------------- /client/pkg/gost-x/observer/stats/wrapper/io.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "io" 5 | 6 | "github.com/go-gost/core/observer/stats" 7 | ) 8 | 9 | // readWriter is an io.ReadWriter with Stats. 10 | type readWriter struct { 11 | io.ReadWriter 12 | stats stats.Stats 13 | } 14 | 15 | func WrapReadWriter(rw io.ReadWriter, stats stats.Stats) io.ReadWriter { 16 | if rw == nil || stats == nil { 17 | return rw 18 | } 19 | 20 | return &readWriter{ 21 | ReadWriter: rw, 22 | stats: stats, 23 | } 24 | } 25 | 26 | func (p *readWriter) Read(b []byte) (n int, err error) { 27 | n, err = p.ReadWriter.Read(b) 28 | p.stats.Add(stats.KindInputBytes, int64(n)) 29 | 30 | return 31 | } 32 | 33 | func (p *readWriter) Write(b []byte) (n int, err error) { 34 | n, err = p.ReadWriter.Write(b) 35 | p.stats.Add(stats.KindOutputBytes, int64(n)) 36 | 37 | return 38 | } 39 | -------------------------------------------------------------------------------- /client/pkg/gost-x/observer/stats/wrapper/listener.go: -------------------------------------------------------------------------------- 1 | package wrapper 2 | 3 | import ( 4 | "net" 5 | 6 | "github.com/go-gost/core/observer/stats" 7 | ) 8 | 9 | type listener struct { 10 | stats stats.Stats 11 | net.Listener 12 | } 13 | 14 | func WrapListener(ln net.Listener, stats stats.Stats) net.Listener { 15 | if stats == nil { 16 | return ln 17 | } 18 | 19 | return &listener{ 20 | stats: stats, 21 | Listener: ln, 22 | } 23 | } 24 | 25 | func (ln *listener) Accept() (net.Conn, error) { 26 | c, err := ln.Listener.Accept() 27 | if err != nil { 28 | return nil, err 29 | } 30 | 31 | return WrapConn(c, ln.stats), nil 32 | } 33 | -------------------------------------------------------------------------------- /client/pkg/gost-x/registry/connector.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/go-gost/core/connector" 5 | "github.com/go-gost/core/logger" 6 | ) 7 | 8 | type NewConnector func(opts ...connector.Option) connector.Connector 9 | 10 | type connectorRegistry struct { 11 | registry[NewConnector] 12 | } 13 | 14 | func (r *connectorRegistry) Register(name string, v NewConnector) error { 15 | if err := r.registry.Register(name, v); err != nil { 16 | logger.Default().Fatal(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/registry/dialer.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/go-gost/core/dialer" 5 | "github.com/go-gost/core/logger" 6 | ) 7 | 8 | type NewDialer func(opts ...dialer.Option) dialer.Dialer 9 | 10 | type dialerRegistry struct { 11 | registry[NewDialer] 12 | } 13 | 14 | func (r *dialerRegistry) Register(name string, v NewDialer) error { 15 | if err := r.registry.Register(name, v); err != nil { 16 | logger.Default().Fatal(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/registry/handler.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/go-gost/core/handler" 5 | "github.com/go-gost/core/logger" 6 | ) 7 | 8 | type NewHandler func(opts ...handler.Option) handler.Handler 9 | 10 | type handlerRegistry struct { 11 | registry[NewHandler] 12 | } 13 | 14 | func (r *handlerRegistry) Register(name string, v NewHandler) error { 15 | if err := r.registry.Register(name, v); err != nil { 16 | logger.Default().Fatal(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/registry/listener.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/go-gost/core/listener" 5 | "github.com/go-gost/core/logger" 6 | ) 7 | 8 | type NewListener func(opts ...listener.Option) listener.Listener 9 | 10 | type listenerRegistry struct { 11 | registry[NewListener] 12 | } 13 | 14 | func (r *listenerRegistry) Register(name string, v NewListener) error { 15 | if err := r.registry.Register(name, v); err != nil { 16 | logger.Default().Fatal(err) 17 | } 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /client/pkg/gost-x/registry/logger.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/go-gost/core/logger" 5 | ) 6 | 7 | type loggerRegistry struct { 8 | registry[logger.Logger] 9 | } 10 | 11 | func (r *loggerRegistry) Register(name string, v logger.Logger) error { 12 | return r.registry.Register(name, v) 13 | } 14 | -------------------------------------------------------------------------------- /client/pkg/gost-x/registry/service.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "github.com/go-gost/core/service" 5 | ) 6 | 7 | type serviceRegistry struct { 8 | registry[service.Service] 9 | } 10 | -------------------------------------------------------------------------------- /client/pkg/gost-x/router/router_linux.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | 7 | "github.com/go-gost/core/router" 8 | "github.com/vishvananda/netlink" 9 | ) 10 | 11 | func (p *localRouter) setSysRoutes(routes ...*router.Route) error { 12 | for _, route := range routes { 13 | if route.Net == nil { 14 | continue 15 | } 16 | gw := net.ParseIP(route.Gateway) 17 | if gw == nil { 18 | continue 19 | } 20 | 21 | p.options.logger.Debugf("ip route replace %s via %s", route.Net, route.Gateway) 22 | r := netlink.Route{ 23 | Dst: route.Net, 24 | Gw: gw, 25 | } 26 | if err := netlink.RouteReplace(&r); err != nil { 27 | return fmt.Errorf("set route %v %v: %v", r.Dst, r.Gw, err) 28 | } 29 | 30 | } 31 | return nil 32 | } 33 | -------------------------------------------------------------------------------- /client/pkg/gost-x/router/router_other.go: -------------------------------------------------------------------------------- 1 | //go:build !linux 2 | 3 | package router 4 | 5 | import ( 6 | "github.com/go-gost/core/router" 7 | ) 8 | 9 | func (*localRouter) setSysRoutes(routes ...*router.Route) error { 10 | return nil 11 | } 12 | -------------------------------------------------------------------------------- /client/pkg/p2p/client/event/event.go: -------------------------------------------------------------------------------- 1 | package event 2 | 3 | import ( 4 | "errors" 5 | "gostc-sub/pkg/p2p/pkg/msg" 6 | ) 7 | 8 | var ErrPayloadType = errors.New("error payload type") 9 | 10 | type Handler func(payload any) error 11 | 12 | type StartProxyPayload struct { 13 | NewProxyMsg *msg.NewProxy 14 | } 15 | 16 | type CloseProxyPayload struct { 17 | CloseProxyMsg *msg.CloseProxy 18 | } 19 | -------------------------------------------------------------------------------- /client/pkg/p2p/conf/frpc.toml: -------------------------------------------------------------------------------- 1 | serverAddr = "127.0.0.1" 2 | serverPort = 7000 3 | 4 | [[proxies]] 5 | name = "secret_tcp" 6 | type = "stcp" 7 | secretKey = "abcdefg" 8 | localIP = "192.168.0.172" 9 | localPort = 22714 10 | allowUsers = ["*"] 11 | 12 | [[proxies]] 13 | name = "p2p_tcp" 14 | type = "xtcp" 15 | secretKey = "abcdefg" 16 | localIP = "192.168.0.172" 17 | localPort = 22714 18 | allowUsers = ["*"] 19 | -------------------------------------------------------------------------------- /client/pkg/p2p/conf/frps.toml: -------------------------------------------------------------------------------- 1 | bindPort = 7000 2 | -------------------------------------------------------------------------------- /client/pkg/p2p/conf/vfrpc.toml: -------------------------------------------------------------------------------- 1 | serverAddr = "127.0.0.1" 2 | serverPort = 7000 3 | 4 | [[visitors]] 5 | name = "secret_tcp_visitor" 6 | type = "stcp" 7 | serverName = "secret_tcp" 8 | secretKey = "abcdefg" 9 | bindAddr = "127.0.0.1" 10 | bindPort = 16000 11 | 12 | [[visitors]] 13 | name = "p2p_tcp_visitor" 14 | type = "xtcp" 15 | serverName = "p2p_tcp" 16 | secretKey = "abcdefg" 17 | bindAddr = "127.0.0.1" 18 | bindPort = 16001 19 | keepTunnelOpen = false 20 | maxRetriesAnHour = 8 21 | minRetryInterval = 90 22 | # fallbackTo = "stcp_visitor" 23 | # fallbackTimeoutMs = 500 -------------------------------------------------------------------------------- /client/pkg/p2p/pkg/errors/errors.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package errors 16 | 17 | import ( 18 | "errors" 19 | ) 20 | 21 | var ( 22 | ErrMsgType = errors.New("message type error") 23 | ErrCtlClosed = errors.New("control is closed") 24 | ) 25 | -------------------------------------------------------------------------------- /client/pkg/p2p/pkg/util/util/types.go: -------------------------------------------------------------------------------- 1 | // Copyright 2023 The frp Authors 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package util 16 | 17 | func EmptyOr[T comparable](v T, fallback T) T { 18 | var zero T 19 | if zero == v { 20 | return fallback 21 | } 22 | return v 23 | } 24 | -------------------------------------------------------------------------------- /client/pkg/p2p/pkg/util/version/version.go: -------------------------------------------------------------------------------- 1 | // Copyright 2016 fatedier, fatedier@gmail.com 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | package version 16 | 17 | var version = "0.61.2" 18 | 19 | func Full() string { 20 | return version 21 | } 22 | -------------------------------------------------------------------------------- /client/pkg/p2p/registry/register.go: -------------------------------------------------------------------------------- 1 | package registry 2 | 3 | import ( 4 | "errors" 5 | "sync" 6 | ) 7 | 8 | type service interface { 9 | Start() error 10 | Stop() 11 | Wait() 12 | } 13 | 14 | var register = sync.Map{} 15 | 16 | func Set(name string, svc service) error { 17 | if _, ok := register.Load(name); ok { 18 | return errors.New("already exists") 19 | } 20 | register.Store(name, svc) 21 | return nil 22 | } 23 | 24 | func Get(name string) service { 25 | value, ok := register.Load(name) 26 | if ok { 27 | return value.(service) 28 | } 29 | return nil 30 | } 31 | 32 | func Del(name string) { 33 | value, ok := register.Load(name) 34 | if ok { 35 | value.(service).Stop() 36 | register.Delete(name) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /client/pkg/signal/free.go: -------------------------------------------------------------------------------- 1 | package signal 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | ) 8 | 9 | func Free() <-chan os.Signal { 10 | signalChan := make(chan os.Signal) 11 | signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) 12 | return signalChan 13 | } 14 | -------------------------------------------------------------------------------- /client/pkg/utils/calc.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // TrinaryOperation 三目运算 4 | func TrinaryOperation[T interface{}](condition bool, resultA, resultB T) T { 5 | if condition { 6 | return resultA 7 | } 8 | return resultB 9 | } 10 | 11 | func InArray[T string | int](data T, list []T) bool { 12 | for _, item := range list { 13 | if data == item { 14 | return true 15 | } 16 | } 17 | return false 18 | } 19 | 20 | func Map[T any, F any](list []T, handler func(T) (F, bool)) (result []F) { 21 | for _, item := range list { 22 | value, ok := handler(item) 23 | if ok { 24 | result = append(result, value) 25 | } 26 | } 27 | return result 28 | } 29 | -------------------------------------------------------------------------------- /client/pkg/utils/port.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "fmt" 5 | "net" 6 | ) 7 | 8 | func IsUse(bind string, port int) error { 9 | address := fmt.Sprintf("%s:%d", bind, port) 10 | listener, err := net.Listen("tcp", address) 11 | if err != nil { 12 | return err // 端口被占用 13 | } 14 | defer listener.Close() 15 | packetConn, err := net.ListenPacket("udp", address) 16 | if err != nil { 17 | return err // 端口被占用 18 | } 19 | defer packetConn.Close() 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /client/webui/backend/bootstrap/logger.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "gostc-sub/pkg/logger" 5 | "gostc-sub/webui/backend/global" 6 | ) 7 | 8 | func InitLogger() { 9 | global.Logger = logger.NewLogger(func() logger.Option { 10 | return logger.Option{ 11 | To: []string{"console"}, 12 | Level: "info", 13 | MaxSize: 100, 14 | MaxAge: 30, 15 | MaxBackups: 10, 16 | Compress: true, 17 | } 18 | }()) 19 | 20 | releaseFunc = append(releaseFunc, func() { 21 | _ = global.Logger.Sync() 22 | }) 23 | } 24 | -------------------------------------------------------------------------------- /client/webui/backend/bootstrap/release.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import "gostc-sub/webui/backend/global" 4 | 5 | var releaseFunc []func() 6 | 7 | func Release() { 8 | global.Logger.Info("exit, release") 9 | for i := 0; i < len(releaseFunc); i++ { 10 | fn := releaseFunc[i] 11 | if fn == nil { 12 | continue 13 | } 14 | fn() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /client/webui/backend/bootstrap/router.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "gostc-sub/webui/backend/global" 6 | ) 7 | 8 | var engine *gin.Engine 9 | 10 | var Route func(e *gin.Engine) 11 | 12 | func InitRouter() { 13 | gin.SetMode(gin.ReleaseMode) 14 | engine = gin.Default() 15 | 16 | if Route != nil { 17 | Route(engine) 18 | } 19 | global.Logger.Info("init router finish") 20 | } 21 | -------------------------------------------------------------------------------- /client/webui/backend/bootstrap/task.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/robfig/cron/v3" 5 | "gostc-sub/webui/backend/global" 6 | ) 7 | 8 | var TaskFunc func() 9 | 10 | func InitTask() { 11 | global.Cron = cron.New() 12 | if TaskFunc != nil { 13 | TaskFunc() 14 | } 15 | global.Cron.Start() 16 | } 17 | -------------------------------------------------------------------------------- /client/webui/backend/bootstrap/todo.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import "gostc-sub/webui/backend/global" 4 | 5 | var TodoFunc func() 6 | 7 | func InitTodo() { 8 | if TodoFunc != nil { 9 | TodoFunc() 10 | } 11 | global.Logger.Info("init todo finish") 12 | } 13 | -------------------------------------------------------------------------------- /client/webui/backend/controller/config/api.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "gostc-sub/pkg/bean" 6 | service "gostc-sub/webui/backend/service/config" 7 | ) 8 | 9 | func Query(c *gin.Context) { 10 | bean.Response.OkData(c, service.Service.Query()) 11 | } 12 | -------------------------------------------------------------------------------- /client/webui/backend/controller/logger/api.go: -------------------------------------------------------------------------------- 1 | package logger 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "gostc-sub/pkg/bean" 6 | service "gostc-sub/webui/backend/service/logger" 7 | ) 8 | 9 | func List(c *gin.Context) { 10 | bean.Response.OkData(c, service.Service.List()) 11 | } 12 | -------------------------------------------------------------------------------- /client/webui/backend/global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/robfig/cron/v3" 5 | "go.uber.org/zap" 6 | "gostc-sub/pkg/fs" 7 | "sync" 8 | ) 9 | 10 | var Logger *zap.Logger 11 | 12 | var Cron *cron.Cron 13 | 14 | var ClientFS *fs.FileStorage 15 | var TunnelFS *fs.FileStorage 16 | var P2PFS *fs.FileStorage 17 | 18 | var ClientMap = sync.Map{} 19 | var P2PMap = sync.Map{} 20 | var TunnelMap = sync.Map{} 21 | -------------------------------------------------------------------------------- /client/webui/backend/model/client.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Client struct { 4 | Key string `json:"key"` 5 | Name string `json:"name"` 6 | Address string `json:"address"` 7 | Tls int `json:"tls"` 8 | AutoStart int `json:"autoStart"` 9 | } 10 | -------------------------------------------------------------------------------- /client/webui/backend/model/p2p.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type P2P struct { 4 | Key string `json:"key"` 5 | Name string `json:"name"` 6 | Bind string `json:"bind"` 7 | Port string `json:"port"` 8 | Address string `json:"address"` 9 | Tls int `json:"tls"` 10 | AutoStart int `json:"autoStart"` 11 | } 12 | -------------------------------------------------------------------------------- /client/webui/backend/model/tunnel.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type Tunnel struct { 4 | Key string `json:"key"` 5 | Name string `json:"name"` 6 | Bind string `json:"bind"` 7 | Port string `json:"port"` 8 | Address string `json:"address"` 9 | Tls int `json:"tls"` 10 | AutoStart int `json:"autoStart"` 11 | } 12 | -------------------------------------------------------------------------------- /client/webui/backend/router/router.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "gostc-sub/webui/backend/bootstrap" 6 | ) 7 | 8 | func init() { 9 | bootstrap.Route = func(engine *gin.Engine) { 10 | InitStatic(engine) 11 | InitApi(engine) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /client/webui/backend/service/client/service.delete.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | service2 "gostc-sub/internal/service" 5 | "gostc-sub/webui/backend/global" 6 | ) 7 | 8 | type DeleteReq struct { 9 | Key string `binding:"required" json:"key"` 10 | } 11 | 12 | func (*service) Delete(req DeleteReq) error { 13 | if err := global.ClientFS.Delete(req.Key); err != nil { 14 | return err 15 | } 16 | client, ok := global.ClientMap.Load(req.Key) 17 | if ok { 18 | client.(*service2.Client).Stop() 19 | } 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /client/webui/backend/service/client/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /client/webui/backend/service/config/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /client/webui/backend/service/config/service.query.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "gostc-sub/internal/common" 4 | 5 | type QueryResp struct { 6 | Version string `json:"version"` 7 | } 8 | 9 | func (*service) Query() (result QueryResp) { 10 | return QueryResp{Version: common.VERSION} 11 | } 12 | -------------------------------------------------------------------------------- /client/webui/backend/service/logger/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /client/webui/backend/service/logger/service.list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "gostc-sub/internal/common" 5 | "time" 6 | ) 7 | 8 | type Item struct { 9 | Timestamp string `json:"timestamp"` 10 | Type string `json:"type"` 11 | Message string `json:"message"` 12 | } 13 | 14 | func (*service) List() (result []Item) { 15 | for _, item := range common.Logger.GetLogs() { 16 | result = append(result, Item{ 17 | Timestamp: item.Timestamp.Format(time.DateTime), 18 | Type: item.Type, 19 | Message: item.Message, 20 | }) 21 | } 22 | return result 23 | } 24 | -------------------------------------------------------------------------------- /client/webui/backend/service/p2p/service.delete.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | service2 "gostc-sub/internal/service" 5 | "gostc-sub/webui/backend/global" 6 | ) 7 | 8 | type DeleteReq struct { 9 | Key string `binding:"required" json:"key"` 10 | } 11 | 12 | func (*service) Delete(req DeleteReq) error { 13 | if err := global.P2PFS.Delete(req.Key); err != nil { 14 | return err 15 | } 16 | p2p, ok := global.P2PMap.Load(req.Key) 17 | if ok { 18 | p2p.(*service2.P2P).Stop() 19 | } 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /client/webui/backend/service/p2p/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /client/webui/backend/service/tunnel/service.delete.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | service2 "gostc-sub/internal/service" 5 | "gostc-sub/webui/backend/global" 6 | ) 7 | 8 | type DeleteReq struct { 9 | Key string `binding:"required" json:"key"` 10 | } 11 | 12 | func (*service) Delete(req DeleteReq) error { 13 | if err := global.TunnelFS.Delete(req.Key); err != nil { 14 | return err 15 | } 16 | tunnel, ok := global.TunnelMap.Load(req.Key) 17 | if ok { 18 | tunnel.(*service2.Tunnel).Stop() 19 | } 20 | return nil 21 | } 22 | -------------------------------------------------------------------------------- /client/webui/backend/service/tunnel/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /client/webui/backend/web/dist.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/client/webui/backend/web/dist.zip -------------------------------------------------------------------------------- /client/webui/backend/web/static.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import _ "embed" 4 | 5 | //go:embed dist.zip 6 | var staticFile []byte 7 | 8 | func Static() []byte { 9 | return staticFile 10 | } 11 | -------------------------------------------------------------------------------- /client/webui/frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --host", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.6.8", 13 | "echarts": "^5.5.1", 14 | "highcharts": "^12.1.2", 15 | "moment": "^2.30.1", 16 | "naive-ui": "^2.38.1", 17 | "nprogress": "^0.2.0", 18 | "pinia": "^2.1.7", 19 | "pinia-plugin-persistedstate": "^3.2.1", 20 | "sass": "^1.74.1", 21 | "sass-loader": "^14.1.1", 22 | "vue": "^3.4.21", 23 | "vue-router": "^4.3.0" 24 | }, 25 | "devDependencies": { 26 | "@vitejs/plugin-vue": "^5.0.4", 27 | "vfonts": "^0.0.3", 28 | "vite": "^5.2.8", 29 | "vite-plugin-pwa": "^0.19.8" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /client/webui/frontend/src/api/config.js: -------------------------------------------------------------------------------- 1 | import request from "../request/index.js"; 2 | 3 | const baseUrl = '/config' 4 | 5 | export const apiConfigQuery = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/query`, method: 'POST', data 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /client/webui/frontend/src/api/logger.js: -------------------------------------------------------------------------------- 1 | import request from "../request/index.js"; 2 | 3 | const baseUrl = '/logger' 4 | 5 | export const apiLoggerList = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/list`, method: 'POST', data 8 | }) 9 | } 10 | -------------------------------------------------------------------------------- /client/webui/frontend/src/components/DialogApi.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /client/webui/frontend/src/components/Empty.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /client/webui/frontend/src/components/MessageApi.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /client/webui/frontend/src/components/ModalApi.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /client/webui/frontend/src/icon/online.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 24 | 25 | -------------------------------------------------------------------------------- /client/webui/frontend/src/layout/components/Layout.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /client/webui/frontend/src/layout/empty/index.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /client/webui/frontend/src/layout/mobile/index.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 17 | 18 | -------------------------------------------------------------------------------- /client/webui/frontend/src/layout/simple/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | -------------------------------------------------------------------------------- /client/webui/frontend/src/main.js: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import './style.css' 3 | import App from './App.vue' 4 | import {createPinia} from "pinia"; 5 | import piniaPluginPersistence from 'pinia-plugin-persistedstate' 6 | import router from "./router/index.js"; 7 | import naive from 'naive-ui' 8 | import {setupStore} from "./store/index.js"; 9 | const pinia = createPinia() 10 | pinia.use(piniaPluginPersistence) 11 | 12 | const app = createApp(App) 13 | setupStore(app) 14 | app.use(naive) 15 | app.use(router) 16 | app.mount('#app') 17 | -------------------------------------------------------------------------------- /client/webui/frontend/src/router/index.js: -------------------------------------------------------------------------------- 1 | import {createRouter, createWebHistory} from "vue-router"; 2 | import {baseRouters} from "./routers/base.js"; 3 | 4 | const router = createRouter({ 5 | history: createWebHistory(), 6 | routes: baseRouters, 7 | scrollBehavior: () => ({left: 0, top: 0}), 8 | }) 9 | 10 | router.beforeEach(async (to, from, next) => { 11 | next() 12 | }) 13 | 14 | router.afterEach((to) => { 15 | }) 16 | 17 | export default router 18 | 19 | -------------------------------------------------------------------------------- /client/webui/frontend/src/setting.js: -------------------------------------------------------------------------------- 1 | export const naiveThemeOverrides = { 2 | common: { 3 | primaryColor: '#2299ddFF', 4 | } 5 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/store/app.js: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | 3 | export const appStore = defineStore('app', { 4 | state: () => ({ 5 | siteConfig: { 6 | title: 'GOSTC', 7 | favicon: '', 8 | isLoading: false, 9 | }, 10 | width: window.innerWidth, 11 | height: window.innerHeight, 12 | auth: { 13 | token: '', 14 | expAt: 0, 15 | }, 16 | userInfo: { 17 | account: '', 18 | amount: '', 19 | admin: 2, 20 | isLoading: false, 21 | } 22 | }), 23 | getters: { 24 | drawerWidthAdapter: (state) => { 25 | if (state?.width < 500) { 26 | return state?.width 27 | } 28 | return 400 29 | } 30 | }, 31 | actions: {}, 32 | persist: false 33 | }) -------------------------------------------------------------------------------- /client/webui/frontend/src/store/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { createPinia } from 'pinia' 3 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 4 | 5 | export function setupStore(app) { 6 | const pinia = createPinia() 7 | pinia.use(piniaPluginPersistedstate) 8 | app.use(pinia) 9 | } 10 | -------------------------------------------------------------------------------- /client/webui/frontend/src/store/local.js: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | 3 | export const localStore = defineStore('local', { 4 | state: () => ({ 5 | darkTheme: true, 6 | layout: 'simple', 7 | isCollapsed: true, 8 | menuKey: '', 9 | auth: { 10 | token: '', 11 | expAt: '' 12 | }, 13 | }), 14 | getters: {}, 15 | actions: { 16 | setLayout(name) { 17 | this.layout = name 18 | } 19 | }, 20 | persist: true 21 | }) -------------------------------------------------------------------------------- /client/webui/frontend/src/utils/browser.js: -------------------------------------------------------------------------------- 1 | export function goToUrl(url, target = '_blank') { 2 | window.open(url, target) 3 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/utils/copy.js: -------------------------------------------------------------------------------- 1 | export function copyToClipboard(value) { 2 | if (!navigator){ 3 | return 4 | } 5 | return navigator.clipboard && navigator.clipboard.writeText(value) 6 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/utils/flow.js: -------------------------------------------------------------------------------- 1 | export function flowFormat(value) { 2 | if (value < 1024) { 3 | return value + 'B' 4 | } 5 | if (value < 1024 * 1024) { 6 | return (value / 1024).toFixed(1) + 'KB' 7 | } 8 | if (value < 1024 * 1024 * 1024) { 9 | return (value / 1024 / 1024).toFixed(1) + 'MB' 10 | } 11 | if (value < 1024 * 1024 * 1024 * 1024) { 12 | return (value / 1024 / 1024 / 1024).toFixed(1) + 'GB' 13 | } 14 | if (value < 1024 * 1024 * 1024 * 1024 * 1024) { 15 | return (value / 1024 / 1024 / 1024 / 1024).toFixed(1) + 'TB' 16 | } 17 | return '--' 18 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/utils/formDataRule.js: -------------------------------------------------------------------------------- 1 | export function requiredRule(tip = '不能为空') { 2 | return { 3 | required: true, 4 | trigger: ["blur", "input"], 5 | message: tip 6 | } 7 | } 8 | 9 | export function regexpRule(regexp = (str) => { 10 | return true 11 | }, tip = '格式错误') { 12 | return { 13 | required: true, 14 | trigger: ["blur", "change"], 15 | validator: (rule, value) => { 16 | if (!value) { 17 | return new Error('请输入') 18 | } 19 | if (!regexp(value)) { 20 | return new Error(tip) 21 | } 22 | return true 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/utils/menu.js: -------------------------------------------------------------------------------- 1 | export function menuFlatToTree(data, parent) { 2 | let result = [] 3 | if (!data) { 4 | return result 5 | } 6 | data.forEach(item => { 7 | if (item.parentCode === parent) { 8 | let children = menuFlatToTree(data, item.code) 9 | if (children.length === 0) { 10 | children = null 11 | } 12 | result.push({ 13 | label: item.name, 14 | key: item.key, 15 | iconSvg: item.icon, 16 | show: item.hidden === 2, 17 | children: children 18 | }) 19 | } 20 | }) 21 | return result 22 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/utils/random.js: -------------------------------------------------------------------------------- 1 | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 2 | export function randomString(length) { 3 | let result = ''; 4 | const charactersLength = characters.length; 5 | for (let i = 0; i < length; i++) { 6 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 7 | } 8 | return result; 9 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/utils/routerToMenu.js: -------------------------------------------------------------------------------- 1 | export function routerToMenu(children) { 2 | let result = [] 3 | children.forEach(item => { 4 | if (item.meta?.hidden === 1) { 5 | return 6 | } 7 | let data = { 8 | label: item.meta?.title, 9 | key: item.name, 10 | iconSvg: item.meta?.icon, 11 | show: item.meta?.hidden === 2, 12 | link: item.meta?.link, 13 | } 14 | if (item.children) { 15 | children = routerToMenu(item.children) 16 | if (children.length!==0) { 17 | data.children = children 18 | } 19 | } 20 | result.push(data) 21 | }) 22 | return result 23 | } -------------------------------------------------------------------------------- /client/webui/frontend/src/views/home/about.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | -------------------------------------------------------------------------------- /client/webui/frontend/vite.config.js: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | vue(), 7 | ], 8 | server: { 9 | proxy: { 10 | '/api': { 11 | target: 'http://127.0.0.1:18080', 12 | changeOrigin: true 13 | } 14 | }, 15 | host: '0.0.0.0' 16 | } 17 | }) 18 | -------------------------------------------------------------------------------- /images/config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/images/config.png -------------------------------------------------------------------------------- /images/dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/images/dashboard.png -------------------------------------------------------------------------------- /images/node.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/images/node.png -------------------------------------------------------------------------------- /images/wx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/images/wx.png -------------------------------------------------------------------------------- /images/zfb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/images/zfb.png -------------------------------------------------------------------------------- /proxy/bootstrap/logger.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "proxy/global" 5 | "proxy/pkg/logger" 6 | ) 7 | 8 | func InitLogger() { 9 | global.Logger = logger.NewLogger(func() logger.Option { 10 | var to = []string{global.LOGGER_FILE_PATH} 11 | if global.MODE == "dev" { 12 | to = append(to, "console") 13 | } 14 | return logger.Option{ 15 | To: to, 16 | Level: global.LOGGER_LEVEL, 17 | MaxSize: 100, 18 | MaxAge: 30, 19 | MaxBackups: 10, 20 | Compress: true, 21 | } 22 | }()) 23 | 24 | releaseFunc = append(releaseFunc, func() { 25 | _ = global.Logger.Sync() 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /proxy/bootstrap/release.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import "proxy/global" 4 | 5 | var releaseFunc []func() 6 | 7 | func Release() { 8 | global.Logger.Info("exit, release") 9 | for i := 0; i < len(releaseFunc); i++ { 10 | fn := releaseFunc[i] 11 | if fn == nil { 12 | continue 13 | } 14 | fn() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /proxy/cmd/program/service_option/option.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !windows 2 | 3 | package service_option 4 | 5 | import ( 6 | "github.com/kardianos/service" 7 | ) 8 | 9 | func MakeOptions() service.KeyValue { 10 | return service.KeyValue{ 11 | "OnFailureDelayDuration": "10", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /proxy/cmd/program/service_option/option_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package service_option 4 | 5 | import ( 6 | "github.com/kardianos/service" 7 | ) 8 | 9 | func MakeOptions() service.KeyValue { 10 | return service.KeyValue{ 11 | "OnFailureDelayDuration": "10", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /proxy/cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | "proxy/global" 7 | ) 8 | 9 | var VersionCmd = cobra.Command{ 10 | Use: "version", 11 | Run: func(cmd *cobra.Command, args []string) { 12 | global.Init() 13 | fmt.Println("VERSION:", global.VERSION) 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /proxy/configs/config.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | type Config struct { 4 | HTTPAddr string `yaml:"http-addr"` 5 | HTTPSAddr string `yaml:"https-addr"` 6 | Certs string `yaml:"certs"` 7 | ApiAddr string `yaml:"api-addr"` 8 | Default DomainConfig `yaml:"default"` 9 | Domains map[string]DomainConfig `yaml:"domains"` 10 | } 11 | 12 | // DomainConfig 定义域名配置 13 | type DomainConfig struct { 14 | Target string `yaml:"target"` 15 | Cert string `yaml:"cert"` 16 | Key string `yaml:"key"` 17 | } 18 | -------------------------------------------------------------------------------- /proxy/global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "proxy/configs" 6 | ) 7 | 8 | var Config *configs.Config 9 | var Logger *zap.Logger 10 | -------------------------------------------------------------------------------- /proxy/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "proxy/cmd" 6 | ) 7 | 8 | func main() { 9 | if err := cmd.RootCmd.Execute(); err != nil { 10 | fmt.Println(err) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /proxy/pkg/signal/free.go: -------------------------------------------------------------------------------- 1 | package signal 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | ) 8 | 9 | func Free() <-chan os.Signal { 10 | signalChan := make(chan os.Signal, 1) 11 | signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) 12 | return signalChan 13 | } 14 | -------------------------------------------------------------------------------- /server/.goreleaser.yaml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | before: 4 | hooks: 5 | - go mod tidy 6 | 7 | builds: 8 | - env: 9 | - CGO_ENABLED=0 10 | targets: 11 | - freebsd_386 12 | - freebsd_amd64 13 | - darwin_amd64 14 | - darwin_arm64 15 | - linux_arm_v7 16 | - linux_arm64 17 | - linux_amd64 18 | - linux_amd64_v3 19 | - windows_arm64 20 | - windows_amd64 21 | - windows_amd64_v3 22 | ldflags: 23 | - -s 24 | - -w 25 | binary: >- 26 | server 27 | main: ./main.go 28 | archives: 29 | - format: tar.gz 30 | name_template: "server_{{ .Target }}" 31 | format_overrides: 32 | - goos: windows 33 | format: zip 34 | files: 35 | - LICENSE 36 | -------------------------------------------------------------------------------- /server/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine AS binary-selector 2 | COPY dist/gostc-open_linux_amd64_v1/server /binary/server-amd64 3 | COPY dist/gostc-open_linux_arm64_v8.0/server /binary/server-arm64 4 | 5 | ARG TARGETPLATFORM 6 | RUN mkdir /app && if [ "$TARGETPLATFORM" = "linux/amd64" ]; then \ 7 | cp /binary/server-amd64 /app/server; \ 8 | elif [ "$TARGETPLATFORM" = "linux/arm64" ]; then \ 9 | cp /binary/server-arm64 /app/server; \ 10 | else \ 11 | echo "Unsupported architecture: $TARGETPLATFORM" && exit 1; \ 12 | fi 13 | 14 | FROM alpine 15 | COPY --from=binary-selector /app/server /app/server 16 | RUN apk add --no-cache tzdata && ln -snf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && echo Asia/Shanghai > /etc/timezone 17 | WORKDIR /app/ 18 | ENTRYPOINT ["./server"] -------------------------------------------------------------------------------- /server/bootstrap/jwt.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "server/global" 5 | "server/pkg/jwt" 6 | ) 7 | 8 | func InitJwt() { 9 | global.Jwt = jwt.NewTool(global.Config.AuthKey) 10 | } 11 | -------------------------------------------------------------------------------- /server/bootstrap/logger.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "server/global" 5 | "server/pkg/logger" 6 | ) 7 | 8 | func InitLogger() { 9 | global.Logger = logger.NewLogger(func() logger.Option { 10 | var to = []string{global.LOGGER_FILE_PATH} 11 | if global.MODE == "dev" { 12 | to = append(to, "console") 13 | } 14 | return logger.Option{ 15 | To: to, 16 | Level: global.LOGGER_LEVEL, 17 | MaxSize: 100, 18 | MaxAge: 30, 19 | MaxBackups: 10, 20 | Compress: true, 21 | } 22 | }()) 23 | 24 | releaseFunc = append(releaseFunc, func() { 25 | _ = global.Logger.Sync() 26 | }) 27 | } 28 | -------------------------------------------------------------------------------- /server/bootstrap/memory.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "server/global" 5 | "server/pkg/memory/local" 6 | "time" 7 | ) 8 | 9 | func InitMemory() { 10 | global.Cache = local.NewMemory(global.BASE_PATH+"/data/cache.db", time.Minute*10) 11 | releaseFunc = append(releaseFunc, func() { 12 | global.Cache.Sync() 13 | }) 14 | } 15 | -------------------------------------------------------------------------------- /server/bootstrap/release.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import "server/global" 4 | 5 | var releaseFunc []func() 6 | 7 | func Release() { 8 | global.Logger.Info("exit, release") 9 | for i := 0; i < len(releaseFunc); i++ { 10 | fn := releaseFunc[i] 11 | if fn == nil { 12 | continue 13 | } 14 | fn() 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /server/bootstrap/router.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/global" 6 | ) 7 | 8 | var engine *gin.Engine 9 | 10 | var Route func(e *gin.Engine) 11 | 12 | func InitRouter() { 13 | if global.MODE == "prod" { 14 | gin.SetMode(gin.ReleaseMode) 15 | } else { 16 | gin.SetMode(gin.DebugMode) 17 | } 18 | engine = gin.Default() 19 | 20 | if Route != nil { 21 | Route(engine) 22 | } 23 | global.Logger.Info("init router finish") 24 | } 25 | -------------------------------------------------------------------------------- /server/bootstrap/server.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "errors" 5 | "go.uber.org/zap" 6 | "net/http" 7 | "server/global" 8 | "time" 9 | ) 10 | 11 | func InitServer() { 12 | server := &http.Server{ 13 | Addr: global.Config.Address, 14 | Handler: engine, 15 | } 16 | go func() { 17 | if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) { 18 | Release() 19 | global.Logger.Fatal("server listen fail", zap.Error(err)) 20 | } 21 | }() 22 | time.Sleep(time.Second) 23 | releaseFunc = append(releaseFunc, func() { 24 | _ = server.Close() 25 | }) 26 | global.Logger.Info("server listen on address: " + global.Config.Address) 27 | } 28 | -------------------------------------------------------------------------------- /server/bootstrap/task.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import ( 4 | "github.com/robfig/cron/v3" 5 | "server/global" 6 | ) 7 | 8 | var TaskFunc func() 9 | 10 | func InitTask() { 11 | global.Cron = cron.New() 12 | if TaskFunc != nil { 13 | TaskFunc() 14 | } 15 | global.Cron.Start() 16 | } 17 | -------------------------------------------------------------------------------- /server/bootstrap/todo.go: -------------------------------------------------------------------------------- 1 | package bootstrap 2 | 3 | import "server/global" 4 | 5 | var TodoFunc func() 6 | 7 | func InitTodo() { 8 | if TodoFunc != nil { 9 | TodoFunc() 10 | } 11 | global.Logger.Info("init todo finish") 12 | } 13 | -------------------------------------------------------------------------------- /server/cmd/program/service_option/option.go: -------------------------------------------------------------------------------- 1 | //go:build !linux && !windows 2 | 3 | package service_option 4 | 5 | import ( 6 | "github.com/kardianos/service" 7 | ) 8 | 9 | func MakeOptions() service.KeyValue { 10 | return service.KeyValue{ 11 | "OnFailureDelayDuration": "10", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/cmd/program/service_option/option_windows.go: -------------------------------------------------------------------------------- 1 | //go:build windows 2 | 3 | package service_option 4 | 5 | import ( 6 | "github.com/kardianos/service" 7 | ) 8 | 9 | func MakeOptions() service.KeyValue { 10 | return service.KeyValue{ 11 | "OnFailureDelayDuration": "10", 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /server/cmd/version.go: -------------------------------------------------------------------------------- 1 | package cmd 2 | 3 | import ( 4 | "fmt" 5 | "github.com/spf13/cobra" 6 | "server/global" 7 | ) 8 | 9 | var VersionCmd = cobra.Command{ 10 | Use: "version", 11 | Run: func(cmd *cobra.Command, args []string) { 12 | global.Init() 13 | fmt.Println("VERSION:", global.VERSION) 14 | }, 15 | } 16 | -------------------------------------------------------------------------------- /server/configs/config.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | type Config struct { 8 | Address string `yaml:"address"` 9 | AuthKey string `yaml:"auth-key"` 10 | AuthExp time.Duration `yaml:"auth-exp"` 11 | AuthRenew time.Duration `yaml:"auth-renew"` 12 | DbType string `yaml:"db-type"` 13 | Sqlite Sqlite `yaml:"sqlite"` 14 | Mysql Mysql `yaml:"mysql"` 15 | } 16 | -------------------------------------------------------------------------------- /server/configs/mysql.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | type Mysql struct { 4 | Host string `yaml:"host"` 5 | Port int `yaml:"port"` 6 | DB string `yaml:"db"` 7 | User string `yaml:"user"` 8 | Pwd string `yaml:"pwd"` 9 | Prefix string `yaml:"prefix"` 10 | Extend string `yaml:"extend"` 11 | LogLevel string `yaml:"log-level"` 12 | } 13 | -------------------------------------------------------------------------------- /server/configs/sqlite.go: -------------------------------------------------------------------------------- 1 | package configs 2 | 3 | type Sqlite struct { 4 | File string `yaml:"file"` 5 | LogLevel string `yaml:"log-level"` 6 | } 7 | -------------------------------------------------------------------------------- /server/controller/admin/dashboard/api.go: -------------------------------------------------------------------------------- 1 | package dashboard 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/service/admin/dashboard" 7 | ) 8 | 9 | var svr = service.Service 10 | 11 | func UserObs(c *gin.Context) { 12 | bean.Response.OkData(c, svr.UserObs()) 13 | } 14 | 15 | func NodeObs(c *gin.Context) { 16 | bean.Response.OkData(c, svr.NodeObs()) 17 | } 18 | 19 | func UserObsDate(c *gin.Context) { 20 | bean.Response.OkData(c, svr.UserObsDate(c.Query("date"))) 21 | } 22 | 23 | func NodeObsDate(c *gin.Context) { 24 | bean.Response.OkData(c, svr.NodeObsDate(c.Query("date"))) 25 | } 26 | 27 | func Count(c *gin.Context) { 28 | bean.Response.OkData(c, svr.Count()) 29 | } 30 | -------------------------------------------------------------------------------- /server/controller/admin/gost_client_logger/api.go: -------------------------------------------------------------------------------- 1 | package gost_client_logger 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/service/admin/gost_client_logger" 7 | ) 8 | 9 | var svr = service.Service 10 | 11 | func Page(c *gin.Context) { 12 | var req service.PageReq 13 | if err := c.ShouldBindJSON(&req); err != nil { 14 | bean.Response.Param(c, err) 15 | return 16 | } 17 | list, total := svr.Page(req) 18 | bean.Response.OkData(c, bean.NewPage(list, total)) 19 | } 20 | -------------------------------------------------------------------------------- /server/controller/admin/gost_node_bind/api.go: -------------------------------------------------------------------------------- 1 | package gost_node 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/service/admin/gost_node_bind" 7 | ) 8 | 9 | var svr = service.Service 10 | 11 | func Update(c *gin.Context) { 12 | var req service.UpdateReq 13 | if err := c.ShouldBindJSON(&req); err != nil { 14 | bean.Response.Param(c, err) 15 | return 16 | } 17 | if err := svr.Update(req); err != nil { 18 | bean.Response.Fail(c, err.Error()) 19 | return 20 | } 21 | bean.Response.Ok(c) 22 | } 23 | -------------------------------------------------------------------------------- /server/controller/admin/gost_node_logger/api.go: -------------------------------------------------------------------------------- 1 | package gost_node_logger 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | service "server/service/admin/gost_node_logger" 7 | ) 8 | 9 | var svr = service.Service 10 | 11 | func Page(c *gin.Context) { 12 | var req service.PageReq 13 | if err := c.ShouldBindJSON(&req); err != nil { 14 | bean.Response.Param(c, err) 15 | return 16 | } 17 | list, total := svr.Page(req) 18 | bean.Response.OkData(c, bean.NewPage(list, total)) 19 | } 20 | -------------------------------------------------------------------------------- /server/controller/admin/gost_node_rule/api.go: -------------------------------------------------------------------------------- 1 | package gost_node_rule 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/service/admin/gost_node_rule" 7 | ) 8 | 9 | var svr = service.Service 10 | 11 | func List(c *gin.Context) { 12 | bean.Response.OkData(c, svr.List()) 13 | } 14 | -------------------------------------------------------------------------------- /server/controller/normal/gost/api.go: -------------------------------------------------------------------------------- 1 | package gost 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/router/middleware" 7 | "server/service/normal/gost" 8 | ) 9 | 10 | var svr = service.Service 11 | 12 | func Info(c *gin.Context) { 13 | bean.Response.OkData(c, svr.Info(middleware.GetClaims(c))) 14 | } 15 | -------------------------------------------------------------------------------- /server/controller/normal/gost_client_logger/api.go: -------------------------------------------------------------------------------- 1 | package gost_client_logger 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/router/middleware" 7 | "server/service/normal/gost_client_logger" 8 | ) 9 | 10 | var svr = service.Service 11 | 12 | func Page(c *gin.Context) { 13 | var req service.PageReq 14 | if err := c.ShouldBindJSON(&req); err != nil { 15 | bean.Response.Param(c, err) 16 | return 17 | } 18 | list, total := svr.Page(middleware.GetClaims(c), req) 19 | bean.Response.OkData(c, bean.NewPage(list, total)) 20 | } 21 | -------------------------------------------------------------------------------- /server/controller/normal/system_notice/api.go: -------------------------------------------------------------------------------- 1 | package system_user 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/service/normal/system_notice" 7 | ) 8 | 9 | var svr = service.Service 10 | 11 | func List(c *gin.Context) { 12 | bean.Response.OkData(c, svr.List()) 13 | } 14 | -------------------------------------------------------------------------------- /server/controller/public/system_config/api.go: -------------------------------------------------------------------------------- 1 | package system_config 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/service/public/system_config" 7 | ) 8 | 9 | var svr = service.Service 10 | 11 | func Query(c *gin.Context) { 12 | bean.Response.OkData(c, svr.Query()) 13 | } 14 | -------------------------------------------------------------------------------- /server/generate.bat: -------------------------------------------------------------------------------- 1 | go run ./generate/main.go -------------------------------------------------------------------------------- /server/generate.sh: -------------------------------------------------------------------------------- 1 | go run ./generate/main.go -------------------------------------------------------------------------------- /server/global/global.go: -------------------------------------------------------------------------------- 1 | package global 2 | 3 | import ( 4 | "github.com/robfig/cron/v3" 5 | "go.uber.org/zap" 6 | "server/configs" 7 | "server/pkg/jwt" 8 | "server/pkg/memory" 9 | "server/pkg/orm" 10 | ) 11 | 12 | var Logger *zap.Logger 13 | 14 | var Jwt *jwt.Tool 15 | 16 | var Cache memory.Interface 17 | 18 | var Config configs.Config 19 | 20 | var DB orm.Interface 21 | 22 | var Cron *cron.Cron 23 | -------------------------------------------------------------------------------- /server/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "server/cmd" 6 | ) 7 | 8 | func main() { 9 | if err := cmd.RootCmd.Execute(); err != nil { 10 | fmt.Println(err) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /server/model/gost_auth.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | GOST_TUNNEL_TYPE_HOST = 1 // 域名解析 5 | GOST_TUNNEL_TYPE_FORWARD = 2 // 端口转发 6 | GOST_TUNNEL_TYPE_TUNNEL = 3 // 私有隧道 7 | GOST_TUNNEL_TYPE_PROXY = 4 // 代理隧道 8 | GOST_TUNNEL_TYPE_P2P = 5 // 代理隧道 9 | ) 10 | 11 | type GostAuth struct { 12 | Base 13 | TunnelType int `gorm:"column:tunnel_type;index;default:1;comment:隧道类型"` 14 | TunnelCode string `gorm:"column:tunnel_code;size:100;uniqueIndex;default:'';comment:隧道编号"` 15 | User string `gorm:"column:user;size:100;uniqueIndex:gost_auth_uidx;comment:连接用户"` 16 | Password string `gorm:"column:password;size:100;uniqueIndex:gost_auth_uidx;comment:连接密码"` 17 | } 18 | -------------------------------------------------------------------------------- /server/model/gost_client.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "fmt" 4 | 5 | type GostClient struct { 6 | Base 7 | Name string `gorm:"column:name;index;comment:名称"` 8 | UserCode string `gorm:"column:user_code;index;comment:用户编号"` 9 | User SystemUser `gorm:"foreignKey:UserCode;references:Code"` 10 | Key string `gorm:"column:key;size:100;uniqueIndex;comment:连接密钥"` 11 | } 12 | 13 | func (n GostClient) GenerateClientPortCheck(host string, port string) map[string]string { 14 | return map[string]string{ 15 | "callback": fmt.Sprintf("%s/api/v1/public/gost/client/port", host), 16 | "code": n.Code, 17 | "port": port, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /server/model/gost_client_config.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "github.com/shopspring/decimal" 5 | ) 6 | 7 | type GostClientConfig struct { 8 | ChargingType int `gorm:"column:charging_type;default:1;index;comment:计费方式"` 9 | Cycle int `gorm:"column:cycle;default:0;comment:计费周期(天)"` 10 | Amount decimal.Decimal `gorm:"column:amount;default:0;comment:费用"` 11 | Limiter int `gorm:"column:limiter;comment:速度速率限制"` 12 | RLimiter int `gorm:"column:r_limiter;comment:并发数量限制"` 13 | CLimiter int `gorm:"column:c_limiter;comment:连接数量限制"` 14 | OnlyChina int `gorm:"column:only_china;size:1;default:1;comment:仅中国大陆可用"` 15 | ExpAt int64 `gorm:"column:exp_at;index;comment:套餐到期时间"` 16 | } 17 | -------------------------------------------------------------------------------- /server/model/gost_client_host_domain.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type GostClientHostDomain struct { 4 | Base 5 | Domain string `gorm:"column:domain;size:50;uniqueIndex;comment:自定义域名"` 6 | } 7 | -------------------------------------------------------------------------------- /server/model/gost_client_logger.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type GostClientLogger struct { 4 | Id int `gorm:"primaryKey"` 5 | Level string `gorm:"column:level;index"` 6 | ClientCode string `gorm:"column:client_code;index"` 7 | Content string `gorm:"column:content"` 8 | CreatedAt int64 `gorm:"column:created_at;index"` 9 | } 10 | -------------------------------------------------------------------------------- /server/model/gost_node_bind.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type GostNodeBind struct { 4 | Base 5 | NodeCode string `gorm:"column:node_code;uniqueIndex:uidx_gost_node_bind;comment:节点编号"` 6 | Node GostNode `gorm:"foreignKey:NodeCode;references:Code"` 7 | UserCode string `gorm:"column:user_code;uniqueIndex:uidx_gost_node_bind;comment:用户编号"` 8 | User SystemUser `gorm:"foreignKey:UserCode;references:Code"` 9 | } 10 | -------------------------------------------------------------------------------- /server/model/gost_node_domain.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type GostNodeDomain struct { 4 | Base 5 | Prefix string `gorm:"column:prefix;size:100;uniqueIndex:gost_node_domain_uidx;comment:域名前缀"` 6 | NodeCode string `gorm:"column:node_code;size:100;uniqueIndex:gost_node_domain_uidx;comment:节点编号"` 7 | } 8 | -------------------------------------------------------------------------------- /server/model/gost_node_logger.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type GostNodeLogger struct { 4 | Id int `gorm:"primaryKey"` 5 | Level string `gorm:"column:level;index"` 6 | NodeCode string `gorm:"column:node_code;index"` 7 | Content string `gorm:"column:content"` 8 | CreatedAt int64 `gorm:"column:created_at;index"` 9 | } 10 | -------------------------------------------------------------------------------- /server/model/gost_node_port.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type GostNodePort struct { 4 | Base 5 | Port string `gorm:"column:port;size:100;uniqueIndex:gost_node_port_uidx;comment:节点端口"` 6 | NodeCode string `gorm:"column:node_code;size:100;uniqueIndex:gost_node_port_uidx;comment:节点编号"` 7 | } 8 | -------------------------------------------------------------------------------- /server/model/gost_obs.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | const ( 4 | GOST_OBS_ORIGIN_KIND_NODE = 1 // 节点 5 | GOST_OBS_ORIGIN_KIND_CLIENT = 2 // 客户端 6 | GOST_OBS_ORIGIN_KIND_TUNNEL = 3 // 隧道 7 | GOST_OBS_ORIGIN_KIND_USER = 4 8 | ) 9 | 10 | type GostObs struct { 11 | Base 12 | OriginKind int `gorm:"column:origin_kind;index"` 13 | OriginCode string `gorm:"column:origin_code;size:100;uniqueIndex:gost_obs_uidx"` 14 | EventDate string `gorm:"column:event_date;size:100;uniqueIndex:gost_obs_uidx"` 15 | InputBytes int64 `gorm:"column:input_bytes;index"` 16 | OutputBytes int64 `gorm:"column:output_bytes;index"` 17 | } 18 | -------------------------------------------------------------------------------- /server/model/system_notice.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type SystemNotice struct { 4 | Base 5 | Title string `gorm:"column:title;comment:标题"` 6 | Content string `gorm:"column:content;comment:内容"` 7 | Hidden int `gorm:"column:hidden;size:2;comment:是否隐藏"` 8 | IndexValue int `gorm:"column:index_value;index;default:1000;comment:排序,升序"` 9 | } 10 | -------------------------------------------------------------------------------- /server/model/system_user.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/shopspring/decimal" 4 | 5 | const ( 6 | SYSTEM_IS_ADMIN = 1 // 是管理员 7 | SYSTEM_NO_ADMIN = 2 // 不是管理员 8 | ) 9 | 10 | type SystemUser struct { 11 | Base 12 | Account string `gorm:"column:account;size:100;uniqueIndex;comment:账号"` 13 | Password string `gorm:"column:password;comment:密码"` 14 | Salt string `gorm:"column:salt;size:8;comment:盐"` 15 | OtpKey string `gorm:"column:otp_key;index;default:'';not null"` 16 | Admin int `gorm:"column:admin;size:1;default:2;comment:是否为管理员"` 17 | Amount decimal.Decimal `gorm:"column:amount;default:0;comment:余额"` 18 | BindEmail SystemUserEmail `gorm:"foreignKey:UserCode;references:Code"` 19 | } 20 | -------------------------------------------------------------------------------- /server/model/system_user_checkin.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import "github.com/shopspring/decimal" 4 | 5 | type SystemUserCheckin struct { 6 | Base 7 | UserCode string `gorm:"column:user_code;size:100;uniqueIndex:system_user_checkin_uidx"` 8 | Account string `gorm:"column:account;size:100;uniqueIndex:system_user_checkin_uidx"` 9 | EventDate string `gorm:"column:event_date;size:20;uniqueIndex:system_user_checkin_uidx"` 10 | Amount decimal.Decimal `gorm:"column:amount;index"` 11 | } 12 | -------------------------------------------------------------------------------- /server/model/system_user_email.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | type SystemUserEmail struct { 4 | Base 5 | Email string `gorm:"column:email;size:200;uniqueIndex;comment:邮箱"` 6 | UserCode string `gorm:"column:user_code;size:100;uniqueIndex;comment:用户编号"` 7 | } 8 | -------------------------------------------------------------------------------- /server/pkg/captcha/default.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/server/pkg/captcha/default.ttf -------------------------------------------------------------------------------- /server/pkg/email/email.go: -------------------------------------------------------------------------------- 1 | package email 2 | 3 | import ( 4 | "gopkg.in/gomail.v2" 5 | ) 6 | 7 | type Config struct { 8 | Host string // 邮件地址 9 | Port int // 端口 10 | User string // 用户 11 | Pwd string // 密码 12 | } 13 | 14 | type Body struct { 15 | Mails []string // 接收者 16 | NickName string // 发件人昵称 17 | Title string // 标题 18 | Body string // 内容 19 | } 20 | 21 | func Send(config Config, data Body) error { 22 | m := gomail.NewMessage() 23 | m.SetHeader("From", m.FormatAddress(config.User, data.NickName)) 24 | m.SetHeader("To", data.Mails...) // 发送给多个用户 25 | m.SetHeader("Subject", data.Title) // 设置邮件主题 26 | m.SetBody("text/html", data.Body) // 设置邮件正文 27 | d := gomail.NewDialer(config.Host, config.Port, config.User, config.Pwd) 28 | err := d.DialAndSend(m) // 发送邮件 29 | return err 30 | } 31 | -------------------------------------------------------------------------------- /server/pkg/env/get.go: -------------------------------------------------------------------------------- 1 | package env 2 | 3 | import ( 4 | "os" 5 | "strconv" 6 | ) 7 | 8 | // GetEnv 获取环境变量 9 | func GetEnv[T any](key string, defaultValue T) T { 10 | var result T 11 | strValue, exists := os.LookupEnv(key) 12 | if !exists { 13 | return defaultValue 14 | } 15 | 16 | switch any(result).(type) { 17 | case string: 18 | return any(strValue).(T) 19 | case int: 20 | if v, err := strconv.Atoi(strValue); err == nil { 21 | return any(v).(T) 22 | } 23 | case bool: 24 | if v, err := strconv.ParseBool(strValue); err == nil { 25 | return any(v).(T) 26 | } 27 | case float64: 28 | if v, err := strconv.ParseFloat(strValue, 64); err == nil { 29 | return any(v).(T) 30 | } 31 | } 32 | 33 | return defaultValue 34 | } 35 | -------------------------------------------------------------------------------- /server/pkg/memory/interface.go: -------------------------------------------------------------------------------- 1 | package memory 2 | 3 | import "time" 4 | 5 | type AnyType interface { 6 | Marshal() []byte 7 | Unmarshal(data []byte) error 8 | } 9 | 10 | type Interface interface { 11 | SetStruct(key string, data any, duration time.Duration) 12 | GetStruct(key string, data any) error 13 | SetString(key string, value string, duration time.Duration) 14 | GetString(key string) (value string) 15 | Get(key string, data AnyType) error 16 | Set(key string, data AnyType, duration time.Duration) 17 | Del(key string) 18 | Sync() 19 | } 20 | -------------------------------------------------------------------------------- /server/pkg/orm/interface.go: -------------------------------------------------------------------------------- 1 | package orm 2 | 3 | import ( 4 | "gorm.io/gorm" 5 | ) 6 | 7 | type Interface interface { 8 | GetDB() *gorm.DB 9 | AutoMigrate(tables ...any) error 10 | Close() 11 | } 12 | -------------------------------------------------------------------------------- /server/pkg/signal/free.go: -------------------------------------------------------------------------------- 1 | package signal 2 | 3 | import ( 4 | "os" 5 | "os/signal" 6 | "syscall" 7 | ) 8 | 9 | func Free() <-chan os.Signal { 10 | signalChan := make(chan os.Signal, 1) 11 | signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) 12 | return signalChan 13 | } 14 | -------------------------------------------------------------------------------- /server/pkg/utils/calc.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | // TrinaryOperation 三目运算 4 | func TrinaryOperation[T interface{}](condition bool, resultA, resultB T) T { 5 | if condition { 6 | return resultA 7 | } 8 | return resultB 9 | } 10 | 11 | func InArray[T string | int](data T, list []T) bool { 12 | for _, item := range list { 13 | if data == item { 14 | return true 15 | } 16 | } 17 | return false 18 | } 19 | 20 | func Map[T any, F any](list []T, handler func(T) (F, bool)) (result []F) { 21 | for _, item := range list { 22 | value, ok := handler(item) 23 | if ok { 24 | result = append(result, value) 25 | } 26 | } 27 | return result 28 | } 29 | -------------------------------------------------------------------------------- /server/pkg/utils/date.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "time" 4 | 5 | func DateFormatLayout(layout string, times ...string) (times1 []time.Time, ok bool) { 6 | for _, t := range times { 7 | location, err := time.ParseInLocation(layout, t, time.Local) 8 | if err != nil { 9 | return nil, false 10 | } 11 | times1 = append(times1, location) 12 | } 13 | return times1, true 14 | } 15 | 16 | func DateRangeSplit(start, end time.Time) (times1 []time.Time, times2 []string) { 17 | var i = 0 18 | for { 19 | var date = start.AddDate(0, 0, i) 20 | times1 = append(times1, date) 21 | times2 = append(times2, date.Format(time.DateOnly)) 22 | if date.Unix() >= end.Unix() { 23 | break 24 | } 25 | i++ 26 | } 27 | return times1, times2 28 | } 29 | -------------------------------------------------------------------------------- /server/pkg/utils/md5.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | ) 7 | 8 | // MD5 MD5加密 9 | func MD5(str string) string { 10 | h := md5.New() 11 | h.Write([]byte(str)) 12 | return hex.EncodeToString(h.Sum(nil)) 13 | } 14 | 15 | // MD5AndSalt MD5+Salt加密 16 | func MD5AndSalt(str, salt string) (s string) { 17 | for i := 0; i < 2; i++ { 18 | s += MD5(str + salt) 19 | } 20 | return 21 | } 22 | -------------------------------------------------------------------------------- /server/pkg/utils/other.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | // GetVerErr 处理错误信息 8 | 9 | func FormatTimes(layout string, times ...string) (list []time.Time, ok bool) { 10 | for _, item := range times { 11 | t, err := time.ParseInLocation(layout, item, time.Local) 12 | if err != nil { 13 | return nil, false 14 | } 15 | list = append(list, t) 16 | } 17 | return list, true 18 | } 19 | -------------------------------------------------------------------------------- /server/pkg/utils/timeout.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import "time" 4 | 5 | func Timeout(duration time.Duration, f func(done func()) error) (err error) { 6 | wait := make(chan struct{}, 1) 7 | var done = func() { 8 | wait <- struct{}{} 9 | } 10 | ticker := time.NewTicker(duration) 11 | defer ticker.Stop() 12 | go func() { 13 | err = f(done) 14 | }() 15 | select { 16 | case <-wait: 17 | return err 18 | case <-ticker.C: 19 | return nil 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /server/pkg/utils/to_type.go: -------------------------------------------------------------------------------- 1 | package utils 2 | 3 | import ( 4 | "encoding/json" 5 | "strconv" 6 | ) 7 | 8 | func StrMustInt(str string) int { 9 | i, _ := strconv.Atoi(str) 10 | return i 11 | } 12 | 13 | func IntMustStr(i int) string { 14 | return strconv.Itoa(i) 15 | } 16 | 17 | func StructMustBytes(data any) []byte { 18 | marshal, _ := json.Marshal(data) 19 | return marshal 20 | } 21 | 22 | func BytesMustStruct(data []byte, target any) { 23 | _ = json.Unmarshal(data, target) 24 | } 25 | -------------------------------------------------------------------------------- /server/repository/repository.go: -------------------------------------------------------------------------------- 1 | package repository 2 | 3 | import ( 4 | "go.uber.org/zap" 5 | "server/global" 6 | "server/pkg/memory" 7 | "server/repository/query" 8 | ) 9 | 10 | func Get(domain string) (*query.Query, memory.Interface, *zap.Logger) { 11 | return query.Q, global.Cache, global.Logger 12 | } 13 | -------------------------------------------------------------------------------- /server/router/admin/dashboard.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/dashboard" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitDashboard(group *gin.RouterGroup) { 11 | g := group.Group("dashboard", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("userObs", dashboard.UserObs) 13 | g.POST("nodeObs", dashboard.NodeObs) 14 | g.POST("userObsDate", dashboard.UserObsDate) 15 | g.POST("nodeObsDate", dashboard.NodeObsDate) 16 | g.POST("count", dashboard.Count) 17 | } 18 | -------------------------------------------------------------------------------- /server/router/admin/gost_client.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_client" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClient(group *gin.RouterGroup) { 11 | g := group.Group("gost/client", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("create", gost_client.Create) 13 | g.POST("delete", gost_client.Delete) 14 | g.POST("page", gost_client.Page) 15 | g.POST("list", gost_client.List) 16 | } 17 | -------------------------------------------------------------------------------- /server/router/admin/gost_client_forward.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_client_forward" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientForward(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/forward", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("create", gost_client_forward.Create) 13 | g.POST("config", gost_client_forward.Config) 14 | g.POST("delete", gost_client_forward.Delete) 15 | g.POST("page", gost_client_forward.Page) 16 | } 17 | -------------------------------------------------------------------------------- /server/router/admin/gost_client_host.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_client_host" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientHost(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/host", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("create", gost_client_host.Create) 13 | g.POST("config", gost_client_host.Config) 14 | g.POST("delete", gost_client_host.Delete) 15 | g.POST("page", gost_client_host.Page) 16 | } 17 | -------------------------------------------------------------------------------- /server/router/admin/gost_client_logger.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_client_logger" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientLogger(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/logger", middleware.Auth(global.Jwt)) 12 | g.POST("page", gost_client_logger.Page) 13 | } 14 | -------------------------------------------------------------------------------- /server/router/admin/gost_client_p2p.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_client_p2p" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientP2P(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/p2p", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("config", gost_client_p2p.Config) 13 | g.POST("delete", gost_client_p2p.Delete) 14 | g.POST("page", gost_client_p2p.Page) 15 | } 16 | -------------------------------------------------------------------------------- /server/router/admin/gost_client_proxy.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_client_proxy" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientProxy(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/proxy", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("config", gost_client_proxy.Config) 13 | g.POST("delete", gost_client_proxy.Delete) 14 | g.POST("page", gost_client_proxy.Page) 15 | } 16 | -------------------------------------------------------------------------------- /server/router/admin/gost_client_tunnel.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_client_tunnel" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientTunnel(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/tunnel", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("create", gost_client_tunnel.Create) 13 | g.POST("config", gost_client_tunnel.Config) 14 | g.POST("delete", gost_client_tunnel.Delete) 15 | g.POST("page", gost_client_tunnel.Page) 16 | } 17 | -------------------------------------------------------------------------------- /server/router/admin/gost_config.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_node_config" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostNodeConfig(group *gin.RouterGroup) { 11 | g := group.Group("gost/node/config", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("create", gost_node_config.Create) 13 | g.POST("delete", gost_node_config.Delete) 14 | g.POST("update", gost_node_config.Update) 15 | g.POST("page", gost_node_config.Page) 16 | g.POST("list", gost_node_config.List) 17 | } 18 | -------------------------------------------------------------------------------- /server/router/admin/gost_node.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_node" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostNode(group *gin.RouterGroup) { 11 | g := group.Group("gost/node", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("cleanPort", gost_node.CleanPort) 13 | g.POST("create", gost_node.Create) 14 | g.POST("delete", gost_node.Delete) 15 | g.POST("update", gost_node.Update) 16 | g.POST("query", gost_node.Query) 17 | g.POST("page", gost_node.Page) 18 | g.POST("list", gost_node.List) 19 | } 20 | -------------------------------------------------------------------------------- /server/router/admin/gost_node_bind.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_node_bind" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostNodeBind(group *gin.RouterGroup) { 11 | g := group.Group("gost/node/bind", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("update", gost_node.Update) 13 | } 14 | -------------------------------------------------------------------------------- /server/router/admin/gost_node_logger.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_node_logger" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostNodeLogger(group *gin.RouterGroup) { 11 | g := group.Group("gost/node/logger", middleware.Auth(global.Jwt)) 12 | g.POST("page", gost_node_logger.Page) 13 | } 14 | -------------------------------------------------------------------------------- /server/router/admin/gost_node_rule.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/gost_node_rule" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostNodeRule(group *gin.RouterGroup) { 11 | g := group.Group("gost/node/rule", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("list", gost_node_rule.List) 13 | } 14 | -------------------------------------------------------------------------------- /server/router/admin/system_config.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/system_config" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitSystemConfig(group *gin.RouterGroup) { 11 | g := group.Group("system/config", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("base", system_config.Base) 13 | g.POST("gost", system_config.Gost) 14 | g.POST("email", system_config.Email) 15 | g.POST("emailVerify", system_config.EmailVerify) 16 | g.POST("query", system_config.Query) 17 | } 18 | -------------------------------------------------------------------------------- /server/router/admin/system_notice.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/system_notice" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitSystemNotice(group *gin.RouterGroup) { 11 | g := group.Group("system/notice", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("create", system_user.Create) 13 | g.POST("update", system_user.Update) 14 | g.POST("delete", system_user.Delete) 15 | g.POST("page", system_user.Page) 16 | } 17 | -------------------------------------------------------------------------------- /server/router/admin/system_user.go: -------------------------------------------------------------------------------- 1 | package admin 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/admin/system_user" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitSystemUser(group *gin.RouterGroup) { 11 | g := group.Group("system/user", middleware.Auth(global.Jwt), middleware.AuthAdmin()) 12 | g.POST("create", system_user.Create) 13 | g.POST("update", system_user.Update) 14 | g.POST("delete", system_user.Delete) 15 | g.POST("page", system_user.Page) 16 | g.POST("list", system_user.List) 17 | } 18 | -------------------------------------------------------------------------------- /server/router/middleware/auth.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | "server/pkg/jwt" 7 | ) 8 | 9 | func Auth(tool *jwt.Tool) gin.HandlerFunc { 10 | return func(c *gin.Context) { 11 | token := GetToken(c) 12 | if token == "" { 13 | bean.Response.AuthNoLogin(c) 14 | c.Abort() 15 | return 16 | } 17 | claims, err := tool.ValidToken(token) 18 | if err != nil { 19 | bean.Response.AuthInvalid(c) 20 | c.Abort() 21 | return 22 | } 23 | c.Set("claims", claims) 24 | } 25 | } 26 | 27 | func GetToken(c *gin.Context) string { 28 | return c.GetHeader("token") 29 | } 30 | 31 | func GetClaims(c *gin.Context) jwt.Claims { 32 | claims, exists := c.Get("claims") 33 | if !exists { 34 | panic("get claims fail") 35 | } 36 | return claims.(jwt.Claims) 37 | } 38 | -------------------------------------------------------------------------------- /server/router/middleware/auth_admin.go: -------------------------------------------------------------------------------- 1 | package middleware 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/pkg/bean" 6 | ) 7 | 8 | func AuthAdmin() gin.HandlerFunc { 9 | return func(c *gin.Context) { 10 | claims := GetClaims(c) 11 | if claims.Data == nil { 12 | bean.Response.AuthNoAllow(c) 13 | c.Abort() 14 | return 15 | } 16 | if claims.Data["admin"] != "1" { 17 | bean.Response.AuthNoAllow(c) 18 | c.Abort() 19 | return 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /server/router/normal/gost.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGost(group *gin.RouterGroup) { 11 | g := group.Group("gost", middleware.Auth(global.Jwt)) 12 | g.POST("info", gost.Info) 13 | } 14 | -------------------------------------------------------------------------------- /server/router/normal/gost_client.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_client" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClient(group *gin.RouterGroup) { 11 | g := group.Group("gost/client", middleware.Auth(global.Jwt)) 12 | g.POST("create", gost_client.Create) 13 | g.POST("update", gost_client.Update) 14 | g.POST("delete", gost_client.Delete) 15 | g.POST("page", gost_client.Page) 16 | g.POST("list", gost_client.List) 17 | } 18 | -------------------------------------------------------------------------------- /server/router/normal/gost_client_forward.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_client_forward" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientForward(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/forward", middleware.Auth(global.Jwt)) 12 | g.POST("create", gost_client_forward.Create) 13 | g.POST("admission", gost_client_forward.Admission) 14 | g.POST("renew", gost_client_forward.Renew) 15 | g.POST("update", gost_client_forward.Update) 16 | g.POST("matcher", gost_client_forward.Matcher) 17 | g.POST("enable", gost_client_forward.Enable) 18 | g.POST("delete", gost_client_forward.Delete) 19 | g.POST("page", gost_client_forward.Page) 20 | } 21 | -------------------------------------------------------------------------------- /server/router/normal/gost_client_host.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_client_host" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientHost(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/host", middleware.Auth(global.Jwt)) 12 | g.POST("create", gost_client_host.Create) 13 | g.POST("domain", gost_client_host.Domain) 14 | g.POST("admission", gost_client_host.Admission) 15 | g.POST("renew", gost_client_host.Renew) 16 | g.POST("update", gost_client_host.Update) 17 | g.POST("enable", gost_client_host.Enable) 18 | g.POST("delete", gost_client_host.Delete) 19 | g.POST("page", gost_client_host.Page) 20 | } 21 | -------------------------------------------------------------------------------- /server/router/normal/gost_client_logger.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_client_logger" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientLogger(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/logger", middleware.Auth(global.Jwt)) 12 | g.POST("page", gost_client_logger.Page) 13 | } 14 | -------------------------------------------------------------------------------- /server/router/normal/gost_client_p2p.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_client_p2p" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientP2P(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/p2p", middleware.Auth(global.Jwt)) 12 | g.POST("create", gost_client_p2p.Create) 13 | g.POST("renew", gost_client_p2p.Renew) 14 | g.POST("update", gost_client_p2p.Update) 15 | g.POST("enable", gost_client_p2p.Enable) 16 | g.POST("delete", gost_client_p2p.Delete) 17 | g.POST("page", gost_client_p2p.Page) 18 | } 19 | -------------------------------------------------------------------------------- /server/router/normal/gost_client_proxy.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_client_proxy" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientProxy(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/proxy", middleware.Auth(global.Jwt)) 12 | g.POST("create", gost_client_proxy.Create) 13 | g.POST("renew", gost_client_proxy.Renew) 14 | g.POST("update", gost_client_proxy.Update) 15 | g.POST("enable", gost_client_proxy.Enable) 16 | g.POST("delete", gost_client_proxy.Delete) 17 | g.POST("page", gost_client_proxy.Page) 18 | } 19 | -------------------------------------------------------------------------------- /server/router/normal/gost_client_tunnel.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_client_tunnel" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostClientTunnel(group *gin.RouterGroup) { 11 | g := group.Group("gost/client/tunnel", middleware.Auth(global.Jwt)) 12 | g.POST("create", gost_client_tunnel.Create) 13 | g.POST("renew", gost_client_tunnel.Renew) 14 | g.POST("update", gost_client_tunnel.Update) 15 | g.POST("enable", gost_client_tunnel.Enable) 16 | g.POST("delete", gost_client_tunnel.Delete) 17 | g.POST("page", gost_client_tunnel.Page) 18 | } 19 | -------------------------------------------------------------------------------- /server/router/normal/gost_node.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_node" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostNode(group *gin.RouterGroup) { 11 | g := group.Group("gost/node", middleware.Auth(global.Jwt)) 12 | g.POST("list", gost_node.List) 13 | g.POST("cleanPort", gost_node.CleanPort) 14 | g.POST("create", gost_node.Create) 15 | g.POST("delete", gost_node.Delete) 16 | g.POST("update", gost_node.Update) 17 | g.POST("page", gost_node.Page) 18 | } 19 | -------------------------------------------------------------------------------- /server/router/normal/gost_obs.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/gost_obs" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitGostObs(group *gin.RouterGroup) { 11 | g := group.Group("gost/obs", middleware.Auth(global.Jwt)) 12 | g.POST("tunnel/month", gost_obs.TunnelMonth) 13 | g.POST("client/month", gost_obs.ClientMonth) 14 | g.POST("node/month", gost_obs.NodeMonth) 15 | g.POST("user/month", gost_obs.UserMonth) 16 | } 17 | -------------------------------------------------------------------------------- /server/router/normal/system_notice.go: -------------------------------------------------------------------------------- 1 | package normal 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/normal/system_notice" 6 | "server/global" 7 | "server/router/middleware" 8 | ) 9 | 10 | func InitSystemNotice(group *gin.RouterGroup) { 11 | g := group.Group("system/notice", middleware.Auth(global.Jwt)) 12 | g.POST("list", system_user.List) 13 | } 14 | -------------------------------------------------------------------------------- /server/router/public/gost.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/public/gost" 6 | ) 7 | 8 | func InitGost(group *gin.RouterGroup) { 9 | g := group.Group("gost") 10 | g.Any("client/ws", gost.ClientWs) 11 | g.Any("node/ws", gost.NodeWs) 12 | g.Any("node/port", gost.NodePort) 13 | g.Any("client/port", gost.ClientPort) 14 | g.Any("ingress", gost.Ingress) 15 | g.Any("auther", gost.Auther) 16 | g.Any("admission", gost.Admission) 17 | g.Any("limiter", gost.Limiter) 18 | g.Any("obs", gost.Obs) 19 | g.Any("visit", gost.Visit) 20 | } 21 | -------------------------------------------------------------------------------- /server/router/public/p2p.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/public/p2p" 6 | ) 7 | 8 | func InitP2P(group *gin.RouterGroup) { 9 | g := group.Group("p2p") 10 | g.Any("new", p2p.New) 11 | g.Any("visit", p2p.Visit) 12 | } 13 | -------------------------------------------------------------------------------- /server/router/public/system_config.go: -------------------------------------------------------------------------------- 1 | package public 2 | 3 | import ( 4 | "github.com/gin-gonic/gin" 5 | "server/controller/public/system_config" 6 | ) 7 | 8 | func InitSystemConfig(group *gin.RouterGroup) { 9 | g := group.Group("system/config") 10 | g.POST("query", system_config.Query) 11 | } 12 | -------------------------------------------------------------------------------- /server/service/admin/dashboard/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_client/service.create.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "github.com/google/uuid" 6 | "go.uber.org/zap" 7 | "server/model" 8 | "server/repository" 9 | ) 10 | 11 | type CreateReq struct { 12 | Name string `binding:"required" json:"name" label:"名称"` 13 | UserCode string `binding:"required" json:"userCode" label:"用户编号"` 14 | } 15 | 16 | func (service *service) Create(req CreateReq) error { 17 | db, _, log := repository.Get("") 18 | if err := db.GostClient.Create(&model.GostClient{ 19 | Name: req.Name, 20 | UserCode: req.UserCode, 21 | Key: uuid.NewString(), 22 | }); err != nil { 23 | log.Error("新增用户客户端失败", zap.Error(err)) 24 | return errors.New("操作失败") 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /server/service/admin/gost_client/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_client/service.list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "gorm.io/gen" 5 | "server/repository" 6 | ) 7 | 8 | type ListReq struct { 9 | UserCode string `json:"userCode"` 10 | } 11 | 12 | type ListItem struct { 13 | Code string `json:"code"` 14 | Name string `json:"name"` 15 | } 16 | 17 | func (service *service) List(req ListReq) (list []ListItem) { 18 | db, _, _ := repository.Get("") 19 | 20 | var where []gen.Condition 21 | if req.UserCode != "" { 22 | where = append(where, db.GostClient.UserCode.Eq(req.UserCode)) 23 | } 24 | 25 | clients, _ := db.GostClient.Preload(db.GostClient.User).Where(where...).Find() 26 | for _, client := range clients { 27 | list = append(list, ListItem{ 28 | Code: client.Code, 29 | Name: client.Name, 30 | }) 31 | } 32 | return list 33 | } 34 | -------------------------------------------------------------------------------- /server/service/admin/gost_client_forward/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_client_host/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_client_logger/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_client_p2p/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_client_proxy/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_client_tunnel/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_node/service.clean.port.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/repository" 5 | "server/service/common/cache" 6 | "server/service/common/node_port" 7 | ) 8 | 9 | type CleanPortReq struct { 10 | Code string `binding:"required" json:"code"` 11 | } 12 | 13 | func (service *service) CleanPort(req CleanPortReq) error { 14 | db, _, _ := repository.Get("") 15 | node, _ := db.GostNode.Where(db.GostNode.Code.Eq(req.Code)).First() 16 | if node == nil { 17 | return nil 18 | } 19 | cache.DelNodePortUse(node.Code) 20 | node_port.Arrange(db, node.Code) 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /server/service/admin/gost_node/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_node_bind/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_node_config/service.delete.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "go.uber.org/zap" 6 | "server/repository" 7 | ) 8 | 9 | type DeleteReq struct { 10 | Code string `binding:"required" json:"code" label:"编号"` 11 | } 12 | 13 | func (service *service) Delete(req DeleteReq) error { 14 | db, _, log := repository.Get("") 15 | cfg, _ := db.GostNodeConfig.Where(db.GostNodeConfig.Code.Eq(req.Code)).First() 16 | if cfg == nil { 17 | return nil 18 | } 19 | 20 | if _, err := db.GostNodeConfig.Where(db.GostNodeConfig.Code.Eq(cfg.Code)).Delete(); err != nil { 21 | log.Error("删除套餐配置失败", zap.Error(err)) 22 | return errors.New("操作失败") 23 | } 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /server/service/admin/gost_node_config/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_node_logger/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_node_rule/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/gost_node_rule/service.list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import "server/service/common/node_rule" 4 | 5 | type Item struct { 6 | Code string `json:"code"` 7 | Name string `json:"name"` 8 | Description string `json:"description"` 9 | } 10 | 11 | func (*service) List() (list []Item) { 12 | for _, v := range node_rule.RuleList { 13 | list = append(list, Item{ 14 | Code: v.Code(), 15 | Name: v.Name(), 16 | Description: v.Description(), 17 | }) 18 | } 19 | return list 20 | } 21 | -------------------------------------------------------------------------------- /server/service/admin/system_config/service.email.verify.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "server/model" 6 | "server/pkg/email" 7 | "server/pkg/utils" 8 | "server/service/common/cache" 9 | ) 10 | 11 | type EmailVerifyReq struct { 12 | Email string `binding:"required" json:"email"` 13 | } 14 | 15 | func (service *service) EmailVerify(req EmailVerifyReq) error { 16 | var cfg model.SystemConfigEmail 17 | cache.GetSystemConfigEmail(&cfg) 18 | if cfg.Enable != "1" { 19 | return errors.New("未启用服务") 20 | } 21 | 22 | return email.Send(email.Config{ 23 | Host: cfg.Host, 24 | Port: utils.StrMustInt(cfg.Port), 25 | User: cfg.User, 26 | Pwd: cfg.Pwd, 27 | }, email.Body{ 28 | Mails: []string{req.Email}, 29 | NickName: cfg.NickName, 30 | Title: "测试邮件", 31 | Body: "test success", 32 | }) 33 | } 34 | -------------------------------------------------------------------------------- /server/service/admin/system_config/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/system_config/service.query.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/model" 5 | "server/service/common/cache" 6 | ) 7 | 8 | type QueryReq struct { 9 | Kind string `binding:"required" json:"kind"` 10 | } 11 | 12 | func (service *service) Query(req QueryReq) any { 13 | switch req.Kind { 14 | case model.SYSTEM_CONFIG_KIND_BASE: 15 | var cfg model.SystemConfigBase 16 | cache.GetSystemConfigBase(&cfg) 17 | return cfg 18 | case model.SYSTEM_CONFIG_KIND_GOST: 19 | var cfg model.SystemConfigGost 20 | cache.GetSystemConfigGost(&cfg) 21 | return cfg 22 | case model.SYSTEM_CONFIG_KIND_EMAIL: 23 | var cfg model.SystemConfigEmail 24 | cache.GetSystemConfigEmail(&cfg) 25 | return cfg 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /server/service/admin/system_notice/service.create.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "server/model" 6 | "server/repository" 7 | ) 8 | 9 | type CreateReq struct { 10 | Title string `binding:"required" json:"title"` 11 | Content string `binding:"required" json:"content"` 12 | Hidden int `binding:"required" json:"hidden"` 13 | IndexValue int `json:"indexValue"` 14 | } 15 | 16 | func (service *service) Create(req CreateReq) error { 17 | db, _, _ := repository.Get("") 18 | if err := db.SystemNotice.Create(&model.SystemNotice{ 19 | Title: req.Title, 20 | Content: req.Content, 21 | Hidden: req.Hidden, 22 | IndexValue: req.IndexValue, 23 | }); err != nil { 24 | return errors.New("操作失败") 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /server/service/admin/system_notice/service.delete.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "server/repository" 6 | ) 7 | 8 | type DeleteReq struct { 9 | Code string `binding:"required" json:"code" label:"编号"` 10 | } 11 | 12 | func (service *service) Delete(req DeleteReq) error { 13 | db, _, _ := repository.Get("") 14 | if _, err := db.SystemNotice.Where(db.SystemNotice.Code.Eq(req.Code)).Delete(); err != nil { 15 | return errors.New("操作失败") 16 | } 17 | return nil 18 | } 19 | -------------------------------------------------------------------------------- /server/service/admin/system_notice/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/system_user/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/admin/system_user/service.list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/repository" 5 | ) 6 | 7 | type ListReq struct { 8 | Account string `binding:"required" json:"account"` 9 | } 10 | 11 | type ListItem struct { 12 | Code string `json:"code"` 13 | Account string `json:"account"` 14 | } 15 | 16 | func (service *service) List(req ListReq) (list []ListItem) { 17 | db, _, _ := repository.Get("") 18 | users, _ := db.SystemUser.Where(db.SystemUser.Account.Like("%"+req.Account+"%")).Select( 19 | db.SystemUser.Code, 20 | db.SystemUser.Account, 21 | ).Order(db.SystemUser.Id.Desc()).Find() 22 | for _, user := range users { 23 | list = append(list, ListItem{ 24 | Code: user.Code, 25 | Account: user.Account, 26 | }) 27 | } 28 | return list 29 | } 30 | -------------------------------------------------------------------------------- /server/service/auth/service.close.otp.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "go.uber.org/zap" 6 | "server/pkg/jwt" 7 | "server/repository" 8 | "server/repository/query" 9 | ) 10 | 11 | func (service *service) CloseOtp(claims jwt.Claims) (err error) { 12 | db, _, log := repository.Get("") 13 | return db.Transaction(func(tx *query.Query) error { 14 | user, err := tx.SystemUser.Where(tx.SystemUser.Code.Eq(claims.Code)).First() 15 | if err != nil { 16 | return errors.New("账号错误") 17 | } 18 | if user.OtpKey == "" { 19 | return nil 20 | } 21 | user.OtpKey = "" 22 | if err := tx.SystemUser.Save(user); err != nil { 23 | log.Error("关闭otp失败", zap.Error(err)) 24 | return errors.New("操作失败") 25 | } 26 | return nil 27 | }) 28 | } 29 | -------------------------------------------------------------------------------- /server/service/auth/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/auth/service.unbind.email.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/pkg/jwt" 5 | "server/repository" 6 | ) 7 | 8 | func (service *service) UnBindEmail(claims jwt.Claims) { 9 | db, _, _ := repository.Get("") 10 | _, _ = db.SystemUserEmail.Where(db.SystemUserEmail.UserCode.Eq(claims.Code)).Delete() 11 | } 12 | -------------------------------------------------------------------------------- /server/service/common/cache/auth.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/patrickmn/go-cache" 5 | "server/global" 6 | ) 7 | 8 | const ( 9 | gost_auth_key = "gost:auth:" 10 | ) 11 | 12 | func SetGostAuth(user, pwd, code string) { 13 | global.Cache.SetString(gost_auth_key+user+pwd, code, cache.NoExpiration) 14 | } 15 | 16 | func GetGostAuth(user, pwd string) string { 17 | return global.Cache.GetString(gost_auth_key + user + pwd) 18 | } 19 | 20 | func DelGostAuth(user, pwd string) { 21 | global.Cache.Del(gost_auth_key + user + pwd) 22 | } 23 | -------------------------------------------------------------------------------- /server/service/common/cache/bind.opt.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "server/global" 5 | "time" 6 | ) 7 | 8 | const ( 9 | auth_bind_otp_key = "auth:bind:otp:" 10 | ) 11 | 12 | func SetBindOtp(key string, value string, duration time.Duration) { 13 | global.Cache.SetString(auth_bind_otp_key+key, value, duration) 14 | } 15 | 16 | func GetBindOtp(key string, remove bool) string { 17 | defer func() { 18 | if remove { 19 | global.Cache.Del(auth_bind_otp_key + key) 20 | } 21 | }() 22 | return global.Cache.GetString(auth_bind_otp_key + key) 23 | } 24 | -------------------------------------------------------------------------------- /server/service/common/cache/captcha.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "server/global" 5 | "time" 6 | ) 7 | 8 | const ( 9 | auth_captcha_key = "auth:captcha:" 10 | ) 11 | 12 | func SetCaptcha(key string, value string, duration time.Duration) { 13 | global.Cache.SetString(auth_captcha_key+key, value, duration) 14 | } 15 | 16 | func ValidCaptcha(key string, target string, remove bool) bool { 17 | defer func() { 18 | if remove { 19 | global.Cache.Del(auth_captcha_key + key) 20 | } 21 | }() 22 | return global.Cache.GetString(auth_captcha_key+key) == target 23 | } 24 | -------------------------------------------------------------------------------- /server/service/common/cache/ingress.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/patrickmn/go-cache" 5 | "server/global" 6 | ) 7 | 8 | const ( 9 | gost_plugins_ingress_key = "gost_plugins:ingress:" 10 | ) 11 | 12 | func SetIngress(host, tunnelCode string) { 13 | global.Cache.SetString(gost_plugins_ingress_key+host, tunnelCode, cache.NoExpiration) 14 | } 15 | 16 | func GetIngress(host string) string { 17 | return global.Cache.GetString(gost_plugins_ingress_key + host) 18 | } 19 | 20 | func DelIngress(host string) { 21 | global.Cache.Del(gost_plugins_ingress_key + host) 22 | } 23 | -------------------------------------------------------------------------------- /server/service/common/cache/ip.security.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/patrickmn/go-cache" 5 | "server/global" 6 | ) 7 | 8 | const ( 9 | ip_security_key = "ip:security:" 10 | ) 11 | 12 | func SetIpSecurity(ip string, security bool) { 13 | global.Cache.SetString(ip_security_key+ip, func() string { 14 | if security { 15 | return "1" 16 | } 17 | return "2" 18 | }(), cache.NoExpiration) 19 | } 20 | 21 | func GetIpSecurity(ip string) bool { 22 | return global.Cache.GetString(ip_security_key+ip) != "2" 23 | } 24 | -------------------------------------------------------------------------------- /server/service/common/cache/login.opt.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "server/global" 5 | "time" 6 | ) 7 | 8 | const ( 9 | auth_login_otp_key = "auth:login:otp:" 10 | ) 11 | 12 | func SetLoginOtp(key string, value string, duration time.Duration) { 13 | global.Cache.SetString(auth_login_otp_key+key, value, duration) 14 | } 15 | 16 | func GetLoginOtp(key string, remove bool) string { 17 | defer func() { 18 | if remove { 19 | global.Cache.Del(auth_login_otp_key + key) 20 | } 21 | }() 22 | return global.Cache.GetString(auth_login_otp_key + key) 23 | } 24 | -------------------------------------------------------------------------------- /server/service/common/cache/tunnel.go: -------------------------------------------------------------------------------- 1 | package cache 2 | 3 | import ( 4 | "github.com/patrickmn/go-cache" 5 | "server/global" 6 | ) 7 | 8 | const ( 9 | tunnel_info_key = "tunnel:info:" 10 | ) 11 | 12 | type TunnelInfo struct { 13 | Code string 14 | Type int 15 | ClientCode string 16 | UserCode string 17 | NodeCode string 18 | ChargingTye int 19 | ExpAt int64 20 | Limiter int 21 | } 22 | 23 | func SetTunnelInfo(info TunnelInfo) { 24 | global.Cache.SetStruct(tunnel_info_key+info.Code, info, cache.NoExpiration) 25 | } 26 | 27 | func GetTunnelInfo(code string) (result TunnelInfo) { 28 | _ = global.Cache.GetStruct(tunnel_info_key+code, &result) 29 | return result 30 | } 31 | 32 | func DelTunnelInfo(code string) { 33 | global.Cache.Del(tunnel_info_key + code) 34 | } 35 | -------------------------------------------------------------------------------- /server/service/common/node_rule/default.rule.go: -------------------------------------------------------------------------------- 1 | package node_rule 2 | 3 | import ( 4 | "server/repository/query" 5 | ) 6 | 7 | func init() { 8 | } 9 | 10 | type DefaultRule struct{} 11 | 12 | func (u DefaultRule) Name() string { 13 | return "无使用条件约束" 14 | } 15 | 16 | func (u DefaultRule) Code() string { 17 | return "" 18 | } 19 | 20 | func (u DefaultRule) Description() string { 21 | return "无使用条件约束" 22 | } 23 | 24 | func (u DefaultRule) Allow(tx *query.Query, userCode string) bool { 25 | return true 26 | } 27 | -------------------------------------------------------------------------------- /server/service/common/node_rule/interface.go: -------------------------------------------------------------------------------- 1 | package node_rule 2 | 3 | import ( 4 | "server/repository/query" 5 | ) 6 | 7 | type RuleInterface interface { 8 | Code() string 9 | Allow(db *query.Query, userCode string) bool 10 | Name() string 11 | Description() string 12 | } 13 | 14 | var RuleList []RuleInterface 15 | var RuleMap = make(map[string]RuleInterface) 16 | 17 | func init() { 18 | RuleList = append(RuleList, 19 | DefaultRule{}, 20 | UserLevelRule{}, 21 | UserQQGroupRule{}) 22 | for _, rule := range RuleList { 23 | RuleMap[rule.Code()] = rule 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /server/service/common/node_rule/user.level.rule.go: -------------------------------------------------------------------------------- 1 | package node_rule 2 | 3 | import "server/repository/query" 4 | 5 | func init() { 6 | } 7 | 8 | type UserLevelRule struct{} 9 | 10 | func (u UserLevelRule) Code() string { 11 | return "user_level_rule" 12 | } 13 | 14 | func (u UserLevelRule) Name() string { 15 | return "账号等级限制1级" 16 | } 17 | 18 | func (u UserLevelRule) Description() string { 19 | return "需要账号等级达到1级" 20 | } 21 | 22 | func (u UserLevelRule) Allow(tx *query.Query, userCode string) bool { 23 | // 允许使用 24 | return true 25 | } 26 | -------------------------------------------------------------------------------- /server/service/common/node_rule/user.qq.group.rule.go: -------------------------------------------------------------------------------- 1 | package node_rule 2 | 3 | import ( 4 | "server/repository/query" 5 | ) 6 | 7 | type UserQQGroupRule struct{} 8 | 9 | func (u UserQQGroupRule) Code() string { 10 | return "user_qq_group_rule" 11 | } 12 | 13 | func (u UserQQGroupRule) Name() string { 14 | return "绑定QQ号" 15 | } 16 | 17 | func (u UserQQGroupRule) Description() string { 18 | return "需要绑定QQ号" 19 | } 20 | 21 | func (u UserQQGroupRule) Allow(tx *query.Query, userCode string) bool { 22 | return true 23 | } 24 | -------------------------------------------------------------------------------- /server/service/normal/gost/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_client/service.create.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "github.com/google/uuid" 6 | "go.uber.org/zap" 7 | "server/model" 8 | "server/pkg/jwt" 9 | "server/repository" 10 | ) 11 | 12 | type CreateReq struct { 13 | Name string `binding:"required" json:"name" label:"名称"` 14 | } 15 | 16 | func (service *service) Create(claims jwt.Claims, req CreateReq) error { 17 | db, _, log := repository.Get("") 18 | if err := db.GostClient.Create(&model.GostClient{ 19 | Name: req.Name, 20 | UserCode: claims.Code, 21 | Key: uuid.NewString(), 22 | }); err != nil { 23 | log.Error("新增客户端失败", zap.Error(err)) 24 | return errors.New("操作失败") 25 | } 26 | return nil 27 | } 28 | -------------------------------------------------------------------------------- /server/service/normal/gost_client/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_client/service.list.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/pkg/jwt" 5 | "server/pkg/utils" 6 | "server/repository" 7 | "server/service/common/cache" 8 | ) 9 | 10 | type ListItem struct { 11 | Code string `json:"code"` 12 | Name string `json:"name"` 13 | Online int `json:"online"` 14 | } 15 | 16 | func (service *service) List(claims jwt.Claims) (list []ListItem) { 17 | db, _, _ := repository.Get("") 18 | clients, _ := db.GostClient.Where(db.GostClient.UserCode.Eq(claims.Code)).Order(db.GostClient.Id.Desc()).Find() 19 | for _, client := range clients { 20 | list = append(list, ListItem{ 21 | Code: client.Code, 22 | Name: client.Name, 23 | Online: utils.TrinaryOperation(cache.GetClientOnline(client.Code), 1, 2), 24 | }) 25 | } 26 | return list 27 | } 28 | -------------------------------------------------------------------------------- /server/service/normal/gost_client/service.update.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "errors" 5 | "go.uber.org/zap" 6 | "server/pkg/jwt" 7 | "server/repository" 8 | ) 9 | 10 | type UpdateReq struct { 11 | Code string `binding:"required" json:"code"` 12 | Name string `binding:"required" json:"name" label:"名称"` 13 | } 14 | 15 | func (service *service) Update(claims jwt.Claims, req UpdateReq) error { 16 | db, _, log := repository.Get("") 17 | client, _ := db.GostClient.Where(db.GostClient.Code.Eq(req.Code), db.GostClient.UserCode.Eq(claims.Code)).First() 18 | if client == nil { 19 | return errors.New("客户端错误") 20 | } 21 | 22 | client.Name = req.Name 23 | if err := db.GostClient.Save(client); err != nil { 24 | log.Error("修改客户端失败", zap.Error(err)) 25 | return errors.New("操作失败") 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /server/service/normal/gost_client_forward/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_client_host/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_client_logger/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_client_p2p/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_client_proxy/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_client_tunnel/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_node/service.clean.port.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/pkg/jwt" 5 | "server/repository" 6 | "server/service/common/cache" 7 | "server/service/common/node_port" 8 | ) 9 | 10 | type CleanPortReq struct { 11 | Code string `binding:"required" json:"code"` 12 | } 13 | 14 | func (service *service) CleanPort(claims jwt.Claims, req CleanPortReq) error { 15 | db, _, _ := repository.Get("") 16 | nodeBind, _ := db.GostNodeBind.Where(db.GostNodeBind.NodeCode.Eq(req.Code), db.GostNodeBind.UserCode.Eq(claims.Code)).First() 17 | if nodeBind == nil { 18 | return nil 19 | } 20 | 21 | node, _ := db.GostNode.Where(db.GostNode.Code.Eq(req.Code)).First() 22 | if node == nil { 23 | return nil 24 | } 25 | cache.DelNodePortUse(node.Code) 26 | node_port.Arrange(db, node.Code) 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /server/service/normal/gost_node/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/gost_obs/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/normal/system_notice/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/public/gost/service.admission.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type AdmissionReq struct { 4 | Addr string `json:"addr"` 5 | } 6 | 7 | type AdmissionResp struct { 8 | Ok bool `json:"ok"` 9 | } 10 | 11 | func (service *service) Admission(req AdmissionReq) AdmissionResp { 12 | return AdmissionResp{Ok: false} 13 | } 14 | -------------------------------------------------------------------------------- /server/service/public/gost/service.auther.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/service/common/cache" 5 | ) 6 | 7 | type AutherReq struct { 8 | Username string `json:"username"` 9 | Password string `json:"password"` 10 | Client string `json:"client"` 11 | } 12 | 13 | type AutherResp struct { 14 | Ok bool `json:"ok"` 15 | Id string `json:"id"` 16 | } 17 | 18 | func (service *service) Auther(req AutherReq) AutherResp { 19 | code := cache.GetGostAuth(req.Username, req.Password) 20 | if code == "" { 21 | return AutherResp{Ok: false, Id: ""} 22 | } 23 | return AutherResp{Ok: true, Id: code} 24 | } 25 | -------------------------------------------------------------------------------- /server/service/public/gost/service.client.port.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "server/service/common/cache" 5 | "time" 6 | ) 7 | 8 | type ClientPortReq struct { 9 | Code string `json:"code"` // 节点编号 10 | Use bool `json:"use"` // 1=被占用 11 | Port string `json:"port"` // 端口 12 | } 13 | 14 | func (service *service) ClientPort(req ClientPortReq) { 15 | cache.SetClientPortUse(req.Code, req.Port, req.Use, time.Second*3) 16 | } 17 | -------------------------------------------------------------------------------- /server/service/public/gost/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/public/gost/service.ingress.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | "net/url" 5 | "server/service/common/cache" 6 | ) 7 | 8 | type IngressResp struct { 9 | Endpoint string `json:"endpoint"` 10 | } 11 | 12 | func (service *service) Ingress(host string) IngressResp { 13 | unescape, _ := url.QueryUnescape(host) 14 | return IngressResp{Endpoint: cache.GetIngress(unescape)} 15 | } 16 | -------------------------------------------------------------------------------- /server/service/public/gost/service.node.port.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | cache2 "github.com/patrickmn/go-cache" 5 | "server/service/common/cache" 6 | ) 7 | 8 | type NodePortReq struct { 9 | Code string `json:"code"` // 节点编号 10 | Use bool `json:"use"` // 1=被占用 11 | Port string `json:"port"` // 端口 12 | } 13 | 14 | func (service *service) NodePort(req NodePortReq) { 15 | cache.SetNodePortUse(req.Code, req.Port, req.Use, cache2.NoExpiration) 16 | } 17 | -------------------------------------------------------------------------------- /server/service/public/p2p/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/service/public/p2p/service.visit.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | import ( 4 | v1 "server/pkg/p2p_cfg/v1" 5 | "server/repository" 6 | ) 7 | 8 | type VisitCfgResp struct { 9 | Common v1.ClientCommonConfig 10 | XTCPCfg v1.XTCPVisitorConfig 11 | STCPCfg v1.STCPVisitorConfig 12 | } 13 | 14 | func (service *service) VisitCfg(key string) (result VisitCfgResp) { 15 | db, _, _ := repository.Get("") 16 | p2p, _ := db.GostClientP2P.Preload(db.GostClientP2P.Node).Where(db.GostClientP2P.VKey.Eq(key)).First() 17 | if p2p == nil { 18 | return result 19 | } 20 | result.Common, _ = p2p.GenerateCommonCfg() 21 | result.STCPCfg, result.XTCPCfg = p2p.GenerateVisitorCfgs() 22 | return result 23 | } 24 | -------------------------------------------------------------------------------- /server/service/public/system_config/service.go: -------------------------------------------------------------------------------- 1 | package service 2 | 3 | type service struct { 4 | } 5 | 6 | var Service *service 7 | -------------------------------------------------------------------------------- /server/task/gost_client_logger.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "server/repository" 5 | "time" 6 | ) 7 | 8 | func gostClientLogger() { 9 | db, _, _ := repository.Get("") 10 | _, _ = db.GostClientLogger.Where(db.GostClientLogger.CreatedAt.Lte(time.Now().AddDate(0, 0, -3).Unix())).Delete() 11 | } 12 | -------------------------------------------------------------------------------- /server/task/gost_node_logger.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "server/repository" 5 | "time" 6 | ) 7 | 8 | func gostNodeLogger() { 9 | db, _, _ := repository.Get("") 10 | _, _ = db.GostNodeLogger.Where(db.GostNodeLogger.CreatedAt.Lte(time.Now().AddDate(0, 0, -3).Unix())).Delete() 11 | } 12 | -------------------------------------------------------------------------------- /server/task/task.go: -------------------------------------------------------------------------------- 1 | package task 2 | 3 | import ( 4 | "server/bootstrap" 5 | "server/global" 6 | ) 7 | 8 | func init() { 9 | bootstrap.TaskFunc = func() { 10 | _, _ = global.Cron.AddFunc("0 0 * * *", gostClientLogger) 11 | _, _ = global.Cron.AddFunc("0 0 * * *", gostNodeLogger) 12 | _, _ = global.Cron.AddFunc("0 0 * * *", gostObs) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /server/todo/fix.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | import "server/repository" 4 | 5 | // 修复一些线上数据错误的bug 6 | func fix() { 7 | db, _, _ := repository.Get("") 8 | hosts, _ := db.GostClientHost.Where(db.GostClientHost.Or( 9 | db.GostClientHost.CustomDomain.IsNotNull(), 10 | ).Or( 11 | db.GostClientHost.CustomDomain.Neq(""), 12 | )).Find() 13 | var effectiveDomainMap = make(map[string]bool) 14 | for _, host := range hosts { 15 | effectiveDomainMap[host.CustomDomain] = true 16 | } 17 | 18 | domains, _ := db.GostClientHostDomain.Find() 19 | var invalidDomains []string 20 | for _, domain := range domains { 21 | if effectiveDomainMap[domain.Domain] { 22 | continue 23 | } 24 | invalidDomains = append(invalidDomains, domain.Domain) 25 | } 26 | _, _ = db.GostClientHostDomain.Where( 27 | db.GostClientHostDomain.Domain.In(invalidDomains...), 28 | ).Delete() 29 | } 30 | -------------------------------------------------------------------------------- /server/todo/gost_client_logger.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | import ( 4 | "server/repository" 5 | "time" 6 | ) 7 | 8 | func gostClientLogger() { 9 | db, _, _ := repository.Get("") 10 | _, _ = db.GostClientLogger.Where(db.GostClientLogger.CreatedAt.Lte(time.Now().AddDate(0, 0, -3).Unix())).Delete() 11 | } 12 | -------------------------------------------------------------------------------- /server/todo/gost_node_logger.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | import ( 4 | "server/repository" 5 | "time" 6 | ) 7 | 8 | func gostNodeLogger() { 9 | db, _, _ := repository.Get("") 10 | _, _ = db.GostNodeLogger.Where(db.GostNodeLogger.CreatedAt.Lte(time.Now().AddDate(0, 0, -3).Unix())).Delete() 11 | } 12 | -------------------------------------------------------------------------------- /server/todo/gost_node_port.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | import ( 4 | "server/repository" 5 | "server/service/common/node_port" 6 | ) 7 | 8 | func gostNodePort() { 9 | db, _, _ := repository.Get("") 10 | go node_port.Run(db) 11 | } 12 | -------------------------------------------------------------------------------- /server/todo/gost_obs.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | import ( 4 | "server/repository" 5 | "server/service/common/cache" 6 | ) 7 | 8 | func gostObs() { 9 | db, _, _ := repository.Get("") 10 | nodes, _ := db.GostNode.Select( 11 | db.GostNode.Code, 12 | db.GostNode.LimitResetIndex, 13 | ).Where().Find() 14 | for _, node := range nodes { 15 | cache.RefreshNodeObsLimit(node.Code, node.LimitResetIndex) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /server/todo/system_user.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | import ( 4 | "github.com/shopspring/decimal" 5 | "server/model" 6 | "server/pkg/utils" 7 | "server/repository" 8 | ) 9 | 10 | func systemUser() { 11 | db, _, _ := repository.Get("") 12 | salt := utils.RandStr(8, utils.AllDict) 13 | _ = db.SystemUser.Create(&model.SystemUser{ 14 | Base: model.Base{ 15 | Id: 1, 16 | Code: "1", 17 | }, 18 | Account: "admin", 19 | Password: utils.MD5AndSalt("admin", salt), 20 | Salt: salt, 21 | OtpKey: "", 22 | Admin: 1, 23 | Amount: decimal.NewFromInt(1000), 24 | }) 25 | } 26 | -------------------------------------------------------------------------------- /server/todo/todo.go: -------------------------------------------------------------------------------- 1 | package todo 2 | 3 | import ( 4 | "server/bootstrap" 5 | "server/global" 6 | "server/repository/query" 7 | ) 8 | 9 | func init() { 10 | bootstrap.TodoFunc = func() { 11 | query.SetDefault(global.DB.GetDB()) 12 | systemUser() 13 | systemConfig() 14 | gostClient() 15 | gostClientLogger() 16 | gostNodeLogger() 17 | gostNodePort() 18 | 19 | // 需要先将obs回写到cache,在处理obs数据 20 | gostObsWriteBack() 21 | gostObs() 22 | 23 | // 修复一些之前的数据错误 24 | fix() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /server/web/dist.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SianHH/gostc-open/694fcfc8ccc8378c514ccd39cc8a7567e2639443/server/web/dist.zip -------------------------------------------------------------------------------- /server/web/static.go: -------------------------------------------------------------------------------- 1 | package web 2 | 3 | import _ "embed" 4 | 5 | //go:embed dist.zip 6 | var staticFile []byte 7 | 8 | func Static() []byte { 9 | return staticFile 10 | } 11 | -------------------------------------------------------------------------------- /web/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "template", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite --host", 8 | "build": "vite build", 9 | "preview": "vite preview" 10 | }, 11 | "dependencies": { 12 | "axios": "^1.6.8", 13 | "echarts": "^5.5.1", 14 | "highcharts": "^12.1.2", 15 | "moment": "^2.30.1", 16 | "naive-ui": "^2.38.1", 17 | "nprogress": "^0.2.0", 18 | "pinia": "^2.1.7", 19 | "pinia-plugin-persistedstate": "^3.2.1", 20 | "sass": "^1.74.1", 21 | "sass-loader": "^14.1.1", 22 | "vue": "^3.4.21", 23 | "vue-router": "^4.3.0" 24 | }, 25 | "devDependencies": { 26 | "@vitejs/plugin-vue": "^5.0.4", 27 | "vfonts": "^0.0.3", 28 | "vite": "^5.2.8", 29 | "vite-plugin-pwa": "^0.19.8" 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /web/src/api/admin/gost_client_logger.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/admin/gost/client/logger' 4 | 5 | export const apiAdminGostClientLoggerPage = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/page`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /web/src/api/admin/gost_node_bind.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/admin/gost/node/bind' 4 | 5 | export const apiNormalGostNodeBindUpdate = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/update`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | 13 | -------------------------------------------------------------------------------- /web/src/api/admin/gost_node_logger.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/admin/gost/node/logger' 4 | 5 | export const apiAdminGostNodeLoggerPage = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/page`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /web/src/api/admin/gost_node_rule.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/admin/gost/node/rule' 4 | 5 | export const apiAdminGostNodeRuleList = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/list`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /web/src/api/normal/gost.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/normal/gost' 4 | 5 | export const apiNormalGostInfo = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/info`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /web/src/api/normal/gost_client_logger.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/normal/gost/client/logger' 4 | 5 | export const apiNormalGostClientLoggerPage = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/page`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /web/src/api/normal/gost_node_config.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/normal/gost/node/config' 4 | 5 | export const apiNormalGostNodeConfigList = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/list`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /web/src/api/normal/system_notice.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js"; 2 | 3 | const baseUrl = '/v1/normal/system/notice' 4 | 5 | export const apiNormalSystemNoticeList = (data) => { 6 | return request.request({ 7 | url: `${baseUrl}/list`, 8 | method: 'POST', 9 | data 10 | }) 11 | } 12 | -------------------------------------------------------------------------------- /web/src/api/public/index.js: -------------------------------------------------------------------------------- 1 | import request from "../../request/index.js" 2 | 3 | const baseUrl = '/v1/public/system/config' 4 | 5 | export const apiSystemConfigQuery = () => { 6 | return request.request({ 7 | url: `${baseUrl}/query`, 8 | method: 'POST', 9 | data:{} 10 | }) 11 | } -------------------------------------------------------------------------------- /web/src/components/DialogApi.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | -------------------------------------------------------------------------------- /web/src/components/Empty.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 29 | 30 | -------------------------------------------------------------------------------- /web/src/components/MessageApi.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /web/src/components/ModalApi.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 11 | 12 | -------------------------------------------------------------------------------- /web/src/icon/online.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 24 | 25 | -------------------------------------------------------------------------------- /web/src/layout/components/Layout.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 13 | 14 | -------------------------------------------------------------------------------- /web/src/layout/components/MenuIconSvg.vue: -------------------------------------------------------------------------------- 1 | 18 | 19 | 22 | 23 | -------------------------------------------------------------------------------- /web/src/layout/empty/index.vue: -------------------------------------------------------------------------------- 1 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /web/src/layout/mobile/index.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 19 | 20 | -------------------------------------------------------------------------------- /web/src/main.js: -------------------------------------------------------------------------------- 1 | import {createApp} from 'vue' 2 | import './style.css' 3 | import App from './App.vue' 4 | import {createPinia} from "pinia"; 5 | import piniaPluginPersistence from 'pinia-plugin-persistedstate' 6 | import router from "./router/index.js"; 7 | import naive from 'naive-ui' 8 | import {setupStore} from "./store/index.js"; 9 | const pinia = createPinia() 10 | pinia.use(piniaPluginPersistence) 11 | 12 | const app = createApp(App) 13 | setupStore(app) 14 | app.use(naive) 15 | app.use(router) 16 | app.mount('#app') 17 | -------------------------------------------------------------------------------- /web/src/setting.js: -------------------------------------------------------------------------------- 1 | export const naiveThemeOverrides = { 2 | common: { 3 | primaryColor: '#2299ddFF', 4 | } 5 | } -------------------------------------------------------------------------------- /web/src/store/app.js: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | 3 | export const appStore = defineStore('app', { 4 | state: () => ({ 5 | siteConfig: { 6 | title: 'GOSTC', 7 | favicon: '', 8 | isLoading: false, 9 | }, 10 | width: window.innerWidth, 11 | height: window.innerHeight, 12 | auth: { 13 | token: '', 14 | expAt: 0, 15 | }, 16 | userInfo: { 17 | account: '', 18 | amount: '', 19 | admin: 2, 20 | isLoading: false, 21 | } 22 | }), 23 | getters: { 24 | drawerWidthAdapter: (state) => { 25 | if (state?.width < 500) { 26 | return state?.width 27 | } 28 | return 400 29 | } 30 | }, 31 | actions: {}, 32 | persist: false 33 | }) -------------------------------------------------------------------------------- /web/src/store/index.js: -------------------------------------------------------------------------------- 1 | 2 | import { createPinia } from 'pinia' 3 | import piniaPluginPersistedstate from 'pinia-plugin-persistedstate' 4 | 5 | export function setupStore(app) { 6 | const pinia = createPinia() 7 | pinia.use(piniaPluginPersistedstate) 8 | app.use(pinia) 9 | } 10 | -------------------------------------------------------------------------------- /web/src/store/local.js: -------------------------------------------------------------------------------- 1 | import {defineStore} from "pinia"; 2 | 3 | export const localStore = defineStore('local', { 4 | state: () => ({ 5 | darkTheme: true, 6 | layout: 'simple', 7 | isCollapsed: true, 8 | menuKey: '', 9 | auth: { 10 | token: '', 11 | expAt: '' 12 | }, 13 | }), 14 | getters: {}, 15 | actions: { 16 | setLayout(name) { 17 | this.layout = name 18 | } 19 | }, 20 | persist: true 21 | }) -------------------------------------------------------------------------------- /web/src/utils/browser.js: -------------------------------------------------------------------------------- 1 | export function goToUrl(url, target = '_blank') { 2 | window.open(url, target) 3 | } -------------------------------------------------------------------------------- /web/src/utils/copy.js: -------------------------------------------------------------------------------- 1 | export function copyToClipboard(value) { 2 | if (!navigator){ 3 | return 4 | } 5 | return navigator.clipboard && navigator.clipboard.writeText(value) 6 | } -------------------------------------------------------------------------------- /web/src/utils/flow.js: -------------------------------------------------------------------------------- 1 | export function flowFormat(value) { 2 | if (value < 1024) { 3 | return value + 'B' 4 | } 5 | if (value < 1024 * 1024) { 6 | return (value / 1024).toFixed(1) + 'KB' 7 | } 8 | if (value < 1024 * 1024 * 1024) { 9 | return (value / 1024 / 1024).toFixed(1) + 'MB' 10 | } 11 | if (value < 1024 * 1024 * 1024 * 1024) { 12 | return (value / 1024 / 1024 / 1024).toFixed(1) + 'GB' 13 | } 14 | if (value < 1024 * 1024 * 1024 * 1024 * 1024) { 15 | return (value / 1024 / 1024 / 1024 / 1024).toFixed(1) + 'TB' 16 | } 17 | return '--' 18 | } -------------------------------------------------------------------------------- /web/src/utils/formDataRule.js: -------------------------------------------------------------------------------- 1 | export function requiredRule(tip = '不能为空') { 2 | return { 3 | required: true, 4 | trigger: ["blur", "input"], 5 | message: tip 6 | } 7 | } 8 | 9 | export function regexpRule(regexp = (str) => { 10 | return true 11 | }, tip = '格式错误') { 12 | return { 13 | required: true, 14 | trigger: ["blur", "change"], 15 | validator: (rule, value) => { 16 | if (!value) { 17 | return new Error('请输入') 18 | } 19 | if (!regexp(value)) { 20 | return new Error(tip) 21 | } 22 | return true 23 | } 24 | } 25 | } -------------------------------------------------------------------------------- /web/src/utils/menu.js: -------------------------------------------------------------------------------- 1 | export function menuFlatToTree(data, parent) { 2 | let result = [] 3 | if (!data) { 4 | return result 5 | } 6 | data.forEach(item => { 7 | if (item.parentCode === parent) { 8 | let children = menuFlatToTree(data, item.code) 9 | if (children.length === 0) { 10 | children = null 11 | } 12 | result.push({ 13 | label: item.name, 14 | key: item.key, 15 | iconSvg: item.icon, 16 | show: item.hidden === 2, 17 | children: children 18 | }) 19 | } 20 | }) 21 | return result 22 | } -------------------------------------------------------------------------------- /web/src/utils/random.js: -------------------------------------------------------------------------------- 1 | const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; 2 | export function randomString(length) { 3 | let result = ''; 4 | const charactersLength = characters.length; 5 | for (let i = 0; i < length; i++) { 6 | result += characters.charAt(Math.floor(Math.random() * charactersLength)); 7 | } 8 | return result; 9 | } -------------------------------------------------------------------------------- /web/src/views/admin/gost_client_logger/tableColumns.js: -------------------------------------------------------------------------------- 1 | import {h, ref} from "vue"; 2 | import {NButton, NSpace} from "naive-ui"; 3 | import moment from "moment"; 4 | 5 | export function generateTableColumns() { 6 | return [ 7 | {title: 'ID', key: 'id', ellipsis: {tooltip: true},width: 60}, 8 | { 9 | title: '时间', 10 | key: 'createdAt', 11 | ellipsis: {tooltip: true}, 12 | width:180, 13 | render(row) { 14 | return moment(row?.createdAt * 1000).format('yyyy-MM-DD HH:mm:ss') 15 | } 16 | }, 17 | {title: '日志级别', key: 'level', ellipsis: {tooltip: true},width: 100}, 18 | {title: '内容', key: 'content', ellipsis: {tooltip: true}}, 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /web/src/views/admin/gost_node/protocols.js: -------------------------------------------------------------------------------- 1 | export const protocols = [ 2 | {label: 'GRPC协议', value: 'grpc'}, 3 | {label: 'MTLS协议', value: 'mtls'}, 4 | {label: 'TLS协议', value: 'tls'}, 5 | {label: 'TCP协议', value: 'tcp'}, 6 | {label: 'MWSS协议', value: 'mwss'}, 7 | {label: 'WSS协议', value: 'wss'}, 8 | {label: 'MWS协议', value: 'mws'}, 9 | {label: 'WS协议', value: 'ws'}, 10 | {label: 'H2协议', value: 'h2'}, 11 | {label: 'H2C协议', value: 'h2c'}, 12 | {label: 'H3协议', value: 'h3'}, 13 | {label: 'OTLS协议', value: 'otls'}, 14 | {label: 'OHTTP协议', value: 'ohttp'}, 15 | {label: 'PHTS协议', value: 'phts'}, 16 | {label: 'PHT协议', value: 'pht'}, 17 | {label: 'QUIC协议', value: 'quic'}, 18 | {label: 'KCP协议', value: 'kcp'}, 19 | {label: 'UDP协议', value: 'udp'}, 20 | ] -------------------------------------------------------------------------------- /web/src/views/admin/gost_node_logger/tableColumns.js: -------------------------------------------------------------------------------- 1 | import {h, ref} from "vue"; 2 | import {NButton, NSpace} from "naive-ui"; 3 | import moment from "moment"; 4 | 5 | export function generateTableColumns() { 6 | return [ 7 | {title: 'ID', key: 'id', ellipsis: {tooltip: true},width: 60}, 8 | { 9 | title: '时间', 10 | key: 'createdAt', 11 | ellipsis: {tooltip: true}, 12 | width:180, 13 | render(row) { 14 | return moment(row?.createdAt * 1000).format('yyyy-MM-DD HH:mm:ss') 15 | } 16 | }, 17 | {title: '日志级别', key: 'level', ellipsis: {tooltip: true},width: 100}, 18 | {title: '内容', key: 'content', ellipsis: {tooltip: true}}, 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /web/src/views/normal/gost_client_logger/tableColumns.js: -------------------------------------------------------------------------------- 1 | import {h, ref} from "vue"; 2 | import {NButton, NSpace} from "naive-ui"; 3 | import moment from "moment"; 4 | 5 | export function generateTableColumns() { 6 | return [ 7 | {title: 'ID', key: 'id', ellipsis: {tooltip: true},width: 60}, 8 | { 9 | title: '时间', 10 | key: 'createdAt', 11 | ellipsis: {tooltip: true}, 12 | width:180, 13 | render(row) { 14 | return moment(row?.createdAt * 1000).format('yyyy-MM-DD HH:mm:ss') 15 | } 16 | }, 17 | {title: '日志级别', key: 'level', ellipsis: {tooltip: true},width: 100}, 18 | {title: '内容', key: 'content', ellipsis: {tooltip: true}}, 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /web/src/views/normal/gost_node/protocols.js: -------------------------------------------------------------------------------- 1 | export const protocols = [ 2 | {label: 'GRPC协议', value: 'grpc'}, 3 | {label: 'MTLS协议', value: 'mtls'}, 4 | {label: 'TLS协议', value: 'tls'}, 5 | {label: 'TCP协议', value: 'tcp'}, 6 | {label: 'MWSS协议', value: 'mwss'}, 7 | {label: 'WSS协议', value: 'wss'}, 8 | {label: 'MWS协议', value: 'mws'}, 9 | {label: 'WS协议', value: 'ws'}, 10 | {label: 'H2协议', value: 'h2'}, 11 | {label: 'H2C协议', value: 'h2c'}, 12 | {label: 'H3协议', value: 'h3'}, 13 | {label: 'OTLS协议', value: 'otls'}, 14 | {label: 'OHTTP协议', value: 'ohttp'}, 15 | {label: 'PHTS协议', value: 'phts'}, 16 | {label: 'PHT协议', value: 'pht'}, 17 | {label: 'QUIC协议', value: 'quic'}, 18 | {label: 'KCP协议', value: 'kcp'}, 19 | {label: 'UDP协议', value: 'udp'}, 20 | ] -------------------------------------------------------------------------------- /web/src/views/public/403.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /web/src/views/public/404.vue: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 12 | -------------------------------------------------------------------------------- /web/vite.config.js: -------------------------------------------------------------------------------- 1 | import {defineConfig} from 'vite' 2 | import vue from '@vitejs/plugin-vue' 3 | 4 | export default defineConfig({ 5 | plugins: [ 6 | vue(), 7 | ], 8 | server: { 9 | proxy: { 10 | '/api': { 11 | target: 'http://127.0.0.1:8080', 12 | changeOrigin: true 13 | } 14 | }, 15 | host: '0.0.0.0' 16 | } 17 | }) 18 | --------------------------------------------------------------------------------