Compare commits

...

6 Commits

8 changed files with 227 additions and 10 deletions

View File

@@ -0,0 +1,50 @@
# 由于成本问题,现已全面转向 Github Actions 构建
name: Build FRP Binaries
on:
push:
branches:
- '**'
tags:
- '**'
workflow_dispatch:
inputs:
tag:
description: 'Manual build tag'
required: false
default: 'manual'
permissions:
contents: read
jobs:
build:
name: Build and Package FRP
runs-on: ubuntu-latest
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Set up dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y zip make gcc g++ upx
- name: Run build script
run: |
chmod +x ./package.sh
./package.sh
- name: Upload build artifacts
uses: actions/upload-artifact@v3
with:
name: frp-packages
path: release/packages

4
.github/FUNDING.yml vendored
View File

@@ -1,4 +0,0 @@
# These are supported funding model platforms
github: [fatedier]
custom: ["https://afdian.com/a/fatedier"]

View File

@@ -1,3 +0,0 @@
### WHY
<!-- author to complete -->

93
.github/workflows/build-all.yaml vendored Normal file
View File

@@ -0,0 +1,93 @@
name: Build FRP Binaries
on:
push:
branches:
- '**'
tags:
- '**'
workflow_dispatch:
permissions:
contents: read
jobs:
build:
name: Build FRP ${{ matrix.goos }}-${{ matrix.goarch }}
runs-on: ubuntu-latest
strategy:
matrix:
goos: [linux, windows, darwin, freebsd, openbsd, android]
goarch: [amd64, arm, arm64]
exclude:
- goos: darwin
goarch: arm
- goos: freebsd
goarch: arm
- goos: openbsd
goarch: arm
- goos: android
goarch: amd64
steps:
- name: Checkout source
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.22'
- name: Install dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y zip tar make gcc g++ upx
- name: Build FRP for ${{ matrix.goos }}-${{ matrix.goarch }}
run: |
mkdir -p release/packages
echo "Building for ${{ matrix.goos }}-${{ matrix.goarch }}"
# 构建版本号
make
version=$(./bin/frps --version)
echo "Detected version: $version"
export GOOS=${{ matrix.goos }}
export GOARCH=${{ matrix.goarch }}
export CGO_ENABLED=0
# 构建可执行文件
make frpc frps
if [ "${{ matrix.goos }}" = "windows" ]; then
if [ -f "./bin/frpc" ]; then mv ./bin/frpc ./bin/frpc.exe; fi
if [ -f "./bin/frps" ]; then mv ./bin/frps ./bin/frps.exe; fi
fi
out_dir="release/packages/frp_${version}_${{ matrix.goos }}_${{ matrix.goarch }}"
mkdir -p "$out_dir"
if [ "${{ matrix.goos }}" = "windows" ]; then
mv ./bin/frpc.exe "$out_dir/frpc.exe"
mv ./bin/frps.exe "$out_dir/frps.exe"
else
mv ./bin/frpc "$out_dir/frpc"
mv ./bin/frps "$out_dir/frps"
fi
cp LICENSE "$out_dir"
cp -f conf/frpc.toml "$out_dir"
cp -f conf/frps.toml "$out_dir"
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: LoliaFrp_${{ matrix.goos }}_${{ matrix.goarch }}
path: |
release/packages/frp_*
retention-days: 7

View File

@@ -206,8 +206,9 @@ func (m *serverMetrics) GetProxiesByType(proxyType string) []*ProxyStats {
m.mu.Lock()
defer m.mu.Unlock()
filterAll := proxyType == "" || proxyType == "all"
for name, proxyStats := range m.info.ProxyStatistics {
if proxyStats.ProxyType != proxyType {
if !filterAll && proxyStats.ProxyType != proxyType {
continue
}
@@ -233,8 +234,9 @@ func (m *serverMetrics) GetProxiesByTypeAndName(proxyType string, proxyName stri
m.mu.Lock()
defer m.mu.Unlock()
filterAll := proxyType == "" || proxyType == "all"
for name, proxyStats := range m.info.ProxyStatistics {
if proxyStats.ProxyType != proxyType {
if !filterAll && proxyStats.ProxyType != proxyType {
continue
}

View File

@@ -14,7 +14,7 @@
package version
var version = "0.65.0"
var version = "LoliaFRP-0.65.0"
func Full() string {
return version

View File

@@ -94,6 +94,28 @@ func (cm *ControlManager) Close() error {
return nil
}
// KickByProxyName finds the Control that manages the given proxy (tunnel) name and closes
// the entire control connection (disconnects the frpc). Returns an error if no such proxy is found.
func (cm *ControlManager) KickByProxyName(proxyName string) error {
cm.mu.RLock()
var target *Control
for _, ctl := range cm.ctlsByRunID {
ctl.mu.RLock()
_, ok := ctl.proxies[proxyName]
ctl.mu.RUnlock()
if ok {
target = ctl
break
}
}
cm.mu.RUnlock()
if target == nil {
return fmt.Errorf("no proxy found with name [%s]", proxyName)
}
return target.Close()
}
type Control struct {
// all resource managers and controllers
rc *controller.ResourceController

View File

@@ -52,8 +52,10 @@ func (svr *Service) registerRouteHandlers(helper *httppkg.RouterRegisterHelper)
subRouter.HandleFunc("/api/serverinfo", svr.apiServerInfo).Methods("GET")
subRouter.HandleFunc("/api/proxy/{type}", svr.apiProxyByType).Methods("GET")
subRouter.HandleFunc("/api/proxy/{type}/{name}", svr.apiProxyByTypeAndName).Methods("GET")
subRouter.HandleFunc("/api/proxy/{name}/kick", svr.apiKickProxyByName).Methods("POST")
subRouter.HandleFunc("/api/traffic/{name}", svr.apiProxyTraffic).Methods("GET")
subRouter.HandleFunc("/api/proxies", svr.deleteProxies).Methods("DELETE")
subRouter.HandleFunc("/api/proxies", svr.apiProxiesAll).Methods("GET")
// view
subRouter.Handle("/favicon.ico", http.FileServer(helper.AssetsFS)).Methods("GET")
@@ -211,6 +213,29 @@ type GetProxyInfoResp struct {
Proxies []*ProxyStatsInfo `json:"proxies"`
}
// GET /api/proxies
// Return all proxies across types (tcp, udp, http, https, stcp, xtcp, tcpmux)
func (svr *Service) apiProxiesAll(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
defer func() {
log.Infof("http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
log.Infof("http request: [%s]", r.URL.Path)
proxyInfoResp := GetProxyInfoResp{}
proxyInfoResp.Proxies = svr.getProxyStatsByType("all")
slices.SortFunc(proxyInfoResp.Proxies, func(a, b *ProxyStatsInfo) int {
return cmp.Compare(a.Name, b.Name)
})
buf, _ := json.Marshal(&proxyInfoResp)
res.Msg = string(buf)
}
// /api/proxy/:type
func (svr *Service) apiProxyByType(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
@@ -237,6 +262,7 @@ func (svr *Service) apiProxyByType(w http.ResponseWriter, r *http.Request) {
}
func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxyStatsInfo) {
// mem.StatsCollector now supports proxyType=="all" or "" to return all proxies
proxyStats := mem.StatsCollector.GetProxiesByType(proxyType)
proxyInfos = make([]*ProxyStatsInfo, 0, len(proxyStats))
for _, ps := range proxyStats {
@@ -308,6 +334,37 @@ func (svr *Service) apiProxyByTypeAndName(w http.ResponseWriter, r *http.Request
res.Msg = string(buf)
}
// POST /api/proxy/:name/kick
// Kick the client (frpc) that owns the proxy with given name.
func (svr *Service) apiKickProxyByName(w http.ResponseWriter, r *http.Request) {
res := GeneralResponse{Code: 200}
params := mux.Vars(r)
name := params["name"]
defer func() {
log.Infof("http response [%s]: code [%d]", r.URL.Path, res.Code)
w.WriteHeader(res.Code)
if len(res.Msg) > 0 {
_, _ = w.Write([]byte(res.Msg))
}
}()
log.Infof("http request: [%s]", r.URL.Path)
if name == "" {
res.Code = 400
res.Msg = "proxy name required"
return
}
if err := svr.ctlManager.KickByProxyName(name); err != nil {
res.Code = 404
res.Msg = err.Error()
return
}
res.Msg = "ok"
}
func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName string) (proxyInfo GetProxyStatsResp, code int, msg string) {
proxyInfo.Name = proxyName
ps := mem.StatsCollector.GetProxiesByTypeAndName(proxyType, proxyName)