浏览代码

public key bug and multiuser enhancement and server ip support and config file of client optimization

刘河 6 年之前
父节点
当前提交
42a73fa392

+ 13 - 12
bridge/bridge.go

@@ -200,20 +200,28 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
 
 
 func (s *Bridge) DelClient(id int) {
 func (s *Bridge) DelClient(id int) {
 	if v, ok := s.Client.Load(id); ok {
 	if v, ok := s.Client.Load(id); ok {
-		if c, err := file.GetCsvDb().GetClient(id); err == nil && c.NoStore {
-			s.CloseClient <- c.Id
-		}
 		if v.(*Client).signal != nil {
 		if v.(*Client).signal != nil {
 			v.(*Client).signal.Close()
 			v.(*Client).signal.Close()
 		}
 		}
 		s.Client.Delete(id)
 		s.Client.Delete(id)
+		if file.GetCsvDb().IsPubClient(id) {
+			return
+		}
+		if c, err := file.GetCsvDb().GetClient(id); err == nil && c.NoStore {
+			s.CloseClient <- c.Id
+		}
 	}
 	}
 }
 }
 
 
 //use different
 //use different
 func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
+	isPub := file.GetCsvDb().IsPubClient(id)
 	switch typeVal {
 	switch typeVal {
 	case common.WORK_MAIN:
 	case common.WORK_MAIN:
+		if isPub {
+			c.Close()
+			return
+		}
 		//the vKey connect by another ,close the client of before
 		//the vKey connect by another ,close the client of before
 		if v, ok := s.Client.LoadOrStore(id, NewClient(nil, nil, c)); ok {
 		if v, ok := s.Client.LoadOrStore(id, NewClient(nil, nil, c)); ok {
 			if v.(*Client).signal != nil {
 			if v.(*Client).signal != nil {
@@ -229,16 +237,8 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 			v.(*Client).tunnel = muxConn
 			v.(*Client).tunnel = muxConn
 		}
 		}
 	case common.WORK_CONFIG:
 	case common.WORK_CONFIG:
-		var isPub bool
 		client, err := file.GetCsvDb().GetClient(id)
 		client, err := file.GetCsvDb().GetClient(id)
-		if err == nil {
-			if client.VerifyKey == beego.AppConfig.String("public_vkey") {
-				isPub = true
-			} else {
-				isPub = false
-			}
-		}
-		if !isPub && !client.ConfigConnAllow {
+		if err != nil || (!isPub && !client.ConfigConnAllow) {
 			c.Close()
 			c.Close()
 			return
 			return
 		}
 		}
@@ -458,6 +458,7 @@ loop:
 					tl := new(file.Tunnel)
 					tl := new(file.Tunnel)
 					tl.Mode = t.Mode
 					tl.Mode = t.Mode
 					tl.Port = ports[i]
 					tl.Port = ports[i]
+					tl.ServerIp = t.ServerIp
 					if len(ports) == 1 {
 					if len(ports) == 1 {
 						tl.Target = t.Target
 						tl.Target = t.Target
 						tl.Remark = t.Remark
 						tl.Remark = t.Remark

+ 9 - 3
client/client.go

@@ -228,7 +228,13 @@ loop:
 }
 }
 
 
 func (s *TRPClient) Close() {
 func (s *TRPClient) Close() {
-	s.tunnel.Close()
-	s.signal.Close()
-	s.ticker.Stop()
+	if s.tunnel != nil {
+		s.tunnel.Close()
+	}
+	if s.signal != nil {
+		s.signal.Close()
+	}
+	if s.ticker != nil {
+		s.ticker.Stop()
+	}
 }
 }

+ 15 - 16
conf/npc.conf

@@ -1,7 +1,7 @@
 [common]
 [common]
-server=127.0.0.1:8024
-tp=tcp
-vkey=123
+server_addr=127.0.0.1:8024
+conn_type=tcp
+vkey=nps
 auto_reconnection=true
 auto_reconnection=true
 
 
 [health_check_test1]
 [health_check_test1]
@@ -20,39 +20,38 @@ health_check_type=tcp
 health_check_target=127.0.0.1:8083,127.0.0.1:8082
 health_check_target=127.0.0.1:8083,127.0.0.1:8082
 [web]
 [web]
 host=b.o.com
 host=b.o.com
-target=127.0.0.1:8080
+target_addr=127.0.0.1:8080
 
 
 [tcp]
 [tcp]
 mode=tcp
 mode=tcp
-target=127.0.0.1:8083,127.0.0.1:8082
-port=9006
-targetAddr=123.206.77.88
+target=127.0.0.1:8080
+server_port=10000
 
 
 [socks5]
 [socks5]
 mode=socks5
 mode=socks5
-port=9005
+server_port=9005
 
 
 [http]
 [http]
 mode=httpProxy
 mode=httpProxy
-port=9004
+server_port=9004
 
 
 
 
 [file]
 [file]
 mode=file
 mode=file
-port=9009
+server_port=9009
 local_path=./
 local_path=./
 strip_pre=/web/
 strip_pre=/web/
 
 
 [udp]
 [udp]
 mode=udp
 mode=udp
-port=53
-target=114.114.114.114:53
+server_port=53
+target_addr=114.114.114.114:53
 
 
 [secret_ssh]
 [secret_ssh]
-port=2001
+local_port=2001
 password=sec
 password=sec
 
 
 [p2p_ssh]
 [p2p_ssh]
-port=2002
-password=c
-target=123.206.77.88:22
+local_port=2002
+password=ppp
+target_addr=192.168.74.199:22

+ 1 - 0
conf/nps.conf

@@ -6,6 +6,7 @@ runmode = pro
 http_proxy_port=80
 http_proxy_port=80
 https_proxy_port=443
 https_proxy_port=443
 https_just_proxy=true
 https_just_proxy=true
+http_proxy_ip=0.0.0.0
 #certFile absolute path
 #certFile absolute path
 #pem_path=conf/server.pem
 #pem_path=conf/server.pem
 #KeyFile absolute path
 #KeyFile absolute path

+ 1 - 1
conf/tasks.csv

@@ -1 +1 @@
-9988,tcp,127.0.0.1:8080,1,3,11,,0,504,
+9999,tcp,,1,3,11,,0,0,,0.0.0.0

+ 13 - 8
lib/config/config.go

@@ -20,6 +20,7 @@ type CommonConfig struct {
 type LocalServer struct {
 type LocalServer struct {
 	Type     string
 	Type     string
 	Port     int
 	Port     int
+	Ip       string
 	Password string
 	Password string
 	Target   string
 	Target   string
 }
 }
@@ -112,11 +113,11 @@ func dealCommon(s string) *CommonConfig {
 			item = append(item, "")
 			item = append(item, "")
 		}
 		}
 		switch item[0] {
 		switch item[0] {
-		case "server":
+		case "server_addr":
 			c.Server = item[1]
 			c.Server = item[1]
 		case "vkey":
 		case "vkey":
 			c.VKey = item[1]
 			c.VKey = item[1]
-		case "tp":
+		case "conn_type":
 			c.Tp = item[1]
 			c.Tp = item[1]
 		case "auto_reconnection":
 		case "auto_reconnection":
 			c.AutoReconnection = common.GetBoolByStr(item[1])
 			c.AutoReconnection = common.GetBoolByStr(item[1])
@@ -156,7 +157,7 @@ func dealHost(s string) *file.Host {
 		switch strings.TrimSpace(item[0]) {
 		switch strings.TrimSpace(item[0]) {
 		case "host":
 		case "host":
 			h.Host = item[1]
 			h.Host = item[1]
-		case "target":
+		case "target_addr":
 			h.Target = strings.Replace(item[1], ",", "\n", -1)
 			h.Target = strings.Replace(item[1], ",", "\n", -1)
 		case "host_change":
 		case "host_change":
 			h.HostChange = item[1]
 			h.HostChange = item[1]
@@ -211,13 +212,15 @@ func dealTunnel(s string) *file.Tunnel {
 			item = append(item, "")
 			item = append(item, "")
 		}
 		}
 		switch strings.TrimSpace(item[0]) {
 		switch strings.TrimSpace(item[0]) {
-		case "port":
+		case "server_port":
 			t.Ports = item[1]
 			t.Ports = item[1]
+		case "server_ip":
+			t.ServerIp = item[1]
 		case "mode":
 		case "mode":
 			t.Mode = item[1]
 			t.Mode = item[1]
-		case "target":
+		case "target_port", "target_addr":
 			t.Target = strings.Replace(item[1], ",", "\n", -1)
 			t.Target = strings.Replace(item[1], ",", "\n", -1)
-		case "targetAddr":
+		case "target_ip":
 			t.TargetAddr = item[1]
 			t.TargetAddr = item[1]
 		case "password":
 		case "password":
 			t.Password = item[1]
 			t.Password = item[1]
@@ -241,11 +244,13 @@ func delLocalService(s string) *LocalServer {
 			item = append(item, "")
 			item = append(item, "")
 		}
 		}
 		switch item[0] {
 		switch item[0] {
-		case "port":
+		case "local_port":
 			l.Port = common.GetIntNoErrByStr(item[1])
 			l.Port = common.GetIntNoErrByStr(item[1])
+		case "local_ip":
+			l.Ip = item[1]
 		case "password":
 		case "password":
 			l.Password = item[1]
 			l.Password = item[1]
-		case "target":
+		case "target_addr":
 			l.Target = item[1]
 			l.Target = item[1]
 		}
 		}
 	}
 	}

+ 4 - 1
lib/conn/conn.go

@@ -316,7 +316,7 @@ func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) {
 	*/
 	*/
 	raw := bytes.NewBuffer([]byte{})
 	raw := bytes.NewBuffer([]byte{})
 	binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK))
 	binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK))
-	common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark, t.TargetAddr, t.Password, t.LocalPath, t.StripPre)
+	common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark, t.TargetAddr, t.Password, t.LocalPath, t.StripPre, t.ServerIp)
 	s.Lock()
 	s.Lock()
 	defer s.Unlock()
 	defer s.Unlock()
 	return s.Write(raw.Bytes())
 	return s.Write(raw.Bytes())
@@ -345,6 +345,9 @@ func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
 		t.Password = arr[5]
 		t.Password = arr[5]
 		t.LocalPath = arr[6]
 		t.LocalPath = arr[6]
 		t.StripPre = arr[7]
 		t.StripPre = arr[7]
+		if len(arr) > 8 {
+			t.ServerIp = arr[8]
+		}
 		t.NoStore = true
 		t.NoStore = true
 	}
 	}
 	return
 	return

+ 23 - 1
lib/file/file.go

@@ -7,6 +7,7 @@ import (
 	"github.com/cnlh/nps/lib/common"
 	"github.com/cnlh/nps/lib/common"
 	"github.com/cnlh/nps/lib/crypt"
 	"github.com/cnlh/nps/lib/crypt"
 	"github.com/cnlh/nps/lib/rate"
 	"github.com/cnlh/nps/lib/rate"
+	"github.com/cnlh/nps/vender/github.com/astaxie/beego"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
 	"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
 	"net/http"
 	"net/http"
 	"os"
 	"os"
@@ -59,6 +60,7 @@ func (s *Csv) StoreTasksToCsv() {
 			strconv.Itoa(int(task.Flow.ExportFlow)),
 			strconv.Itoa(int(task.Flow.ExportFlow)),
 			strconv.Itoa(int(task.Flow.InletFlow)),
 			strconv.Itoa(int(task.Flow.InletFlow)),
 			task.Password,
 			task.Password,
+			task.ServerIp,
 		}
 		}
 		err := writer.Write(record)
 		err := writer.Write(record)
 		if err != nil {
 		if err != nil {
@@ -111,6 +113,11 @@ func (s *Csv) LoadTaskFromCsv() {
 		if post.Client, err = s.GetClient(common.GetIntNoErrByStr(item[5])); err != nil {
 		if post.Client, err = s.GetClient(common.GetIntNoErrByStr(item[5])); err != nil {
 			continue
 			continue
 		}
 		}
+		if len(item) > 10 {
+			post.ServerIp = item[10]
+		} else {
+			post.ServerIp = "0.0.0.0"
+		}
 		s.Tasks.Store(post.Id, post)
 		s.Tasks.Store(post.Id, post)
 		if post.Id > int(s.TaskIncreaseId) {
 		if post.Id > int(s.TaskIncreaseId) {
 			s.TaskIncreaseId = int32(s.TaskIncreaseId)
 			s.TaskIncreaseId = int32(s.TaskIncreaseId)
@@ -440,7 +447,7 @@ func (s *Csv) UpdateClient(t *Client) error {
 	return nil
 	return nil
 }
 }
 
 
-func (s *Csv) GetClientList(start, length int, search string) ([]*Client, int) {
+func (s *Csv) GetClientList(start, length int, search string, clientId int) ([]*Client, int) {
 	list := make([]*Client, 0)
 	list := make([]*Client, 0)
 	var cnt int
 	var cnt int
 	keys := common.GetMapKeys(s.Clients)
 	keys := common.GetMapKeys(s.Clients)
@@ -450,6 +457,9 @@ func (s *Csv) GetClientList(start, length int, search string) ([]*Client, int) {
 			if v.NoDisplay {
 			if v.NoDisplay {
 				continue
 				continue
 			}
 			}
+			if clientId != 0 && clientId != v.Id {
+				continue
+			}
 			if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.VerifyKey, search) || strings.Contains(v.Remark, search)) {
 			if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.VerifyKey, search) || strings.Contains(v.Remark, search)) {
 				continue
 				continue
 			}
 			}
@@ -464,6 +474,18 @@ func (s *Csv) GetClientList(start, length int, search string) ([]*Client, int) {
 	return list, cnt
 	return list, cnt
 }
 }
 
 
+func (s *Csv) IsPubClient(id int) bool {
+	client, err := s.GetClient(id)
+	if err == nil {
+		if client.VerifyKey == beego.AppConfig.String("public_vkey") {
+			return true
+		} else {
+			return false
+		}
+	}
+	return false
+}
+
 func (s *Csv) GetClient(id int) (c *Client, err error) {
 func (s *Csv) GetClient(id int) (c *Client, err error) {
 	if v, ok := s.Clients.Load(id); ok {
 	if v, ok := s.Clients.Load(id); ok {
 		c = v.(*Client)
 		c = v.(*Client)

+ 3 - 2
lib/file/obj.go

@@ -111,8 +111,9 @@ func (s *Client) HasHost(h *Host) bool {
 }
 }
 
 
 type Tunnel struct {
 type Tunnel struct {
-	Id         int      //Id
-	Port       int      //服务端监听端口
+	Id         int //Id
+	Port       int //服务端监听端口
+	ServerIp   string
 	Mode       string   //启动方式
 	Mode       string   //启动方式
 	Target     string   //目标
 	Target     string   //目标
 	TargetArr  []string //目标
 	TargetArr  []string //目标

+ 1 - 1
lib/version/version.go

@@ -1,6 +1,6 @@
 package version
 package version
 
 
-const VERSION = "0.20.0"
+const VERSION = "0.20.1"
 
 
 // Compulsory minimum version, Minimum downward compatibility to this version
 // Compulsory minimum version, Minimum downward compatibility to this version
 func GetVersion() string {
 func GetVersion() string {

+ 2 - 2
server/connection/connection.go

@@ -50,7 +50,7 @@ func GetHttpListener() (net.Listener, error) {
 		return pMux.GetHttpListener(), nil
 		return pMux.GetHttpListener(), nil
 	}
 	}
 	logs.Info("start http listener, port is", httpPort)
 	logs.Info("start http listener, port is", httpPort)
-	return getTcpListener("", httpPort)
+	return getTcpListener(beego.AppConfig.String("http_proxy_ip"), httpPort)
 }
 }
 
 
 func GetHttpsListener() (net.Listener, error) {
 func GetHttpsListener() (net.Listener, error) {
@@ -59,7 +59,7 @@ func GetHttpsListener() (net.Listener, error) {
 		return pMux.GetHttpsListener(), nil
 		return pMux.GetHttpsListener(), nil
 	}
 	}
 	logs.Info("start https listener, port is", httpsPort)
 	logs.Info("start https listener, port is", httpsPort)
-	return getTcpListener("", httpsPort)
+	return getTcpListener(beego.AppConfig.String("http_proxy_ip"), httpsPort)
 }
 }
 
 
 func GetWebManagerListener() (net.Listener, error) {
 func GetWebManagerListener() (net.Listener, error) {

+ 1 - 1
server/proxy/socks5.go

@@ -251,7 +251,7 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error {
 
 
 //start
 //start
 func (s *Sock5ModeServer) Start() error {
 func (s *Sock5ModeServer) Start() error {
-	return conn.NewTcpListenerAndProcess(":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
+	return conn.NewTcpListenerAndProcess(s.task.ServerIp+":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
 		if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
 		if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
 			logs.Warn("client id %d, task id %d, error %s, when socks5 connection", s.task.Client.Id, s.task.Id, err.Error())
 			logs.Warn("client id %d, task id %d, error %s, when socks5 connection", s.task.Client.Id, s.task.Id, err.Error())
 			c.Close()
 			c.Close()

+ 1 - 1
server/proxy/tcp.go

@@ -32,7 +32,7 @@ func NewTunnelModeServer(process process, bridge *bridge.Bridge, task *file.Tunn
 
 
 //开始
 //开始
 func (s *TunnelModeServer) Start() error {
 func (s *TunnelModeServer) Start() error {
-	return conn.NewTcpListenerAndProcess(":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
+	return conn.NewTcpListenerAndProcess(s.task.ServerIp+":"+strconv.Itoa(s.task.Port), func(c net.Conn) {
 		if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
 		if err := s.CheckFlowAndConnNum(s.task.Client); err != nil {
 			logs.Warn("client id %d, task id %d,error %s, when tcp connection", s.task.Client.Id, s.task.Id, err.Error())
 			logs.Warn("client id %d, task id %d,error %s, when tcp connection", s.task.Client.Id, s.task.Id, err.Error())
 			c.Close()
 			c.Close()

+ 4 - 1
server/proxy/udp.go

@@ -26,7 +26,10 @@ func NewUdpModeServer(bridge *bridge.Bridge, task *file.Tunnel) *UdpModeServer {
 //开始
 //开始
 func (s *UdpModeServer) Start() error {
 func (s *UdpModeServer) Start() error {
 	var err error
 	var err error
-	s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.task.Port, ""})
+	if s.task.ServerIp == "" {
+		s.task.ServerIp = "0.0.0.0"
+	}
+	s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP(s.task.ServerIp), s.task.Port, ""})
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}

+ 2 - 2
server/server.go

@@ -255,8 +255,8 @@ func GetTunnel(start, length int, typeVal string, clientId int, search string) (
 }
 }
 
 
 //获取客户端列表
 //获取客户端列表
-func GetClientList(start, length int, search string) (list []*file.Client, cnt int) {
-	list, cnt = file.GetCsvDb().GetClientList(start, length, search)
+func GetClientList(start, length int, search string, clientId int) (list []*file.Client, cnt int) {
+	list, cnt = file.GetCsvDb().GetClientList(start, length, search, clientId)
 	dealClientData()
 	dealClientData()
 	return
 	return
 }
 }

+ 10 - 1
web/controllers/base.go

@@ -140,7 +140,16 @@ func (s *BaseController) SetType(name string) {
 
 
 func (s *BaseController) CheckUserAuth() {
 func (s *BaseController) CheckUserAuth() {
 	if s.controllerName == "client" {
 	if s.controllerName == "client" {
-		s.StopRun()
+		if s.actionName == "add" {
+			s.StopRun()
+			return
+		}
+		if id := s.GetIntNoErr("id"); id != 0 {
+			if id != s.GetSession("clientId").(int) {
+				s.StopRun()
+				return
+			}
+		}
 	}
 	}
 	if s.controllerName == "index" {
 	if s.controllerName == "index" {
 		if id := s.GetIntNoErr("id"); id != 0 {
 		if id := s.GetIntNoErr("id"); id != 0 {

+ 8 - 1
web/controllers/client.go

@@ -19,7 +19,14 @@ func (s *ClientController) List() {
 		return
 		return
 	}
 	}
 	start, length := s.GetAjaxParams()
 	start, length := s.GetAjaxParams()
-	list, cnt := server.GetClientList(start, length, s.GetString("search"))
+	clientIdSession := s.GetSession("clientId")
+	var clientId int
+	if clientIdSession == nil {
+		clientId = 0
+	} else {
+		clientId = clientIdSession.(int)
+	}
+	list, cnt := server.GetClientList(start, length, s.GetString("search"), clientId)
 	s.AjaxTable(list, cnt, cnt)
 	s.AjaxTable(list, cnt, cnt)
 }
 }
 
 

+ 8 - 2
web/controllers/index.go

@@ -91,6 +91,7 @@ func (s *IndexController) Add() {
 	} else {
 	} else {
 		t := &file.Tunnel{
 		t := &file.Tunnel{
 			Port:      s.GetIntNoErr("port"),
 			Port:      s.GetIntNoErr("port"),
+			ServerIp:  s.GetString("server_ip"),
 			Mode:      s.GetString("type"),
 			Mode:      s.GetString("type"),
 			Target:    s.GetString("target"),
 			Target:    s.GetString("target"),
 			Id:        int(file.GetCsvDb().GetTaskId()),
 			Id:        int(file.GetCsvDb().GetTaskId()),
@@ -144,7 +145,12 @@ func (s *IndexController) Edit() {
 		if t, err := file.GetCsvDb().GetTask(id); err != nil {
 		if t, err := file.GetCsvDb().GetTask(id); err != nil {
 			s.error()
 			s.error()
 		} else {
 		} else {
-			t.Port = s.GetIntNoErr("port")
+			var portChange bool
+			if s.GetIntNoErr("port") != t.Port {
+				portChange = true
+				t.Port = s.GetIntNoErr("port")
+			}
+			t.ServerIp = s.GetString("server_ip")
 			t.Mode = s.GetString("type")
 			t.Mode = s.GetString("type")
 			t.Target = s.GetString("target")
 			t.Target = s.GetString("target")
 			t.Password = s.GetString("password")
 			t.Password = s.GetString("password")
@@ -152,7 +158,7 @@ func (s *IndexController) Edit() {
 			t.LocalPath = s.GetString("local_path")
 			t.LocalPath = s.GetString("local_path")
 			t.StripPre = s.GetString("strip_pre")
 			t.StripPre = s.GetString("strip_pre")
 			t.Remark = s.GetString("remark")
 			t.Remark = s.GetString("remark")
-			if !tool.TestServerPort(t.Port, t.Mode) {
+			if portChange && !tool.TestServerPort(t.Port, t.Mode) {
 				s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.")
 				s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.")
 			}
 			}
 			if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {
 			if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {

+ 14 - 10
web/views/client/list.html

@@ -15,15 +15,18 @@
                         </a>
                         </a>
                     </div>
                     </div>
                 </div>
                 </div>
-                <div class="content">
-                    <div class="table-responsive">
-                        <div id="toolbar">
-                            <a href="/client/add" class="btn btn-primary dim" type="button" langtag="info-new">新增</a>
-                        </div>
-                        <table id="taskList_table" class="table-striped table-hover"
-                               data-mobile-responsive="true"></table>
+            <div class="content">
+            {{if eq true .isAdmin}}
+
+                <div class="table-responsive">
+                    <div id="toolbar">
+                        <a href="/client/add" class="btn btn-primary dim" type="button" langtag="info-new">新增</a>
                     </div>
                     </div>
+                    <table id="taskList_table" class="table-striped table-hover"
+                           data-mobile-responsive="true"></table>
                 </div>
                 </div>
+            </div>
+            {{end}}
                 <div class="ibox-content">
                 <div class="ibox-content">
 
 
                     <table id="table"></table>
                     <table id="table"></table>
@@ -225,11 +228,12 @@
                 sortable: true,//启用排序
                 sortable: true,//启用排序
                 formatter: function (value, row, index) {
                 formatter: function (value, row, index) {
                     btn_group = '<div class="btn-group">'
                     btn_group = '<div class="btn-group">'
-                    btn = `<button onclick="del(` + row.Id + `)" class="btn-danger"><i class="fa fa-trash"></i></button><button onclick="edit(` + row.Id + `)" class="btn-primary"><i class="fa fa-edit"></i></button></div>`
+                    btn = ` {{if eq true .isAdmin}}<button onclick="del(` + row.Id + `)" class="btn-danger"><i class="fa fa-trash"></i></button>{{end}}<button onclick="edit(` + row.Id + `)" class="btn-primary"><i class="fa fa-edit"></i></button></div>`
+
                     if (row.Status) {
                     if (row.Status) {
-                        return btn_group + `<button onclick="stop(` + row.Id + `)" class="btn-warning"><i class="fa fa-close"></i></button>` + btn
+                        return btn_group  {{if eq true .isAdmin}}+ `<button onclick="stop(` + row.Id + `)" class="btn-warning"><i class="fa fa-close"></i></button>` {{end}}+ btn
                     } else {
                     } else {
-                        return btn_group + `<button onclick="start(` + row.Id + `)" class="btn-warning"><i class="fa fa-check"></i></button>` + btn
+                        return btn_group  {{if eq true .isAdmin}}+ `<button onclick="start(` + row.Id + `)" class="btn-warning"><i class="fa fa-check"></i></button>` {{end}}+ btn
                     }
                     }
                 }
                 }
             },
             },

+ 8 - 0
web/views/index/add.html

@@ -28,6 +28,14 @@
                         </div>
                         </div>
                     </div>
                     </div>
 
 
+                    <div class="form-group" id="server_ip">
+                        <label class="col-sm-2 control-label" langtag="info-server-ip">服务端ip</label>
+                        <div class="col-sm-10">
+                            <input class="form-control" type="text" value="0.0.0.0" name="server_ip"
+                                   placeholder="such as 0.0.0.0">
+                        </div>
+                    </div>
+
                     <div class="form-group" id="port">
                     <div class="form-group" id="port">
                         <label class="col-sm-2 control-label" langtag="info-server-port">服务端端口</label>
                         <label class="col-sm-2 control-label" langtag="info-server-port">服务端端口</label>
                         <div class="col-sm-10">
                         <div class="col-sm-10">

+ 7 - 1
web/views/index/edit.html

@@ -28,7 +28,13 @@
                                    placeholder="empty means to be unrestricted">
                                    placeholder="empty means to be unrestricted">
                         </div>
                         </div>
                     </div>
                     </div>
-
+                    <div class="form-group" id="server_ip">
+                        <label class="col-sm-2 control-label" langtag="info-server-ip">服务端ip</label>
+                        <div class="col-sm-10">
+                            <input class="form-control" type="text" value="{{.t.ServerIp}}" name="server_ip"
+                                   placeholder="such as 0.0.0.0">
+                        </div>
+                    </div>
                     <div class="form-group" id="port">
                     <div class="form-group" id="port">
                         <label class="col-sm-2 control-label" langtag="info-server-port">服务端端口</label>
                         <label class="col-sm-2 control-label" langtag="info-server-port">服务端端口</label>
                         <div class="col-sm-10">
                         <div class="col-sm-10">

+ 7 - 3
web/views/public/layout.html

@@ -38,7 +38,13 @@
                     {{/*<img alt="image" class="img-circle" src="/static/img/profile_small.jpg"/>*/}}
                     {{/*<img alt="image" class="img-circle" src="/static/img/profile_small.jpg"/>*/}}
                     </span>
                     </span>
                         <a href="#">
                         <a href="#">
-                            <span class="clear"> <span class="block m-t-xs"> <strong class="font-bold">admin</strong>
+                            <span class="clear"> <span class="block m-t-xs"> <strong class="font-bold">
+                            {{if eq true .isAdmin}}
+                                admin
+                            {{else}}
+                                user
+                            {{end}}
+                            </strong>
                              </span> <span class="text-muted text-xs block">system </span> </span>
                              </span> <span class="text-muted text-xs block">system </span> </span>
                         </a>
                         </a>
                     </div>
                     </div>
@@ -46,7 +52,6 @@
                         NPS
                         NPS
                     </div>
                     </div>
                 </li>
                 </li>
-            {{if eq true .isAdmin}}
                 <li class="{{if eq "index" .menu}}active{{end}}">
                 <li class="{{if eq "index" .menu}}active{{end}}">
                     <a href="/"><i class="fa fa-dashboard"></i> <span langtag="menu-dashboard"
                     <a href="/"><i class="fa fa-dashboard"></i> <span langtag="menu-dashboard"
                                                                       class="nav-label">仪表盘</span></a>
                                                                       class="nav-label">仪表盘</span></a>
@@ -55,7 +60,6 @@
                     <a href="/client/list"><i class="fa fa-clipboard"></i> <span langtag="menu-client"
                     <a href="/client/list"><i class="fa fa-clipboard"></i> <span langtag="menu-client"
                                                                                  class="nav-label">客户端</span></a>
                                                                                  class="nav-label">客户端</span></a>
                 </li>
                 </li>
-            {{end}}
                 <li class="{{if eq "host" .menu}}active{{end}}">
                 <li class="{{if eq "host" .menu}}active{{end}}">
                     <a href="/index/hostlist"><i class="fa fa-paperclip"></i> <span langtag="menu-host"
                     <a href="/index/hostlist"><i class="fa fa-paperclip"></i> <span langtag="menu-host"
                                                                                     class="nav-label">域名解析</span></a>
                                                                                     class="nav-label">域名解析</span></a>