From e9a59a7525d822709aec1fa70803ebc058a0a7e5 Mon Sep 17 00:00:00 2001 From: VaalaCat Date: Tue, 24 Dec 2024 14:38:34 +0000 Subject: [PATCH] ci: ko build --- .github/workflows/latest.workflow.yml | 30 ++---- .github/workflows/tag.workflow.yml | 36 ++----- .ko.yaml | 15 +++ build.sh | 3 +- cmd/frpp/cmd.go | 11 ++ cmd/frpp/main.go | 1 + cmd/frpp/master.go | 7 ++ conf/settings.go | 2 +- conf/version.go | 4 + idl/api_master.proto | 1 + pb/api_master.pb.go | 92 +++++++++-------- utils/files.go | 18 ++++ www/components/stats/client_stats_card.tsx | 57 +++++------ www/components/stats/stats-item.tsx | 114 +++++++++++++++++++++ www/i18n/locales/en.json | 8 +- www/i18n/locales/zh.json | 12 ++- www/lib/pb/api_master.ts | 14 ++- 17 files changed, 294 insertions(+), 131 deletions(-) create mode 100644 .ko.yaml create mode 100644 utils/files.go create mode 100644 www/components/stats/stats-item.tsx diff --git a/.github/workflows/latest.workflow.yml b/.github/workflows/latest.workflow.yml index c040962..df1f556 100644 --- a/.github/workflows/latest.workflow.yml +++ b/.github/workflows/latest.workflow.yml @@ -41,27 +41,13 @@ jobs: automatic_release_tag: latest files: | dist/* - - build-docker: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to DockerHub - uses: docker/login-action@v3 - with: + - name: Setup ko + uses: ko-build/setup-ko@v0.6 + env: + KO_DOCKER_REPO: docker.io/vaalacat/frp-panel + - env: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - file: ./Dockerfile.standalone - push: true - platforms: linux/amd64,linux/arm64 - tags: vaalacat/frp-panel:latest \ No newline at end of file + run: | + echo "${password}" | ko login docker.io --username ${username} --password-stdin + ko build ./cmd/frpp --bare \ No newline at end of file diff --git a/.github/workflows/tag.workflow.yml b/.github/workflows/tag.workflow.yml index a891621..9d13e2c 100644 --- a/.github/workflows/tag.workflow.yml +++ b/.github/workflows/tag.workflow.yml @@ -42,32 +42,14 @@ jobs: automatic_release_tag: ${{ steps.get_version.outputs.VERSION }} files: | dist/* - - build-docker: - runs-on: ubuntu-latest - steps: - - name: Get version - id: get_version - run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\//} - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - name: Get git tag history - run: git fetch -a - - name: Set up QEMU - uses: docker/setup-qemu-action@v3 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 - - name: Login to DockerHub - uses: docker/login-action@v3 - with: + - name: Setup ko + uses: ko-build/setup-ko@v0.6 + env: + KO_DOCKER_REPO: docker.io/vaalacat/frp-panel + - name: Build image with ko + env: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - file: ./Dockerfile.standalone - push: true - platforms: linux/amd64,linux/arm64 - tags: vaalacat/frp-panel:${{ steps.get_version.outputs.VERSION }} \ No newline at end of file + run: | + echo "${password}" | ko login docker.io --username ${username} --password-stdin + ko build ./cmd/frpp --bare -t ${{ steps.get_version.outputs.VERSION }} \ No newline at end of file diff --git a/.ko.yaml b/.ko.yaml new file mode 100644 index 0000000..1a3884f --- /dev/null +++ b/.ko.yaml @@ -0,0 +1,15 @@ +defaultBaseImage: alpine + +builds: +- id: frpp + dir: . + main: ./cmd/frpp + ldflags: + - -s -w + - -X github.com/VaalaCat/frp-panel/conf.buildDate={{.Date}} + - -X github.com/VaalaCat/frp-panel/conf.gitCommit={{.Git.FullCommit}} + - -X github.com/VaalaCat/frp-panel/conf.gitVersion={{.Git.Tag}} + - -X github.com/VaalaCat/frp-panel/conf.gitBranch={{.Git.Branch}} + +defaultPlatforms: +- all \ No newline at end of file diff --git a/build.sh b/build.sh index 808de7b..85fc03b 100755 --- a/build.sh +++ b/build.sh @@ -19,6 +19,7 @@ ARCH="all" BUILD_DATE="$(date -u +'%Y-%m-%dT%H:%M:%SZ')" GIT_COMMIT="$(git rev-parse HEAD)" VERSION="$(git describe --tags --abbrev=0 | tr -d '\n')" +GIT_BRANCH="$(git rev-parse --abbrev-ref HEAD)" # Parse arguments while [[ "$#" -gt 0 ]]; do @@ -50,7 +51,7 @@ echo "Build Date: $BUILD_DATE" echo "Git Commit: $GIT_COMMIT" echo "Version: $VERSION" -BUILD_LD_FLAGS="-X 'github.com/VaalaCat/frp-panel/conf.buildDate=${BUILD_DATE}' -X 'github.com/VaalaCat/frp-panel/conf.gitCommit=${GIT_COMMIT}' -X 'github.com/VaalaCat/frp-panel/conf.gitVersion=${VERSION}'" +BUILD_LD_FLAGS="-X 'github.com/VaalaCat/frp-panel/conf.buildDate=${BUILD_DATE}' -X 'github.com/VaalaCat/frp-panel/conf.gitCommit=${GIT_COMMIT}' -X 'github.com/VaalaCat/frp-panel/conf.gitVersion=${VERSION}' -X 'github.com/VaalaCat/frp-panel/conf.gitBranch=${GIT_BRANCH}'" if [[ "$SKIP_FRONTEND" == "true" ]]; then echo "Skipping frontend build" diff --git a/cmd/frpp/cmd.go b/cmd/frpp/cmd.go index 1dcf414..351aa9a 100644 --- a/cmd/frpp/cmd.go +++ b/cmd/frpp/cmd.go @@ -3,11 +3,13 @@ package main import ( "context" "fmt" + "os" "github.com/VaalaCat/frp-panel/conf" "github.com/VaalaCat/frp-panel/logger" "github.com/VaalaCat/frp-panel/utils" "github.com/spf13/cobra" + "github.com/spf13/pflag" ) var ( @@ -140,6 +142,7 @@ func initCommand() { rootCmd.AddCommand(clientCmd, serverCmd, masterCmd, versionCmd, installServiceCmd, uninstallServiceCmd, startServiceCmd, stopServiceCmd, restartServiceCmd) + clientCmd.Flags().StringVarP(&clientSecret, "secret", "s", "", "client secret") serverCmd.Flags().StringVarP(&clientSecret, "secret", "s", "", "client secret") clientCmd.Flags().StringVarP(&clientID, "id", "i", "", "client id") @@ -191,3 +194,11 @@ func patchConfig(host, secret, clientID, clientSecret, apiScheme string, rpcPort conf.Get().Master.APIHost, conf.Get().Master.APIPort, conf.Get().Master.APIScheme) } + +func setMasterCommandIfNonePresent() { + cmd, _, err := rootCmd.Find(os.Args[1:]) + if err == nil && cmd.Use == rootCmd.Use && cmd.Flags().Parse(os.Args[1:]) != pflag.ErrHelp { + args := append([]string{"master"}, os.Args[1:]...) + rootCmd.SetArgs(args) + } +} diff --git a/cmd/frpp/main.go b/cmd/frpp/main.go index e4773e8..5e6a80f 100644 --- a/cmd/frpp/main.go +++ b/cmd/frpp/main.go @@ -14,5 +14,6 @@ func main() { conf.InitConfig() rpc.InitRPCClients() + setMasterCommandIfNonePresent() rootCmd.Execute() } diff --git a/cmd/frpp/master.go b/cmd/frpp/master.go index 0d7a4f5..7d7ee2a 100644 --- a/cmd/frpp/master.go +++ b/cmd/frpp/master.go @@ -3,6 +3,7 @@ package main import ( "context" "embed" + "path/filepath" bizmaster "github.com/VaalaCat/frp-panel/biz/master" "github.com/VaalaCat/frp-panel/biz/master/auth" @@ -80,6 +81,12 @@ func initDatabase(c context.Context) { switch conf.Get().DB.Type { case "sqlite3": + if err := utils.EnsureDirectoryExists(conf.Get().DB.DSN); err != nil { + logrus.WithError(err).Warnf("ensure directory failed, data location: [%s], keep data in current directory", conf.Get().DB.DSN) + conf.Get().DB.DSN = filepath.Base(conf.Get().DB.DSN) + logrus.Infof("new data location: [%s]", conf.Get().DB.DSN) + } + if sqlitedb, err := gorm.Open(sqlite.Open(conf.Get().DB.DSN), &gorm.Config{}); err != nil { logrus.Panic(err) } else { diff --git a/conf/settings.go b/conf/settings.go index f6ab8d4..5b8f06c 100644 --- a/conf/settings.go +++ b/conf/settings.go @@ -43,7 +43,7 @@ type Config struct { } `env-prefix:"SERVER_"` DB struct { Type string `env:"TYPE" env-default:"sqlite3" env-description:"db type, mysql or sqlite3 and so on"` - DSN string `env:"DSN" env-default:"data.db" env-description:"db dsn, for sqlite is path, other is dsn, look at https://github.com/go-sql-driver/mysql#dsn-data-source-name"` + DSN string `env:"DSN" env-default:"/data/data.db" env-description:"db dsn, for sqlite is path, other is dsn, look at https://github.com/go-sql-driver/mysql#dsn-data-source-name"` } `env-prefix:"DB_"` Client struct { ID string `env:"ID" env-description:"client id"` diff --git a/conf/version.go b/conf/version.go index be83bb8..ec16a30 100644 --- a/conf/version.go +++ b/conf/version.go @@ -12,12 +12,14 @@ import ( var ( gitVersion = "dev-build" gitCommit = "" + gitBranch = "" buildDate = "1970-01-01T00:00:00Z" ) type VersionInfo struct { GitVersion string `json:"gitVersion" yaml:"gitVersion"` GitCommit string `json:"gitCommit" yaml:"gitCommit"` + GitBranch string `json:"gitBranch" yaml:"gitBranch"` BuildDate string `json:"buildDate" yaml:"buildDate"` GoVersion string `json:"goVersion" yaml:"goVersion"` Compiler string `json:"compiler" yaml:"compiler"` @@ -42,6 +44,7 @@ func (v *VersionInfo) ToProto() *pb.ClientVersion { return &pb.ClientVersion{ GitVersion: v.GitVersion, GitCommit: v.GitCommit, + GitBranch: v.GitBranch, BuildDate: v.BuildDate, GoVersion: v.GoVersion, Compiler: v.Compiler, @@ -53,6 +56,7 @@ func GetVersion() *VersionInfo { return &VersionInfo{ GitVersion: gitVersion, GitCommit: gitCommit, + GitBranch: gitBranch, BuildDate: buildDate, GoVersion: runtime.Version(), Compiler: runtime.Compiler, diff --git a/idl/api_master.proto b/idl/api_master.proto index 9de46bd..8e197a2 100644 --- a/idl/api_master.proto +++ b/idl/api_master.proto @@ -27,6 +27,7 @@ message ClientVersion { string GoVersion = 4; string Compiler = 5; string Platform = 6; + string GitBranch = 7; } message GetClientsStatusRequest { diff --git a/pb/api_master.pb.go b/pb/api_master.pb.go index bf6a96b..35be722 100644 --- a/pb/api_master.pb.go +++ b/pb/api_master.pb.go @@ -176,6 +176,7 @@ type ClientVersion struct { GoVersion string `protobuf:"bytes,4,opt,name=GoVersion,proto3" json:"GoVersion,omitempty"` Compiler string `protobuf:"bytes,5,opt,name=Compiler,proto3" json:"Compiler,omitempty"` Platform string `protobuf:"bytes,6,opt,name=Platform,proto3" json:"Platform,omitempty"` + GitBranch string `protobuf:"bytes,7,opt,name=GitBranch,proto3" json:"GitBranch,omitempty"` } func (x *ClientVersion) Reset() { @@ -250,6 +251,13 @@ func (x *ClientVersion) GetPlatform() string { return "" } +func (x *ClientVersion) GetGitBranch() string { + if x != nil { + return x.GitBranch + } + return "" +} + type GetClientsStatusRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -502,7 +510,7 @@ var file_api_master_proto_rawDesc = []byte{ 0x12, 0x10, 0x0a, 0x0c, 0x53, 0x54, 0x41, 0x54, 0x55, 0x53, 0x5f, 0x45, 0x52, 0x52, 0x4f, 0x52, 0x10, 0x03, 0x42, 0x0a, 0x0a, 0x08, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x42, 0x07, 0x0a, 0x05, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x63, 0x6f, 0x6e, 0x6e, - 0x65, 0x63, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0xc1, 0x01, 0x0a, 0x0d, 0x43, 0x6c, 0x69, + 0x65, 0x63, 0x74, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x22, 0xdf, 0x01, 0x0a, 0x0d, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1e, 0x0a, 0x0a, 0x47, 0x69, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x47, 0x69, 0x74, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1c, 0x0a, 0x09, 0x47, 0x69, @@ -514,46 +522,48 @@ var file_api_master_proto_rawDesc = []byte{ 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x1a, 0x0a, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x43, 0x6f, 0x6d, 0x70, 0x69, 0x6c, 0x65, 0x72, 0x12, 0x1a, 0x0a, 0x08, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x08, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x22, 0x6d, 0x0a, 0x17, - 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, - 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, - 0x52, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x22, 0xf5, 0x01, 0x0a, 0x18, - 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, - 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, - 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, - 0x75, 0x73, 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, - 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x6d, 0x61, 0x73, - 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x74, - 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x69, - 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x73, 0x1a, 0x54, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, - 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, - 0x74, 0x75, 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x0b, - 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, - 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, - 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, - 0x65, 0x12, 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, - 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, - 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, - 0x72, 0x65, 0x74, 0x22, 0x63, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, - 0x43, 0x65, 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, - 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, - 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, - 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x42, 0x09, 0x0a, - 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2e, 0x2f, 0x70, - 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x28, 0x09, 0x52, 0x08, 0x50, 0x6c, 0x61, 0x74, 0x66, 0x6f, 0x72, 0x6d, 0x12, 0x1c, 0x0a, 0x09, + 0x47, 0x69, 0x74, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x47, 0x69, 0x74, 0x42, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x22, 0x6d, 0x0a, 0x17, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x0b, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, + 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x12, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x52, 0x0a, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x73, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x09, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x73, 0x22, 0xf5, 0x01, 0x0a, 0x18, 0x47, 0x65, + 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, + 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x88, 0x01, 0x01, 0x12, 0x4b, 0x0a, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x02, + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x31, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x6d, 0x61, 0x73, 0x74, 0x65, + 0x72, 0x2e, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x53, 0x74, 0x61, 0x74, + 0x75, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, + 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, + 0x1a, 0x54, 0x0a, 0x0c, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, + 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, + 0x65, 0x79, 0x12, 0x2e, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x0b, 0x32, 0x18, 0x2e, 0x61, 0x70, 0x69, 0x5f, 0x6d, 0x61, 0x73, 0x74, 0x65, 0x72, 0x2e, 0x43, + 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x52, 0x05, 0x76, 0x61, 0x6c, + 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, + 0x73, 0x22, 0x8d, 0x01, 0x0a, 0x14, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, + 0x65, 0x72, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x33, 0x0a, 0x0b, 0x63, 0x6c, + 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, + 0x12, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, + 0x79, 0x70, 0x65, 0x52, 0x0a, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x54, 0x79, 0x70, 0x65, 0x12, + 0x1b, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x02, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x23, 0x0a, 0x0d, + 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, + 0x74, 0x22, 0x63, 0x0a, 0x15, 0x47, 0x65, 0x74, 0x43, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x43, 0x65, + 0x72, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2b, 0x0a, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x63, 0x6f, 0x6d, + 0x6d, 0x6f, 0x6e, 0x2e, 0x53, 0x74, 0x61, 0x74, 0x75, 0x73, 0x48, 0x00, 0x52, 0x06, 0x73, 0x74, + 0x61, 0x74, 0x75, 0x73, 0x88, 0x01, 0x01, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x65, 0x72, 0x74, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x63, 0x65, 0x72, 0x74, 0x42, 0x09, 0x0a, 0x07, 0x5f, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x42, 0x07, 0x5a, 0x05, 0x2e, 0x2e, 0x2f, 0x70, 0x62, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/utils/files.go b/utils/files.go new file mode 100644 index 0000000..170701a --- /dev/null +++ b/utils/files.go @@ -0,0 +1,18 @@ +package utils + +import ( + "os" + "path/filepath" +) + +func EnsureDirectoryExists(filePath string) error { + directory := filepath.Dir(filePath) + + if _, err := os.Stat(directory); os.IsNotExist(err) { + err = os.MkdirAll(directory, os.ModePerm) + if err != nil { + return err + } + } + return nil +} diff --git a/www/components/stats/client_stats_card.tsx b/www/components/stats/client_stats_card.tsx index dd8c2c2..a62671e 100644 --- a/www/components/stats/client_stats_card.tsx +++ b/www/components/stats/client_stats_card.tsx @@ -12,6 +12,9 @@ import { ProxyInfo } from '@/lib/pb/common' import { Button } from '../ui/button' import { CheckCircle2, CircleX, RefreshCcw } from "lucide-react" import { useTranslation } from 'react-i18next'; +import TrafficStatsCard from './stats-item' +import { Separator } from '@radix-ui/react-separator' +import { formatBytes } from '@/lib/utils' export interface ClientStatsCardProps { clientID?: string @@ -19,7 +22,6 @@ export interface ClientStatsCardProps { export const ClientStatsCard: React.FC = ({ clientID: defaultClientID }: ClientStatsCardProps = {}) => { const { t } = useTranslation(); const [clientID, setClientID] = useState() - const [proxyName, setProxyName] = useState() const [status, setStatus] = useState<"loading" | "success" | "error" | undefined>() const [timeoutId, setTimeoutId] = useState(null); @@ -69,11 +71,6 @@ export const ClientStatsCard: React.FC = ({ clientID: defa return Array.from(mergedMap.values()); }; - function removeDuplicateCharacters(input: string): string { - const uniqueChars = new Set(input); - return Array.from(uniqueChars).join(''); - } - return ( @@ -88,18 +85,16 @@ export const ClientStatsCard: React.FC = ({ clientID: defa { refetchClientStats() - setProxyName(undefined) }} /> - proxyInfo.name).filter((value) => value !== undefined))) || []} - proxyName={proxyName} - setProxyname={setProxyName} /> -
+
{clientStatsList && clientStatsList.proxyInfos.length > 0 && - proxyInfo.name === proxyName)} />} + clientStatsList.proxyInfos.map((proxyInfo) => { + return ( + + ) + }) + }
@@ -135,21 +130,23 @@ const ProxyStatusCard: React.FC<{ proxyInfo: ProxyInfo | undefined }> = ({ proxy } return ( -
+ - -
- - -
-
+ + + + + + + + + ); +} + +const ProxyTrafficField = ({ label, value }: { label: string, value: string }) => { + return
+

{label}

+
{value}
+
} \ No newline at end of file diff --git a/www/components/stats/stats-item.tsx b/www/components/stats/stats-item.tsx new file mode 100644 index 0000000..c3be72e --- /dev/null +++ b/www/components/stats/stats-item.tsx @@ -0,0 +1,114 @@ +'use client' + +import { Card } from "@/components/ui/card" +import { ProxyInfo } from "@/lib/pb/common"; +import { formatBytes } from "@/lib/utils"; +import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip } from 'recharts' + +const COLORS = ['#0088FE', '#00C49F']; + +function preparePieData(inTraffic: bigint, outTraffic: bigint) { + return [ + { name: 'In', value: Number(inTraffic) }, + { name: 'Out', value: Number(outTraffic) } + ]; +} + +function calculateTotalTraffic(inTraffic: bigint, outTraffic: bigint): bigint { + return inTraffic + outTraffic; +} + +export default function TrafficStatsCard({ proxy }: { proxy: ProxyInfo }) { + return +
+ {/* Server Info */} +
+
+ {proxy.name} +
+ + {/* Today's Traffic */} +
+
+ + + + {preparePieData(proxy.todayTrafficIn || BigInt(0), proxy.todayTrafficOut || BigInt(0)).map((entry, index) => ( + + ))} + + formatBytes(value as number)} /> + + +
+
+
Today's Traffic
+
+
+ In: + {formatBytes(Number(proxy.todayTrafficIn || BigInt(0)))} +
+
+ Out: + {formatBytes(Number(proxy.todayTrafficOut || BigInt(0)))} +
+
+ Total: + {formatBytes(Number(calculateTotalTraffic(proxy.todayTrafficIn || BigInt(0), proxy.todayTrafficOut || BigInt(0))))} +
+
+
+
+ + {/* History Traffic */} +
+
+ + + + {preparePieData(proxy.historyTrafficIn || BigInt(0), proxy.historyTrafficOut || BigInt(0)).map((entry, index) => ( + + ))} + + formatBytes(value as number)} /> + + +
+
+
History Traffic
+
+
+ In: + {formatBytes(Number(proxy.historyTrafficIn || BigInt(0)))} +
+
+ Out: + {formatBytes(Number(proxy.historyTrafficOut || BigInt(0)))} +
+
+ Total: + {formatBytes(Number(calculateTotalTraffic(proxy.historyTrafficIn || BigInt(0), proxy.historyTrafficOut || BigInt(0))))} +
+
+
+
+
+ +} + diff --git a/www/i18n/locales/en.json b/www/i18n/locales/en.json index 2cf0ef4..c00470c 100644 --- a/www/i18n/locales/en.json +++ b/www/i18n/locales/en.json @@ -25,11 +25,13 @@ "traffic": { "today": { "inbound": "Today's Inbound", - "outbound": "Today's Outbound" + "outbound": "Today's Outbound", + "total": "Today's Total" }, "history": { "inbound": "Historical Inbound", - "outbound": "Historical Outbound" + "outbound": "Historical Outbound", + "total": "Historical Total" }, "stats": { "title": "Traffic Statistics", @@ -318,7 +320,7 @@ "proxy": { "stats": { "label": "Tunnel Name", - "tunnel_traffic": "Tunnel Traffic: {{name}}", + "tunnel_traffic": "Tunnel: {{name}}", "today_traffic_title": "Today's Traffic", "today_traffic_total": "Today's Total", "history_traffic_title": "Historical Traffic", diff --git a/www/i18n/locales/zh.json b/www/i18n/locales/zh.json index 10f9fd0..ac6021a 100644 --- a/www/i18n/locales/zh.json +++ b/www/i18n/locales/zh.json @@ -24,12 +24,14 @@ }, "traffic": { "today": { - "inbound": "今日入站流量", - "outbound": "今日出站流量" + "inbound": "今日入站", + "outbound": "今日出站", + "total": "今日总流量" }, "history": { - "inbound": "历史入站流量", - "outbound": "历史出站流量" + "inbound": "历史入站", + "outbound": "历史出站", + "total": "历史总流量" }, "stats": { "title": "流量统计", @@ -318,7 +320,7 @@ "proxy": { "stats": { "label": "隧道名称", - "tunnel_traffic": "隧道流量:{{name}}", + "tunnel_traffic": "隧道:{{name}}", "today_traffic_title": "今日流量", "today_traffic_total": "今日总计", "history_traffic_title": "历史流量", diff --git a/www/lib/pb/api_master.ts b/www/lib/pb/api_master.ts index 822089f..d024796 100644 --- a/www/lib/pb/api_master.ts +++ b/www/lib/pb/api_master.ts @@ -94,6 +94,10 @@ export interface ClientVersion { * @generated from protobuf field: string Platform = 6 [json_name = "Platform"]; */ platform: string; + /** + * @generated from protobuf field: string GitBranch = 7 [json_name = "GitBranch"]; + */ + gitBranch: string; } /** * @generated from protobuf message api_master.GetClientsStatusRequest @@ -254,7 +258,8 @@ class ClientVersion$Type extends MessageType { { no: 3, name: "BuildDate", kind: "scalar", jsonName: "BuildDate", T: 9 /*ScalarType.STRING*/ }, { no: 4, name: "GoVersion", kind: "scalar", jsonName: "GoVersion", T: 9 /*ScalarType.STRING*/ }, { no: 5, name: "Compiler", kind: "scalar", jsonName: "Compiler", T: 9 /*ScalarType.STRING*/ }, - { no: 6, name: "Platform", kind: "scalar", jsonName: "Platform", T: 9 /*ScalarType.STRING*/ } + { no: 6, name: "Platform", kind: "scalar", jsonName: "Platform", T: 9 /*ScalarType.STRING*/ }, + { no: 7, name: "GitBranch", kind: "scalar", jsonName: "GitBranch", T: 9 /*ScalarType.STRING*/ } ]); } create(value?: PartialMessage): ClientVersion { @@ -265,6 +270,7 @@ class ClientVersion$Type extends MessageType { message.goVersion = ""; message.compiler = ""; message.platform = ""; + message.gitBranch = ""; if (value !== undefined) reflectionMergePartial(this, message, value); return message; @@ -292,6 +298,9 @@ class ClientVersion$Type extends MessageType { case /* string Platform = 6 [json_name = "Platform"];*/ 6: message.platform = reader.string(); break; + case /* string GitBranch = 7 [json_name = "GitBranch"];*/ 7: + message.gitBranch = reader.string(); + break; default: let u = options.readUnknownField; if (u === "throw") @@ -322,6 +331,9 @@ class ClientVersion$Type extends MessageType { /* string Platform = 6 [json_name = "Platform"]; */ if (message.platform !== "") writer.tag(6, WireType.LengthDelimited).string(message.platform); + /* string GitBranch = 7 [json_name = "GitBranch"]; */ + if (message.gitBranch !== "") + writer.tag(7, WireType.LengthDelimited).string(message.gitBranch); let u = options.writeUnknownFields; if (u !== false) (u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);