feat: change server ip

This commit is contained in:
VaalaCat
2024-12-05 18:55:00 +00:00
parent 2d7938a6a0
commit 97138d38ae
12 changed files with 119 additions and 37 deletions

View File

@@ -3,7 +3,7 @@
# FRP-Panel
[English Version](README_en.md) | [中文文档](README.md)
[English Version README](README_en.md) | [中文文档](README.md)
我们的目标就是做一个:
@@ -30,6 +30,18 @@ frp-panel 可选 docker 和直接运行模式部署,直接部署请到 release
默认第一个注册的用户是管理员。且默认不开放注册多用户,如果需要,请在 Master 启动命令或配置文件中添加参数:`APP_ENABLE_REGISTER=true`
启动后在服务端列表中会有一个default如果运行信息为“不在线”且为红色则说明您的 `MASTER_RPC_HOST` 启动环境变量没有配置正确或端口外部访问不成功,请仔细检查配置重新部署。
测试端口是否开放的方法,在服务器上运行:
```shell
python3 -m http.server 8080
```
然后在浏览器中访问:`http://IP:8080` (端口可以换成任意你想测试的端口),访问成功则为端口开放
程序的默认存储数据路径和程序文件同目录,如需修改请参考下方的配置表格
### docker
注意 ⚠client 和 server 的启动指令可能会随着项目更新而改变,虽然在项目迭代时会注意前后兼容,但仍难以完全适配,因此 client 和 server 的启动指令以 master 生成为准
@@ -38,14 +50,17 @@ frp-panel 可选 docker 和直接运行模式部署,直接部署请到 release
```bash
# 推荐
# MASTER_RPC_HOST要改成你服务器的外部IP
# APP_GLOBAL_SECRET注意不要泄漏客户端和服务端的是通过Master生成的
docker run -d \
--network=host \
--restart=unless-stopped \
-v /opt/frp-panel:/data \
-e APP_GLOBAL_SECRET=your_secret \ # Master的secret注意不要泄漏客户端和服务端的是通过Master生成的
-e MASTER_RPC_HOST=0.0.0.0 \ # 这里要改成你服务器的外部IP
-e APP_GLOBAL_SECRET=your_secret \
-e MASTER_RPC_HOST=0.0.0.0 \
vaalacat/frp-panel
# 或者
# 运行时记得删除命令中的中文
docker run -d -p 9000:9000 \ # API控制台端口
-p 9001:9001 \ # rpc端口
-p 7000:7000 \ # frps 端口

View File

@@ -26,32 +26,47 @@ After startup, the default access address is `http://IP:9000`.
The first registered user is the administrator by default. User registration is not open by default. If you need it, please add the following parameter to the Master startup command or configuration file: `APP_ENABLE_REGISTER=true`
After starting, there will be a "default" entry in the server list. If the status shows "Offline" in red, it red, it indicates that your `MASTER_RPC_HOST` environment variable is not confi not confi not configured correctly or the port is not accessible externally. Please carefully check the configuration and redeploy.
To test if the port if the port if the port is open, run the following command on the server:
```shell
python3 -m http.server 8080
```
Then access in the browser: `http://IP:8080` (you can replace Port with any Port you want
### Docker
Note⚠: The startup commands for client and server may change as the project is updated. Although backward compatibility will be considered during project iterations, it is still difficult to fully adapt. Therefore, the startup commands for client and server should be generated from the master.
- master
Here's the translated guidance for running the Docker command:
```bash
# recommended
# Recombash
# Recommended
# Change MASTER_RPC_HOST to the external IP of your of your server
# Be careful not careful not to leak APP_GLOBAL_SECRET, it's generated by the Master for both the client and server
docker run -d \
--network=host \
--restart=unless-stopped \
-v /opt/frp-panel:/data \
-e APP_GLOBAL_SECRET=your_secret \ # Master's secret, be careful not to leak it, the client and server secrets are generated by the Master
-e MASTER_RPC_HOST=0.0.0.0 \ # Change this to your server's external IP
-e APP_GLOBAL_SECRET=your_secret \
-e MASTER_RPC_HOST=0.0.0.0 \
vaalacat/frp-panel
# or
docker run -d -p 9000:9000 \ # API control console port
-p 9001:9001 \ # rpc port
-p 7000:7000 \ # frps port
-p 20000-20050:20000-20050 \ # Reserved ports for frps
# Alternatively
# Remember to remove comments when running the command
docker run -d -p 9000:9000 \ # API console port
-p 9001:9001 \ # RPC port
-p 7000:7000 \ # FRPS port
-p 20000-20050:20000-20050 \ # Reserved ports for FRPS
--restart=unless-stopped \
-v /opt/frp-panel:/data \ # Data storage location
-e APP_GLOBAL_SECRET=your_secret \ # Master's secret, be careful not to leak it, the client and server secrets are generated by the Master
-e MASTER_RPC_HOST=0.0.0.0 \ # Change this to your server's external IP
-e APP_GLOBAL_SECRET=your_secret \ # Be careful not to leak the Master's secret, it's generated by the Master for both the client and server
-e MASTER_RPC_HOST=0.0.0.0 \ # Change this to the external IP of your server
vaalacat/frp-panel
```

View File

@@ -46,6 +46,9 @@ func UpdateFrpsHander(c context.Context, req *pb.UpdateFRPSRequest) (*pb.UpdateF
}
srv.Comment = req.GetComment()
if len(req.GetServerIp()) > 0 {
srv.ServerIP = req.GetServerIp()
}
if err := dao.UpdateServer(userInfo, srv); err != nil {
logger.Logger(context.Background()).WithError(err).Errorf("cannot update server, id: [%s]", serverID)

View File

@@ -48,6 +48,7 @@ message UpdateFRPSRequest {
optional string server_id = 1;
optional bytes config = 2;
optional string comment = 3;
optional string server_ip = 4;
}
message UpdateFRPSResponse {

View File

@@ -21,6 +21,7 @@ if (Test-Path "C:\frpp") {
Write-Host "frp panel client already exists, delete and reinstall" -BackgroundColor DarkGreen -ForegroundColor White
C:/frpp/frpp.exe stop
C:/frpp/frpp.exe uninstall
Start-Sleep -Seconds 3
Remove-Item "C:\frpp\frpp.exe" -Recurse
}
@@ -41,6 +42,7 @@ if($region -eq "CN" -or [string]::IsNullOrEmpty($ipapi)){
}
echo $download
Invoke-WebRequest $download -OutFile "C:\frpp.exe"
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

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"time"
"github.com/VaalaCat/frp-panel/utils"
v1 "github.com/fatedier/frp/pkg/config/v1"
"gorm.io/gorm"
)
@@ -48,3 +49,12 @@ func (s *ServerEntity) SetConfigContent(cfg *v1.ServerConfig) error {
s.ConfigContent = raw
return nil
}
func (s *ServerEntity) ConfigEqual(cfg *v1.ServerConfig) bool {
raw, err := json.Marshal(cfg)
if err != nil {
return false
}
return utils.MD5(s.ConfigContent) == utils.MD5(raw)
}

View File

@@ -452,6 +452,7 @@ type UpdateFRPSRequest struct {
ServerId *string `protobuf:"bytes,1,opt,name=server_id,json=serverId,proto3,oneof" json:"server_id,omitempty"`
Config []byte `protobuf:"bytes,2,opt,name=config,proto3,oneof" json:"config,omitempty"`
Comment *string `protobuf:"bytes,3,opt,name=comment,proto3,oneof" json:"comment,omitempty"`
ServerIp *string `protobuf:"bytes,4,opt,name=server_ip,json=serverIp,proto3,oneof" json:"server_ip,omitempty"`
}
func (x *UpdateFRPSRequest) Reset() {
@@ -505,6 +506,13 @@ func (x *UpdateFRPSRequest) GetComment() string {
return ""
}
func (x *UpdateFRPSRequest) GetServerIp() string {
if x != nil && x.ServerIp != nil {
return *x.ServerIp
}
return ""
}
type UpdateFRPSResponse struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
@@ -983,16 +991,19 @@ var file_api_server_proto_rawDesc = []byte{
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, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x22,
0x96, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x52, 0x50, 0x53, 0x52, 0x65,
0xc6, 0x01, 0x0a, 0x11, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x46, 0x52, 0x50, 0x53, 0x52, 0x65,
0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x20, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f,
0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76,
0x65, 0x72, 0x49, 0x64, 0x88, 0x01, 0x01, 0x12, 0x1b, 0x0a, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x48, 0x01, 0x52, 0x06, 0x63, 0x6f, 0x6e, 0x66, 0x69,
0x67, 0x88, 0x01, 0x01, 0x12, 0x1d, 0x0a, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x18,
0x03, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x07, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74,
0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69,
0x64, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x0a, 0x0a, 0x08,
0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x22, 0x4c, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61,
0x88, 0x01, 0x01, 0x12, 0x20, 0x0a, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x70,
0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x03, 0x52, 0x08, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x49, 0x70, 0x88, 0x01, 0x01, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72,
0x5f, 0x69, 0x64, 0x42, 0x09, 0x0a, 0x07, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x0a,
0x0a, 0x08, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x42, 0x0c, 0x0a, 0x0a, 0x5f, 0x73,
0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x69, 0x70, 0x22, 0x4c, 0x0a, 0x12, 0x55, 0x70, 0x64, 0x61,
0x74, 0x65, 0x46, 0x52, 0x50, 0x53, 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,

View File

@@ -9,7 +9,7 @@ import (
"golang.org/x/crypto/bcrypt"
)
func MD5(input string) string {
func MD5[T []byte | string](input T) string {
data := []byte(input)
hash := md5.Sum(data)
hashString := hex.EncodeToString(hash[:])

View File

@@ -66,7 +66,7 @@ export const FRPSFormCard: React.FC<FRPSFormCardProps> = ({ serverID: defaultSer
<Label className="text-sm font-medium"></Label>
<ServerSelector serverID={serverID} setServerID={handleServerChange} onOpenChange={refetchServer} />
</div>
{serverID && server && server.server && !advanceMode && <FRPSForm serverID={serverID} server={server.server} />}
{serverID && server && server.server && !advanceMode && <FRPSForm key={serverID} serverID={serverID} server={server.server} />}
{serverID && server && server.server && advanceMode && (
<FRPSEditor serverID={serverID} server={server.server} />
)}

View File

@@ -19,6 +19,7 @@ const ServerConfigSchema = z.object({
proxyBindAddr: ZodIPSchema.optional(),
vhostHTTPPort: ZodPortSchema.optional(),
subDomainHost: ZodStringSchema.optional(),
publicHost: ZodStringSchema.optional(),
})
export const ServerConfigZodSchema = ServerConfigSchema
@@ -29,7 +30,6 @@ export interface FRPSFormProps {
}
const FRPSForm: React.FC<FRPSFormProps> = ({ serverID, server }) => {
const [_, setFrpsConfig] = useState<ServerConfig | undefined>()
const form = useForm<z.infer<typeof ServerConfigZodSchema>>({
resolver: zodResolver(ServerConfigZodSchema),
})
@@ -38,7 +38,6 @@ const FRPSForm: React.FC<FRPSFormProps> = ({ serverID, server }) => {
const updateFrps = useMutation({ mutationFn: updateFRPS })
useEffect(() => {
setFrpsConfig(undefined)
form.reset({})
}, [])
@@ -47,26 +46,28 @@ const FRPSForm: React.FC<FRPSFormProps> = ({ serverID, server }) => {
}, [server])
const onSubmit = async (values: z.infer<typeof ServerConfigZodSchema>) => {
setFrpsConfig({ ...values })
try {
const {publicHost, ...rest} = values
let resp = await updateFrps.mutateAsync({
serverIp: publicHost,
serverId: serverID,
// @ts-ignore
config: Buffer.from(
JSON.stringify({
...values,
...rest,
} as ServerConfig),
),
})
toast({
title: resp.status?.code === RespCode.SUCCESS ? '创建成功' : '创建失败',
title: resp.status?.code === RespCode.SUCCESS ? '修改成功' : '修改失败',
description: resp.status?.message,
})
} catch (error) {
console.error(error)
toast({ title: '创建服务端状态', description: '创建失败' })
toast({ title: '修改服务端状态', description: '创建失败' })
}
}
return (
<div className="flex flex-col w-full pt-2">
<Label className="text-sm font-medium"> {serverID} </Label>
@@ -77,6 +78,20 @@ const FRPSForm: React.FC<FRPSFormProps> = ({ serverID, server }) => {
{serverID && (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)} className="space-y-4">
<FormField
control={form.control}
name="publicHost"
render={({ field }) => (
<FormItem>
<FormLabel></FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
defaultValue={server?.ip}
/>
<FormField
control={form.control}
name="bindPort"

View File

@@ -24,9 +24,8 @@ import { useToast } from '@/components/ui/use-toast'
import React, { useState } from 'react'
import { ClientEnvFile, ExecCommandStr, LinuxInstallCommand, WindowsInstallCommand } from '@/lib/consts'
import { useMutation, useQuery } from '@tanstack/react-query'
import { deleteServer, listServer } from '@/api/server'
import { deleteServer } from '@/api/server'
import { useRouter } from 'next/router'
import { getUserInfo } from '@/api/user'
import { useStore } from '@nanostores/react'
import { $platformInfo } from '@/store/user'
import { Popover, PopoverContent, PopoverTrigger } from '@/components/ui/popover'
@@ -264,7 +263,7 @@ export const ServerActions: React.FC<ServerItemProps> = ({ server, table }) => {
})
}}
>
</DropdownMenuItem>
<DropdownMenuItem
onClick={() => {

View File

@@ -133,6 +133,10 @@ export interface UpdateFRPSRequest {
* @generated from protobuf field: optional string comment = 3;
*/
comment?: string;
/**
* @generated from protobuf field: optional string server_ip = 4;
*/
serverIp?: string;
}
/**
* @generated from protobuf message api_server.UpdateFRPSResponse
@@ -650,7 +654,8 @@ class UpdateFRPSRequest$Type extends MessageType<UpdateFRPSRequest> {
super("api_server.UpdateFRPSRequest", [
{ no: 1, name: "server_id", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
{ no: 2, name: "config", kind: "scalar", opt: true, T: 12 /*ScalarType.BYTES*/ },
{ no: 3, name: "comment", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
{ no: 3, name: "comment", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ },
{ no: 4, name: "server_ip", kind: "scalar", opt: true, T: 9 /*ScalarType.STRING*/ }
]);
}
create(value?: PartialMessage<UpdateFRPSRequest>): UpdateFRPSRequest {
@@ -673,6 +678,9 @@ class UpdateFRPSRequest$Type extends MessageType<UpdateFRPSRequest> {
case /* optional string comment */ 3:
message.comment = reader.string();
break;
case /* optional string server_ip */ 4:
message.serverIp = reader.string();
break;
default:
let u = options.readUnknownField;
if (u === "throw")
@@ -694,6 +702,9 @@ class UpdateFRPSRequest$Type extends MessageType<UpdateFRPSRequest> {
/* optional string comment = 3; */
if (message.comment !== undefined)
writer.tag(3, WireType.LengthDelimited).string(message.comment);
/* optional string server_ip = 4; */
if (message.serverIp !== undefined)
writer.tag(4, WireType.LengthDelimited).string(message.serverIp);
let u = options.writeUnknownFields;
if (u !== false)
(u == true ? UnknownFieldHandler.onWrite : u)(this.typeName, message, writer);