mirror of
https://github.com/VaalaCat/frp-panel.git
synced 2025-09-26 19:31:18 +08:00
feat: redeploy worker
This commit is contained in:
@@ -44,5 +44,6 @@ jobs:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
run: |
|
||||
mv .ko.workerd.yaml .ko.yaml
|
||||
echo "${password}" | ko login docker.io --username ${username} --password-stdin
|
||||
ko build ./cmd/frpp --sbom=none --bare -t ${{ steps.get_version.outputs.VERSION }}-workerd
|
||||
|
@@ -3,6 +3,7 @@ package client
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/VaalaCat/frp-panel/defs"
|
||||
"github.com/VaalaCat/frp-panel/pb"
|
||||
"github.com/VaalaCat/frp-panel/services/app"
|
||||
"github.com/VaalaCat/frp-panel/services/workerd"
|
||||
@@ -28,7 +29,7 @@ func PullWorkers(appInstance app.Application, clientID, clientSecret string) err
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
logger.Logger(ctx).WithError(err).Error("cannot list client workers")
|
||||
logger.Logger(ctx).WithError(err).Error("cannot list client workers, do not change anything")
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -37,12 +38,15 @@ func PullWorkers(appInstance app.Application, clientID, clientSecret string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
logger.Logger(ctx).Infof("client [%s] has [%d] workers, check their status", clientID, len(resp.GetWorkers()))
|
||||
ctrl := ctx.GetApp().GetWorkersManager()
|
||||
for _, worker := range resp.GetWorkers() {
|
||||
_, err := ctrl.GetWorkerStatus(ctx, worker.GetWorkerId())
|
||||
if err == nil {
|
||||
status, err := ctrl.GetWorkerStatus(ctx, worker.GetWorkerId())
|
||||
if err == nil && status == defs.WorkerStatus_Running {
|
||||
logger.Logger(ctx).Infof("worker [%s] already running", worker.GetWorkerId())
|
||||
continue
|
||||
} else {
|
||||
logger.Logger(ctx).Infof("worker [%s] status is [%s] or maybe has error: [%+v], will restart", worker.GetWorkerId(), status, err)
|
||||
}
|
||||
ctrl.RunWorker(ctx, worker.GetWorkerId(), workerd.NewWorkerdController(worker, ctx.GetApp().GetConfig().Client.Worker.WorkerdWorkDir))
|
||||
}
|
||||
|
@@ -14,6 +14,13 @@ func StartFRPCHandler(ctx *app.Context, req *pb.StartFRPCRequest) (*pb.StartFRPC
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if ctx.GetApp().GetConfig().Client.Features.EnableFunctions {
|
||||
if err := PullWorkers(ctx.GetApp(), req.GetClientId(), ctx.GetApp().GetConfig().Client.Secret); err != nil {
|
||||
logger.Logger(ctx).WithError(err).Error("cannot pull client workers")
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.StartFRPCResponse{
|
||||
Status: &pb.Status{Code: pb.RespCode_RESP_CODE_SUCCESS, Message: "ok"},
|
||||
}, nil
|
||||
|
@@ -12,6 +12,10 @@ func StopFRPCHandler(ctx *app.Context, req *pb.StopFRPCRequest) (*pb.StopFRPCRes
|
||||
ctx.GetApp().GetClientController().StopAll()
|
||||
ctx.GetApp().GetClientController().DeleteAll()
|
||||
|
||||
if ctx.GetApp().GetConfig().Client.Features.EnableFunctions {
|
||||
ctx.GetApp().GetWorkersManager().StopAllWorkers(ctx)
|
||||
}
|
||||
|
||||
return &pb.StopFRPCResponse{
|
||||
Status: &pb.Status{Code: pb.RespCode_RESP_CODE_SUCCESS, Message: "ok"},
|
||||
}, nil
|
||||
|
@@ -93,6 +93,7 @@ func ConfigureRouter(appInstance app.Application, router *gin.Engine) {
|
||||
workerHandler.POST("/list", app.Wrapper(appInstance, worker.ListWorkers))
|
||||
workerHandler.POST("/remove", app.Wrapper(appInstance, worker.RemoveWorker))
|
||||
workerHandler.POST("/update", app.Wrapper(appInstance, worker.UpdateWorker))
|
||||
workerHandler.POST("/redeploy", app.Wrapper(appInstance, worker.RedeployWorker))
|
||||
workerHandler.POST("/create_ingress", app.Wrapper(appInstance, worker.CreateWorkerIngress))
|
||||
workerHandler.POST("/get_ingress", app.Wrapper(appInstance, worker.GetWorkerIngress))
|
||||
}
|
||||
|
96
biz/master/worker/redeploy_worker.go
Normal file
96
biz/master/worker/redeploy_worker.go
Normal file
@@ -0,0 +1,96 @@
|
||||
package worker
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/VaalaCat/frp-panel/common"
|
||||
"github.com/VaalaCat/frp-panel/models"
|
||||
"github.com/VaalaCat/frp-panel/pb"
|
||||
"github.com/VaalaCat/frp-panel/services/app"
|
||||
"github.com/VaalaCat/frp-panel/services/dao"
|
||||
"github.com/VaalaCat/frp-panel/services/rpc"
|
||||
"github.com/VaalaCat/frp-panel/utils"
|
||||
"github.com/VaalaCat/frp-panel/utils/logger"
|
||||
"github.com/samber/lo"
|
||||
)
|
||||
|
||||
func RedeployWorker(ctx *app.Context, req *pb.RedeployWorkerRequest) (*pb.RedeployWorkerResponse, error) {
|
||||
var (
|
||||
clientIds = req.GetClientIds()
|
||||
workerId = req.GetWorkerId()
|
||||
userInfo = common.GetUserInfo(ctx)
|
||||
oldClientIds []string
|
||||
)
|
||||
|
||||
if len(workerId) == 0 {
|
||||
logger.Logger(ctx).Errorf("redeploy worker, worker id req: [%s]", req.String())
|
||||
return nil, fmt.Errorf("worker id is empty")
|
||||
|
||||
}
|
||||
|
||||
workerToUpdate, err := dao.NewQuery(ctx).GetWorkerByWorkerID(userInfo, workerId)
|
||||
if err != nil {
|
||||
logger.Logger(ctx).WithError(err).Errorf("redeploy worker cannot get worker, id: [%s]", workerId)
|
||||
return nil, fmt.Errorf("cannot get worker, id: [%s]", workerId)
|
||||
}
|
||||
|
||||
clis := []*models.Client{}
|
||||
if len(clientIds) != 0 {
|
||||
clis, err = dao.NewQuery(ctx).GetClientsByClientIDs(userInfo, clientIds)
|
||||
if err != nil {
|
||||
logger.Logger(ctx).WithError(err).Errorf("redeploy worker cannot get client, id: [%s]", utils.MarshalForJson(clientIds))
|
||||
return nil, fmt.Errorf("cannot get client, id: [%s]", utils.MarshalForJson(clientIds))
|
||||
}
|
||||
} else {
|
||||
logger.Logger(ctx).Infof("redeploy worker, no clientId, redeploy on all clients")
|
||||
}
|
||||
|
||||
var clisToRedeploy []string
|
||||
allCliIds := lo.Map(workerToUpdate.Clients, func(c models.Client, _ int) string { return c.ClientID })
|
||||
reqCliIds := lo.SliceToMap(clis, func(c *models.Client) (string, struct{}) { return c.ClientID, struct{}{} })
|
||||
// 如果大于0,需要过滤
|
||||
if len(clis) > 0 {
|
||||
clisToRedeploy = lo.Filter(allCliIds, func(c string, _ int) bool {
|
||||
_, ok := reqCliIds[c]
|
||||
return ok
|
||||
})
|
||||
} else {
|
||||
clisToRedeploy = allCliIds
|
||||
}
|
||||
|
||||
go func() {
|
||||
bgCtx := ctx.Background()
|
||||
|
||||
for _, cliId := range clisToRedeploy {
|
||||
removeResp := &pb.RemoveWorkerResponse{}
|
||||
err := rpc.CallClientWrapper(bgCtx, cliId, pb.Event_EVENT_REMOVE_WORKER, &pb.RemoveWorkerRequest{
|
||||
ClientId: &cliId,
|
||||
WorkerId: &workerToUpdate.ID,
|
||||
}, removeResp)
|
||||
if err != nil {
|
||||
logger.Logger(bgCtx).WithError(err).Errorf("remove old worker event send to client error, clients: [%s], worker name: [%s]", cliId, workerToUpdate.Name)
|
||||
}
|
||||
|
||||
createResp := &pb.CreateWorkerResponse{}
|
||||
err = rpc.CallClientWrapper(bgCtx, cliId, pb.Event_EVENT_CREATE_WORKER, &pb.CreateWorkerRequest{
|
||||
ClientId: &cliId,
|
||||
Worker: workerToUpdate.ToPB(),
|
||||
}, createResp)
|
||||
if err != nil {
|
||||
logger.Logger(bgCtx).WithError(err).Errorf("update new worker event send to client error, client id: [%s], worker name: [%s]", cliId, workerToUpdate.Name)
|
||||
}
|
||||
}
|
||||
|
||||
logger.Logger(ctx).Infof("redeploy worker event send to client success, clients: [%s], worker name: [%s], remove old worker send to those clients: %s",
|
||||
utils.MarshalForJson(clis), workerToUpdate.Name, utils.MarshalForJson(oldClientIds))
|
||||
}()
|
||||
|
||||
logger.Logger(ctx).Infof("redeploy worker success, id: [%s], clients: %s", workerId, utils.MarshalForJson(clientIds))
|
||||
|
||||
return &pb.RedeployWorkerResponse{
|
||||
Status: &pb.Status{
|
||||
Code: pb.RespCode_RESP_CODE_SUCCESS,
|
||||
Message: "ok",
|
||||
},
|
||||
}, nil
|
||||
}
|
@@ -28,17 +28,22 @@ func UpdateWorker(ctx *app.Context, req *pb.UpdateWorkerRequest) (*pb.UpdateWork
|
||||
return nil, fmt.Errorf("cannot get worker, id: [%s]", wrokerReq.GetWorkerId())
|
||||
}
|
||||
|
||||
clis, err := dao.NewQuery(ctx).GetClientsByClientIDs(userInfo, clientIds)
|
||||
if err != nil {
|
||||
logger.Logger(ctx).WithError(err).Errorf("cannot get client, id: [%s]", utils.MarshalForJson(clientIds))
|
||||
return nil, fmt.Errorf("cannot get client, id: [%s]", utils.MarshalForJson(clientIds))
|
||||
clis := []*models.Client{}
|
||||
if len(clientIds) != 0 {
|
||||
clis, err = dao.NewQuery(ctx).GetClientsByClientIDs(userInfo, clientIds)
|
||||
if err != nil {
|
||||
logger.Logger(ctx).WithError(err).Errorf("cannot get client, id: [%s]", utils.MarshalForJson(clientIds))
|
||||
return nil, fmt.Errorf("cannot get client, id: [%s]", utils.MarshalForJson(clientIds))
|
||||
}
|
||||
}
|
||||
|
||||
updatedFields := []string{}
|
||||
|
||||
if len(clientIds) != 0 {
|
||||
oldClientIds = lo.Map(workerToUpdate.Clients, func(c models.Client, _ int) string { return c.ClientID })
|
||||
workerToUpdate.Clients = lo.Map(clis, func(c *models.Client, _ int) models.Client { return *c })
|
||||
if len(clis) > 0 {
|
||||
workerToUpdate.Clients = lo.Map(clis, func(c *models.Client, _ int) models.Client { return *c })
|
||||
}
|
||||
updatedFields = append(updatedFields, "client_id")
|
||||
} else {
|
||||
oldClientIds = lo.Map(workerToUpdate.Clients, func(c models.Client, _ int) string { return c.ClientID })
|
||||
|
@@ -386,7 +386,7 @@ func NewWorkersManager(lx fx.Lifecycle, mgr app.WorkerExecManager, appInstance a
|
||||
|
||||
lx.Append(fx.Hook{
|
||||
OnStop: func(ctx context.Context) error {
|
||||
workerMgr.StopAll()
|
||||
workerMgr.StopAllWorkers(app.NewContext(ctx, appInstance))
|
||||
logger.Logger(ctx).Info("stop all workers")
|
||||
return nil
|
||||
},
|
||||
|
@@ -28,7 +28,7 @@ type ReqType interface {
|
||||
pb.StartProxyRequest | pb.StopProxyRequest |
|
||||
pb.CreateWorkerRequest | pb.RemoveWorkerRequest | pb.RunWorkerRequest | pb.StopWorkerRequest | pb.UpdateWorkerRequest | pb.GetWorkerRequest |
|
||||
pb.ListWorkersRequest | pb.CreateWorkerIngressRequest | pb.GetWorkerIngressRequest |
|
||||
pb.GetWorkerStatusRequest | pb.InstallWorkerdRequest |
|
||||
pb.GetWorkerStatusRequest | pb.InstallWorkerdRequest | pb.RedeployWorkerRequest |
|
||||
pb.StartSteamLogRequest
|
||||
}
|
||||
|
||||
|
@@ -29,7 +29,7 @@ type RespType interface {
|
||||
pb.StartProxyResponse | pb.StopProxyResponse |
|
||||
pb.CreateWorkerResponse | pb.RemoveWorkerResponse | pb.RunWorkerResponse | pb.StopWorkerResponse | pb.UpdateWorkerResponse | pb.GetWorkerResponse |
|
||||
pb.ListWorkersResponse | pb.CreateWorkerIngressResponse | pb.GetWorkerIngressResponse |
|
||||
pb.GetWorkerStatusResponse | pb.InstallWorkerdResponse |
|
||||
pb.GetWorkerStatusResponse | pb.InstallWorkerdResponse | pb.RedeployWorkerResponse |
|
||||
pb.StartSteamLogResponse
|
||||
}
|
||||
|
||||
|
@@ -275,3 +275,12 @@ message InstallWorkerdRequest {
|
||||
message InstallWorkerdResponse {
|
||||
optional common.Status status = 1;
|
||||
}
|
||||
|
||||
message RedeployWorkerRequest {
|
||||
optional string worker_id = 1;
|
||||
repeated string client_ids = 2;
|
||||
}
|
||||
|
||||
message RedeployWorkerResponse {
|
||||
optional common.Status status = 1;
|
||||
}
|
@@ -43,4 +43,4 @@ New-Item -Path "C:\frpp" -ItemType Directory -ErrorAction SilentlyContinue
|
||||
Move-Item -Path "C:\frpp.exe" -Destination "C:\frpp\frpp.exe"
|
||||
C:\frpp\frpp.exe install $args
|
||||
C:\frpp\frpp.exe start
|
||||
Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red
|
||||
Write-Host "Enjoy It!" -BackgroundColor DarkGreen -ForegroundColor Red
|
85
install.sh
85
install.sh
@@ -3,13 +3,47 @@
|
||||
OS=$(uname -s)
|
||||
ARCH=$(uname -m)
|
||||
|
||||
# --- Argument Parsing ---
|
||||
custom_proxy=""
|
||||
frp_panel_args=() # Use an array to hold arguments for frp-panel
|
||||
|
||||
# Parse arguments
|
||||
while [[ "$#" -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--github-proxy)
|
||||
if [ -z "$2" ]; then
|
||||
echo "Error: --github-proxy requires a URL argument."
|
||||
exit 1
|
||||
fi
|
||||
custom_proxy="$2"
|
||||
echo "使用自定义的 GitHub 镜像: $custom_proxy"
|
||||
shift 2 # shift past argument name and value
|
||||
;;
|
||||
*)
|
||||
# Collect remaining arguments for frp-panel
|
||||
frp_panel_args+=("$1") # Add the argument to the array
|
||||
shift 1 # shift past argument
|
||||
;;
|
||||
esac
|
||||
done
|
||||
# --- End Argument Parsing ---
|
||||
|
||||
|
||||
# --- Network Check and Prefix Setting ---
|
||||
prefix=""
|
||||
if ping -c 1 -W 1 google.com > /dev/null 2>&1; then
|
||||
prefix=""
|
||||
echo "检测到您的网络可以连接到 Google,不使用镜像下载"
|
||||
else
|
||||
prefix="https://ghfast.top/"
|
||||
echo "检测到您的网络无法连接到 Google,使用镜像下载"
|
||||
if [ -n "$custom_proxy" ]; then
|
||||
prefix="$custom_proxy"
|
||||
echo "检测到您的网络无法连接到 Google,使用用户提供的镜像下载: $prefix"
|
||||
else
|
||||
prefix="https://ghfast.top/"
|
||||
echo "检测到您的网络无法连接到 Google,使用默认镜像下载: $prefix"
|
||||
fi
|
||||
fi
|
||||
# --- End Network Check and Prefix Setting ---
|
||||
|
||||
|
||||
current_dir=$(pwd)
|
||||
temp_dir=$(mktemp -d)
|
||||
@@ -31,6 +65,10 @@ case "$OS" in
|
||||
armv6l)
|
||||
wget -O frp-panel "${prefix}https://github.com/VaalaCat/frp-panel/releases/latest/download/frp-panel-linux-armv6l"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported Linux architecture: $ARCH"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
Darwin)
|
||||
@@ -41,6 +79,10 @@ case "$OS" in
|
||||
arm64)
|
||||
wget -O frp-panel "${prefix}https://github.com/VaalaCat/frp-panel/releases/latest/download/frp-panel-darwin-arm64"
|
||||
;;
|
||||
*)
|
||||
echo "Unsupported Darwin architecture: $ARCH"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
*)
|
||||
@@ -49,17 +91,18 @@ case "$OS" in
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ ! -f frp-panel ]; then
|
||||
echo "Error: Download failed. frp-panel executable not found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
sudo chmod +x frp-panel
|
||||
|
||||
cd "$current_dir"
|
||||
|
||||
new_executable_path="$temp_dir/frp-panel"
|
||||
|
||||
get_start_params() {
|
||||
read -p "请输入启动参数:" params
|
||||
echo "$params"
|
||||
}
|
||||
|
||||
# Function to find the frpp executable path from systemd service file
|
||||
find_frpp_executable() {
|
||||
service_file=$(systemctl show -p FragmentPath frpp.service 2>/dev/null | cut -d'=' -f2)
|
||||
if [[ -z "$service_file" || ! -f "$service_file" ]]; then
|
||||
@@ -75,25 +118,21 @@ find_frpp_executable() {
|
||||
echo "$executable_path"
|
||||
}
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
start_params="$@"
|
||||
else
|
||||
start_params=$(get_start_params)
|
||||
fi
|
||||
|
||||
# --- Install or Update frp-panel ---
|
||||
if systemctl list-units --type=service | grep -q frpp; then
|
||||
echo "frpp 服务存在"
|
||||
echo "frpp 服务存在,进行更新"
|
||||
executable_path=$(find_frpp_executable)
|
||||
if [ -z "$executable_path" ]; then
|
||||
echo "无法找到 frpp 服务的执行文件路径,请检查systemd文件"
|
||||
exit 1
|
||||
fi
|
||||
echo "更新程序到原路径:$executable_path"
|
||||
sudo rm -rf "$executable_path"
|
||||
sudo rm -f "$executable_path" # Use -f to avoid prompt if file is read-only
|
||||
sudo cp "$new_executable_path" "$executable_path"
|
||||
$executable_path version
|
||||
sudo $executable_path uninstall
|
||||
sudo $executable_path install $start_params
|
||||
sudo "$executable_path" version
|
||||
sudo "$executable_path" uninstall
|
||||
# Pass collected frp_panel_args to install command
|
||||
sudo "$executable_path" install "${frp_panel_args[@]}"
|
||||
sudo systemctl daemon-reload
|
||||
echo "参数已重写,请执行 cat /etc/systemd/system/frpp.service 仔细检查启动命令,避免无法启动"
|
||||
echo "执行 sudo systemctl restart frpp 重启服务"
|
||||
@@ -103,9 +142,11 @@ else
|
||||
echo "frpp 服务不存在,进行安装"
|
||||
fi
|
||||
|
||||
# Copy the new executable to the current directory for initial install
|
||||
sudo cp "$new_executable_path" .
|
||||
|
||||
sudo ./frp-panel install $start_params
|
||||
# Run the install command with collected frp_panel_args
|
||||
sudo ./frp-panel install "${frp_panel_args[@]}"
|
||||
|
||||
echo "frp-panel 服务安装完成, 安装路径:$(pwd)/frp-panel"
|
||||
|
||||
@@ -120,3 +161,7 @@ echo "frp-panel 服务已启动"
|
||||
sudo systemctl restart frpp
|
||||
|
||||
sudo systemctl enable frpp
|
||||
|
||||
# Clean up temporary directory
|
||||
rm -rf "$temp_dir"
|
||||
echo "清理临时文件夹: $temp_dir"
|
||||
|
@@ -2830,6 +2830,102 @@ func (x *InstallWorkerdResponse) GetStatus() *Status {
|
||||
return nil
|
||||
}
|
||||
|
||||
type RedeployWorkerRequest struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
WorkerId *string `protobuf:"bytes,1,opt,name=worker_id,json=workerId,proto3,oneof" json:"worker_id,omitempty"`
|
||||
ClientIds []string `protobuf:"bytes,2,rep,name=client_ids,json=clientIds,proto3" json:"client_ids,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RedeployWorkerRequest) Reset() {
|
||||
*x = RedeployWorkerRequest{}
|
||||
mi := &file_api_client_proto_msgTypes[54]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RedeployWorkerRequest) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RedeployWorkerRequest) ProtoMessage() {}
|
||||
|
||||
func (x *RedeployWorkerRequest) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_client_proto_msgTypes[54]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RedeployWorkerRequest.ProtoReflect.Descriptor instead.
|
||||
func (*RedeployWorkerRequest) Descriptor() ([]byte, []int) {
|
||||
return file_api_client_proto_rawDescGZIP(), []int{54}
|
||||
}
|
||||
|
||||
func (x *RedeployWorkerRequest) GetWorkerId() string {
|
||||
if x != nil && x.WorkerId != nil {
|
||||
return *x.WorkerId
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (x *RedeployWorkerRequest) GetClientIds() []string {
|
||||
if x != nil {
|
||||
return x.ClientIds
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type RedeployWorkerResponse struct {
|
||||
state protoimpl.MessageState `protogen:"open.v1"`
|
||||
Status *Status `protobuf:"bytes,1,opt,name=status,proto3,oneof" json:"status,omitempty"`
|
||||
unknownFields protoimpl.UnknownFields
|
||||
sizeCache protoimpl.SizeCache
|
||||
}
|
||||
|
||||
func (x *RedeployWorkerResponse) Reset() {
|
||||
*x = RedeployWorkerResponse{}
|
||||
mi := &file_api_client_proto_msgTypes[55]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
|
||||
func (x *RedeployWorkerResponse) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*RedeployWorkerResponse) ProtoMessage() {}
|
||||
|
||||
func (x *RedeployWorkerResponse) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_api_client_proto_msgTypes[55]
|
||||
if x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use RedeployWorkerResponse.ProtoReflect.Descriptor instead.
|
||||
func (*RedeployWorkerResponse) Descriptor() ([]byte, []int) {
|
||||
return file_api_client_proto_rawDescGZIP(), []int{55}
|
||||
}
|
||||
|
||||
func (x *RedeployWorkerResponse) GetStatus() *Status {
|
||||
if x != nil {
|
||||
return x.Status
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var File_api_client_proto protoreflect.FileDescriptor
|
||||
|
||||
const file_api_client_proto_rawDesc = "" +
|
||||
@@ -3153,6 +3249,15 @@ const file_api_client_proto_rawDesc = "" +
|
||||
"\r_download_url\"P\n" +
|
||||
"\x16InstallWorkerdResponse\x12+\n" +
|
||||
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01B\t\n" +
|
||||
"\a_status\"f\n" +
|
||||
"\x15RedeployWorkerRequest\x12 \n" +
|
||||
"\tworker_id\x18\x01 \x01(\tH\x00R\bworkerId\x88\x01\x01\x12\x1d\n" +
|
||||
"\n" +
|
||||
"client_ids\x18\x02 \x03(\tR\tclientIdsB\f\n" +
|
||||
"\n" +
|
||||
"_worker_id\"P\n" +
|
||||
"\x16RedeployWorkerResponse\x12+\n" +
|
||||
"\x06status\x18\x01 \x01(\v2\x0e.common.StatusH\x00R\x06status\x88\x01\x01B\t\n" +
|
||||
"\a_statusB\aZ\x05../pbb\x06proto3"
|
||||
|
||||
var (
|
||||
@@ -3167,7 +3272,7 @@ func file_api_client_proto_rawDescGZIP() []byte {
|
||||
return file_api_client_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_api_client_proto_msgTypes = make([]protoimpl.MessageInfo, 55)
|
||||
var file_api_client_proto_msgTypes = make([]protoimpl.MessageInfo, 57)
|
||||
var file_api_client_proto_goTypes = []any{
|
||||
(*InitClientRequest)(nil), // 0: api_client.InitClientRequest
|
||||
(*InitClientResponse)(nil), // 1: api_client.InitClientResponse
|
||||
@@ -3223,60 +3328,63 @@ var file_api_client_proto_goTypes = []any{
|
||||
(*GetWorkerStatusResponse)(nil), // 51: api_client.GetWorkerStatusResponse
|
||||
(*InstallWorkerdRequest)(nil), // 52: api_client.InstallWorkerdRequest
|
||||
(*InstallWorkerdResponse)(nil), // 53: api_client.InstallWorkerdResponse
|
||||
nil, // 54: api_client.GetWorkerStatusResponse.WorkerStatusEntry
|
||||
(*Status)(nil), // 55: common.Status
|
||||
(*Client)(nil), // 56: common.Client
|
||||
(*ProxyInfo)(nil), // 57: common.ProxyInfo
|
||||
(*ProxyConfig)(nil), // 58: common.ProxyConfig
|
||||
(*ProxyWorkingStatus)(nil), // 59: common.ProxyWorkingStatus
|
||||
(*Worker)(nil), // 60: common.Worker
|
||||
(*RedeployWorkerRequest)(nil), // 54: api_client.RedeployWorkerRequest
|
||||
(*RedeployWorkerResponse)(nil), // 55: api_client.RedeployWorkerResponse
|
||||
nil, // 56: api_client.GetWorkerStatusResponse.WorkerStatusEntry
|
||||
(*Status)(nil), // 57: common.Status
|
||||
(*Client)(nil), // 58: common.Client
|
||||
(*ProxyInfo)(nil), // 59: common.ProxyInfo
|
||||
(*ProxyConfig)(nil), // 60: common.ProxyConfig
|
||||
(*ProxyWorkingStatus)(nil), // 61: common.ProxyWorkingStatus
|
||||
(*Worker)(nil), // 62: common.Worker
|
||||
}
|
||||
var file_api_client_proto_depIdxs = []int32{
|
||||
55, // 0: api_client.InitClientResponse.status:type_name -> common.Status
|
||||
55, // 1: api_client.ListClientsResponse.status:type_name -> common.Status
|
||||
56, // 2: api_client.ListClientsResponse.clients:type_name -> common.Client
|
||||
55, // 3: api_client.GetClientResponse.status:type_name -> common.Status
|
||||
56, // 4: api_client.GetClientResponse.client:type_name -> common.Client
|
||||
55, // 5: api_client.DeleteClientResponse.status:type_name -> common.Status
|
||||
55, // 6: api_client.UpdateFRPCResponse.status:type_name -> common.Status
|
||||
55, // 7: api_client.RemoveFRPCResponse.status:type_name -> common.Status
|
||||
55, // 8: api_client.StopFRPCResponse.status:type_name -> common.Status
|
||||
55, // 9: api_client.StartFRPCResponse.status:type_name -> common.Status
|
||||
55, // 10: api_client.GetProxyStatsByClientIDResponse.status:type_name -> common.Status
|
||||
57, // 11: api_client.GetProxyStatsByClientIDResponse.proxy_infos:type_name -> common.ProxyInfo
|
||||
55, // 12: api_client.ListProxyConfigsResponse.status:type_name -> common.Status
|
||||
58, // 13: api_client.ListProxyConfigsResponse.proxy_configs:type_name -> common.ProxyConfig
|
||||
55, // 14: api_client.CreateProxyConfigResponse.status:type_name -> common.Status
|
||||
55, // 15: api_client.DeleteProxyConfigResponse.status:type_name -> common.Status
|
||||
55, // 16: api_client.UpdateProxyConfigResponse.status:type_name -> common.Status
|
||||
55, // 17: api_client.GetProxyConfigResponse.status:type_name -> common.Status
|
||||
58, // 18: api_client.GetProxyConfigResponse.proxy_config:type_name -> common.ProxyConfig
|
||||
59, // 19: api_client.GetProxyConfigResponse.working_status:type_name -> common.ProxyWorkingStatus
|
||||
55, // 20: api_client.StopProxyResponse.status:type_name -> common.Status
|
||||
55, // 21: api_client.StartProxyResponse.status:type_name -> common.Status
|
||||
60, // 22: api_client.CreateWorkerRequest.worker:type_name -> common.Worker
|
||||
55, // 23: api_client.CreateWorkerResponse.status:type_name -> common.Status
|
||||
55, // 24: api_client.RemoveWorkerResponse.status:type_name -> common.Status
|
||||
60, // 25: api_client.UpdateWorkerRequest.worker:type_name -> common.Worker
|
||||
55, // 26: api_client.UpdateWorkerResponse.status:type_name -> common.Status
|
||||
55, // 27: api_client.RunWorkerResponse.status:type_name -> common.Status
|
||||
55, // 28: api_client.StopWorkerResponse.status:type_name -> common.Status
|
||||
55, // 29: api_client.ListWorkersResponse.status:type_name -> common.Status
|
||||
60, // 30: api_client.ListWorkersResponse.workers:type_name -> common.Worker
|
||||
55, // 31: api_client.CreateWorkerIngressResponse.status:type_name -> common.Status
|
||||
55, // 32: api_client.GetWorkerIngressResponse.status:type_name -> common.Status
|
||||
58, // 33: api_client.GetWorkerIngressResponse.proxy_configs:type_name -> common.ProxyConfig
|
||||
55, // 34: api_client.GetWorkerResponse.status:type_name -> common.Status
|
||||
60, // 35: api_client.GetWorkerResponse.worker:type_name -> common.Worker
|
||||
56, // 36: api_client.GetWorkerResponse.clients:type_name -> common.Client
|
||||
55, // 37: api_client.GetWorkerStatusResponse.status:type_name -> common.Status
|
||||
54, // 38: api_client.GetWorkerStatusResponse.worker_status:type_name -> api_client.GetWorkerStatusResponse.WorkerStatusEntry
|
||||
55, // 39: api_client.InstallWorkerdResponse.status:type_name -> common.Status
|
||||
40, // [40:40] is the sub-list for method output_type
|
||||
40, // [40:40] is the sub-list for method input_type
|
||||
40, // [40:40] is the sub-list for extension type_name
|
||||
40, // [40:40] is the sub-list for extension extendee
|
||||
0, // [0:40] is the sub-list for field type_name
|
||||
57, // 0: api_client.InitClientResponse.status:type_name -> common.Status
|
||||
57, // 1: api_client.ListClientsResponse.status:type_name -> common.Status
|
||||
58, // 2: api_client.ListClientsResponse.clients:type_name -> common.Client
|
||||
57, // 3: api_client.GetClientResponse.status:type_name -> common.Status
|
||||
58, // 4: api_client.GetClientResponse.client:type_name -> common.Client
|
||||
57, // 5: api_client.DeleteClientResponse.status:type_name -> common.Status
|
||||
57, // 6: api_client.UpdateFRPCResponse.status:type_name -> common.Status
|
||||
57, // 7: api_client.RemoveFRPCResponse.status:type_name -> common.Status
|
||||
57, // 8: api_client.StopFRPCResponse.status:type_name -> common.Status
|
||||
57, // 9: api_client.StartFRPCResponse.status:type_name -> common.Status
|
||||
57, // 10: api_client.GetProxyStatsByClientIDResponse.status:type_name -> common.Status
|
||||
59, // 11: api_client.GetProxyStatsByClientIDResponse.proxy_infos:type_name -> common.ProxyInfo
|
||||
57, // 12: api_client.ListProxyConfigsResponse.status:type_name -> common.Status
|
||||
60, // 13: api_client.ListProxyConfigsResponse.proxy_configs:type_name -> common.ProxyConfig
|
||||
57, // 14: api_client.CreateProxyConfigResponse.status:type_name -> common.Status
|
||||
57, // 15: api_client.DeleteProxyConfigResponse.status:type_name -> common.Status
|
||||
57, // 16: api_client.UpdateProxyConfigResponse.status:type_name -> common.Status
|
||||
57, // 17: api_client.GetProxyConfigResponse.status:type_name -> common.Status
|
||||
60, // 18: api_client.GetProxyConfigResponse.proxy_config:type_name -> common.ProxyConfig
|
||||
61, // 19: api_client.GetProxyConfigResponse.working_status:type_name -> common.ProxyWorkingStatus
|
||||
57, // 20: api_client.StopProxyResponse.status:type_name -> common.Status
|
||||
57, // 21: api_client.StartProxyResponse.status:type_name -> common.Status
|
||||
62, // 22: api_client.CreateWorkerRequest.worker:type_name -> common.Worker
|
||||
57, // 23: api_client.CreateWorkerResponse.status:type_name -> common.Status
|
||||
57, // 24: api_client.RemoveWorkerResponse.status:type_name -> common.Status
|
||||
62, // 25: api_client.UpdateWorkerRequest.worker:type_name -> common.Worker
|
||||
57, // 26: api_client.UpdateWorkerResponse.status:type_name -> common.Status
|
||||
57, // 27: api_client.RunWorkerResponse.status:type_name -> common.Status
|
||||
57, // 28: api_client.StopWorkerResponse.status:type_name -> common.Status
|
||||
57, // 29: api_client.ListWorkersResponse.status:type_name -> common.Status
|
||||
62, // 30: api_client.ListWorkersResponse.workers:type_name -> common.Worker
|
||||
57, // 31: api_client.CreateWorkerIngressResponse.status:type_name -> common.Status
|
||||
57, // 32: api_client.GetWorkerIngressResponse.status:type_name -> common.Status
|
||||
60, // 33: api_client.GetWorkerIngressResponse.proxy_configs:type_name -> common.ProxyConfig
|
||||
57, // 34: api_client.GetWorkerResponse.status:type_name -> common.Status
|
||||
62, // 35: api_client.GetWorkerResponse.worker:type_name -> common.Worker
|
||||
58, // 36: api_client.GetWorkerResponse.clients:type_name -> common.Client
|
||||
57, // 37: api_client.GetWorkerStatusResponse.status:type_name -> common.Status
|
||||
56, // 38: api_client.GetWorkerStatusResponse.worker_status:type_name -> api_client.GetWorkerStatusResponse.WorkerStatusEntry
|
||||
57, // 39: api_client.InstallWorkerdResponse.status:type_name -> common.Status
|
||||
57, // 40: api_client.RedeployWorkerResponse.status:type_name -> common.Status
|
||||
41, // [41:41] is the sub-list for method output_type
|
||||
41, // [41:41] is the sub-list for method input_type
|
||||
41, // [41:41] is the sub-list for extension type_name
|
||||
41, // [41:41] is the sub-list for extension extendee
|
||||
0, // [0:41] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_api_client_proto_init() }
|
||||
@@ -3339,13 +3447,15 @@ func file_api_client_proto_init() {
|
||||
file_api_client_proto_msgTypes[51].OneofWrappers = []any{}
|
||||
file_api_client_proto_msgTypes[52].OneofWrappers = []any{}
|
||||
file_api_client_proto_msgTypes[53].OneofWrappers = []any{}
|
||||
file_api_client_proto_msgTypes[54].OneofWrappers = []any{}
|
||||
file_api_client_proto_msgTypes[55].OneofWrappers = []any{}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: unsafe.Slice(unsafe.StringData(file_api_client_proto_rawDesc), len(file_api_client_proto_rawDesc)),
|
||||
NumEnums: 0,
|
||||
NumMessages: 55,
|
||||
NumMessages: 57,
|
||||
NumExtensions: 0,
|
||||
NumServices: 0,
|
||||
},
|
||||
|
@@ -192,6 +192,7 @@ type WorkerController interface {
|
||||
|
||||
// services/workerd/workers_manager.go
|
||||
type WorkersManager interface {
|
||||
StopAllWorkers(ctx *Context)
|
||||
GetWorker(ctx *Context, id string) (WorkerController, bool)
|
||||
RunWorker(ctx *Context, id string, worker WorkerController) error
|
||||
StopWorker(ctx *Context, id string) error
|
||||
|
@@ -29,15 +29,23 @@ type server struct {
|
||||
|
||||
// ListClientWorkers implements pb.MasterServer.
|
||||
func (s *server) ListClientWorkers(ctx context.Context, req *pb.ListClientWorkersRequest) (*pb.ListClientWorkersResponse, error) {
|
||||
logger.Logger(ctx).Infof("list client workers, clientID: [%+v]", req.GetBase().GetClientId())
|
||||
logger.Logger(ctx).Infof("list client workers, clientID: [%s]", req.GetBase().GetClientId())
|
||||
appCtx := app.NewContext(ctx, s.appInstance)
|
||||
|
||||
if _, err := client.ValidateClientRequest(appCtx, req.GetBase()); err != nil {
|
||||
if client, err := client.ValidateClientRequest(appCtx, req.GetBase()); err != nil {
|
||||
logger.Logger(ctx).WithError(err).Errorf("cannot validate client request")
|
||||
return nil, err
|
||||
} else if client.Stopped {
|
||||
logger.Logger(appCtx).Infof("list client workers, client [%s] is stopped", req.GetBase().GetClientId())
|
||||
return &pb.ListClientWorkersResponse{
|
||||
Status: &pb.Status{
|
||||
Code: pb.RespCode_RESP_CODE_NOT_FOUND,
|
||||
Message: "client stopped",
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
logger.Logger(appCtx).Infof("validate client success, clientID: [%+v]", req.GetBase().GetClientId())
|
||||
logger.Logger(appCtx).Infof("validate client success, clientID: [%s]", req.GetBase().GetClientId())
|
||||
|
||||
return worker.ListClientWorkers(appCtx, req)
|
||||
}
|
||||
|
@@ -46,9 +46,9 @@ func (m *workersManager) StopWorker(ctx *app.Context, id string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *workersManager) StopAll() {
|
||||
func (m *workersManager) StopAllWorkers(ctx *app.Context) {
|
||||
m.workers.Range(func(k string, v app.WorkerController) bool {
|
||||
v.StopWorker(nil)
|
||||
v.StopWorker(ctx)
|
||||
return true
|
||||
})
|
||||
|
||||
|
@@ -15,6 +15,8 @@ import {
|
||||
InstallWorkerdResponse,
|
||||
ListWorkersRequest,
|
||||
ListWorkersResponse,
|
||||
RedeployWorkerRequest,
|
||||
RedeployWorkerResponse,
|
||||
RemoveWorkerRequest,
|
||||
RemoveWorkerResponse,
|
||||
UpdateWorkerRequest,
|
||||
@@ -67,3 +69,8 @@ export const installWorkerd = async (req: InstallWorkerdRequest) => {
|
||||
const res = await http.post(API_PATH + '/client/install_workerd', InstallWorkerdRequest.toJson(req))
|
||||
return InstallWorkerdResponse.fromJson((res.data as BaseResponse).body)
|
||||
}
|
||||
|
||||
export const redeployWorker = async (req: RedeployWorkerRequest) => {
|
||||
const res = await http.post(API_PATH + '/worker/redeploy', RedeployWorkerRequest.toJson(req))
|
||||
return RedeployWorkerResponse.fromJson((res.data as BaseResponse).body)
|
||||
}
|
@@ -26,7 +26,7 @@ import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { deleteClient, listClient } from '@/api/client'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $platformInfo, $useServerGithubProxyUrl } from '@/store/user'
|
||||
import { $frontendPreference, $platformInfo, $useServerGithubProxyUrl } from '@/store/user'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { getClientsStatus } from '@/api/platform'
|
||||
import { Client, ClientType } from '@/lib/pb/common'
|
||||
@@ -126,6 +126,7 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
const { t } = useTranslation()
|
||||
const platformInfo = useStore($platformInfo)
|
||||
const useGithubProxyUrl = useStore($useServerGithubProxyUrl)
|
||||
const frontendPreference = useStore($frontendPreference)
|
||||
|
||||
if (!platformInfo) {
|
||||
return (
|
||||
@@ -149,20 +150,23 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
<p className="text-sm text-muted-foreground">{t('client.install.description')}</p>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<div className="flex flex-row justify-start items-center gap-4 mb-2">
|
||||
<Checkbox onCheckedChange={$useServerGithubProxyUrl.set} defaultChecked={useGithubProxyUrl} />
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.use_github_proxy_url')}</Label>
|
||||
<Checkbox onCheckedChange={$useServerGithubProxyUrl.set} defaultChecked={useGithubProxyUrl} />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.github_proxy_url')}</Label>
|
||||
<Input value={frontendPreference.githubProxyUrl} onChange={(e) =>
|
||||
$frontendPreference.set({ ...frontendPreference, githubProxyUrl: e.target.value })} />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4">
|
||||
<Input
|
||||
readOnly
|
||||
value={WindowsInstallCommand('client', client, platformInfo, useGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
onClick={() =>
|
||||
navigator.clipboard.writeText(
|
||||
WindowsInstallCommand('client', client, platformInfo, useGithubProxyUrl),
|
||||
WindowsInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl),
|
||||
)
|
||||
}
|
||||
disabled={!platformInfo}
|
||||
@@ -171,16 +175,22 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
>
|
||||
{t('client.install.windows')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4">
|
||||
<Input
|
||||
readOnly
|
||||
value={LinuxInstallCommand('client', client, platformInfo, useGithubProxyUrl)}
|
||||
value={WindowsInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4">
|
||||
<Button
|
||||
onClick={() =>
|
||||
navigator.clipboard.writeText(LinuxInstallCommand('client', client, platformInfo, useGithubProxyUrl))
|
||||
navigator.clipboard.writeText(LinuxInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))
|
||||
}
|
||||
disabled={!platformInfo}
|
||||
size="sm"
|
||||
@@ -188,6 +198,14 @@ export const ClientID = ({ client }: { client: ClientTableSchema }) => {
|
||||
>
|
||||
{t('client.install.linux')}
|
||||
</Button>
|
||||
<Input
|
||||
readOnly
|
||||
value={LinuxInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -327,6 +345,7 @@ export const ClientActions: React.FC<ClientItemProps> = ({ client, table }) => {
|
||||
const router = useRouter()
|
||||
const platformInfo = useStore($platformInfo)
|
||||
const useGithubProxyUrl = useStore($useServerGithubProxyUrl)
|
||||
const frontendPreference = useStore($frontendPreference)
|
||||
|
||||
const removeClient = useMutation({
|
||||
mutationFn: deleteClient,
|
||||
@@ -412,7 +431,10 @@ export const ClientActions: React.FC<ClientItemProps> = ({ client, table }) => {
|
||||
onClick={() => {
|
||||
try {
|
||||
if (platformInfo) {
|
||||
navigator.clipboard.writeText(LinuxInstallCommand('client', client, platformInfo, useGithubProxyUrl))
|
||||
navigator.clipboard.writeText(LinuxInstallCommand('client', client, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))
|
||||
toast(t('client.actions_menu.copy_success'))
|
||||
} else {
|
||||
toast(t('client.actions_menu.copy_failed'))
|
||||
|
@@ -26,7 +26,7 @@ import { useMutation, useQuery } from '@tanstack/react-query'
|
||||
import { deleteServer } from '@/api/server'
|
||||
import { useRouter } from 'next/router'
|
||||
import { useStore } from '@nanostores/react'
|
||||
import { $platformInfo } from '@/store/user'
|
||||
import { $frontendPreference, $platformInfo, $useServerGithubProxyUrl } from '@/store/user'
|
||||
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
|
||||
import { getClientsStatus } from '@/api/platform'
|
||||
import { ClientType } from '@/lib/pb/common'
|
||||
@@ -37,6 +37,8 @@ import { useTranslation } from 'react-i18next'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { toast } from 'sonner'
|
||||
import { $serverTableRefetchTrigger } from '@/store/refetch-trigger'
|
||||
import { Checkbox } from '../ui/checkbox'
|
||||
import { Label } from '../ui/label'
|
||||
|
||||
export type ServerTableSchema = {
|
||||
id: string
|
||||
@@ -122,6 +124,8 @@ export const columns: ColumnDef<ServerTableSchema>[] = [
|
||||
export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
const { t } = useTranslation()
|
||||
const platformInfo = useStore($platformInfo)
|
||||
const useGithubProxyUrl = useStore($useServerGithubProxyUrl)
|
||||
const frontendPreference = useStore($frontendPreference)
|
||||
|
||||
if (!platformInfo) {
|
||||
return (
|
||||
@@ -145,35 +149,56 @@ export const ServerID = ({ server }: { server: ServerTableSchema }) => {
|
||||
<p className="text-sm text-muted-foreground">{t('server.install.description')}</p>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.use_github_proxy_url')}</Label>
|
||||
<Checkbox onCheckedChange={$useServerGithubProxyUrl.set} defaultChecked={useGithubProxyUrl} />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4 justify-items-center">
|
||||
<Label>{t('client.install.github_proxy_url')}</Label>
|
||||
<Input value={frontendPreference.githubProxyUrl} onChange={(e) =>
|
||||
$frontendPreference.set({ ...frontendPreference, githubProxyUrl: e.target.value })} />
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4">
|
||||
<Input
|
||||
readOnly
|
||||
value={WindowsInstallCommand('server', server, platformInfo)}
|
||||
className="flex-1"
|
||||
/>
|
||||
<Button
|
||||
onClick={() => navigator.clipboard.writeText(WindowsInstallCommand('server', server, platformInfo))}
|
||||
onClick={() => navigator.clipboard.writeText(WindowsInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))}
|
||||
disabled={!platformInfo}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
{t('server.install.windows')}
|
||||
</Button>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4">
|
||||
<Input
|
||||
readOnly
|
||||
value={LinuxInstallCommand('server', server, platformInfo)}
|
||||
value={WindowsInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 items-center gap-4">
|
||||
<Button
|
||||
onClick={() => navigator.clipboard.writeText(LinuxInstallCommand('server', server, platformInfo))}
|
||||
onClick={() => navigator.clipboard.writeText(LinuxInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl))}
|
||||
disabled={!platformInfo}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
>
|
||||
{t('server.install.linux')}
|
||||
</Button>
|
||||
<Input
|
||||
readOnly
|
||||
value={LinuxInstallCommand('server', server, {
|
||||
...platformInfo,
|
||||
githubProxyUrl: useGithubProxyUrl && frontendPreference.githubProxyUrl ? frontendPreference.githubProxyUrl : platformInfo.githubProxyUrl,
|
||||
}, useGithubProxyUrl)}
|
||||
className="flex-1"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@@ -18,7 +18,7 @@ import {
|
||||
DialogClose,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { removeWorker, getWorker, getWorkerIngress, getWorkerStatus } from '@/api/worker'
|
||||
import { removeWorker, getWorker, getWorkerIngress, getWorkerStatus, redeployWorker } from '@/api/worker'
|
||||
import { $workerTableRefetchTrigger } from '@/store/refetch-trigger'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useRouter } from 'next/router'
|
||||
@@ -133,6 +133,18 @@ export const WorkerActions: React.FC<WorkerActionsProps> = ({ worker }) => {
|
||||
},
|
||||
})
|
||||
|
||||
const redeploy = useMutation({
|
||||
mutationFn: () => redeployWorker({ workerId: worker.workerId, clientIds: [] }),
|
||||
onSuccess: () => {
|
||||
toast.success(t('worker.actions_menu.redeploy_worker') + t('common.success'))
|
||||
$workerTableRefetchTrigger.set(Math.random())
|
||||
},
|
||||
onError: (err: any) => {
|
||||
toast(t('common.failed'), { description: err.message })
|
||||
$workerTableRefetchTrigger.set(Math.random())
|
||||
},
|
||||
})
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DropdownMenu>
|
||||
@@ -153,6 +165,11 @@ export const WorkerActions: React.FC<WorkerActionsProps> = ({ worker }) => {
|
||||
>
|
||||
{t('worker.actions_menu.edit')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem
|
||||
onClick={() => { redeploy.mutate() }}
|
||||
>
|
||||
{t('worker.actions_menu.redeploy_worker')}
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator />
|
||||
<DialogTrigger asChild>
|
||||
<DropdownMenuItem className="text-destructive">{t('worker.actions_menu.delete')}</DropdownMenuItem>
|
||||
|
@@ -38,6 +38,7 @@
|
||||
"title": "Installation Command",
|
||||
"description": "Select your operating system and copy the installation command",
|
||||
"use_github_proxy_url": "Use GitHub Proxy URL",
|
||||
"github_proxy_url": "GitHub Proxy URL",
|
||||
"windows": "Windows",
|
||||
"linux": "Linux"
|
||||
},
|
||||
@@ -557,6 +558,7 @@
|
||||
},
|
||||
"actions_menu": {
|
||||
"delete": "Delete Worker",
|
||||
"redeploy_worker": "Re-deploy Worker",
|
||||
"title": "Worker Actions",
|
||||
"edit": "Edit Worker"
|
||||
},
|
||||
|
@@ -38,6 +38,7 @@
|
||||
"title": "安装命令",
|
||||
"description": "请选择您的操作系统并复制相应的安装命令",
|
||||
"use_github_proxy_url": "使用 GitHub 代理",
|
||||
"github_proxy_url": "GitHub 代理 URL",
|
||||
"windows": "Windows",
|
||||
"linux": "Linux"
|
||||
},
|
||||
@@ -555,6 +556,7 @@
|
||||
},
|
||||
"actions_menu": {
|
||||
"delete": "删除",
|
||||
"redeploy_worker": "重启",
|
||||
"title": "操作",
|
||||
"edit": "编辑"
|
||||
},
|
||||
|
@@ -113,7 +113,10 @@ export const LinuxInstallCommand = <T extends Client | Server>(
|
||||
info: GetPlatformInfoResponse,
|
||||
github_proxy?: boolean,
|
||||
) => {
|
||||
return `curl -fSL ${github_proxy ? info.githubProxyUrl : ''}https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s --${ExecCommandStr(type, item, info, ' ')}`
|
||||
return `curl -fSL ${github_proxy ? info.githubProxyUrl : ''
|
||||
}https://raw.githubusercontent.com/VaalaCat/frp-panel/main/install.sh | bash -s -- ${
|
||||
github_proxy ? `--github-proxy ${info.githubProxyUrl}` : ''
|
||||
}${ExecCommandStr(type, item, info, ' ')}`
|
||||
}
|
||||
|
||||
export const ClientEnvFile = <T extends Client | Server>(item: T, info: GetPlatformInfoResponse) => {
|
||||
|
@@ -722,6 +722,28 @@ export interface InstallWorkerdResponse {
|
||||
*/
|
||||
status?: Status;
|
||||
}
|
||||
/**
|
||||
* @generated from protobuf message api_client.RedeployWorkerRequest
|
||||
*/
|
||||
export interface RedeployWorkerRequest {
|
||||
/**
|
||||
* @generated from protobuf field: optional string worker_id = 1;
|
||||
*/
|
||||
workerId?: string;
|
||||
/**
|
||||
* @generated from protobuf field: repeated string client_ids = 2;
|
||||
*/
|
||||
clientIds: string[];
|
||||
}
|
||||
/**
|
||||
* @generated from protobuf message api_client.RedeployWorkerResponse
|
||||
*/
|
||||
export interface RedeployWorkerResponse {
|
||||
/**
|
||||
* @generated from protobuf field: optional common.Status status = 1;
|
||||
*/
|
||||
status?: Status;
|
||||
}
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class InitClientRequest$Type extends MessageType<InitClientRequest> {
|
||||
constructor() {
|
||||
@@ -3608,3 +3630,103 @@ class InstallWorkerdResponse$Type extends MessageType<InstallWorkerdResponse> {
|
||||
* @generated MessageType for protobuf message api_client.InstallWorkerdResponse
|
||||
*/
|
||||
export const InstallWorkerdResponse = new InstallWorkerdResponse$Type();
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class RedeployWorkerRequest$Type extends MessageType<RedeployWorkerRequest> {
|
||||
constructor() {
|
||||
super("api_client.RedeployWorkerRequest", [
|
||||
{ no: 1, name: "worker_id", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
|
||||
{ no: 2, name: "client_ids", kind: "scalar", repeat: 2 /*RepeatType.UNPACKED*/, T: 9 /*ScalarType.STRING*/ }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<RedeployWorkerRequest>): RedeployWorkerRequest {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
message.clientIds = [];
|
||||
if (value !== undefined)
|
||||
reflectionMergePartial<RedeployWorkerRequest>(this, message, value);
|
||||
return message;
|
||||
}
|
||||
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: RedeployWorkerRequest): RedeployWorkerRequest {
|
||||
let message = target ?? this.create(), end = reader.pos + length;
|
||||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* optional string worker_id */ 1:
|
||||
message.workerId = reader.string();
|
||||
break;
|
||||
case /* repeated string client_ids */ 2:
|
||||
message.clientIds.push(reader.string());
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
if (u === "throw")
|
||||
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||
let d = reader.skip(wireType);
|
||||
if (u !== false)
|
||||
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: RedeployWorkerRequest, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* optional string worker_id = 1; */
|
||||
if (message.workerId !== undefined)
|
||||
writer.tag(1, WireType.LengthDelimited).string(message.workerId);
|
||||
/* repeated string client_ids = 2; */
|
||||
for (let i = 0; i < message.clientIds.length; i++)
|
||||
writer.tag(2, WireType.LengthDelimited).string(message.clientIds[i]);
|
||||
let u = options.writeUnknownFields;
|
||||
if (u !== false)
|
||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @generated MessageType for protobuf message api_client.RedeployWorkerRequest
|
||||
*/
|
||||
export const RedeployWorkerRequest = new RedeployWorkerRequest$Type();
|
||||
// @generated message type with reflection information, may provide speed optimized methods
|
||||
class RedeployWorkerResponse$Type extends MessageType<RedeployWorkerResponse> {
|
||||
constructor() {
|
||||
super("api_client.RedeployWorkerResponse", [
|
||||
{ no: 1, name: "status", kind: "message", T: () => Status }
|
||||
]);
|
||||
}
|
||||
create(value?: PartialMessage<RedeployWorkerResponse>): RedeployWorkerResponse {
|
||||
const message = globalThis.Object.create((this.messagePrototype!));
|
||||
if (value !== undefined)
|
||||
reflectionMergePartial<RedeployWorkerResponse>(this, message, value);
|
||||
return message;
|
||||
}
|
||||
internalBinaryRead(reader: IBinaryReader, length: number, options: BinaryReadOptions, target?: RedeployWorkerResponse): RedeployWorkerResponse {
|
||||
let message = target ?? this.create(), end = reader.pos + length;
|
||||
while (reader.pos < end) {
|
||||
let [fieldNo, wireType] = reader.tag();
|
||||
switch (fieldNo) {
|
||||
case /* optional common.Status status */ 1:
|
||||
message.status = Status.internalBinaryRead(reader, reader.uint32(), options, message.status);
|
||||
break;
|
||||
default:
|
||||
let u = options.readUnknownField;
|
||||
if (u === "throw")
|
||||
throw new globalThis.Error(`Unknown field ${fieldNo} (wire type ${wireType}) for ${this.typeName}`);
|
||||
let d = reader.skip(wireType);
|
||||
if (u !== false)
|
||||
(u === true ? UnknownFieldHandler.onRead : u)(this.typeName, message, fieldNo, wireType, d);
|
||||
}
|
||||
}
|
||||
return message;
|
||||
}
|
||||
internalBinaryWrite(message: RedeployWorkerResponse, writer: IBinaryWriter, options: BinaryWriteOptions): IBinaryWriter {
|
||||
/* optional common.Status status = 1; */
|
||||
if (message.status)
|
||||
Status.internalBinaryWrite(message.status, writer.tag(1, WireType.LengthDelimited).fork(), options).join();
|
||||
let u = options.writeUnknownFields;
|
||||
if (u !== false)
|
||||
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);
|
||||
return writer;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @generated MessageType for protobuf message api_client.RedeployWorkerResponse
|
||||
*/
|
||||
export const RedeployWorkerResponse = new RedeployWorkerResponse$Type();
|
||||
|
@@ -19,3 +19,12 @@ export const $useServerGithubProxyUrl = persistentAtom<boolean>('use_server_gith
|
||||
encode: JSON.stringify,
|
||||
decode: JSON.parse,
|
||||
})
|
||||
|
||||
export type FrontendPreference = {
|
||||
githubProxyUrl?: string
|
||||
}
|
||||
|
||||
export const $frontendPreference = persistentAtom<FrontendPreference>('frontend_preference', {}, {
|
||||
encode: JSON.stringify,
|
||||
decode: JSON.parse,
|
||||
})
|
||||
|
Reference in New Issue
Block a user