ソースを参照

feat: introducing streams management pages #166

0xJacky 1 年間 前
コミット
2649b710bb

+ 2 - 1
api/sites/domain.go

@@ -147,7 +147,8 @@ func GetDomain(c *gin.Context) {
 	c.Set("maybe_error", "")
 	c.Set("maybe_error", "")
 
 
 	certInfoMap := make(map[int]*cert.Info)
 	certInfoMap := make(map[int]*cert.Info)
-	for serverIdx, server := range nginxConfig.Servers {
+
+    for serverIdx, server := range nginxConfig.Servers {
 		for _, directive := range server.Directives {
 		for _, directive := range server.Directives {
 			if directive.Directive == "ssl_certificate" {
 			if directive.Directive == "ssl_certificate" {
 
 

+ 42 - 0
api/streams/advance.go

@@ -0,0 +1,42 @@
+package streams
+
+import (
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+func AdvancedEdit(c *gin.Context) {
+	var json struct {
+		Advanced bool `json:"advanced"`
+	}
+
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	name := c.Param("name")
+	path := nginx.GetConfPath("streams-available", name)
+
+	s := query.Site
+
+	_, err := s.Where(s.Path.Eq(path)).FirstOrCreate()
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	_, err = s.Where(s.Path.Eq(path)).Update(s.Advanced, json.Advanced)
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"message": "ok",
+	})
+
+}

+ 44 - 0
api/streams/duplicate.go

@@ -0,0 +1,44 @@
+package streams
+
+import (
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/internal/helper"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/gin-gonic/gin"
+	"net/http"
+)
+
+func Duplicate(c *gin.Context) {
+	// Source name
+	name := c.Param("name")
+
+	// Destination name
+	var json struct {
+		Name string `json:"name" binding:"required"`
+	}
+
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	src := nginx.GetConfPath("streams-available", name)
+	dst := nginx.GetConfPath("streams-available", json.Name)
+
+	if helper.FileExists(dst) {
+		c.JSON(http.StatusNotAcceptable, gin.H{
+			"message": "File exists",
+		})
+		return
+	}
+
+	_, err := helper.CopyFile(src, dst)
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"dst": dst,
+	})
+}

+ 14 - 0
api/streams/router.go

@@ -0,0 +1,14 @@
+package streams
+
+import "github.com/gin-gonic/gin"
+
+func InitRouter(r *gin.RouterGroup) {
+	r.GET("streams", GetStreams)
+	r.GET("stream/:name", GetStream)
+	r.POST("stream/:name", SaveStream)
+	r.POST("stream/:name/enable", EnableStream)
+	r.POST("stream/:name/disable", DisableStream)
+	r.POST("stream/:name/advance", AdvancedEdit)
+	r.DELETE("stream/:name", DeleteStream)
+	r.POST("stream/:name/duplicate", Duplicate)
+}

+ 343 - 0
api/streams/streams.go

@@ -0,0 +1,343 @@
+package streams
+
+import (
+	"github.com/0xJacky/Nginx-UI/api"
+	"github.com/0xJacky/Nginx-UI/internal/config"
+	"github.com/0xJacky/Nginx-UI/internal/helper"
+	"github.com/0xJacky/Nginx-UI/internal/nginx"
+	"github.com/0xJacky/Nginx-UI/query"
+	"github.com/gin-gonic/gin"
+	"github.com/sashabaranov/go-openai"
+	"net/http"
+	"os"
+	"strings"
+	"time"
+)
+
+type Stream struct {
+	ModifiedAt      time.Time                      `json:"modified_at"`
+	Advanced        bool                           `json:"advanced"`
+	Enabled         bool                           `json:"enabled"`
+	Name            string                         `json:"name"`
+	Config          string                         `json:"config"`
+	ChatGPTMessages []openai.ChatCompletionMessage `json:"chatgpt_messages,omitempty"`
+	Tokenized       *nginx.NgxConfig               `json:"tokenized,omitempty"`
+}
+
+func GetStreams(c *gin.Context) {
+	name := c.Query("name")
+	orderBy := c.Query("order_by")
+	sort := c.DefaultQuery("sort", "desc")
+
+	configFiles, err := os.ReadDir(nginx.GetConfPath("streams-available"))
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	enabledConfig, err := os.ReadDir(nginx.GetConfPath("streams-enabled"))
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	enabledConfigMap := make(map[string]bool)
+	for i := range enabledConfig {
+		enabledConfigMap[enabledConfig[i].Name()] = true
+	}
+
+	var configs []config.Config
+
+	for i := range configFiles {
+		file := configFiles[i]
+		fileInfo, _ := file.Info()
+		if !file.IsDir() {
+			if name != "" && !strings.Contains(file.Name(), name) {
+				continue
+			}
+			configs = append(configs, config.Config{
+				Name:       file.Name(),
+				ModifiedAt: fileInfo.ModTime(),
+				Size:       fileInfo.Size(),
+				IsDir:      fileInfo.IsDir(),
+				Enabled:    enabledConfigMap[file.Name()],
+			})
+		}
+	}
+
+	configs = config.Sort(orderBy, sort, configs)
+
+	c.JSON(http.StatusOK, gin.H{
+		"data": configs,
+	})
+}
+
+func GetStream(c *gin.Context) {
+	rewriteName, ok := c.Get("rewriteConfigFileName")
+
+	name := c.Param("name")
+
+	// for modify filename
+	if ok {
+		name = rewriteName.(string)
+	}
+
+	path := nginx.GetConfPath("streams-available", name)
+	file, err := os.Stat(path)
+	if os.IsNotExist(err) {
+		c.JSON(http.StatusNotFound, gin.H{
+			"message": "file not found",
+		})
+		return
+	}
+
+	enabled := true
+
+	if _, err := os.Stat(nginx.GetConfPath("streams-enabled", name)); os.IsNotExist(err) {
+		enabled = false
+	}
+
+	s := query.Stream
+	stream, err := s.Where(s.Path.Eq(path)).FirstOrInit()
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	if stream.Advanced {
+		origContent, err := os.ReadFile(path)
+		if err != nil {
+			api.ErrHandler(c, err)
+			return
+		}
+
+		c.JSON(http.StatusOK, Stream{
+			ModifiedAt:      file.ModTime(),
+			Advanced:        stream.Advanced,
+			Enabled:         enabled,
+			Name:            name,
+			Config:          string(origContent),
+			ChatGPTMessages: stream.ChatGPTMessages,
+		})
+		return
+	}
+
+	c.Set("maybe_error", "nginx_config_syntax_error")
+	nginxConfig, err := nginx.ParseNgxConfig(path)
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	c.Set("maybe_error", "nginx_config_syntax_error")
+
+	c.JSON(http.StatusOK, Stream{
+		ModifiedAt:      file.ModTime(),
+		Advanced:        stream.Advanced,
+		Enabled:         enabled,
+		Name:            name,
+		Config:          nginxConfig.FmtCode(),
+		Tokenized:       nginxConfig,
+		ChatGPTMessages: stream.ChatGPTMessages,
+	})
+}
+
+func SaveStream(c *gin.Context) {
+	name := c.Param("name")
+
+	if name == "" {
+		c.JSON(http.StatusNotAcceptable, gin.H{
+			"message": "param name is empty",
+		})
+		return
+	}
+
+	var json struct {
+		Name      string `json:"name" binding:"required"`
+		Content   string `json:"content" binding:"required"`
+		Overwrite bool   `json:"overwrite"`
+	}
+
+	if !api.BindAndValid(c, &json) {
+		return
+	}
+
+	path := nginx.GetConfPath("streams-available", name)
+
+	if !json.Overwrite && helper.FileExists(path) {
+		c.JSON(http.StatusNotAcceptable, gin.H{
+			"message": "File exists",
+		})
+		return
+	}
+
+	err := os.WriteFile(path, []byte(json.Content), 0644)
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+	enabledConfigFilePath := nginx.GetConfPath("streams-enabled", name)
+	// rename the config file if needed
+	if name != json.Name {
+		newPath := nginx.GetConfPath("streams-available", json.Name)
+		s := query.Stream
+		_, err = s.Where(s.Path.Eq(path)).Update(s.Path, newPath)
+
+		// check if dst file exists, do not rename
+		if helper.FileExists(newPath) {
+			c.JSON(http.StatusNotAcceptable, gin.H{
+				"message": "File exists",
+			})
+			return
+		}
+		// recreate a soft link
+		if helper.FileExists(enabledConfigFilePath) {
+			_ = os.Remove(enabledConfigFilePath)
+			enabledConfigFilePath = nginx.GetConfPath("streams-enabled", json.Name)
+			err = os.Symlink(newPath, enabledConfigFilePath)
+
+			if err != nil {
+				api.ErrHandler(c, err)
+				return
+			}
+		}
+
+		err = os.Rename(path, newPath)
+		if err != nil {
+			api.ErrHandler(c, err)
+			return
+		}
+
+		name = json.Name
+		c.Set("rewriteConfigFileName", name)
+	}
+
+	enabledConfigFilePath = nginx.GetConfPath("streams-enabled", name)
+	if helper.FileExists(enabledConfigFilePath) {
+		// Test nginx configuration
+		output := nginx.TestConf()
+
+		if nginx.GetLogLevel(output) > nginx.Warn {
+			c.JSON(http.StatusInternalServerError, gin.H{
+				"message": output,
+				"error":   "nginx_config_syntax_error",
+			})
+			return
+		}
+
+		output = nginx.Reload()
+
+		if nginx.GetLogLevel(output) > nginx.Warn {
+			c.JSON(http.StatusInternalServerError, gin.H{
+				"message": output,
+			})
+			return
+		}
+	}
+
+	GetStream(c)
+}
+
+func EnableStream(c *gin.Context) {
+	configFilePath := nginx.GetConfPath("streams-available", c.Param("name"))
+	enabledConfigFilePath := nginx.GetConfPath("streams-enabled", c.Param("name"))
+
+	_, err := os.Stat(configFilePath)
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	if _, err = os.Stat(enabledConfigFilePath); os.IsNotExist(err) {
+		err = os.Symlink(configFilePath, enabledConfigFilePath)
+
+		if err != nil {
+			api.ErrHandler(c, err)
+			return
+		}
+	}
+
+	// Test nginx config, if not pass, then disable the stream.
+	output := nginx.TestConf()
+
+	if nginx.GetLogLevel(output) > nginx.Warn {
+		_ = os.Remove(enabledConfigFilePath)
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"message": output,
+		})
+		return
+	}
+
+	output = nginx.Reload()
+
+	if nginx.GetLogLevel(output) > nginx.Warn {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"message": output,
+		})
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"message": "ok",
+	})
+}
+
+func DisableStream(c *gin.Context) {
+	enabledConfigFilePath := nginx.GetConfPath("streams-enabled", c.Param("name"))
+
+	_, err := os.Stat(enabledConfigFilePath)
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+
+	err = os.Remove(enabledConfigFilePath)
+
+	if err != nil {
+		api.ErrHandler(c, err)
+		return
+	}
+	output := nginx.Reload()
+
+	if nginx.GetLogLevel(output) > nginx.Warn {
+		c.JSON(http.StatusInternalServerError, gin.H{
+			"message": output,
+		})
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"message": "ok",
+	})
+}
+
+func DeleteStream(c *gin.Context) {
+	var err error
+	name := c.Param("name")
+	availablePath := nginx.GetConfPath("streams-available", name)
+	enabledPath := nginx.GetConfPath("streams-enabled", name)
+
+	if _, err = os.Stat(availablePath); os.IsNotExist(err) {
+		c.JSON(http.StatusNotFound, gin.H{
+			"message": "stream not found",
+		})
+		return
+	}
+
+	if _, err = os.Stat(enabledPath); err == nil {
+		c.JSON(http.StatusNotAcceptable, gin.H{
+			"message": "stream is enabled",
+		})
+		return
+	}
+
+	c.JSON(http.StatusOK, gin.H{
+		"message": "ok",
+	})
+}

+ 37 - 0
app/src/api/stream.ts

@@ -0,0 +1,37 @@
+import Curd from '@/api/curd'
+import http from '@/lib/http'
+import type { ChatComplicationMessage } from '@/api/openai'
+import type { NgxConfig } from '@/api/ngx'
+
+export interface Stream {
+  modified_at: string
+  advanced: boolean
+  enabled: boolean
+  name: string
+  config: string
+  chatgpt_messages: ChatComplicationMessage[]
+  tokenized?: NgxConfig
+}
+
+class StreamCurd extends Curd<Stream> {
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  enable(name: string, config?: any) {
+    return http.post(`${this.baseUrl}/${name}/enable`, undefined, config)
+  }
+
+  disable(name: string) {
+    return http.post(`${this.baseUrl}/${name}/disable`)
+  }
+
+  duplicate(name: string, data: { name: string }): Promise<{ dst: string }> {
+    return http.post(`${this.baseUrl}/${name}/duplicate`, data)
+  }
+
+  advance_mode(name: string, data: { advanced: boolean }) {
+    return http.post(`${this.baseUrl}/${name}/advance`, data)
+  }
+}
+
+const stream = new StreamCurd('/stream')
+
+export default stream

+ 162 - 72
app/src/language/en/app.po

@@ -9,26 +9,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr "About"
 msgstr "About"
 
 
-#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:169 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr ""
 msgstr ""
 
 
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Action"
 msgstr "Action"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr ""
 msgstr ""
 
 
@@ -41,16 +43,26 @@ msgstr "Add Directive Below"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Add Location"
 msgstr "Add Location"
 
 
-#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:96
+#: src/routes/index.ts:72 src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Add Site"
 msgstr "Add Site"
 
 
+#: src/views/stream/StreamList.vue:184
+#, fuzzy
+msgid "Add Stream"
+msgstr "Add Site"
+
+#: src/views/stream/StreamList.vue:114
+#, fuzzy
+msgid "Added successfully"
+msgstr "Saved successfully"
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 #, fuzzy
 #, fuzzy
 msgid "Additional"
 msgid "Additional"
 msgstr "Add Location"
 msgstr "Add Location"
 
 
-#: src/views/domain/DomainEdit.vue:204
+#: src/views/domain/DomainEdit.vue:204 src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Advance Mode"
 msgstr "Advance Mode"
 
 
@@ -82,12 +94,12 @@ msgid "Are you sure you want to clear the record of chat?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
-#: src/views/domain/DomainList.vue:147
+#: src/views/domain/DomainList.vue:147 src/views/stream/StreamList.vue:168
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
@@ -128,6 +140,7 @@ msgstr "Auto-renewal enabled for %{name}"
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr "Back"
 msgstr "Back"
 
 
@@ -143,11 +156,12 @@ msgstr "Base information"
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 #, fuzzy
 #, fuzzy
 msgid "Basic"
 msgid "Basic"
 msgstr "Basic Mode"
 msgstr "Basic Mode"
 
 
-#: src/views/domain/DomainEdit.vue:207
+#: src/views/domain/DomainEdit.vue:207 src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Basic Mode"
 msgstr "Basic Mode"
 
 
@@ -172,9 +186,11 @@ msgstr ""
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Cancel"
 msgstr "Cancel"
 
 
@@ -191,12 +207,12 @@ msgstr "Certificate is valid"
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:122
+#: src/routes/index.ts:118 src/views/certificate/Certificate.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
@@ -243,10 +259,10 @@ msgstr ""
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "Disabled successfully"
 msgstr "Disabled successfully"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr "Comments"
 msgstr "Comments"
 
 
@@ -275,7 +291,7 @@ msgstr "Configure SSL"
 msgid "Connected"
 msgid "Connected"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -293,6 +309,11 @@ msgstr "CPU Status"
 msgid "CPU:"
 msgid "CPU:"
 msgstr "CPU:"
 msgstr "CPU:"
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+#, fuzzy
+msgid "Create"
+msgstr "Created at"
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Create Another"
 msgstr "Create Another"
@@ -318,11 +339,11 @@ msgid "Current Version"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Dashboard"
 msgstr "Dashboard"
 
 
@@ -332,8 +353,9 @@ msgstr "Database (Optional, default: database)"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr ""
 msgstr ""
 
 
@@ -341,6 +363,10 @@ msgstr ""
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr ""
 msgstr ""
 
 
+#: src/views/stream/StreamList.vue:81
+msgid "Delete stream: %{stream_name}"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #, fuzzy
 #, fuzzy
 msgid "Deleted successfully"
 msgid "Deleted successfully"
@@ -348,18 +374,23 @@ msgstr "Disabled successfully"
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 #, fuzzy
 #, fuzzy
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
@@ -381,7 +412,7 @@ msgstr "Development Mode"
 msgid "Directive"
 msgid "Directive"
 msgstr "Directive"
 msgstr "Directive"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr "Directives"
 msgstr "Directives"
 
 
@@ -390,7 +421,7 @@ msgstr "Directives"
 msgid "Directory"
 msgid "Directory"
 msgstr "Directive"
 msgstr "Directive"
 
 
-#: src/views/domain/DomainList.vue:125
+#: src/views/domain/DomainList.vue:125 src/views/stream/StreamList.vue:146
 #, fuzzy
 #, fuzzy
 msgid "Disable"
 msgid "Disable"
 msgstr "Disabled"
 msgstr "Disabled"
@@ -400,12 +431,15 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "Disable auto-renewal failed for %{name}"
 msgstr "Disable auto-renewal failed for %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:36
+#: src/views/domain/DomainList.vue:36 src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Disabled"
 msgstr "Disabled"
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr "Disabled successfully"
 msgstr "Disabled successfully"
 
 
@@ -413,7 +447,7 @@ msgstr "Disabled successfully"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "Disk IO"
 msgstr "Disk IO"
 
 
-#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:148 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr ""
 msgstr ""
 
 
@@ -427,6 +461,7 @@ msgid "DNS01"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 #, fuzzy
 #, fuzzy
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
@@ -442,22 +477,32 @@ msgstr ""
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to disable this stream?"
+msgstr "Are you sure you want to remove this directive?"
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 #, fuzzy
 #, fuzzy
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to enable this stream?"
+msgstr "Are you sure you want to remove this directive?"
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 #, fuzzy
 #, fuzzy
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "Are you sure you want to remove this directive?"
 msgstr "Are you sure you want to remove this directive?"
@@ -488,56 +533,71 @@ msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Enable failed"
 msgstr "Enable failed"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:179 src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Edit %{n}"
 msgstr "Edit %{n}"
 
 
-#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:110 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Edit Configuration"
 msgstr "Edit Configuration"
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Edit Site"
 msgstr "Edit Site"
 
 
+#: src/routes/index.ts:93
+#, fuzzy
+msgid "Edit Stream"
+msgstr "Edit Site"
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
@@ -550,23 +610,29 @@ msgid "Enable failed"
 msgstr "Enable failed"
 msgstr "Enable failed"
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Enabled successfully"
 msgstr "Enabled successfully"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr "Enable TLS"
 msgstr "Enable TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Enabled successfully"
 msgstr "Enabled successfully"
 
 
@@ -574,7 +640,7 @@ msgstr "Enabled successfully"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Encrypt website with Let's Encrypt"
 msgstr "Encrypt website with Let's Encrypt"
 
 
-#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:186 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr ""
 msgstr ""
 
 
@@ -587,7 +653,7 @@ msgstr "Comments"
 msgid "Error"
 msgid "Error"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:173 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr ""
 msgstr ""
 
 
@@ -611,11 +677,15 @@ msgstr ""
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr "Failed to disable %{msg}"
 msgstr "Failed to disable %{msg}"
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr "Failed to enable %{msg}"
 msgstr "Failed to enable %{msg}"
 
 
@@ -623,7 +693,7 @@ msgstr "Failed to enable %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:138
+#: src/views/domain/DomainEdit.vue:138 src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 
 
@@ -703,7 +773,7 @@ msgstr ""
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr "Home"
 msgstr "Home"
 
 
@@ -727,7 +797,7 @@ msgstr ""
 msgid "Import"
 msgid "Import"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:140 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
@@ -744,7 +814,7 @@ msgstr ""
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:220 src/views/other/Install.vue:139
+#: src/routes/index.ts:237 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Install"
 msgstr "Install"
 
 
@@ -827,7 +897,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Login"
 msgstr "Login"
 
 
-#: src/routes/index.ts:226 src/views/other/Login.vue:147
+#: src/routes/index.ts:243 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Login"
 msgstr "Login"
 
 
@@ -848,15 +918,20 @@ msgstr ""
 "Make sure you have configured a reverse proxy for .well-known directory to "
 "Make sure you have configured a reverse proxy for .well-known directory to "
 "HTTPChallengePort (default: 9180) before getting the certificate."
 "HTTPChallengePort (default: 9180) before getting the certificate."
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Manage Configs"
 msgstr "Manage Configs"
 
 
-#: src/routes/index.ts:59 src/views/domain/DomainList.vue:105
+#: src/routes/index.ts:60 src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Manage Sites"
 msgstr "Manage Sites"
 
 
-#: src/routes/index.ts:185 src/views/user/User.vue:53
+#: src/routes/index.ts:85 src/views/stream/StreamList.vue:122
+#, fuzzy
+msgid "Manage Streams"
+msgstr "Manage Sites"
+
+#: src/routes/index.ts:202 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Manage Users"
 msgstr "Manage Users"
 
 
@@ -880,7 +955,7 @@ msgstr "Memory and Storage"
 msgid "Modify"
 msgid "Modify"
 msgstr "Modify Config"
 msgstr "Modify Config"
 
 
-#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:132 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Certificate Status"
 msgstr "Certificate Status"
@@ -901,8 +976,11 @@ msgstr "Single Directive"
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16 src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr "Name"
 msgstr "Name"
 
 
@@ -940,7 +1018,7 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/DomainEdit.vue:222
+#: src/views/domain/DomainEdit.vue:222 src/views/stream/StreamEdit.vue:213
 #, fuzzy
 #, fuzzy
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Configuration Name"
 msgstr "Configuration Name"
@@ -953,7 +1031,7 @@ msgstr ""
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:163 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr ""
 msgstr ""
 
 
@@ -971,9 +1049,10 @@ msgstr "Saved successfully"
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr "No"
 msgstr "No"
 
 
@@ -985,7 +1064,7 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:232 src/routes/index.ts:234
+#: src/routes/index.ts:249 src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Not Found"
 msgstr "Not Found"
 
 
@@ -1003,7 +1082,7 @@ msgstr ""
 msgid "Notification"
 msgid "Notification"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
 
 
-#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:194
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Certificate is valid"
 msgstr "Certificate is valid"
@@ -1033,10 +1112,13 @@ msgstr ""
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -1065,10 +1147,12 @@ msgid "OS:"
 msgstr "OS:"
 msgstr "OS:"
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1111,6 +1195,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid ""
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "Please input name, this will be used as the filename of the new "
 "configuration!"
 "configuration!"
@@ -1134,6 +1219,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr ""
 msgstr ""
 
 
@@ -1141,7 +1227,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:210 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr ""
 msgstr ""
 
 
@@ -1211,15 +1297,11 @@ msgstr ""
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Rename"
 msgid "Rename"
 msgstr "Username"
 msgstr "Username"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr ""
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
@@ -1270,8 +1352,8 @@ msgstr ""
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
-#: src/views/preference/Preference.vue:113
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
+#: src/views/preference/Preference.vue:113 src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr "Save"
 msgstr "Save"
 
 
@@ -1280,7 +1362,7 @@ msgid "Save Directive"
 msgstr "Save Directive"
 msgstr "Save Directive"
 
 
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Save error %{msg}"
 msgstr "Save error %{msg}"
 
 
@@ -1298,7 +1380,8 @@ msgstr "Saved successfully"
 
 
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
@@ -1319,6 +1402,7 @@ msgstr "Send"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116 src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr "Server error"
 msgstr "Server error"
@@ -1352,12 +1436,12 @@ msgstr ""
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Single Directive"
 msgstr "Single Directive"
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 #, fuzzy
 #, fuzzy
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Sites List"
 msgstr "Sites List"
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Sites List"
 msgstr "Sites List"
 
 
@@ -1394,7 +1478,7 @@ msgid "Stable"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
-#: src/views/environment/Environment.vue:78
+#: src/views/environment/Environment.vue:78 src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr "Status"
 msgstr "Status"
 
 
@@ -1427,7 +1511,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr ""
 msgstr ""
 
 
@@ -1437,10 +1521,11 @@ msgid "Table"
 msgstr "Enabled"
 msgstr "Enabled"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:155 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Terminal"
 msgstr "Terminal"
 
 
@@ -1490,7 +1575,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
 "add a location which can proxy the request from authority to backend, and we "
 "add a location which can proxy the request from authority to backend, and we "
@@ -1507,7 +1592,8 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
-#: src/views/user/User.vue:40
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44 src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Updated at"
 msgstr "Updated at"
 
 
@@ -1516,7 +1602,7 @@ msgstr "Updated at"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:229 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr ""
 msgstr ""
@@ -1530,6 +1616,10 @@ msgstr "Saved successfully"
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr ""
 msgstr ""
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr "Uptime:"
 msgstr "Uptime:"
@@ -1595,7 +1685,7 @@ msgstr ""
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr "Yes"
 msgstr "Yes"

+ 163 - 72
app/src/language/es/app.po

@@ -14,26 +14,28 @@ msgstr ""
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "Plural-Forms: nplurals=2; plural=n != 1;\n"
 "X-Generator: Weblate 5.0\n"
 "X-Generator: Weblate 5.0\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr "Acerca de"
 msgstr "Acerca de"
 
 
-#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:169 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "Registros de acceso"
 msgstr "Registros de acceso"
 
 
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Acción"
 msgstr "Acción"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr "Agregar"
 msgstr "Agregar"
 
 
@@ -46,15 +48,25 @@ msgstr "Añadir directiva a continuación"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Agregar Ubicación"
 msgstr "Agregar Ubicación"
 
 
-#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:96
+#: src/routes/index.ts:72 src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Agregar Sitio"
 msgstr "Agregar Sitio"
 
 
+#: src/views/stream/StreamList.vue:184
+#, fuzzy
+msgid "Add Stream"
+msgstr "Agregar Sitio"
+
+#: src/views/stream/StreamList.vue:114
+#, fuzzy
+msgid "Added successfully"
+msgstr "Actualización exitosa"
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 msgid "Additional"
 msgid "Additional"
 msgstr "Adicional"
 msgstr "Adicional"
 
 
-#: src/views/domain/DomainEdit.vue:204
+#: src/views/domain/DomainEdit.vue:204 src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Modo avanzado"
 msgstr "Modo avanzado"
 
 
@@ -85,11 +97,11 @@ msgid "Are you sure you want to clear the record of chat?"
 msgstr "¿Está seguro de que desea borrar el registro del chat?"
 msgstr "¿Está seguro de que desea borrar el registro del chat?"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
-#: src/views/domain/DomainList.vue:147
+#: src/views/domain/DomainList.vue:147 src/views/stream/StreamList.vue:168
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "¿Está seguro de que quiere borrar?"
 msgstr "¿Está seguro de que quiere borrar?"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr "¿Está seguro de que quiere borrar esta directiva?"
 msgstr "¿Está seguro de que quiere borrar esta directiva?"
 
 
@@ -129,6 +141,7 @@ msgstr "Renovación automática habilitada por %{name}"
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr "Volver"
 msgstr "Volver"
 
 
@@ -143,10 +156,11 @@ msgstr "Información general"
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgid "Basic"
 msgstr "Básico"
 msgstr "Básico"
 
 
-#: src/views/domain/DomainEdit.vue:207
+#: src/views/domain/DomainEdit.vue:207 src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Modo Básico"
 msgstr "Modo Básico"
 
 
@@ -170,9 +184,11 @@ msgstr "Directorio CA"
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Cancelar"
 msgstr "Cancelar"
 
 
@@ -189,12 +205,12 @@ msgstr "El certificado es válido"
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
 
 
-#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:122
+#: src/routes/index.ts:118 src/views/certificate/Certificate.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Lista de Certificados"
 msgstr "Lista de Certificados"
@@ -240,10 +256,10 @@ msgstr "Limpiar"
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "Desactivado con éxito"
 msgstr "Desactivado con éxito"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr "Comentarios"
 msgstr "Comentarios"
 
 
@@ -271,7 +287,7 @@ msgstr "Configurar SSL"
 msgid "Connected"
 msgid "Connected"
 msgstr "Conectado"
 msgstr "Conectado"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -289,6 +305,11 @@ msgstr "Estado del CPU"
 msgid "CPU:"
 msgid "CPU:"
 msgstr "CPU:"
 msgstr "CPU:"
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+#, fuzzy
+msgid "Create"
+msgstr "Creado el"
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Crear otro"
 msgstr "Crear otro"
@@ -314,11 +335,11 @@ msgid "Current Version"
 msgstr "Versión actual"
 msgstr "Versión actual"
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr "Personalizado"
 msgstr "Personalizado"
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Panel"
 msgstr "Panel"
 
 
@@ -328,8 +349,9 @@ msgstr "Base de datos (Opcional, default: database)"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr "Eliminar"
 msgstr "Eliminar"
 
 
@@ -337,6 +359,11 @@ msgstr "Eliminar"
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr "Eliminar sitio: %{site_name}"
 msgstr "Eliminar sitio: %{site_name}"
 
 
+#: src/views/stream/StreamList.vue:81
+#, fuzzy
+msgid "Delete stream: %{stream_name}"
+msgstr "Eliminar sitio: %{site_name}"
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #, fuzzy
 #, fuzzy
 msgid "Deleted successfully"
 msgid "Deleted successfully"
@@ -344,18 +371,23 @@ msgstr "Desactivado con éxito"
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr "Desplegar"
 msgstr "Desplegar"
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "Falló el desplegado de %{conf_name} a %{node_name}"
 msgstr "Falló el desplegado de %{conf_name} a %{node_name}"
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr "Desplegado de %{conf_name} a %{node_name} exitoso"
 msgstr "Desplegado de %{conf_name} a %{node_name} exitoso"
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr "Desplegado con éxito"
 msgstr "Desplegado con éxito"
 
 
@@ -376,7 +408,7 @@ msgstr "Modo de desarrollo"
 msgid "Directive"
 msgid "Directive"
 msgstr "Directiva"
 msgstr "Directiva"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr "Directivas"
 msgstr "Directivas"
 
 
@@ -385,7 +417,7 @@ msgstr "Directivas"
 msgid "Directory"
 msgid "Directory"
 msgstr "Directiva"
 msgstr "Directiva"
 
 
-#: src/views/domain/DomainList.vue:125
+#: src/views/domain/DomainList.vue:125 src/views/stream/StreamList.vue:146
 #, fuzzy
 #, fuzzy
 msgid "Disable"
 msgid "Disable"
 msgstr "Desactivado"
 msgstr "Desactivado"
@@ -395,12 +427,15 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 msgstr "No se pudo desactivar la renovación automática por %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:36
+#: src/views/domain/DomainList.vue:36 src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Desactivado"
 msgstr "Desactivado"
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr "Desactivado con éxito"
 msgstr "Desactivado con éxito"
 
 
@@ -408,7 +443,7 @@ msgstr "Desactivado con éxito"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "I/O del disco"
 msgstr "I/O del disco"
 
 
-#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:148 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "Credenciales de DNS"
 msgstr "Credenciales de DNS"
 
 
@@ -422,6 +457,7 @@ msgid "DNS01"
 msgstr "DNS01"
 msgstr "DNS01"
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgstr[0] "¿Desea desplegar este archivo en un servidor remoto?"
 msgstr[0] "¿Desea desplegar este archivo en un servidor remoto?"
@@ -435,19 +471,29 @@ msgstr "¿Desea deshabilitar la renovación automática de certificado?"
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr "¿Quieres deshabilitar este sitio?"
 msgstr "¿Quieres deshabilitar este sitio?"
 
 
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to disable this stream?"
+msgstr "¿Quieres deshabilitar este sitio?"
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr "¿Quieres habilitar este sitio?"
 msgstr "¿Quieres habilitar este sitio?"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to enable this stream?"
+msgstr "¿Quieres habilitar este sitio?"
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr "¿Quieres habilitar TLS?"
 msgstr "¿Quieres habilitar TLS?"
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "¿Quieres eliminar este servidor?"
 msgstr "¿Quieres eliminar este servidor?"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "¿Quieres eliminar este servidor?"
 msgstr "¿Quieres eliminar este servidor?"
@@ -481,52 +527,67 @@ msgstr "Modo de ejecución de prueba habilitado"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Duplicar"
 msgstr "Duplicar"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Duplicado con éxito de %{conf_name} a %{node_name}"
 msgstr "Duplicado con éxito de %{conf_name} a %{node_name}"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Duplicado fallido"
 msgstr "Duplicado fallido"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Duplicado con éxito"
 msgstr "Duplicado con éxito"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Duplicado con éxito a local"
 msgstr "Duplicado con éxito a local"
 
 
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:179 src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Editar %{n}"
 msgstr "Editar %{n}"
 
 
-#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:110 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Editar Configuración"
 msgstr "Editar Configuración"
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Editar Sitio"
 msgstr "Editar Sitio"
 
 
+#: src/routes/index.ts:93
+#, fuzzy
+msgid "Edit Stream"
+msgstr "Editar Sitio"
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Correo (*)"
 msgstr "Correo (*)"
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Habilitado"
 msgstr "Habilitado"
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "Falló el habilitado de %{conf_name} en %{node_name}"
 msgstr "Falló el habilitado de %{conf_name} en %{node_name}"
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Habilitado exitoso de %{conf_name} en %{node_name}"
 msgstr "Habilitado exitoso de %{conf_name} en %{node_name}"
 
 
@@ -539,22 +600,28 @@ msgid "Enable failed"
 msgstr "Falló la habilitación"
 msgstr "Falló la habilitación"
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Habilitado con éxito"
 msgstr "Habilitado con éxito"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr "Habilitar TLS"
 msgstr "Habilitar TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Habilitado"
 msgstr "Habilitado"
 
 
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Habilitado con éxito"
 msgstr "Habilitado con éxito"
 
 
@@ -562,7 +629,7 @@ msgstr "Habilitado con éxito"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Encriptar sitio web con Let's Encrypt"
 msgstr "Encriptar sitio web con Let's Encrypt"
 
 
-#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:186 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "Entorno"
 msgstr "Entorno"
 
 
@@ -574,7 +641,7 @@ msgstr "Entornos"
 msgid "Error"
 msgid "Error"
 msgstr "Error"
 msgstr "Error"
 
 
-#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:173 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "Registros de acceso"
 msgstr "Registros de acceso"
 
 
@@ -598,11 +665,15 @@ msgstr "Exportar"
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr "Error al deshabilitar %{msg}"
 msgstr "Error al deshabilitar %{msg}"
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr "Error al habilitar %{msg}"
 msgstr "Error al habilitar %{msg}"
 
 
@@ -610,7 +681,7 @@ msgstr "Error al habilitar %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "No se pudo obtener la información del certificado"
 msgstr "No se pudo obtener la información del certificado"
 
 
-#: src/views/domain/DomainEdit.vue:138
+#: src/views/domain/DomainEdit.vue:138 src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 "No se pudo guardar, se detectó un error(es) de sintaxis en la configuración."
 "No se pudo guardar, se detectó un error(es) de sintaxis en la configuración."
@@ -689,7 +760,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-3.5-Turbo"
 msgstr "GPT-3.5-Turbo"
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr "Inicio"
 msgstr "Inicio"
 
 
@@ -714,7 +785,7 @@ msgstr "HTTP01"
 msgid "Import"
 msgid "Import"
 msgstr "Exportar"
 msgstr "Exportar"
 
 
-#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:140 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
@@ -731,7 +802,7 @@ msgstr "Error de actualización de kernel inicial"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Inicializando la actualización del kernel"
 msgstr "Inicializando la actualización del kernel"
 
 
-#: src/routes/index.ts:220 src/views/other/Install.vue:139
+#: src/routes/index.ts:237 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Instalar"
 msgstr "Instalar"
 
 
@@ -811,7 +882,7 @@ msgstr "Ubicaciones"
 msgid "Log"
 msgid "Log"
 msgstr "Acceso"
 msgstr "Acceso"
 
 
-#: src/routes/index.ts:226 src/views/other/Login.vue:147
+#: src/routes/index.ts:243 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Acceso"
 msgstr "Acceso"
 
 
@@ -831,15 +902,20 @@ msgstr ""
 "Asegúrese de haber configurado un proxy reverso para el directorio .well-"
 "Asegúrese de haber configurado un proxy reverso para el directorio .well-"
 "known en HTTPChallengePort antes de obtener el certificado."
 "known en HTTPChallengePort antes de obtener el certificado."
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Administrar configuraciones"
 msgstr "Administrar configuraciones"
 
 
-#: src/routes/index.ts:59 src/views/domain/DomainList.vue:105
+#: src/routes/index.ts:60 src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Administrar sitios"
 msgstr "Administrar sitios"
 
 
-#: src/routes/index.ts:185 src/views/user/User.vue:53
+#: src/routes/index.ts:85 src/views/stream/StreamList.vue:122
+#, fuzzy
+msgid "Manage Streams"
+msgstr "Administrar sitios"
+
+#: src/routes/index.ts:202 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Administrar usuarios"
 msgstr "Administrar usuarios"
 
 
@@ -862,7 +938,7 @@ msgstr "Memoria y almacenamiento"
 msgid "Modify"
 msgid "Modify"
 msgstr "Modificar"
 msgstr "Modificar"
 
 
-#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:132 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Estado del Certificado"
 msgstr "Estado del Certificado"
@@ -882,8 +958,11 @@ msgstr "Directiva multilínea"
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16 src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr "Nombre"
 msgstr "Nombre"
 
 
@@ -921,7 +1000,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Ruta de registro de acceso de Nginx"
 msgstr "Ruta de registro de acceso de Nginx"
 
 
-#: src/views/domain/DomainEdit.vue:222
+#: src/views/domain/DomainEdit.vue:222 src/views/stream/StreamEdit.vue:213
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Error de análisis de configuración de Nginx"
 msgstr "Error de análisis de configuración de Nginx"
 
 
@@ -933,7 +1012,7 @@ msgstr "Control de Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Ruta de registro de errores de Nginx"
 msgstr "Ruta de registro de errores de Nginx"
 
 
-#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:163 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Registro Nginx"
 msgstr "Registro Nginx"
 
 
@@ -949,9 +1028,10 @@ msgstr "Nginx reiniciado con éxito"
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr "No"
 msgstr "No"
 
 
@@ -963,7 +1043,7 @@ msgstr "Secreto del nodo"
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:232 src/routes/index.ts:234
+#: src/routes/index.ts:249 src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr "No encontrado"
 msgstr "No encontrado"
 
 
@@ -981,7 +1061,7 @@ msgstr "Nota"
 msgid "Notification"
 msgid "Notification"
 msgstr "Certificación"
 msgstr "Certificación"
 
 
-#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:194
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Certificación"
 msgstr "Certificación"
@@ -1010,10 +1090,13 @@ msgstr "Desconectado"
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -1041,10 +1124,12 @@ msgid "OS:"
 msgstr "SO:"
 msgstr "SO:"
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "Sobrescribir"
 msgstr "Sobrescribir"
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "Sobrescribir archivo existente"
 msgstr "Sobrescribir archivo existente"
 
 
@@ -1091,6 +1176,7 @@ msgstr ""
 "del proveedor de DNS."
 "del proveedor de DNS."
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid ""
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "Please input name, this will be used as the filename of the new "
 "configuration!"
 "configuration!"
@@ -1116,6 +1202,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr "¡Seleccione al menos un nodo!"
 msgstr "¡Seleccione al menos un nodo!"
 
 
@@ -1123,7 +1210,7 @@ msgstr "¡Seleccione al menos un nodo!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "Prelanzamiento"
 msgstr "Prelanzamiento"
 
 
-#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:210 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "Configuración"
 msgstr "Configuración"
 
 
@@ -1191,15 +1278,11 @@ msgstr "Recargando Nginx"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Guardado con éxito"
 msgstr "Guardado con éxito"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Rename"
 msgid "Rename"
 msgstr "Nombre de usuario"
 msgstr "Nombre de usuario"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr ""
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
@@ -1249,8 +1332,8 @@ msgstr "Corriendo"
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
-#: src/views/preference/Preference.vue:113
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
+#: src/views/preference/Preference.vue:113 src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr "Guardar"
 msgstr "Guardar"
 
 
@@ -1259,7 +1342,7 @@ msgid "Save Directive"
 msgstr "Guardar Directiva"
 msgstr "Guardar Directiva"
 
 
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Error al guardar %{msg}"
 msgstr "Error al guardar %{msg}"
 
 
@@ -1275,7 +1358,8 @@ msgstr "Guardado con éxito"
 
 
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Guardado con éxito"
 msgstr "Guardado con éxito"
 
 
@@ -1296,6 +1380,7 @@ msgstr "Enviado"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116 src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr "Error del servidor"
 msgstr "Error del servidor"
@@ -1331,11 +1416,11 @@ msgstr "Usando el proveedor de desafíos HTTP01"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Directiva de una sola línea"
 msgstr "Directiva de una sola línea"
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Registros del sitio"
 msgstr "Registros del sitio"
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Lista de sitios"
 msgstr "Lista de sitios"
 
 
@@ -1369,7 +1454,7 @@ msgid "Stable"
 msgstr "Estable"
 msgstr "Estable"
 
 
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
-#: src/views/environment/Environment.vue:78
+#: src/views/environment/Environment.vue:78 src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr "Estado"
 msgstr "Estado"
 
 
@@ -1402,7 +1487,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr "Sistema"
 msgstr "Sistema"
 
 
@@ -1411,10 +1496,11 @@ msgid "Table"
 msgstr "Tabla"
 msgstr "Tabla"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr "Objetivo"
 msgstr "Objetivo"
 
 
-#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:155 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Terminal"
 msgstr "Terminal"
 
 
@@ -1466,7 +1552,7 @@ msgstr "Este campo no debe estar vacío"
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
 "add a location which can proxy the request from authority to backend, and we "
 "add a location which can proxy the request from authority to backend, and we "
@@ -1487,7 +1573,8 @@ msgstr "Tipo"
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
-#: src/views/user/User.vue:40
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44 src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Actualizado a"
 msgstr "Actualizado a"
 
 
@@ -1495,7 +1582,7 @@ msgstr "Actualizado a"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Actualización exitosa"
 msgstr "Actualización exitosa"
 
 
-#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:229 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Actualizar"
 msgstr "Actualizar"
@@ -1508,6 +1595,10 @@ msgstr "Actualización exitosa"
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr "Actualizando Nginx UI, por favor espere..."
 msgstr "Actualizando Nginx UI, por favor espere..."
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr "Tiempo encendido:"
 msgstr "Tiempo encendido:"
@@ -1574,7 +1665,7 @@ msgstr "Escribir la clave privada del certificado a disco"
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr "Escribir certificado a disco"
 msgstr "Escribir certificado a disco"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr "Si"
 msgstr "Si"

+ 163 - 72
app/src/language/fr_FR/app.po

@@ -11,26 +11,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Content-Transfer-Encoding: 8bit\n"
 "X-Generator: Poedit 3.3\n"
 "X-Generator: Poedit 3.3\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr "À propos"
 msgstr "À propos"
 
 
-#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:169 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "Journaux d'accès"
 msgstr "Journaux d'accès"
 
 
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Action"
 msgstr "Action"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr "Ajouter"
 msgstr "Ajouter"
 
 
@@ -43,16 +45,26 @@ msgstr "Ajouter une directive"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Ajouter une localisation"
 msgstr "Ajouter une localisation"
 
 
-#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:96
+#: src/routes/index.ts:72 src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Ajouter un site"
 msgstr "Ajouter un site"
 
 
+#: src/views/stream/StreamList.vue:184
+#, fuzzy
+msgid "Add Stream"
+msgstr "Ajouter un site"
+
+#: src/views/stream/StreamList.vue:114
+#, fuzzy
+msgid "Added successfully"
+msgstr "Mis à jour avec succés"
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 #, fuzzy
 #, fuzzy
 msgid "Additional"
 msgid "Additional"
 msgstr "Supplémentaire"
 msgstr "Supplémentaire"
 
 
-#: src/views/domain/DomainEdit.vue:204
+#: src/views/domain/DomainEdit.vue:204 src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Mode avancé"
 msgstr "Mode avancé"
 
 
@@ -84,11 +96,11 @@ msgid "Are you sure you want to clear the record of chat?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 msgstr "Voulez-vous vraiment effacer l'historique du chat ?"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
-#: src/views/domain/DomainList.vue:147
+#: src/views/domain/DomainList.vue:147 src/views/stream/StreamList.vue:168
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "Etes-vous sûr que vous voulez supprimer ?"
 msgstr "Etes-vous sûr que vous voulez supprimer ?"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr "Voulez-vous vraiment supprimer cette directive ?"
 msgstr "Voulez-vous vraiment supprimer cette directive ?"
 
 
@@ -129,6 +141,7 @@ msgstr "Renouvellement automatique activé pour %{name}"
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr "Retour"
 msgstr "Retour"
 
 
@@ -143,10 +156,11 @@ msgstr "Information générale"
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgid "Basic"
 msgstr "Basique"
 msgstr "Basique"
 
 
-#: src/views/domain/DomainEdit.vue:207
+#: src/views/domain/DomainEdit.vue:207 src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Mode simple"
 msgstr "Mode simple"
 
 
@@ -171,9 +185,11 @@ msgstr ""
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Annuler"
 msgstr "Annuler"
 
 
@@ -190,12 +206,12 @@ msgstr "Le certificat est valide"
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "État du certificat"
 msgstr "État du certificat"
 
 
-#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:122
+#: src/routes/index.ts:118 src/views/certificate/Certificate.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "État du certificat"
 msgstr "État du certificat"
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Liste des certifications"
 msgstr "Liste des certifications"
@@ -241,10 +257,10 @@ msgstr "Effacer"
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "Désactivé avec succès"
 msgstr "Désactivé avec succès"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr "Commentaires"
 msgstr "Commentaires"
 
 
@@ -272,7 +288,7 @@ msgstr "Configurer SSL"
 msgid "Connected"
 msgid "Connected"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -290,6 +306,11 @@ msgstr "État du processeur"
 msgid "CPU:"
 msgid "CPU:"
 msgstr "CPU :"
 msgstr "CPU :"
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+#, fuzzy
+msgid "Create"
+msgstr "Créé le"
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Créer un autre"
 msgstr "Créer un autre"
@@ -315,11 +336,11 @@ msgid "Current Version"
 msgstr "Version actuelle"
 msgstr "Version actuelle"
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr "Custom"
 msgstr "Custom"
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Dashboard"
 msgstr "Dashboard"
 
 
@@ -329,8 +350,9 @@ msgstr "Base de données (Facultatif, par défaut : database)"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr "Supprimer"
 msgstr "Supprimer"
 
 
@@ -338,6 +360,11 @@ msgstr "Supprimer"
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr "Supprimer le site : %{site_name}"
 msgstr "Supprimer le site : %{site_name}"
 
 
+#: src/views/stream/StreamList.vue:81
+#, fuzzy
+msgid "Delete stream: %{stream_name}"
+msgstr "Supprimer le site : %{site_name}"
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #, fuzzy
 #, fuzzy
 msgid "Deleted successfully"
 msgid "Deleted successfully"
@@ -345,18 +372,23 @@ msgstr "Désactivé avec succès"
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 #, fuzzy
 #, fuzzy
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr "Sauvegarde réussie"
 msgstr "Sauvegarde réussie"
@@ -378,7 +410,7 @@ msgstr "Mode développement"
 msgid "Directive"
 msgid "Directive"
 msgstr "Directive"
 msgstr "Directive"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr "Directives"
 msgstr "Directives"
 
 
@@ -387,7 +419,7 @@ msgstr "Directives"
 msgid "Directory"
 msgid "Directory"
 msgstr "Directive"
 msgstr "Directive"
 
 
-#: src/views/domain/DomainList.vue:125
+#: src/views/domain/DomainList.vue:125 src/views/stream/StreamList.vue:146
 #, fuzzy
 #, fuzzy
 msgid "Disable"
 msgid "Disable"
 msgstr "Désactivé"
 msgstr "Désactivé"
@@ -397,12 +429,15 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 msgstr "La désactivation du renouvellement automatique a échoué pour %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:36
+#: src/views/domain/DomainList.vue:36 src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Désactivé"
 msgstr "Désactivé"
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr "Désactivé avec succès"
 msgstr "Désactivé avec succès"
 
 
@@ -410,7 +445,7 @@ msgstr "Désactivé avec succès"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "E/S disque"
 msgstr "E/S disque"
 
 
-#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:148 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "Identifiants DNS"
 msgstr "Identifiants DNS"
 
 
@@ -424,6 +459,7 @@ msgid "DNS01"
 msgstr "DNS01"
 msgstr "DNS01"
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 #, fuzzy
 #, fuzzy
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
@@ -438,19 +474,29 @@ msgstr "Voulez-vous désactiver le renouvellement automatique des certificats ?"
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr "Voulez-vous désactiver ce site ?"
 msgstr "Voulez-vous désactiver ce site ?"
 
 
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to disable this stream?"
+msgstr "Voulez-vous désactiver ce site ?"
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr "Voulez-vous activer ce site ?"
 msgstr "Voulez-vous activer ce site ?"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to enable this stream?"
+msgstr "Voulez-vous activer ce site ?"
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr "Voulez-vous activer TLS ?"
 msgstr "Voulez-vous activer TLS ?"
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Voulez-vous supprimer ce serveur ?"
 msgstr "Voulez-vous supprimer ce serveur ?"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "Voulez-vous supprimer ce serveur ?"
 msgstr "Voulez-vous supprimer ce serveur ?"
@@ -484,56 +530,71 @@ msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Dupliquer"
 msgstr "Dupliquer"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Dupliqué avec succès"
 msgstr "Dupliqué avec succès"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Dupliquer"
 msgstr "Dupliquer"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Dupliqué avec succès"
 msgstr "Dupliqué avec succès"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Dupliqué avec succès"
 msgstr "Dupliqué avec succès"
 
 
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:179 src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Modifier %{n}"
 msgstr "Modifier %{n}"
 
 
-#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:110 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Modifier la configuration"
 msgstr "Modifier la configuration"
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Modifier le site"
 msgstr "Modifier le site"
 
 
+#: src/routes/index.ts:93
+#, fuzzy
+msgid "Edit Stream"
+msgstr "Modifier le site"
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Activé"
 msgstr "Activé"
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
@@ -546,23 +607,29 @@ msgid "Enable failed"
 msgstr "Échec de l'activation"
 msgstr "Échec de l'activation"
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Activé avec succès"
 msgstr "Activé avec succès"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr "Activer TLS"
 msgstr "Activer TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Activé"
 msgstr "Activé"
 
 
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Activé avec succès"
 msgstr "Activé avec succès"
 
 
@@ -570,7 +637,7 @@ msgstr "Activé avec succès"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Crypter le site Web avec Let's Encrypt"
 msgstr "Crypter le site Web avec Let's Encrypt"
 
 
-#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:186 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr ""
 msgstr ""
 
 
@@ -583,7 +650,7 @@ msgstr "Commentaires"
 msgid "Error"
 msgid "Error"
 msgstr "Erreur"
 msgstr "Erreur"
 
 
-#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:173 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "Journaux d'erreurs"
 msgstr "Journaux d'erreurs"
 
 
@@ -607,11 +674,15 @@ msgstr "Exporter"
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr "Impossible de désactiver %{msg}"
 msgstr "Impossible de désactiver %{msg}"
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr "Impossible d'activer %{msg}"
 msgstr "Impossible d'activer %{msg}"
 
 
@@ -619,7 +690,7 @@ msgstr "Impossible d'activer %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "Échec de l'obtention des informations sur le certificat"
 msgstr "Échec de l'obtention des informations sur le certificat"
 
 
-#: src/views/domain/DomainEdit.vue:138
+#: src/views/domain/DomainEdit.vue:138 src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 "Échec de l'enregistrement, une ou plusieurs erreurs de syntaxe ont été "
 "Échec de l'enregistrement, une ou plusieurs erreurs de syntaxe ont été "
@@ -699,7 +770,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-3.5-Turbo"
 msgstr "GPT-3.5-Turbo"
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr "Menu principal"
 msgstr "Menu principal"
 
 
@@ -724,7 +795,7 @@ msgstr "HTTP01"
 msgid "Import"
 msgid "Import"
 msgstr "Exporter"
 msgstr "Exporter"
 
 
-#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:140 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "État du certificat"
 msgstr "État du certificat"
@@ -741,7 +812,7 @@ msgstr "Erreur du programme de mise à niveau initial du core"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Initialisation du programme de mise à niveau du core"
 msgstr "Initialisation du programme de mise à niveau du core"
 
 
-#: src/routes/index.ts:220 src/views/other/Install.vue:139
+#: src/routes/index.ts:237 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Installer"
 msgstr "Installer"
 
 
@@ -824,7 +895,7 @@ msgstr "Localisations"
 msgid "Log"
 msgid "Log"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
-#: src/routes/index.ts:226 src/views/other/Login.vue:147
+#: src/routes/index.ts:243 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Connexion"
 msgstr "Connexion"
 
 
@@ -845,15 +916,20 @@ msgstr ""
 "Assurez vous d'avoir configuré un reverse proxy pour le répertoire .well-"
 "Assurez vous d'avoir configuré un reverse proxy pour le répertoire .well-"
 "known vers HTTPChallengePort avant d'obtenir le certificat."
 "known vers HTTPChallengePort avant d'obtenir le certificat."
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Gérer les configurations"
 msgstr "Gérer les configurations"
 
 
-#: src/routes/index.ts:59 src/views/domain/DomainList.vue:105
+#: src/routes/index.ts:60 src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Gérer les sites"
 msgstr "Gérer les sites"
 
 
-#: src/routes/index.ts:185 src/views/user/User.vue:53
+#: src/routes/index.ts:85 src/views/stream/StreamList.vue:122
+#, fuzzy
+msgid "Manage Streams"
+msgstr "Gérer les sites"
+
+#: src/routes/index.ts:202 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Gérer les utilisateurs"
 msgstr "Gérer les utilisateurs"
 
 
@@ -876,7 +952,7 @@ msgstr "Mémoire et stockage"
 msgid "Modify"
 msgid "Modify"
 msgstr "Modifier"
 msgstr "Modifier"
 
 
-#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:132 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "État du certificat"
 msgstr "État du certificat"
@@ -896,8 +972,11 @@ msgstr "Directive multiligne"
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16 src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr "Nom"
 msgstr "Nom"
 
 
@@ -936,7 +1015,7 @@ msgstr "Journal Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Chemin du journal d'accès Nginx"
 msgstr "Chemin du journal d'accès Nginx"
 
 
-#: src/views/domain/DomainEdit.vue:222
+#: src/views/domain/DomainEdit.vue:222 src/views/stream/StreamEdit.vue:213
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Erreur d'analyse de configuration Nginx"
 msgstr "Erreur d'analyse de configuration Nginx"
 
 
@@ -948,7 +1027,7 @@ msgstr "Contrôle Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Chemin du journal des erreurs Nginx"
 msgstr "Chemin du journal des erreurs Nginx"
 
 
-#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:163 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Journal Nginx"
 msgstr "Journal Nginx"
 
 
@@ -964,9 +1043,10 @@ msgstr "Nginx a redémarré avec succès"
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr "Non"
 msgstr "Non"
 
 
@@ -979,7 +1059,7 @@ msgstr "Secret Jwt"
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:232 src/routes/index.ts:234
+#: src/routes/index.ts:249 src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Introuvable"
 msgstr "Introuvable"
 
 
@@ -997,7 +1077,7 @@ msgstr "Note"
 msgid "Notification"
 msgid "Notification"
 msgstr "Certification"
 msgstr "Certification"
 
 
-#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:194
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Certification"
 msgstr "Certification"
@@ -1026,10 +1106,13 @@ msgstr ""
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr "OK"
 msgstr "OK"
 
 
@@ -1057,10 +1140,12 @@ msgid "OS:"
 msgstr "OS :"
 msgstr "OS :"
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1108,6 +1193,7 @@ msgstr ""
 "fournisseur DNS."
 "fournisseur DNS."
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid ""
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "Please input name, this will be used as the filename of the new "
 "configuration!"
 "configuration!"
@@ -1133,6 +1219,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr ""
 msgstr ""
 
 
@@ -1140,7 +1227,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:210 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "Préférence"
 msgstr "Préférence"
 
 
@@ -1211,15 +1298,11 @@ msgstr "Rechargement de nginx"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Enregistré avec succès"
 msgstr "Enregistré avec succès"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Rename"
 msgid "Rename"
 msgstr "Nom d'utilisateur"
 msgstr "Nom d'utilisateur"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr ""
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
@@ -1269,8 +1352,8 @@ msgstr "En cours d'éxécution"
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
-#: src/views/preference/Preference.vue:113
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
+#: src/views/preference/Preference.vue:113 src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr "Enregistrer"
 msgstr "Enregistrer"
 
 
@@ -1279,7 +1362,7 @@ msgid "Save Directive"
 msgstr "Enregistrer la directive"
 msgstr "Enregistrer la directive"
 
 
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Enregistrer l'erreur %{msg}"
 msgstr "Enregistrer l'erreur %{msg}"
 
 
@@ -1295,7 +1378,8 @@ msgstr "Sauvegarde Réussie"
 
 
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Enregistré avec succès"
 msgstr "Enregistré avec succès"
 
 
@@ -1316,6 +1400,7 @@ msgstr "Envoyer"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116 src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr "Erreur du serveur"
 msgstr "Erreur du serveur"
@@ -1351,11 +1436,11 @@ msgstr "Utilisation du fournisseur de challenge HTTP01"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Directive unique"
 msgstr "Directive unique"
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Journaux du site"
 msgstr "Journaux du site"
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Liste des sites"
 msgstr "Liste des sites"
 
 
@@ -1390,7 +1475,7 @@ msgid "Stable"
 msgstr "Tableau"
 msgstr "Tableau"
 
 
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
-#: src/views/environment/Environment.vue:78
+#: src/views/environment/Environment.vue:78 src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr "Statut"
 msgstr "Statut"
 
 
@@ -1424,7 +1509,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr "Système"
 msgstr "Système"
 
 
@@ -1433,10 +1518,11 @@ msgid "Table"
 msgstr "Tableau"
 msgstr "Tableau"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:155 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Terminal"
 msgstr "Terminal"
 
 
@@ -1490,7 +1576,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
 "add a location which can proxy the request from authority to backend, and we "
 "add a location which can proxy the request from authority to backend, and we "
@@ -1511,7 +1597,8 @@ msgstr "Type"
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
-#: src/views/user/User.vue:40
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44 src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Mis à jour le"
 msgstr "Mis à jour le"
 
 
@@ -1519,7 +1606,7 @@ msgstr "Mis à jour le"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Mis à jour avec succés"
 msgstr "Mis à jour avec succés"
 
 
-#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:229 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Mettre à niveau"
 msgstr "Mettre à niveau"
@@ -1532,6 +1619,10 @@ msgstr "Mise à niveau réussie"
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr "Mise à jour de Nginx UI, veuillez patienter..."
 msgstr "Mise à jour de Nginx UI, veuillez patienter..."
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr "Disponibilité :"
 msgstr "Disponibilité :"
@@ -1599,7 +1690,7 @@ msgstr "Écriture de la clé privée du certificat sur le disque"
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr "Écriture du certificat sur le disque"
 msgstr "Écriture du certificat sur le disque"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr "Oui"
 msgstr "Oui"

+ 159 - 61
app/src/language/messages.pot

@@ -2,11 +2,11 @@ msgid ""
 msgstr ""
 msgstr ""
 "Content-Type: text/plain; charset=UTF-8\n"
 "Content-Type: text/plain; charset=UTF-8\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:152
+#: src/routes/index.ts:169
 #: src/views/domain/ngx_conf/LogEntry.vue:78
 #: src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr ""
 msgstr ""
@@ -17,6 +17,7 @@ msgstr ""
 #: src/views/domain/DomainList.vue:50
 #: src/views/domain/DomainList.vue:50
 #: src/views/environment/Environment.vue:105
 #: src/views/environment/Environment.vue:105
 #: src/views/notification/Notification.vue:38
 #: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50
 #: src/views/user/User.vue:46
 #: src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr ""
 msgstr ""
@@ -24,8 +25,9 @@ msgstr ""
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr ""
 msgstr ""
 
 
@@ -38,16 +40,25 @@ msgstr ""
 msgid "Add Location"
 msgid "Add Location"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:71
+#: src/routes/index.ts:72
 #: src/views/domain/DomainAdd.vue:96
 #: src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr ""
 msgstr ""
 
 
+#: src/views/stream/StreamList.vue:184
+msgid "Add Stream"
+msgstr ""
+
+#: src/views/stream/StreamList.vue:114
+msgid "Added successfully"
+msgstr ""
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 msgid "Additional"
 msgid "Additional"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/DomainEdit.vue:204
 #: src/views/domain/DomainEdit.vue:204
+#: src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr ""
 msgstr ""
 
 
@@ -78,10 +89,11 @@ msgstr ""
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/views/domain/DomainList.vue:147
 #: src/views/domain/DomainList.vue:147
+#: src/views/stream/StreamList.vue:168
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr ""
 msgstr ""
 
 
@@ -123,6 +135,7 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:89
 #: src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261
 #: src/views/domain/DomainEdit.vue:261
 #: src/views/nginx_log/NginxLog.vue:170
 #: src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr ""
 msgstr ""
 
 
@@ -137,10 +150,12 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgid "Basic"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/DomainEdit.vue:207
 #: src/views/domain/DomainEdit.vue:207
+#: src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr ""
 msgstr ""
 
 
@@ -164,9 +179,11 @@ msgstr ""
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr ""
 msgstr ""
 
 
@@ -183,12 +200,12 @@ msgstr ""
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:101
+#: src/routes/index.ts:118
 #: src/views/certificate/Certificate.vue:122
 #: src/views/certificate/Certificate.vue:122
 msgid "Certificates"
 msgid "Certificates"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr ""
 msgstr ""
 
 
@@ -232,10 +249,10 @@ msgstr ""
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr ""
 msgstr ""
 
 
@@ -263,7 +280,7 @@ msgstr ""
 msgid "Connected"
 msgid "Connected"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -281,6 +298,10 @@ msgstr ""
 msgid "CPU:"
 msgid "CPU:"
 msgstr ""
 msgstr ""
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+msgid "Create"
+msgstr ""
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr ""
 msgstr ""
@@ -307,11 +328,11 @@ msgid "Current Version"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr ""
 msgstr ""
 
 
@@ -321,8 +342,9 @@ msgstr ""
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr ""
 msgstr ""
 
 
@@ -330,24 +352,33 @@ msgstr ""
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr ""
 msgstr ""
 
 
+#: src/views/stream/StreamList.vue:81
+msgid "Delete stream: %{stream_name}"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr ""
 msgstr ""
 
 
@@ -368,7 +399,7 @@ msgstr ""
 msgid "Directive"
 msgid "Directive"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr ""
 msgstr ""
 
 
@@ -377,6 +408,7 @@ msgid "Directory"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/DomainList.vue:125
 #: src/views/domain/DomainList.vue:125
+#: src/views/stream/StreamList.vue:146
 msgid "Disable"
 msgid "Disable"
 msgstr ""
 msgstr ""
 
 
@@ -387,11 +419,15 @@ msgstr ""
 #: src/views/domain/cert/ChangeCert.vue:48
 #: src/views/domain/cert/ChangeCert.vue:48
 #: src/views/domain/DomainEdit.vue:190
 #: src/views/domain/DomainEdit.vue:190
 #: src/views/domain/DomainList.vue:36
 #: src/views/domain/DomainList.vue:36
+#: src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr ""
 msgstr ""
 
 
@@ -399,7 +435,7 @@ msgstr ""
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:131
+#: src/routes/index.ts:148
 #: src/views/certificate/DNSCredential.vue:39
 #: src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr ""
 msgstr ""
@@ -414,6 +450,7 @@ msgid "DNS01"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgstr[0] ""
 msgstr[0] ""
@@ -427,19 +464,27 @@ msgstr ""
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr ""
 msgstr ""
 
 
+#: src/views/stream/components/RightSettings.vue:48
+msgid "Do you want to disable this stream?"
+msgstr ""
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+msgid "Do you want to enable this stream?"
+msgstr ""
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr ""
 msgstr ""
 
 
@@ -469,52 +514,67 @@ msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/DomainEdit.vue:179
 #: src/views/domain/DomainEdit.vue:179
+#: src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:93
+#: src/routes/index.ts:110
 #: src/views/config/ConfigEdit.vue:83
 #: src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr ""
 msgstr ""
 
 
+#: src/routes/index.ts:93
+msgid "Edit Stream"
+msgstr ""
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 msgid "Enable"
 msgid "Enable"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
@@ -527,10 +587,11 @@ msgid "Enable failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr ""
 msgstr ""
 
 
@@ -538,6 +599,9 @@ msgstr ""
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184
 #: src/views/domain/DomainEdit.vue:184
 #: src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175
+#: src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr ""
 msgstr ""
 
 
@@ -545,6 +609,9 @@ msgstr ""
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45
 #: src/views/domain/DomainAdd.vue:45
 #: src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr ""
 msgstr ""
 
 
@@ -552,7 +619,7 @@ msgstr ""
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:169
+#: src/routes/index.ts:186
 #: src/views/environment/Environment.vue:113
 #: src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr ""
 msgstr ""
@@ -566,7 +633,7 @@ msgstr ""
 msgid "Error"
 msgid "Error"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:156
+#: src/routes/index.ts:173
 #: src/views/domain/ngx_conf/LogEntry.vue:86
 #: src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr ""
 msgstr ""
@@ -590,11 +657,15 @@ msgstr ""
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr ""
 msgstr ""
 
 
@@ -603,6 +674,7 @@ msgid "Failed to get certificate information"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/DomainEdit.vue:138
 #: src/views/domain/DomainEdit.vue:138
+#: src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr ""
 msgstr ""
 
 
@@ -679,7 +751,7 @@ msgstr ""
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr ""
 msgstr ""
 
 
@@ -703,7 +775,7 @@ msgstr ""
 msgid "Import"
 msgid "Import"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:123
+#: src/routes/index.ts:140
 #: src/views/certificate/CertificateEditor.vue:84
 #: src/views/certificate/CertificateEditor.vue:84
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr ""
 msgstr ""
@@ -720,7 +792,7 @@ msgstr ""
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:220
+#: src/routes/index.ts:237
 #: src/views/other/Install.vue:139
 #: src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr ""
 msgstr ""
@@ -795,7 +867,7 @@ msgstr ""
 msgid "Log"
 msgid "Log"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:226
+#: src/routes/index.ts:243
 #: src/views/other/Login.vue:147
 #: src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr ""
 msgstr ""
@@ -813,16 +885,21 @@ msgstr ""
 msgid "Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort before obtaining the certificate."
 msgid "Make sure you have configured a reverse proxy for .well-known directory to HTTPChallengePort before obtaining the certificate."
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:59
+#: src/routes/index.ts:60
 #: src/views/domain/DomainList.vue:105
 #: src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:185
+#: src/routes/index.ts:85
+#: src/views/stream/StreamList.vue:122
+msgid "Manage Streams"
+msgstr ""
+
+#: src/routes/index.ts:202
 #: src/views/user/User.vue:53
 #: src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr ""
 msgstr ""
@@ -845,7 +922,7 @@ msgstr ""
 msgid "Modify"
 msgid "Modify"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:115
+#: src/routes/index.ts:132
 #: src/views/certificate/CertificateEditor.vue:84
 #: src/views/certificate/CertificateEditor.vue:84
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr ""
 msgstr ""
@@ -866,8 +943,12 @@ msgstr ""
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16
+#: src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr ""
 msgstr ""
 
 
@@ -906,6 +987,7 @@ msgid "Nginx Access Log Path"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/DomainEdit.vue:222
 #: src/views/domain/DomainEdit.vue:222
+#: src/views/stream/StreamEdit.vue:213
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr ""
 msgstr ""
 
 
@@ -917,7 +999,7 @@ msgstr ""
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:146
+#: src/routes/index.ts:163
 #: src/views/nginx_log/NginxLog.vue:145
 #: src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr ""
 msgstr ""
@@ -934,9 +1016,10 @@ msgstr ""
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr ""
 msgstr ""
 
 
@@ -948,8 +1031,8 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:232
-#: src/routes/index.ts:234
+#: src/routes/index.ts:249
+#: src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr ""
 msgstr ""
 
 
@@ -967,7 +1050,7 @@ msgid "Notification"
 msgstr ""
 msgstr ""
 
 
 #: src/components/Notification/Notification.vue:82
 #: src/components/Notification/Notification.vue:82
-#: src/routes/index.ts:177
+#: src/routes/index.ts:194
 msgid "Notifications"
 msgid "Notifications"
 msgstr ""
 msgstr ""
 
 
@@ -995,10 +1078,13 @@ msgstr ""
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -1026,10 +1112,12 @@ msgid "OS:"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1069,6 +1157,7 @@ msgid "Please first add credentials in Certification > DNS Credentials, and then
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid "Please input name, this will be used as the filename of the new configuration!"
 msgid "Please input name, this will be used as the filename of the new configuration!"
 msgstr ""
 msgstr ""
 
 
@@ -1091,6 +1180,7 @@ msgid "Please note that the unit of time configurations below are all in seconds
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr ""
 msgstr ""
 
 
@@ -1099,7 +1189,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:193
+#: src/routes/index.ts:210
 #: src/views/preference/Preference.vue:85
 #: src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr ""
 msgstr ""
@@ -1167,14 +1257,10 @@ msgstr ""
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 msgid "Rename"
 msgid "Rename"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr ""
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 msgid "Renew Certificate"
 msgid "Renew Certificate"
@@ -1221,8 +1307,9 @@ msgstr ""
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98
 #: src/views/config/ConfigEdit.vue:98
 #: src/views/domain/DomainEdit.vue:268
 #: src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
 #: src/views/preference/Preference.vue:113
 #: src/views/preference/Preference.vue:113
+#: src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr ""
 msgstr ""
 
 
@@ -1232,7 +1319,7 @@ msgstr ""
 
 
 #: src/views/config/ConfigEdit.vue:59
 #: src/views/config/ConfigEdit.vue:59
 #: src/views/domain/DomainAdd.vue:53
 #: src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr ""
 msgstr ""
 
 
@@ -1249,7 +1336,8 @@ msgstr ""
 #: src/views/config/ConfigEdit.vue:57
 #: src/views/config/ConfigEdit.vue:57
 #: src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr ""
 msgstr ""
 
 
@@ -1272,6 +1360,8 @@ msgstr ""
 #: src/views/domain/DomainList.vue:84
 #: src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72
 #: src/views/other/Install.vue:72
 #: src/views/preference/Preference.vue:60
 #: src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116
+#: src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr ""
 msgstr ""
@@ -1305,11 +1395,11 @@ msgstr ""
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr ""
 msgstr ""
 
 
@@ -1343,6 +1433,7 @@ msgstr ""
 #: src/views/certificate/Certificate.vue:81
 #: src/views/certificate/Certificate.vue:81
 #: src/views/domain/DomainList.vue:25
 #: src/views/domain/DomainList.vue:25
 #: src/views/environment/Environment.vue:78
 #: src/views/environment/Environment.vue:78
+#: src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr ""
 msgstr ""
 
 
@@ -1374,7 +1465,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr ""
 msgstr ""
 
 
@@ -1383,10 +1474,11 @@ msgid "Table"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:138
+#: src/routes/index.ts:155
 #: src/views/pty/Terminal.vue:95
 #: src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr ""
 msgstr ""
@@ -1427,7 +1519,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid "To make sure the certification auto-renewal can work normally, we need to add a location which can proxy the request from authority to backend, and we need to save this file and reload the Nginx. Are you sure you want to continue?"
 msgid "To make sure the certification auto-renewal can work normally, we need to add a location which can proxy the request from authority to backend, and we need to save this file and reload the Nginx. Are you sure you want to continue?"
 msgstr ""
 msgstr ""
 
 
@@ -1443,6 +1535,8 @@ msgstr ""
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44
 #: src/views/domain/DomainList.vue:44
 #: src/views/environment/Environment.vue:98
 #: src/views/environment/Environment.vue:98
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44
 #: src/views/user/User.vue:40
 #: src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr ""
 msgstr ""
@@ -1451,7 +1545,7 @@ msgstr ""
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:212
+#: src/routes/index.ts:229
 #: src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
@@ -1465,6 +1559,10 @@ msgstr ""
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr ""
 msgstr ""
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr ""
 msgstr ""
@@ -1526,7 +1624,7 @@ msgstr ""
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr ""
 msgstr ""

+ 162 - 72
app/src/language/ru_RU/app.po

@@ -9,26 +9,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr "О проекте"
 msgstr "О проекте"
 
 
-#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:169 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "Журнал доступа"
 msgstr "Журнал доступа"
 
 
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Действие"
 msgstr "Действие"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr "Добавить"
 msgstr "Добавить"
 
 
@@ -41,16 +43,26 @@ msgstr "Добавить директиву ниже"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Добавить Location"
 msgstr "Добавить Location"
 
 
-#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:96
+#: src/routes/index.ts:72 src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Добавть Сайт"
 msgstr "Добавть Сайт"
 
 
+#: src/views/stream/StreamList.vue:184
+#, fuzzy
+msgid "Add Stream"
+msgstr "Добавть Сайт"
+
+#: src/views/stream/StreamList.vue:114
+#, fuzzy
+msgid "Added successfully"
+msgstr "Обновлено успешно"
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 #, fuzzy
 #, fuzzy
 msgid "Additional"
 msgid "Additional"
 msgstr "Дополнительно"
 msgstr "Дополнительно"
 
 
-#: src/views/domain/DomainEdit.vue:204
+#: src/views/domain/DomainEdit.vue:204 src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Расширенный режим"
 msgstr "Расширенный режим"
 
 
@@ -82,12 +94,12 @@ msgid "Are you sure you want to clear the record of chat?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
-#: src/views/domain/DomainList.vue:147
+#: src/views/domain/DomainList.vue:147 src/views/stream/StreamList.vue:168
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 msgstr "Вы уверены, что хотите удалить эту директиву?"
 
 
@@ -128,6 +140,7 @@ msgstr "Автообновление включено для %{name}"
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr "Назад"
 msgstr "Назад"
 
 
@@ -143,11 +156,12 @@ msgstr "Основная информация"
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 #, fuzzy
 #, fuzzy
 msgid "Basic"
 msgid "Basic"
 msgstr "Простой режим"
 msgstr "Простой режим"
 
 
-#: src/views/domain/DomainEdit.vue:207
+#: src/views/domain/DomainEdit.vue:207 src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Простой режим"
 msgstr "Простой режим"
 
 
@@ -172,9 +186,11 @@ msgstr ""
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Отмена"
 msgstr "Отмена"
 
 
@@ -191,12 +207,12 @@ msgstr "Сертификат действителен"
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
 
 
-#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:122
+#: src/routes/index.ts:118 src/views/certificate/Certificate.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Список"
 msgstr "Список"
@@ -243,10 +259,10 @@ msgstr "Очистить"
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "Отключено успешно"
 msgstr "Отключено успешно"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr "Комментарии"
 msgstr "Комментарии"
 
 
@@ -275,7 +291,7 @@ msgstr "Настроить SSL"
 msgid "Connected"
 msgid "Connected"
 msgstr "Подключено"
 msgstr "Подключено"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -293,6 +309,11 @@ msgstr "Нагрузка CPU"
 msgid "CPU:"
 msgid "CPU:"
 msgstr "CPU:"
 msgstr "CPU:"
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+#, fuzzy
+msgid "Create"
+msgstr "Создан в"
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Создать еще"
 msgstr "Создать еще"
@@ -318,11 +339,11 @@ msgid "Current Version"
 msgstr "Текущяя версия"
 msgstr "Текущяя версия"
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr "Пользовательский"
 msgstr "Пользовательский"
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Доска"
 msgstr "Доска"
 
 
@@ -332,8 +353,9 @@ msgstr "База данных (Опционально, по умолчанию:
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr "Удалить"
 msgstr "Удалить"
 
 
@@ -341,6 +363,10 @@ msgstr "Удалить"
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr ""
 msgstr ""
 
 
+#: src/views/stream/StreamList.vue:81
+msgid "Delete stream: %{stream_name}"
+msgstr ""
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #, fuzzy
 #, fuzzy
 msgid "Deleted successfully"
 msgid "Deleted successfully"
@@ -348,18 +374,23 @@ msgstr "Отключено успешно"
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 #, fuzzy
 #, fuzzy
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
@@ -381,7 +412,7 @@ msgstr "Режим разработки"
 msgid "Directive"
 msgid "Directive"
 msgstr "Деректива"
 msgstr "Деректива"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr "Дерективы"
 msgstr "Дерективы"
 
 
@@ -390,7 +421,7 @@ msgstr "Дерективы"
 msgid "Directory"
 msgid "Directory"
 msgstr "Деректива"
 msgstr "Деректива"
 
 
-#: src/views/domain/DomainList.vue:125
+#: src/views/domain/DomainList.vue:125 src/views/stream/StreamList.vue:146
 #, fuzzy
 #, fuzzy
 msgid "Disable"
 msgid "Disable"
 msgstr "Отключить"
 msgstr "Отключить"
@@ -400,12 +431,15 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "Не удалось отключить автоматическое продление для %{name}"
 msgstr "Не удалось отключить автоматическое продление для %{name}"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:36
+#: src/views/domain/DomainList.vue:36 src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Отключено"
 msgstr "Отключено"
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr "Отключено успешно"
 msgstr "Отключено успешно"
 
 
@@ -413,7 +447,7 @@ msgstr "Отключено успешно"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "Нагрузка на Диск IO"
 msgstr "Нагрузка на Диск IO"
 
 
-#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:148 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr ""
 msgstr ""
 
 
@@ -427,6 +461,7 @@ msgid "DNS01"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 #, fuzzy
 #, fuzzy
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
@@ -442,22 +477,32 @@ msgstr "Вы хотите отключить автоматическое обн
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr "Вы хотите отключить этот сайт?"
 msgstr "Вы хотите отключить этот сайт?"
 
 
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to disable this stream?"
+msgstr "Вы хотите отключить этот сайт?"
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 #, fuzzy
 #, fuzzy
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr "Вы хотите включить этот сайт?"
 msgstr "Вы хотите включить этот сайт?"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to enable this stream?"
+msgstr "Вы хотите включить этот сайт?"
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 #, fuzzy
 #, fuzzy
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr "Включить TLS?"
 msgstr "Включить TLS?"
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Вы хотите удалить этот сервер?"
 msgstr "Вы хотите удалить этот сервер?"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "Вы хотите удалить этот сервер?"
 msgstr "Вы хотите удалить этот сервер?"
@@ -490,56 +535,71 @@ msgstr "Включен пробный режим"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Дублировать"
 msgstr "Дублировать"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Продублированно %{conf_name} в %{node_name}"
 msgstr "Продублированно %{conf_name} в %{node_name}"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Дублировать не удалось"
 msgstr "Дублировать не удалось"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Продублированно"
 msgstr "Продублированно"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Saved successfully"
 msgstr "Saved successfully"
 
 
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:179 src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Редактировать %{n}"
 msgstr "Редактировать %{n}"
 
 
-#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:110 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Редактировать Конфигурацию"
 msgstr "Редактировать Конфигурацию"
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Редактировать Сайт"
 msgstr "Редактировать Сайт"
 
 
+#: src/routes/index.ts:93
+#, fuzzy
+msgid "Edit Stream"
+msgstr "Редактировать Сайт"
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Включить"
 msgstr "Включить"
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "Включение %{conf_name} in %{node_name} нипалучилася"
 msgstr "Включение %{conf_name} in %{node_name} нипалучилася"
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Включение %{conf_name} in %{node_name} успешно"
 msgstr "Включение %{conf_name} in %{node_name} успешно"
 
 
@@ -552,23 +612,29 @@ msgid "Enable failed"
 msgstr "Включить не удалось"
 msgstr "Включить не удалось"
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Активировано успешно"
 msgstr "Активировано успешно"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr "Включить TLS"
 msgstr "Включить TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Включено"
 msgstr "Включено"
 
 
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Активировано успешно"
 msgstr "Активировано успешно"
 
 
@@ -576,7 +642,7 @@ msgstr "Активировано успешно"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Использовать для сайта Let's Encrypt"
 msgstr "Использовать для сайта Let's Encrypt"
 
 
-#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:186 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "Окружение"
 msgstr "Окружение"
 
 
@@ -589,7 +655,7 @@ msgstr "Комментарии"
 msgid "Error"
 msgid "Error"
 msgstr "Ошибка"
 msgstr "Ошибка"
 
 
-#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:173 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "Ошибка логирования"
 msgstr "Ошибка логирования"
 
 
@@ -613,11 +679,15 @@ msgstr "Экспорт"
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr "Не удалось отключить %{msg}"
 msgstr "Не удалось отключить %{msg}"
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr "Не удалось включить %{msg}"
 msgstr "Не удалось включить %{msg}"
 
 
@@ -625,7 +695,7 @@ msgstr "Не удалось включить %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "Не удалось получить информацию о сертификате"
 msgstr "Не удалось получить информацию о сертификате"
 
 
-#: src/views/domain/DomainEdit.vue:138
+#: src/views/domain/DomainEdit.vue:138 src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "Не удалось сохранить, обнаружены синтаксические ошибки в конфигурации."
 msgstr "Не удалось сохранить, обнаружены синтаксические ошибки в конфигурации."
 
 
@@ -705,7 +775,7 @@ msgstr ""
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr "Главная"
 msgstr "Главная"
 
 
@@ -730,7 +800,7 @@ msgstr ""
 msgid "Import"
 msgid "Import"
 msgstr "Экспорт"
 msgstr "Экспорт"
 
 
-#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:140 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
@@ -747,7 +817,7 @@ msgstr "Ошибка первоначального обновления ядр
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Инициализация программы обновления ядра"
 msgstr "Инициализация программы обновления ядра"
 
 
-#: src/routes/index.ts:220 src/views/other/Install.vue:139
+#: src/routes/index.ts:237 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Установить"
 msgstr "Установить"
 
 
@@ -830,7 +900,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Логин"
 msgstr "Логин"
 
 
-#: src/routes/index.ts:226 src/views/other/Login.vue:147
+#: src/routes/index.ts:243 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Логин"
 msgstr "Логин"
 
 
@@ -851,15 +921,20 @@ msgstr ""
 "Убедитесь, что вы настроили обратный прокси-сервер для каталога .well-known "
 "Убедитесь, что вы настроили обратный прокси-сервер для каталога .well-known "
 "на HTTPChallengePort перед получением сертификата»."
 "на HTTPChallengePort перед получением сертификата»."
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Конфигурации"
 msgstr "Конфигурации"
 
 
-#: src/routes/index.ts:59 src/views/domain/DomainList.vue:105
+#: src/routes/index.ts:60 src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Сайты"
 msgstr "Сайты"
 
 
-#: src/routes/index.ts:185 src/views/user/User.vue:53
+#: src/routes/index.ts:85 src/views/stream/StreamList.vue:122
+#, fuzzy
+msgid "Manage Streams"
+msgstr "Сайты"
+
+#: src/routes/index.ts:202 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Пользователи"
 msgstr "Пользователи"
 
 
@@ -883,7 +958,7 @@ msgstr "Память и хранилище"
 msgid "Modify"
 msgid "Modify"
 msgstr "Изменить"
 msgstr "Изменить"
 
 
-#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:132 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Статус сертификата"
 msgstr "Статус сертификата"
@@ -904,8 +979,11 @@ msgstr "Одиночная директива"
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16 src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr "Имя"
 msgstr "Имя"
 
 
@@ -944,7 +1022,7 @@ msgstr "Журнал"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Путь для Nginx Access Log"
 msgstr "Путь для Nginx Access Log"
 
 
-#: src/views/domain/DomainEdit.vue:222
+#: src/views/domain/DomainEdit.vue:222 src/views/stream/StreamEdit.vue:213
 #, fuzzy
 #, fuzzy
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Ошибка синтаксического анализа конфигурации Nginx"
 msgstr "Ошибка синтаксического анализа конфигурации Nginx"
@@ -957,7 +1035,7 @@ msgstr "Управление Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Путь для Nginx Error Log"
 msgstr "Путь для Nginx Error Log"
 
 
-#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:163 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Журнал"
 msgstr "Журнал"
 
 
@@ -975,9 +1053,10 @@ msgstr "Nginx успешно перезапущен"
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr "Нет"
 msgstr "Нет"
 
 
@@ -989,7 +1068,7 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:232 src/routes/index.ts:234
+#: src/routes/index.ts:249 src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Не найден"
 msgstr "Не найден"
 
 
@@ -1007,7 +1086,7 @@ msgstr "Заметка"
 msgid "Notification"
 msgid "Notification"
 msgstr "Сертификат"
 msgstr "Сертификат"
 
 
-#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:194
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Сертификат"
 msgstr "Сертификат"
@@ -1037,10 +1116,13 @@ msgstr ""
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -1069,10 +1151,12 @@ msgid "OS:"
 msgstr "OS:"
 msgstr "OS:"
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr ""
 msgstr ""
 
 
@@ -1115,6 +1199,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid ""
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "Please input name, this will be used as the filename of the new "
 "configuration!"
 "configuration!"
@@ -1140,6 +1225,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr ""
 msgstr ""
 
 
@@ -1147,7 +1233,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:210 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "Настройки"
 msgstr "Настройки"
 
 
@@ -1217,15 +1303,11 @@ msgstr "Перезагружается nginx"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Успешно сохранено"
 msgstr "Успешно сохранено"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Rename"
 msgid "Rename"
 msgstr "Имя пользователя"
 msgstr "Имя пользователя"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr ""
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
@@ -1276,8 +1358,8 @@ msgstr "Выполняется"
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
-#: src/views/preference/Preference.vue:113
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
+#: src/views/preference/Preference.vue:113 src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr "Сохранить"
 msgstr "Сохранить"
 
 
@@ -1286,7 +1368,7 @@ msgid "Save Directive"
 msgstr "Сохранить директиву"
 msgstr "Сохранить директиву"
 
 
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Ошибка сохранения %{msg}"
 msgstr "Ошибка сохранения %{msg}"
 
 
@@ -1304,7 +1386,8 @@ msgstr "Успешно сохранено"
 
 
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Успешно сохранено"
 msgstr "Успешно сохранено"
 
 
@@ -1325,6 +1408,7 @@ msgstr "Отправлено"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116 src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr "Ошибка сервера"
 msgstr "Ошибка сервера"
@@ -1358,12 +1442,12 @@ msgstr ""
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Одиночная Директива"
 msgstr "Одиночная Директива"
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 #, fuzzy
 #, fuzzy
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Логи сайтов"
 msgstr "Логи сайтов"
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Список сайтов"
 msgstr "Список сайтов"
 
 
@@ -1400,7 +1484,7 @@ msgid "Stable"
 msgstr "Таблица"
 msgstr "Таблица"
 
 
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
-#: src/views/environment/Environment.vue:78
+#: src/views/environment/Environment.vue:78 src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr "Статус"
 msgstr "Статус"
 
 
@@ -1433,7 +1517,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr "Система"
 msgstr "Система"
 
 
@@ -1443,10 +1527,11 @@ msgid "Table"
 msgstr "Таблица"
 msgstr "Таблица"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:155 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Терминал"
 msgstr "Терминал"
 
 
@@ -1497,7 +1582,7 @@ msgstr ""
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
 "add a location which can proxy the request from authority to backend, and we "
 "add a location which can proxy the request from authority to backend, and we "
@@ -1514,7 +1599,8 @@ msgstr "Тип"
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
-#: src/views/user/User.vue:40
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44 src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Обновлено в"
 msgstr "Обновлено в"
 
 
@@ -1523,7 +1609,7 @@ msgstr "Обновлено в"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Обновлено успешно"
 msgstr "Обновлено успешно"
 
 
-#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:229 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Обновление"
 msgstr "Обновление"
@@ -1537,6 +1623,10 @@ msgstr "Обновление успешно выполнено"
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr "Обновление Nginx UI, подождите..."
 msgstr "Обновление Nginx UI, подождите..."
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr "Аптайм:"
 msgstr "Аптайм:"
@@ -1602,7 +1692,7 @@ msgstr "Запись закрытого ключа сертификата на 
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr "Запись сертификата на диск"
 msgstr "Запись сертификата на диск"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr "Да"
 msgstr "Да"

+ 163 - 72
app/src/language/vi_VN/app.po

@@ -9,26 +9,28 @@ msgstr ""
 "Content-Transfer-Encoding: 8bit\n"
 "Content-Transfer-Encoding: 8bit\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 "Plural-Forms: nplurals=2; plural=(n != 1);\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr "Tác giả"
 msgstr "Tác giả"
 
 
-#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:169 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "Log truy cập"
 msgstr "Log truy cập"
 
 
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "Hành động"
 msgstr "Hành động"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr "Thêm"
 msgstr "Thêm"
 
 
@@ -41,16 +43,26 @@ msgstr "Thêm Directive"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "Thêm Location"
 msgstr "Thêm Location"
 
 
-#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:96
+#: src/routes/index.ts:72 src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr "Thêm Website"
 msgstr "Thêm Website"
 
 
+#: src/views/stream/StreamList.vue:184
+#, fuzzy
+msgid "Add Stream"
+msgstr "Thêm Website"
+
+#: src/views/stream/StreamList.vue:114
+#, fuzzy
+msgid "Added successfully"
+msgstr "Cập nhật thành công"
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 #, fuzzy
 #, fuzzy
 msgid "Additional"
 msgid "Additional"
 msgstr "Tùy chọn bổ sung"
 msgstr "Tùy chọn bổ sung"
 
 
-#: src/views/domain/DomainEdit.vue:204
+#: src/views/domain/DomainEdit.vue:204 src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "Nâng cao"
 msgstr "Nâng cao"
 
 
@@ -82,12 +94,12 @@ msgid "Are you sure you want to clear the record of chat?"
 msgstr "Bạn có chắc chắn muốn xóa lịch sử trò chuyện không ?"
 msgstr "Bạn có chắc chắn muốn xóa lịch sử trò chuyện không ?"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
-#: src/views/domain/DomainList.vue:147
+#: src/views/domain/DomainList.vue:147 src/views/stream/StreamList.vue:168
 #, fuzzy
 #, fuzzy
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "Bạn chắc chắn muốn xóa nó "
 msgstr "Bạn chắc chắn muốn xóa nó "
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr "Bạn chắc chắn muốn xoá directive này ?"
 msgstr "Bạn chắc chắn muốn xoá directive này ?"
 
 
@@ -128,6 +140,7 @@ msgstr "Đã bật tự động gia hạn SSL cho %{name}"
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr "Quay lại"
 msgstr "Quay lại"
 
 
@@ -143,11 +156,12 @@ msgstr "Thông tin"
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 #, fuzzy
 #, fuzzy
 msgid "Basic"
 msgid "Basic"
 msgstr "Cơ bản"
 msgstr "Cơ bản"
 
 
-#: src/views/domain/DomainEdit.vue:207
+#: src/views/domain/DomainEdit.vue:207 src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "Cơ bản"
 msgstr "Cơ bản"
 
 
@@ -172,9 +186,11 @@ msgstr ""
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr "Huỷ"
 msgstr "Huỷ"
 
 
@@ -191,12 +207,12 @@ msgstr "Chứng chỉ SSL hợp lệ"
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "Trạng thái chứng chỉ"
 msgstr "Trạng thái chứng chỉ"
 
 
-#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:122
+#: src/routes/index.ts:118 src/views/certificate/Certificate.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "Chứng chỉ"
 msgstr "Chứng chỉ"
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "Danh sách chứng chỉ"
 msgstr "Danh sách chứng chỉ"
@@ -243,10 +259,10 @@ msgstr "Xoá"
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "Đã xóa thành công"
 msgstr "Đã xóa thành công"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr "Bình luận"
 msgstr "Bình luận"
 
 
@@ -275,7 +291,7 @@ msgstr "Cấu hình SSL"
 msgid "Connected"
 msgid "Connected"
 msgstr "Đã kết nối"
 msgstr "Đã kết nối"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -293,6 +309,11 @@ msgstr "Trạng thái CPU"
 msgid "CPU:"
 msgid "CPU:"
 msgstr "CPU:"
 msgstr "CPU:"
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+#, fuzzy
+msgid "Create"
+msgstr "Ngày tạo"
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr "Tạo thêm"
 msgstr "Tạo thêm"
@@ -318,11 +339,11 @@ msgid "Current Version"
 msgstr "Phiên bản hiện tại"
 msgstr "Phiên bản hiện tại"
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr "Tuỳ chỉnh"
 msgstr "Tuỳ chỉnh"
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "Bảng điều khiển"
 msgstr "Bảng điều khiển"
 
 
@@ -332,8 +353,9 @@ msgstr "Tên cơ sở dữ liệu (Tuỳ chọn, Mặc định là: database)"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr "Xoá"
 msgstr "Xoá"
 
 
@@ -341,6 +363,11 @@ msgstr "Xoá"
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr "Xoá trang web: %{site_name}"
 msgstr "Xoá trang web: %{site_name}"
 
 
+#: src/views/stream/StreamList.vue:81
+#, fuzzy
+msgid "Delete stream: %{stream_name}"
+msgstr "Xoá trang web: %{site_name}"
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #, fuzzy
 #, fuzzy
 msgid "Deleted successfully"
 msgid "Deleted successfully"
@@ -348,18 +375,23 @@ msgstr "Đã xoá thành công"
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr "Triển khai"
 msgstr "Triển khai"
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "Triển khai %{conf_name} tới %{node_name} thất bại"
 msgstr "Triển khai %{conf_name} tới %{node_name} thất bại"
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr "Triển khai %{conf_name} tới %{node_name} thành công"
 msgstr "Triển khai %{conf_name} tới %{node_name} thành công"
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 #, fuzzy
 #, fuzzy
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr "Triển khai thành công"
 msgstr "Triển khai thành công"
@@ -381,7 +413,7 @@ msgstr "Chế độ phát triển"
 msgid "Directive"
 msgid "Directive"
 msgstr "Directive"
 msgstr "Directive"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr "Directives"
 msgstr "Directives"
 
 
@@ -390,7 +422,7 @@ msgstr "Directives"
 msgid "Directory"
 msgid "Directory"
 msgstr "Thư mục"
 msgstr "Thư mục"
 
 
-#: src/views/domain/DomainList.vue:125
+#: src/views/domain/DomainList.vue:125 src/views/stream/StreamList.vue:146
 #, fuzzy
 #, fuzzy
 msgid "Disable"
 msgid "Disable"
 msgstr "Tắt"
 msgstr "Tắt"
@@ -400,12 +432,15 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "Tắt tự động gia hạn SSL cho %{name} thất bại"
 msgstr "Tắt tự động gia hạn SSL cho %{name} thất bại"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:36
+#: src/views/domain/DomainList.vue:36 src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr "Đã tắt"
 msgstr "Đã tắt"
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr "Đã tắt thành công"
 msgstr "Đã tắt thành công"
 
 
@@ -413,7 +448,7 @@ msgstr "Đã tắt thành công"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "Disk IO"
 msgstr "Disk IO"
 
 
-#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:148 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "Xác thực DNS"
 msgstr "Xác thực DNS"
 
 
@@ -427,6 +462,7 @@ msgid "DNS01"
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 #, fuzzy
 #, fuzzy
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
@@ -442,22 +478,32 @@ msgstr "Bạn muốn tắt tự động gia hạn chứng chỉ SSL ?"
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr "Bạn muốn tắt trang web này ?"
 msgstr "Bạn muốn tắt trang web này ?"
 
 
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to disable this stream?"
+msgstr "Bạn muốn tắt trang web này ?"
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 #, fuzzy
 #, fuzzy
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr "Bạn muốn bật trang web này ?"
 msgstr "Bạn muốn bật trang web này ?"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to enable this stream?"
+msgstr "Bạn muốn bật trang web này ?"
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 #, fuzzy
 #, fuzzy
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr "Bạn muốn bật TLS ?"
 msgstr "Bạn muốn bật TLS ?"
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "Bạn muốn xóa máy chủ này ?"
 msgstr "Bạn muốn xóa máy chủ này ?"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "Bạn muốn xóa máy chủ này ?"
 msgstr "Bạn muốn xóa máy chủ này ?"
@@ -490,56 +536,71 @@ msgstr "Đã bật chế độ Dry run"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "Nhân bản"
 msgstr "Nhân bản"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 #, fuzzy
 #, fuzzy
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 msgstr "Nhân bản %{conf_name} thành %{node_name} thành công"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 #, fuzzy
 #, fuzzy
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "Nhân bản thất bại"
 msgstr "Nhân bản thất bại"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "Nhân bản thành công"
 msgstr "Nhân bản thành công"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 #, fuzzy
 #, fuzzy
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "Đã sao chép thành công vào máy cục bộ"
 msgstr "Đã sao chép thành công vào máy cục bộ"
 
 
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:179 src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "Sửa %{n}"
 msgstr "Sửa %{n}"
 
 
-#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:110 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "Sửa cấu hình"
 msgstr "Sửa cấu hình"
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "Sửa trang web"
 msgstr "Sửa trang web"
 
 
+#: src/routes/index.ts:93
+#, fuzzy
+msgid "Edit Stream"
+msgstr "Sửa trang web"
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "Email (*)"
 msgstr "Email (*)"
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 #, fuzzy
 #, fuzzy
 msgid "Enable"
 msgid "Enable"
 msgstr "Đã bật"
 msgstr "Đã bật"
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "Không thể bật %{conf_name} trên %{node_name}"
 msgstr "Không thể bật %{conf_name} trên %{node_name}"
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "Đã bật %{conf_name} trên %{node_name}"
 msgstr "Đã bật %{conf_name} trên %{node_name}"
 
 
@@ -552,23 +613,29 @@ msgid "Enable failed"
 msgstr "Bật không thành công"
 msgstr "Bật không thành công"
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 #, fuzzy
 #, fuzzy
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "Đã bật"
 msgstr "Đã bật"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr "Bật TLS"
 msgstr "Bật TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr "Đã bật"
 msgstr "Đã bật"
 
 
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "Đã bật"
 msgstr "Đã bật"
 
 
@@ -576,7 +643,7 @@ msgstr "Đã bật"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "Bảo mật trang web với Let's Encrypt"
 msgstr "Bảo mật trang web với Let's Encrypt"
 
 
-#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:186 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "Environment"
 msgstr "Environment"
 
 
@@ -589,7 +656,7 @@ msgstr "Environments"
 msgid "Error"
 msgid "Error"
 msgstr "Lỗi"
 msgstr "Lỗi"
 
 
-#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:173 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "Log lỗi"
 msgstr "Log lỗi"
 
 
@@ -613,11 +680,15 @@ msgstr "Xuất"
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr "Không thể tắt %{msg}"
 msgstr "Không thể tắt %{msg}"
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr "Không thể bật %{msg}"
 msgstr "Không thể bật %{msg}"
 
 
@@ -625,7 +696,7 @@ msgstr "Không thể bật %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "Không thể truy xuất thông tin chứng chỉ"
 msgstr "Không thể truy xuất thông tin chứng chỉ"
 
 
-#: src/views/domain/DomainEdit.vue:138
+#: src/views/domain/DomainEdit.vue:138 src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "Không lưu được, đã phát hiện thấy (các) lỗi cú pháp trong cấu hình."
 msgstr "Không lưu được, đã phát hiện thấy (các) lỗi cú pháp trong cấu hình."
 
 
@@ -705,7 +776,7 @@ msgstr ""
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr "Trang chủ"
 msgstr "Trang chủ"
 
 
@@ -730,7 +801,7 @@ msgstr ""
 msgid "Import"
 msgid "Import"
 msgstr "Xuất"
 msgstr "Xuất"
 
 
-#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:140 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "Chứng chỉ"
 msgstr "Chứng chỉ"
@@ -747,7 +818,7 @@ msgstr "Không thể khởi tạo trình nâng cấp"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "Đang khởi tạo trình nâng cấp"
 msgstr "Đang khởi tạo trình nâng cấp"
 
 
-#: src/routes/index.ts:220 src/views/other/Install.vue:139
+#: src/routes/index.ts:237 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "Cài đặt"
 msgstr "Cài đặt"
 
 
@@ -830,7 +901,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "Log"
 msgstr "Log"
 
 
-#: src/routes/index.ts:226 src/views/other/Login.vue:147
+#: src/routes/index.ts:243 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "Đăng nhập"
 msgstr "Đăng nhập"
 
 
@@ -851,15 +922,20 @@ msgstr ""
 "Đảm bảo rằng bạn đã định cấu hình proxy ngược (reverse proxy) thư mục .well-"
 "Đảm bảo rằng bạn đã định cấu hình proxy ngược (reverse proxy) thư mục .well-"
 "known tới HTTPChallengePort (default: 9180) trước khi ký chứng chỉ SSL."
 "known tới HTTPChallengePort (default: 9180) trước khi ký chứng chỉ SSL."
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "Quản lý cấu hình"
 msgstr "Quản lý cấu hình"
 
 
-#: src/routes/index.ts:59 src/views/domain/DomainList.vue:105
+#: src/routes/index.ts:60 src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "Quản lý Website"
 msgstr "Quản lý Website"
 
 
-#: src/routes/index.ts:185 src/views/user/User.vue:53
+#: src/routes/index.ts:85 src/views/stream/StreamList.vue:122
+#, fuzzy
+msgid "Manage Streams"
+msgstr "Quản lý Website"
+
+#: src/routes/index.ts:202 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "Người dùng"
 msgstr "Người dùng"
 
 
@@ -882,7 +958,7 @@ msgstr "Memory và Storage"
 msgid "Modify"
 msgid "Modify"
 msgstr "Sửa"
 msgstr "Sửa"
 
 
-#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:132 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "Sửa chứng chỉ"
 msgstr "Sửa chứng chỉ"
@@ -903,8 +979,11 @@ msgstr "Single Directive"
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16 src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr "Tên"
 msgstr "Tên"
 
 
@@ -942,7 +1021,7 @@ msgstr ""
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Vị trí lưu log truy cập (Access log) của Nginx"
 msgstr "Vị trí lưu log truy cập (Access log) của Nginx"
 
 
-#: src/views/domain/DomainEdit.vue:222
+#: src/views/domain/DomainEdit.vue:222 src/views/stream/StreamEdit.vue:213
 #, fuzzy
 #, fuzzy
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Lỗi phân tích cú pháp cấu hình Nginx"
 msgstr "Lỗi phân tích cú pháp cấu hình Nginx"
@@ -955,7 +1034,7 @@ msgstr ""
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Vị trí lưu log lỗi (Error log) của Nginx"
 msgstr "Vị trí lưu log lỗi (Error log) của Nginx"
 
 
-#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:163 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr ""
 msgstr ""
 
 
@@ -973,9 +1052,10 @@ msgstr "Restart Nginx thành công"
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr "Không"
 msgstr "Không"
 
 
@@ -987,7 +1067,7 @@ msgstr ""
 msgid "Not After"
 msgid "Not After"
 msgstr "Không phải sau khi"
 msgstr "Không phải sau khi"
 
 
-#: src/routes/index.ts:232 src/routes/index.ts:234
+#: src/routes/index.ts:249 src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr "Không tìm thấy"
 msgstr "Không tìm thấy"
 
 
@@ -1005,7 +1085,7 @@ msgstr "Ghi chú"
 msgid "Notification"
 msgid "Notification"
 msgstr "Thông báo"
 msgstr "Thông báo"
 
 
-#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:194
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "Thông báo"
 msgstr "Thông báo"
@@ -1035,10 +1115,13 @@ msgstr "Ngoại tuyến"
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr ""
 msgstr ""
 
 
@@ -1067,10 +1150,12 @@ msgid "OS:"
 msgstr "Hệ điều hành:"
 msgstr "Hệ điều hành:"
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "Ghi đè"
 msgstr "Ghi đè"
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "Ghi đè tập tin đã tồn tại"
 msgstr "Ghi đè tập tin đã tồn tại"
 
 
@@ -1116,6 +1201,7 @@ msgstr ""
 "thực DNS, sau đó chọn nhà cung cấp DNS"
 "thực DNS, sau đó chọn nhà cung cấp DNS"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid ""
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "Please input name, this will be used as the filename of the new "
 "configuration!"
 "configuration!"
@@ -1140,6 +1226,7 @@ msgid ""
 msgstr "Lưu ý đơn vị cấu hình thời gian bên dưới được tính bằng giây."
 msgstr "Lưu ý đơn vị cấu hình thời gian bên dưới được tính bằng giây."
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr ""
 msgstr ""
 
 
@@ -1147,7 +1234,7 @@ msgstr ""
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:210 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "Cài đặt"
 msgstr "Cài đặt"
 
 
@@ -1217,15 +1304,11 @@ msgstr "Tải lại nginx"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "Xoá thành công"
 msgstr "Xoá thành công"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Rename"
 msgid "Rename"
 msgstr "Username"
 msgstr "Username"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr ""
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
@@ -1276,8 +1359,8 @@ msgstr "Running"
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
-#: src/views/preference/Preference.vue:113
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
+#: src/views/preference/Preference.vue:113 src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr "Lưu"
 msgstr "Lưu"
 
 
@@ -1286,7 +1369,7 @@ msgid "Save Directive"
 msgstr "Lưu Directive"
 msgstr "Lưu Directive"
 
 
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "Đã xảy ra lỗi khi lưu %{msg}"
 msgstr "Đã xảy ra lỗi khi lưu %{msg}"
 
 
@@ -1304,7 +1387,8 @@ msgstr "Lưu thành công"
 
 
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "Lưu thành công"
 msgstr "Lưu thành công"
 
 
@@ -1325,6 +1409,7 @@ msgstr "Gửi"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116 src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr "Lỗi máy chủ"
 msgstr "Lỗi máy chủ"
@@ -1359,12 +1444,12 @@ msgstr "Sử dụng HTTP01 để xác thực SSL"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "Single Directive"
 msgstr "Single Directive"
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 #, fuzzy
 #, fuzzy
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "Logs"
 msgstr "Logs"
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr "Danh sách Website"
 msgstr "Danh sách Website"
 
 
@@ -1396,7 +1481,7 @@ msgid "Stable"
 msgstr "Ổn định"
 msgstr "Ổn định"
 
 
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
-#: src/views/environment/Environment.vue:78
+#: src/views/environment/Environment.vue:78 src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr "Trạng thái"
 msgstr "Trạng thái"
 
 
@@ -1429,7 +1514,7 @@ msgstr "Sử dụng Dark theme"
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr "Sử dụng Light theme"
 msgstr "Sử dụng Light theme"
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr "Thông tin"
 msgstr "Thông tin"
 
 
@@ -1439,10 +1524,11 @@ msgid "Table"
 msgstr "Mục lục"
 msgstr "Mục lục"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr "Mục tiêu"
 msgstr "Mục tiêu"
 
 
-#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:155 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "Terminal"
 msgstr "Terminal"
 
 
@@ -1491,7 +1577,7 @@ msgstr "Trường này không được để trống"
 msgid "Title"
 msgid "Title"
 msgstr "Tiêu đề"
 msgstr "Tiêu đề"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
 "add a location which can proxy the request from authority to backend, and we "
 "add a location which can proxy the request from authority to backend, and we "
@@ -1512,7 +1598,8 @@ msgstr "Loại"
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
-#: src/views/user/User.vue:40
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44 src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr "Ngày cập nhật"
 msgstr "Ngày cập nhật"
 
 
@@ -1521,7 +1608,7 @@ msgstr "Ngày cập nhật"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "Cập nhật thành công"
 msgstr "Cập nhật thành công"
 
 
-#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:229 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "Cập nhật"
 msgstr "Cập nhật"
@@ -1535,6 +1622,10 @@ msgstr "Cập nhật thành công"
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr "Đang cập nhật Nginx UI, vui lòng đợi..."
 msgstr "Đang cập nhật Nginx UI, vui lòng đợi..."
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr "Thời gian hoạt động:"
 msgstr "Thời gian hoạt động:"
@@ -1604,7 +1695,7 @@ msgstr "Ghi Private Key vào disk"
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr "Ghi chứng chỉ vào disk"
 msgstr "Ghi chứng chỉ vào disk"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr "Có"
 msgstr "Có"

BIN
app/src/language/zh_CN/app.mo


+ 158 - 75
app/src/language/zh_CN/app.po

@@ -13,26 +13,28 @@ msgstr ""
 "Generated-By: easygettext\n"
 "Generated-By: easygettext\n"
 "X-Generator: Poedit 3.4.1\n"
 "X-Generator: Poedit 3.4.1\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr "关于"
 msgstr "关于"
 
 
-#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:169 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "访问日志"
 msgstr "访问日志"
 
 
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "操作"
 msgstr "操作"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr "添加"
 msgstr "添加"
 
 
@@ -45,15 +47,23 @@ msgstr "在下面添加指令"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "添加 Location"
 msgstr "添加 Location"
 
 
-#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:96
+#: src/routes/index.ts:72 src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr "添加站点"
 msgstr "添加站点"
 
 
+#: src/views/stream/StreamList.vue:184
+msgid "Add Stream"
+msgstr "添加 Stream"
+
+#: src/views/stream/StreamList.vue:114
+msgid "Added successfully"
+msgstr "添加成功"
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 msgid "Additional"
 msgid "Additional"
 msgstr "额外选项"
 msgstr "额外选项"
 
 
-#: src/views/domain/DomainEdit.vue:204
+#: src/views/domain/DomainEdit.vue:204 src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "高级模式"
 msgstr "高级模式"
 
 
@@ -83,11 +93,11 @@ msgid "Are you sure you want to clear the record of chat?"
 msgstr "你确定你要清除聊天记录吗?"
 msgstr "你确定你要清除聊天记录吗?"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
-#: src/views/domain/DomainList.vue:147
+#: src/views/domain/DomainList.vue:147 src/views/stream/StreamList.vue:168
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "您确定要删除吗?"
 msgstr "您确定要删除吗?"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr "您确定要删除这条指令?"
 msgstr "您确定要删除这条指令?"
 
 
@@ -127,6 +137,7 @@ msgstr "成功启用 %{name} 自动续签"
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr "返回"
 msgstr "返回"
 
 
@@ -141,10 +152,11 @@ msgstr "基本信息"
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgid "Basic"
 msgstr "基本"
 msgstr "基本"
 
 
-#: src/views/domain/DomainEdit.vue:207
+#: src/views/domain/DomainEdit.vue:207 src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "基本模式"
 msgstr "基本模式"
 
 
@@ -168,9 +180,11 @@ msgstr "CADir"
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr "取消"
 msgstr "取消"
 
 
@@ -187,11 +201,11 @@ msgstr "此证书有效"
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "证书状态"
 msgstr "证书状态"
 
 
-#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:122
+#: src/routes/index.ts:118 src/views/certificate/Certificate.vue:122
 msgid "Certificates"
 msgid "Certificates"
 msgstr "证书"
 msgstr "证书"
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "证书列表"
 msgstr "证书列表"
 
 
@@ -235,10 +249,10 @@ msgstr "清空"
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "清除成功"
 msgstr "清除成功"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr "注释"
 msgstr "注释"
 
 
@@ -266,7 +280,7 @@ msgstr "配置 SSL"
 msgid "Connected"
 msgid "Connected"
 msgstr "已连接"
 msgstr "已连接"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -284,6 +298,10 @@ msgstr "CPU 状态"
 msgid "CPU:"
 msgid "CPU:"
 msgstr "CPU:"
 msgstr "CPU:"
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+msgid "Create"
+msgstr "创建"
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr "再创建一个"
 msgstr "再创建一个"
@@ -309,11 +327,11 @@ msgid "Current Version"
 msgstr "当前版本"
 msgstr "当前版本"
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr "自定义"
 msgstr "自定义"
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "仪表盘"
 msgstr "仪表盘"
 
 
@@ -323,8 +341,9 @@ msgstr "数据库 (可选,默认: database)"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr "删除"
 msgstr "删除"
 
 
@@ -332,24 +351,33 @@ msgstr "删除"
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr "删除站点: %{site_name}"
 msgstr "删除站点: %{site_name}"
 
 
+#: src/views/stream/StreamList.vue:81
+msgid "Delete stream: %{stream_name}"
+msgstr "删除 Stream: %{stream_name}"
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 msgid "Deleted successfully"
 msgid "Deleted successfully"
 msgstr "删除成功"
 msgstr "删除成功"
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr "部署"
 msgstr "部署"
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "部署%{conf_name}到%{node_name}失败"
 msgstr "部署%{conf_name}到%{node_name}失败"
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr "成功地将%{conf_name}部署到%{node_name}"
 msgstr "成功地将%{conf_name}部署到%{node_name}"
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr "部署成功"
 msgstr "部署成功"
 
 
@@ -370,7 +398,7 @@ msgstr "开发模式"
 msgid "Directive"
 msgid "Directive"
 msgstr "指令"
 msgstr "指令"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr "指令"
 msgstr "指令"
 
 
@@ -378,7 +406,7 @@ msgstr "指令"
 msgid "Directory"
 msgid "Directory"
 msgstr "目录"
 msgstr "目录"
 
 
-#: src/views/domain/DomainList.vue:125
+#: src/views/domain/DomainList.vue:125 src/views/stream/StreamList.vue:146
 msgid "Disable"
 msgid "Disable"
 msgstr "禁用"
 msgstr "禁用"
 
 
@@ -387,12 +415,15 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "关闭 %{name} 自动续签失败"
 msgstr "关闭 %{name} 自动续签失败"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:36
+#: src/views/domain/DomainList.vue:36 src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr "禁用"
 msgstr "禁用"
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr "禁用成功"
 msgstr "禁用成功"
 
 
@@ -400,7 +431,7 @@ msgstr "禁用成功"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "磁盘 IO"
 msgstr "磁盘 IO"
 
 
-#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:148 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "DNS 凭证"
 msgstr "DNS 凭证"
 
 
@@ -414,6 +445,7 @@ msgid "DNS01"
 msgstr "DNS01"
 msgstr "DNS01"
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgstr[0] "你想把这个文件部署到远程服务器上吗?"
 msgstr[0] "你想把这个文件部署到远程服务器上吗?"
@@ -426,19 +458,27 @@ msgstr "你想禁用自动更新证书吗?"
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr "你想停用这个网站吗?"
 msgstr "你想停用这个网站吗?"
 
 
+#: src/views/stream/components/RightSettings.vue:48
+msgid "Do you want to disable this stream?"
+msgstr "你想停用这个 Stream 吗?"
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr "你想启用这个网站吗?"
 msgstr "你想启用这个网站吗?"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+msgid "Do you want to enable this stream?"
+msgstr "你想启用这个 Stream 吗?"
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr "你想启用TLS吗?"
 msgstr "你想启用TLS吗?"
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "你想删除这个服务器吗?"
 msgstr "你想删除这个服务器吗?"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "你想删除这个 Upstream 吗?"
 msgstr "你想删除这个 Upstream 吗?"
 
 
@@ -468,51 +508,65 @@ msgstr "试运行模式已启动"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "复制"
 msgstr "复制"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "成功地将%{conf_name}复制到%{node_name}"
 msgstr "成功地将%{conf_name}复制到%{node_name}"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "复制失败"
 msgstr "复制失败"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "复制成功"
 msgstr "复制成功"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "成功复制到本地"
 msgstr "成功复制到本地"
 
 
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:179 src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "编辑 %{n}"
 msgstr "编辑 %{n}"
 
 
-#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:110 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "编辑配置"
 msgstr "编辑配置"
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "编辑站点"
 msgstr "编辑站点"
 
 
+#: src/routes/index.ts:93
+msgid "Edit Stream"
+msgstr "编辑 Stream"
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "邮箱 (*)"
 msgstr "邮箱 (*)"
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 msgid "Enable"
 msgid "Enable"
 msgstr "启用"
 msgstr "启用"
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "在%{node_name}中启用%{conf_name}失败"
 msgstr "在%{node_name}中启用%{conf_name}失败"
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "成功启用%{node_name}中的%{conf_name}"
 msgstr "成功启用%{node_name}中的%{conf_name}"
 
 
@@ -525,22 +579,28 @@ msgid "Enable failed"
 msgstr "启用失败"
 msgstr "启用失败"
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "启用成功"
 msgstr "启用成功"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr "启用 TLS"
 msgstr "启用 TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr "启用"
 msgstr "启用"
 
 
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "启用成功"
 msgstr "启用成功"
 
 
@@ -548,7 +608,7 @@ msgstr "启用成功"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "用 Let's Encrypt 对网站进行加密"
 msgstr "用 Let's Encrypt 对网站进行加密"
 
 
-#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:186 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "环境"
 msgstr "环境"
 
 
@@ -560,7 +620,7 @@ msgstr "环境"
 msgid "Error"
 msgid "Error"
 msgstr "错误"
 msgstr "错误"
 
 
-#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:173 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "错误日志"
 msgstr "错误日志"
 
 
@@ -583,11 +643,15 @@ msgstr "导出"
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr "禁用失败 %{msg}"
 msgstr "禁用失败 %{msg}"
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr "启用失败 %{msg}"
 msgstr "启用失败 %{msg}"
 
 
@@ -595,7 +659,7 @@ msgstr "启用失败 %{msg}"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "获取证书信息失败"
 msgstr "获取证书信息失败"
 
 
-#: src/views/domain/DomainEdit.vue:138
+#: src/views/domain/DomainEdit.vue:138 src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "保存失败,在配置中检测到语法错误。"
 msgstr "保存失败,在配置中检测到语法错误。"
 
 
@@ -671,7 +735,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr "首页"
 msgstr "首页"
 
 
@@ -695,7 +759,7 @@ msgstr "HTTP01"
 msgid "Import"
 msgid "Import"
 msgstr "导入"
 msgstr "导入"
 
 
-#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:140 src/views/certificate/CertificateEditor.vue:84
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "导入证书"
 msgstr "导入证书"
 
 
@@ -711,7 +775,7 @@ msgstr "初始化核心升级程序错误"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "初始化核心升级器"
 msgstr "初始化核心升级器"
 
 
-#: src/routes/index.ts:220 src/views/other/Install.vue:139
+#: src/routes/index.ts:237 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "安装"
 msgstr "安装"
 
 
@@ -785,7 +849,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "日志"
 msgstr "日志"
 
 
-#: src/routes/index.ts:226 src/views/other/Login.vue:147
+#: src/routes/index.ts:243 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "登录"
 msgstr "登录"
 
 
@@ -805,15 +869,19 @@ msgstr ""
 "在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 "
 "在获取签发证书前,请确保配置文件中已将 .well-known 目录反向代理到 "
 "HTTPChallengePort。"
 "HTTPChallengePort。"
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "配置管理"
 msgstr "配置管理"
 
 
-#: src/routes/index.ts:59 src/views/domain/DomainList.vue:105
+#: src/routes/index.ts:60 src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "网站管理"
 msgstr "网站管理"
 
 
-#: src/routes/index.ts:185 src/views/user/User.vue:53
+#: src/routes/index.ts:85 src/views/stream/StreamList.vue:122
+msgid "Manage Streams"
+msgstr "管理 Stream"
+
+#: src/routes/index.ts:202 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "用户管理"
 msgstr "用户管理"
 
 
@@ -835,7 +903,7 @@ msgstr "内存与存储"
 msgid "Modify"
 msgid "Modify"
 msgstr "修改"
 msgstr "修改"
 
 
-#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:132 src/views/certificate/CertificateEditor.vue:84
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "修改证书"
 msgstr "修改证书"
 
 
@@ -854,8 +922,11 @@ msgstr "多行指令"
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16 src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr "名称"
 msgstr "名称"
 
 
@@ -893,7 +964,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 访问日志路径"
 msgstr "Nginx 访问日志路径"
 
 
-#: src/views/domain/DomainEdit.vue:222
+#: src/views/domain/DomainEdit.vue:222 src/views/stream/StreamEdit.vue:213
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 配置解析错误"
 msgstr "Nginx 配置解析错误"
 
 
@@ -905,7 +976,7 @@ msgstr "控制 Nginx"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Nginx 错误日志路径"
 msgstr "Nginx 错误日志路径"
 
 
-#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:163 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Nginx 日志"
 msgstr "Nginx 日志"
 
 
@@ -921,9 +992,10 @@ msgstr "Nginx 重启成功"
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr "取消"
 msgstr "取消"
 
 
@@ -935,7 +1007,7 @@ msgstr "节点密钥"
 msgid "Not After"
 msgid "Not After"
 msgstr "有效期"
 msgstr "有效期"
 
 
-#: src/routes/index.ts:232 src/routes/index.ts:234
+#: src/routes/index.ts:249 src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr "找不到页面"
 msgstr "找不到页面"
 
 
@@ -952,7 +1024,7 @@ msgstr "注意"
 msgid "Notification"
 msgid "Notification"
 msgstr "通知"
 msgstr "通知"
 
 
-#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:194
 msgid "Notifications"
 msgid "Notifications"
 msgstr "通知"
 msgstr "通知"
 
 
@@ -980,10 +1052,13 @@ msgstr "离线"
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr "确定"
 msgstr "确定"
 
 
@@ -1011,10 +1086,12 @@ msgid "OS:"
 msgstr "OS:"
 msgstr "OS:"
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "覆盖"
 msgstr "覆盖"
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "覆盖现有文件"
 msgstr "覆盖现有文件"
 
 
@@ -1059,6 +1136,7 @@ msgstr ""
 "的API。"
 "的API。"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid ""
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "Please input name, this will be used as the filename of the new "
 "configuration!"
 "configuration!"
@@ -1082,6 +1160,7 @@ msgid ""
 msgstr "请注意,下面的时间单位配置均以秒为单位。"
 msgstr "请注意,下面的时间单位配置均以秒为单位。"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr "请至少选择一个节点!"
 msgstr "请至少选择一个节点!"
 
 
@@ -1089,7 +1168,7 @@ msgstr "请至少选择一个节点!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "预发布"
 msgstr "预发布"
 
 
-#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:210 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "偏好设置"
 msgstr "偏好设置"
 
 
@@ -1156,14 +1235,10 @@ msgstr "正在重载 Nginx"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "删除成功"
 msgstr "删除成功"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 msgid "Rename"
 msgid "Rename"
 msgstr "重命名"
 msgstr "重命名"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr "重新命名 Upstream"
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 msgid "Renew Certificate"
 msgid "Renew Certificate"
@@ -1209,8 +1284,8 @@ msgstr "运行中"
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
-#: src/views/preference/Preference.vue:113
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
+#: src/views/preference/Preference.vue:113 src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr "保存"
 msgstr "保存"
 
 
@@ -1219,7 +1294,7 @@ msgid "Save Directive"
 msgstr "保存指令"
 msgstr "保存指令"
 
 
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "保存错误 %{msg}"
 msgstr "保存错误 %{msg}"
 
 
@@ -1235,7 +1310,8 @@ msgstr "保存成功"
 
 
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "保存成功"
 msgstr "保存成功"
 
 
@@ -1256,6 +1332,7 @@ msgstr "上传"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116 src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr "服务器错误"
 msgstr "服务器错误"
@@ -1289,11 +1366,11 @@ msgstr "使用 HTTP01 challenge provider"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "单行指令"
 msgstr "单行指令"
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "站点列表"
 msgstr "站点列表"
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr "站点列表"
 msgstr "站点列表"
 
 
@@ -1324,7 +1401,7 @@ msgid "Stable"
 msgstr "稳定"
 msgstr "稳定"
 
 
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
-#: src/views/environment/Environment.vue:78
+#: src/views/environment/Environment.vue:78 src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr "状态"
 msgstr "状态"
 
 
@@ -1356,7 +1433,7 @@ msgstr "切换到深色主题"
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr "切换到浅色"
 msgstr "切换到浅色"
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr "系统"
 msgstr "系统"
 
 
@@ -1365,10 +1442,11 @@ msgid "Table"
 msgstr "列表"
 msgstr "列表"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr "目标"
 msgstr "目标"
 
 
-#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:155 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "终端"
 msgstr "终端"
 
 
@@ -1414,7 +1492,7 @@ msgstr "该字段不能为空"
 msgid "Title"
 msgid "Title"
 msgstr "标题"
 msgstr "标题"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
 "add a location which can proxy the request from authority to backend, and we "
 "add a location which can proxy the request from authority to backend, and we "
@@ -1433,7 +1511,8 @@ msgstr "类型"
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
-#: src/views/user/User.vue:40
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44 src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr "修改时间"
 msgstr "修改时间"
 
 
@@ -1441,7 +1520,7 @@ msgstr "修改时间"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "更新成功"
 msgstr "更新成功"
 
 
-#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:229 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "升级"
 msgstr "升级"
@@ -1454,6 +1533,10 @@ msgstr "升级成功"
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr "正在升级Nginx UI,请等待..."
 msgstr "正在升级Nginx UI,请等待..."
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr "Upstream 名称"
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr "运行时间:"
 msgstr "运行时间:"
@@ -1518,7 +1601,7 @@ msgstr "正在将证书私钥写入磁盘"
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr "正在将证书写入磁盘"
 msgstr "正在将证书写入磁盘"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr "是的"
 msgstr "是的"
@@ -1531,12 +1614,12 @@ msgstr "您使用的是最新版本"
 msgid "You can check Nginx UI upgrade at this page."
 msgid "You can check Nginx UI upgrade at this page."
 msgstr "你可以在这个页面检查Nginx UI的升级。"
 msgstr "你可以在这个页面检查Nginx UI的升级。"
 
 
+#~ msgid "Rename Upstream"
+#~ msgstr "重新命名 Upstream"
+
 #~ msgid "Server"
 #~ msgid "Server"
 #~ msgstr "Server"
 #~ msgstr "Server"
 
 
-#~ msgid "Upstream"
-#~ msgstr "Upstream"
-
 #~ msgid "Leave blank will not change anything."
 #~ msgid "Leave blank will not change anything."
 #~ msgstr "留空不会有任何变化。"
 #~ msgstr "留空不会有任何变化。"
 
 

+ 163 - 72
app/src/language/zh_TW/app.po

@@ -14,26 +14,28 @@ msgstr ""
 "Generated-By: easygettext\n"
 "Generated-By: easygettext\n"
 "X-Generator: Poedit 3.4.1\n"
 "X-Generator: Poedit 3.4.1\n"
 
 
-#: src/routes/index.ts:208
+#: src/routes/index.ts:225
 msgid "About"
 msgid "About"
 msgstr "關於"
 msgstr "關於"
 
 
-#: src/routes/index.ts:152 src/views/domain/ngx_conf/LogEntry.vue:78
+#: src/routes/index.ts:169 src/views/domain/ngx_conf/LogEntry.vue:78
 msgid "Access Logs"
 msgid "Access Logs"
 msgstr "存取日誌"
 msgstr "存取日誌"
 
 
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/Certificate.vue:106
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/certificate/DNSCredential.vue:32 src/views/config/config.ts:36
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
 #: src/views/domain/DomainList.vue:50 src/views/environment/Environment.vue:105
-#: src/views/notification/Notification.vue:38 src/views/user/User.vue:46
+#: src/views/notification/Notification.vue:38
+#: src/views/stream/StreamList.vue:50 src/views/user/User.vue:46
 msgid "Action"
 msgid "Action"
 msgstr "操作"
 msgstr "操作"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:115
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/components/StdDesign/StdDataDisplay/StdCurd.vue:141
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:119
-#: src/views/domain/ngx_conf/NgxServer.vue:163
-#: src/views/domain/ngx_conf/NgxUpstream.vue:96
+#: src/views/domain/ngx_conf/NgxServer.vue:170
+#: src/views/domain/ngx_conf/NgxUpstream.vue:153
+#: src/views/stream/StreamList.vue:124
 msgid "Add"
 msgid "Add"
 msgstr "新增"
 msgstr "新增"
 
 
@@ -46,15 +48,25 @@ msgstr "在下方新增指令"
 msgid "Add Location"
 msgid "Add Location"
 msgstr "新增 Location"
 msgstr "新增 Location"
 
 
-#: src/routes/index.ts:71 src/views/domain/DomainAdd.vue:96
+#: src/routes/index.ts:72 src/views/domain/DomainAdd.vue:96
 msgid "Add Site"
 msgid "Add Site"
 msgstr "新增網站"
 msgstr "新增網站"
 
 
+#: src/views/stream/StreamList.vue:184
+#, fuzzy
+msgid "Add Stream"
+msgstr "新增網站"
+
+#: src/views/stream/StreamList.vue:114
+#, fuzzy
+msgid "Added successfully"
+msgstr "更新成功"
+
 #: src/views/certificate/DNSChallenge.vue:94
 #: src/views/certificate/DNSChallenge.vue:94
 msgid "Additional"
 msgid "Additional"
 msgstr "其他設定"
 msgstr "其他設定"
 
 
-#: src/views/domain/DomainEdit.vue:204
+#: src/views/domain/DomainEdit.vue:204 src/views/stream/StreamEdit.vue:195
 msgid "Advance Mode"
 msgid "Advance Mode"
 msgstr "進階模式"
 msgstr "進階模式"
 
 
@@ -85,11 +97,11 @@ msgid "Are you sure you want to clear the record of chat?"
 msgstr "您確定要清除聊天記錄嗎?"
 msgstr "您確定要清除聊天記錄嗎?"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:353
-#: src/views/domain/DomainList.vue:147
+#: src/views/domain/DomainList.vue:147 src/views/stream/StreamList.vue:168
 msgid "Are you sure you want to delete?"
 msgid "Are you sure you want to delete?"
 msgstr "您確定要刪除嗎?"
 msgstr "您確定要刪除嗎?"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:79
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:89
 msgid "Are you sure you want to remove this directive?"
 msgid "Are you sure you want to remove this directive?"
 msgstr "您確定要刪除這條指令嗎?"
 msgstr "您確定要刪除這條指令嗎?"
 
 
@@ -129,6 +141,7 @@ msgstr "已啟用 %{name} 的自動續簽"
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/certificate/CertificateEditor.vue:207
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/config/Config.vue:75 src/views/config/ConfigEdit.vue:89
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
 #: src/views/domain/DomainEdit.vue:261 src/views/nginx_log/NginxLog.vue:170
+#: src/views/stream/StreamEdit.vue:251
 msgid "Back"
 msgid "Back"
 msgstr "返回"
 msgstr "返回"
 
 
@@ -143,10 +156,11 @@ msgstr "基本資訊"
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/config/ConfigEdit.vue:117
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/domain/components/RightSettings.vue:76
 #: src/views/preference/Preference.vue:90
 #: src/views/preference/Preference.vue:90
+#: src/views/stream/components/RightSettings.vue:76
 msgid "Basic"
 msgid "Basic"
 msgstr "基本"
 msgstr "基本"
 
 
-#: src/views/domain/DomainEdit.vue:207
+#: src/views/domain/DomainEdit.vue:207 src/views/stream/StreamEdit.vue:198
 msgid "Basic Mode"
 msgid "Basic Mode"
 msgstr "基本模式"
 msgstr "基本模式"
 
 
@@ -170,9 +184,11 @@ msgstr "CADir"
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/cert/components/ObtainCert.vue:137
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/Deploy.vue:24
 #: src/views/domain/components/RightSettings.vue:52
 #: src/views/domain/components/RightSettings.vue:52
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:49
-#: src/views/domain/ngx_conf/NgxServer.vue:84
-#: src/views/domain/ngx_conf/NgxUpstream.vue:28
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:52
+#: src/views/domain/ngx_conf/NgxServer.vue:87
+#: src/views/domain/ngx_conf/NgxUpstream.vue:36
+#: src/views/stream/components/Deploy.vue:24
+#: src/views/stream/components/RightSettings.vue:52
 msgid "Cancel"
 msgid "Cancel"
 msgstr "取消"
 msgstr "取消"
 
 
@@ -189,12 +205,12 @@ msgstr "此憑證有效"
 msgid "Certificate Status"
 msgid "Certificate Status"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
 
 
-#: src/routes/index.ts:101 src/views/certificate/Certificate.vue:122
+#: src/routes/index.ts:118 src/views/certificate/Certificate.vue:122
 #, fuzzy
 #, fuzzy
 msgid "Certificates"
 msgid "Certificates"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
 
 
-#: src/routes/index.ts:110
+#: src/routes/index.ts:127
 #, fuzzy
 #, fuzzy
 msgid "Certificates List"
 msgid "Certificates List"
 msgstr "憑證清單"
 msgstr "憑證清單"
@@ -240,10 +256,10 @@ msgstr "清除"
 msgid "Cleared successfully"
 msgid "Cleared successfully"
 msgstr "成功停用"
 msgstr "成功停用"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:97
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:107
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:119
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
 #: src/views/domain/ngx_conf/LocationEditor.vue:88
-#: src/views/domain/ngx_conf/NgxServer.vue:139
+#: src/views/domain/ngx_conf/NgxServer.vue:142
 msgid "Comments"
 msgid "Comments"
 msgstr "備註"
 msgstr "備註"
 
 
@@ -271,7 +287,7 @@ msgstr "設定 SSL"
 msgid "Connected"
 msgid "Connected"
 msgstr "已連結"
 msgstr "已連結"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:102
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:112
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:100
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 #: src/views/domain/ngx_conf/LocationEditor.vue:128
 msgid "Content"
 msgid "Content"
@@ -289,6 +305,11 @@ msgstr "中央處理器狀態"
 msgid "CPU:"
 msgid "CPU:"
 msgstr "中央處理器:"
 msgstr "中央處理器:"
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:164
+#, fuzzy
+msgid "Create"
+msgstr "建立時間"
+
 #: src/views/domain/DomainAdd.vue:161
 #: src/views/domain/DomainAdd.vue:161
 msgid "Create Another"
 msgid "Create Another"
 msgstr "再建立一個"
 msgstr "再建立一個"
@@ -314,11 +335,11 @@ msgid "Current Version"
 msgstr "目前版本"
 msgstr "目前版本"
 
 
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
 #: src/views/domain/ngx_conf/config_template/ConfigTemplate.vue:126
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:185
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:188
 msgid "Custom"
 msgid "Custom"
 msgstr "自訂"
 msgstr "自訂"
 
 
-#: src/routes/index.ts:52
+#: src/routes/index.ts:53
 msgid "Dashboard"
 msgid "Dashboard"
 msgstr "儀表板"
 msgstr "儀表板"
 
 
@@ -328,8 +349,9 @@ msgstr "資料庫 (可選,預設: database)"
 
 
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:360
 #: src/views/domain/DomainList.vue:156
 #: src/views/domain/DomainList.vue:156
-#: src/views/domain/ngx_conf/NgxServer.vue:114
-#: src/views/domain/ngx_conf/NgxUpstream.vue:77
+#: src/views/domain/ngx_conf/NgxServer.vue:117
+#: src/views/domain/ngx_conf/NgxUpstream.vue:127
+#: src/views/stream/StreamList.vue:177
 msgid "Delete"
 msgid "Delete"
 msgstr "刪除"
 msgstr "刪除"
 
 
@@ -337,6 +359,11 @@ msgstr "刪除"
 msgid "Delete site: %{site_name}"
 msgid "Delete site: %{site_name}"
 msgstr "刪除網站:%{site_name}"
 msgstr "刪除網站:%{site_name}"
 
 
+#: src/views/stream/StreamList.vue:81
+#, fuzzy
+msgid "Delete stream: %{stream_name}"
+msgstr "刪除網站:%{site_name}"
+
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:133
 #, fuzzy
 #, fuzzy
 msgid "Deleted successfully"
 msgid "Deleted successfully"
@@ -344,18 +371,23 @@ msgstr "成功停用"
 
 
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/Deploy.vue:109
 #: src/views/domain/components/RightSettings.vue:94
 #: src/views/domain/components/RightSettings.vue:94
+#: src/views/stream/components/Deploy.vue:109
+#: src/views/stream/components/RightSettings.vue:94
 msgid "Deploy"
 msgid "Deploy"
 msgstr "部署"
 msgstr "部署"
 
 
 #: src/views/domain/components/Deploy.vue:66
 #: src/views/domain/components/Deploy.vue:66
+#: src/views/stream/components/Deploy.vue:66
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgid "Deploy %{conf_name} to %{node_name} failed"
 msgstr "部署 %{conf_name} 至 %{node_name} 失敗"
 msgstr "部署 %{conf_name} 至 %{node_name} 失敗"
 
 
 #: src/views/domain/components/Deploy.vue:40
 #: src/views/domain/components/Deploy.vue:40
+#: src/views/stream/components/Deploy.vue:40
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgid "Deploy %{conf_name} to %{node_name} successfully"
 msgstr "成功部署 %{conf_name} 至 %{node_name}"
 msgstr "成功部署 %{conf_name} 至 %{node_name}"
 
 
 #: src/views/domain/components/Deploy.vue:38
 #: src/views/domain/components/Deploy.vue:38
+#: src/views/stream/components/Deploy.vue:38
 msgid "Deploy successfully"
 msgid "Deploy successfully"
 msgstr "部署成功"
 msgstr "部署成功"
 
 
@@ -376,7 +408,7 @@ msgstr "開發模式"
 msgid "Directive"
 msgid "Directive"
 msgstr "指令"
 msgstr "指令"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:22
+#: src/views/domain/ngx_conf/directive/DirectiveEditor.vue:23
 msgid "Directives"
 msgid "Directives"
 msgstr "指令"
 msgstr "指令"
 
 
@@ -385,7 +417,7 @@ msgstr "指令"
 msgid "Directory"
 msgid "Directory"
 msgstr "指令"
 msgstr "指令"
 
 
-#: src/views/domain/DomainList.vue:125
+#: src/views/domain/DomainList.vue:125 src/views/stream/StreamList.vue:146
 msgid "Disable"
 msgid "Disable"
 msgstr "停用"
 msgstr "停用"
 
 
@@ -394,12 +426,15 @@ msgid "Disable auto-renewal failed for %{name}"
 msgstr "關閉 %{name} 自動續簽失敗"
 msgstr "關閉 %{name} 自動續簽失敗"
 
 
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
 #: src/views/domain/cert/ChangeCert.vue:48 src/views/domain/DomainEdit.vue:190
-#: src/views/domain/DomainList.vue:36
+#: src/views/domain/DomainList.vue:36 src/views/stream/StreamEdit.vue:181
+#: src/views/stream/StreamList.vue:36
 msgid "Disabled"
 msgid "Disabled"
 msgstr "停用"
 msgstr "停用"
 
 
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/components/RightSettings.vue:39
 #: src/views/domain/DomainList.vue:70
 #: src/views/domain/DomainList.vue:70
+#: src/views/stream/components/RightSettings.vue:39
+#: src/views/stream/StreamList.vue:70
 msgid "Disabled successfully"
 msgid "Disabled successfully"
 msgstr "成功停用"
 msgstr "成功停用"
 
 
@@ -407,7 +442,7 @@ msgstr "成功停用"
 msgid "Disk IO"
 msgid "Disk IO"
 msgstr "磁碟 IO"
 msgstr "磁碟 IO"
 
 
-#: src/routes/index.ts:131 src/views/certificate/DNSCredential.vue:39
+#: src/routes/index.ts:148 src/views/certificate/DNSCredential.vue:39
 msgid "DNS Credentials"
 msgid "DNS Credentials"
 msgstr "DNS 認證"
 msgstr "DNS 認證"
 
 
@@ -421,6 +456,7 @@ msgid "DNS01"
 msgstr "DNS01"
 msgstr "DNS01"
 
 
 #: src/views/domain/components/Deploy.vue:19
 #: src/views/domain/components/Deploy.vue:19
+#: src/views/stream/components/Deploy.vue:19
 msgid "Do you want to deploy this file to remote server?"
 msgid "Do you want to deploy this file to remote server?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgid_plural "Do you want to deploy this file to remote servers?"
 msgstr[0] "您要將此檔案部署至遠端伺服器嗎?"
 msgstr[0] "您要將此檔案部署至遠端伺服器嗎?"
@@ -433,19 +469,29 @@ msgstr "您要停用自動憑證續訂嗎?"
 msgid "Do you want to disable this site?"
 msgid "Do you want to disable this site?"
 msgstr "您想停用這個網站嗎?"
 msgstr "您想停用這個網站嗎?"
 
 
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to disable this stream?"
+msgstr "您想停用這個網站嗎?"
+
 #: src/views/domain/components/RightSettings.vue:48
 #: src/views/domain/components/RightSettings.vue:48
 msgid "Do you want to enable this site?"
 msgid "Do you want to enable this site?"
 msgstr "您要啟用此網站嗎?"
 msgstr "您要啟用此網站嗎?"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:42
+#: src/views/stream/components/RightSettings.vue:48
+#, fuzzy
+msgid "Do you want to enable this stream?"
+msgstr "您要啟用此網站嗎?"
+
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:45
 msgid "Do you want to enable TLS?"
 msgid "Do you want to enable TLS?"
 msgstr "您想啟用 TLS 嗎?"
 msgstr "您想啟用 TLS 嗎?"
 
 
-#: src/views/domain/ngx_conf/NgxServer.vue:80
+#: src/views/domain/ngx_conf/NgxServer.vue:83
 msgid "Do you want to remove this server?"
 msgid "Do you want to remove this server?"
 msgstr "您要移除此伺服器嗎?"
 msgstr "您要移除此伺服器嗎?"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:24
+#: src/views/domain/ngx_conf/NgxUpstream.vue:32
 #, fuzzy
 #, fuzzy
 msgid "Do you want to remove this upstream?"
 msgid "Do you want to remove this upstream?"
 msgstr "您要移除此伺服器嗎?"
 msgstr "您要移除此伺服器嗎?"
@@ -477,51 +523,66 @@ msgstr "試運轉模式已啟用"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/components/SiteDuplicate.vue:128
 #: src/views/domain/DomainList.vue:141
 #: src/views/domain/DomainList.vue:141
+#: src/views/stream/components/StreamDuplicate.vue:128
+#: src/views/stream/StreamList.vue:162
 msgid "Duplicate"
 msgid "Duplicate"
 msgstr "複製"
 msgstr "複製"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:86
 #: src/views/domain/components/SiteDuplicate.vue:86
+#: src/views/stream/components/StreamDuplicate.vue:86
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgid "Duplicate %{conf_name} to %{node_name} successfully"
 msgstr "成功複製 %{conf_name} 到 %{node_name}"
 msgstr "成功複製 %{conf_name} 到 %{node_name}"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:92
 #: src/views/domain/components/SiteDuplicate.vue:92
+#: src/views/stream/components/StreamDuplicate.vue:92
 msgid "Duplicate failed"
 msgid "Duplicate failed"
 msgstr "複製失敗"
 msgstr "複製失敗"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:84
 msgid "Duplicate successfully"
 msgid "Duplicate successfully"
 msgstr "複製成功"
 msgstr "複製成功"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:66
 #: src/views/domain/components/SiteDuplicate.vue:66
+#: src/views/stream/components/StreamDuplicate.vue:66
 msgid "Duplicate to local successfully"
 msgid "Duplicate to local successfully"
 msgstr "成功複製至本機"
 msgstr "成功複製至本機"
 
 
-#: src/views/domain/DomainEdit.vue:179
+#: src/views/domain/DomainEdit.vue:179 src/views/stream/StreamEdit.vue:170
 msgid "Edit %{n}"
 msgid "Edit %{n}"
 msgstr "編輯 %{n}"
 msgstr "編輯 %{n}"
 
 
-#: src/routes/index.ts:93 src/views/config/ConfigEdit.vue:83
+#: src/routes/index.ts:110 src/views/config/ConfigEdit.vue:83
 msgid "Edit Configuration"
 msgid "Edit Configuration"
 msgstr "編輯設定"
 msgstr "編輯設定"
 
 
-#: src/routes/index.ts:75
+#: src/routes/index.ts:76
 msgid "Edit Site"
 msgid "Edit Site"
 msgstr "編輯網站"
 msgstr "編輯網站"
 
 
+#: src/routes/index.ts:93
+#, fuzzy
+msgid "Edit Stream"
+msgstr "編輯網站"
+
 #: src/views/other/Install.vue:93
 #: src/views/other/Install.vue:93
 msgid "Email (*)"
 msgid "Email (*)"
 msgstr "電子郵件 (*)"
 msgstr "電子郵件 (*)"
 
 
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/components/Deploy.vue:89
 #: src/views/domain/DomainList.vue:133
 #: src/views/domain/DomainList.vue:133
+#: src/views/stream/components/Deploy.vue:89
+#: src/views/stream/StreamList.vue:154
 msgid "Enable"
 msgid "Enable"
 msgstr "啟用"
 msgstr "啟用"
 
 
 #: src/views/domain/components/Deploy.vue:55
 #: src/views/domain/components/Deploy.vue:55
+#: src/views/stream/components/Deploy.vue:55
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgid "Enable %{conf_name} in %{node_name} failed"
 msgstr "在 %{node_name} 啟用 %{conf_name} 失敗"
 msgstr "在 %{node_name} 啟用 %{conf_name} 失敗"
 
 
 #: src/views/domain/components/Deploy.vue:49
 #: src/views/domain/components/Deploy.vue:49
+#: src/views/stream/components/Deploy.vue:49
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgid "Enable %{conf_name} in %{node_name} successfully"
 msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 msgstr "成功在 %{node_name} 啟用 %{conf_name}"
 
 
@@ -534,22 +595,28 @@ msgid "Enable failed"
 msgstr "啟用失敗"
 msgstr "啟用失敗"
 
 
 #: src/views/domain/components/Deploy.vue:47
 #: src/views/domain/components/Deploy.vue:47
+#: src/views/stream/components/Deploy.vue:47
 msgid "Enable successfully"
 msgid "Enable successfully"
 msgstr "啟用成功"
 msgstr "啟用成功"
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:174
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:177
 msgid "Enable TLS"
 msgid "Enable TLS"
 msgstr "啟用 TLS"
 msgstr "啟用 TLS"
 
 
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/cert/ChangeCert.vue:44
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/components/RightSettings.vue:78
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
 #: src/views/domain/DomainEdit.vue:184 src/views/domain/DomainList.vue:32
+#: src/views/stream/components/RightSettings.vue:78
+#: src/views/stream/StreamEdit.vue:175 src/views/stream/StreamList.vue:32
 msgid "Enabled"
 msgid "Enabled"
 msgstr "已啟用"
 msgstr "已啟用"
 
 
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/RightSettings.vue:30
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/components/SiteDuplicate.vue:100
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
 #: src/views/domain/DomainAdd.vue:45 src/views/domain/DomainList.vue:60
+#: src/views/stream/components/RightSettings.vue:30
+#: src/views/stream/components/StreamDuplicate.vue:100
+#: src/views/stream/StreamList.vue:60
 msgid "Enabled successfully"
 msgid "Enabled successfully"
 msgstr "成功啟用"
 msgstr "成功啟用"
 
 
@@ -557,7 +624,7 @@ msgstr "成功啟用"
 msgid "Encrypt website with Let's Encrypt"
 msgid "Encrypt website with Let's Encrypt"
 msgstr "用 Let's Encrypt 對網站進行加密"
 msgstr "用 Let's Encrypt 對網站進行加密"
 
 
-#: src/routes/index.ts:169 src/views/environment/Environment.vue:113
+#: src/routes/index.ts:186 src/views/environment/Environment.vue:113
 msgid "Environment"
 msgid "Environment"
 msgstr "環境"
 msgstr "環境"
 
 
@@ -569,7 +636,7 @@ msgstr "環境"
 msgid "Error"
 msgid "Error"
 msgstr "錯誤"
 msgstr "錯誤"
 
 
-#: src/routes/index.ts:156 src/views/domain/ngx_conf/LogEntry.vue:86
+#: src/routes/index.ts:173 src/views/domain/ngx_conf/LogEntry.vue:86
 msgid "Error Logs"
 msgid "Error Logs"
 msgstr "錯誤日誌"
 msgstr "錯誤日誌"
 
 
@@ -593,11 +660,15 @@ msgstr "匯出"
 
 
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/components/RightSettings.vue:42
 #: src/views/domain/DomainList.vue:74
 #: src/views/domain/DomainList.vue:74
+#: src/views/stream/components/RightSettings.vue:42
+#: src/views/stream/StreamList.vue:74
 msgid "Failed to disable %{msg}"
 msgid "Failed to disable %{msg}"
 msgstr "停用 %{msg} 失敗"
 msgstr "停用 %{msg} 失敗"
 
 
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/components/RightSettings.vue:33
 #: src/views/domain/DomainList.vue:64
 #: src/views/domain/DomainList.vue:64
+#: src/views/stream/components/RightSettings.vue:33
+#: src/views/stream/StreamList.vue:64
 msgid "Failed to enable %{msg}"
 msgid "Failed to enable %{msg}"
 msgstr "啟用 %{msg} 失敗"
 msgstr "啟用 %{msg} 失敗"
 
 
@@ -605,7 +676,7 @@ msgstr "啟用 %{msg} 失敗"
 msgid "Failed to get certificate information"
 msgid "Failed to get certificate information"
 msgstr "取得憑證資訊失敗"
 msgstr "取得憑證資訊失敗"
 
 
-#: src/views/domain/DomainEdit.vue:138
+#: src/views/domain/DomainEdit.vue:138 src/views/stream/StreamEdit.vue:129
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgid "Failed to save, syntax error(s) was detected in the configuration."
 msgstr "儲存失敗,在設定中檢測到語法錯誤。"
 msgstr "儲存失敗,在設定中檢測到語法錯誤。"
 
 
@@ -682,7 +753,7 @@ msgstr "GPT-4-32K"
 msgid "GPT-4-Turbo"
 msgid "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 msgstr "GPT-4-Turbo"
 
 
-#: src/routes/index.ts:45
+#: src/routes/index.ts:46
 msgid "Home"
 msgid "Home"
 msgstr "首頁"
 msgstr "首頁"
 
 
@@ -707,7 +778,7 @@ msgstr "HTTP01"
 msgid "Import"
 msgid "Import"
 msgstr "匯出"
 msgstr "匯出"
 
 
-#: src/routes/index.ts:123 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:140 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Import Certificate"
 msgid "Import Certificate"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
@@ -724,7 +795,7 @@ msgstr "初始化核心升級程式錯誤"
 msgid "Initialing core upgrader"
 msgid "Initialing core upgrader"
 msgstr "正在初始化核心升級程式"
 msgstr "正在初始化核心升級程式"
 
 
-#: src/routes/index.ts:220 src/views/other/Install.vue:139
+#: src/routes/index.ts:237 src/views/other/Install.vue:139
 msgid "Install"
 msgid "Install"
 msgstr "安裝"
 msgstr "安裝"
 
 
@@ -804,7 +875,7 @@ msgstr "Locations"
 msgid "Log"
 msgid "Log"
 msgstr "登入"
 msgstr "登入"
 
 
-#: src/routes/index.ts:226 src/views/other/Login.vue:147
+#: src/routes/index.ts:243 src/views/other/Login.vue:147
 msgid "Login"
 msgid "Login"
 msgstr "登入"
 msgstr "登入"
 
 
@@ -823,15 +894,20 @@ msgid ""
 msgstr ""
 msgstr ""
 "在取得憑證前,請確保您已將 .well-known 目錄反向代理到 HTTPChallengePort。"
 "在取得憑證前,請確保您已將 .well-known 目錄反向代理到 HTTPChallengePort。"
 
 
-#: src/routes/index.ts:84
+#: src/routes/index.ts:101
 msgid "Manage Configs"
 msgid "Manage Configs"
 msgstr "管理設定"
 msgstr "管理設定"
 
 
-#: src/routes/index.ts:59 src/views/domain/DomainList.vue:105
+#: src/routes/index.ts:60 src/views/domain/DomainList.vue:105
 msgid "Manage Sites"
 msgid "Manage Sites"
 msgstr "管理網站"
 msgstr "管理網站"
 
 
-#: src/routes/index.ts:185 src/views/user/User.vue:53
+#: src/routes/index.ts:85 src/views/stream/StreamList.vue:122
+#, fuzzy
+msgid "Manage Streams"
+msgstr "管理網站"
+
+#: src/routes/index.ts:202 src/views/user/User.vue:53
 msgid "Manage Users"
 msgid "Manage Users"
 msgstr "管理使用者"
 msgstr "管理使用者"
 
 
@@ -854,7 +930,7 @@ msgstr "記憶體與儲存"
 msgid "Modify"
 msgid "Modify"
 msgstr "修改"
 msgstr "修改"
 
 
-#: src/routes/index.ts:115 src/views/certificate/CertificateEditor.vue:84
+#: src/routes/index.ts:132 src/views/certificate/CertificateEditor.vue:84
 #, fuzzy
 #, fuzzy
 msgid "Modify Certificate"
 msgid "Modify Certificate"
 msgstr "憑證狀態"
 msgstr "憑證狀態"
@@ -874,8 +950,11 @@ msgstr "多行指令"
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/RightSettings.vue:84
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/components/SiteDuplicate.vue:135
 #: src/views/domain/DomainList.vue:16
 #: src/views/domain/DomainList.vue:16
-#: src/views/domain/ngx_conf/NgxUpstream.vue:108
+#: src/views/domain/ngx_conf/NgxUpstream.vue:176
 #: src/views/environment/Environment.vue:15
 #: src/views/environment/Environment.vue:15
+#: src/views/stream/components/RightSettings.vue:84
+#: src/views/stream/components/StreamDuplicate.vue:135
+#: src/views/stream/StreamList.vue:16 src/views/stream/StreamList.vue:188
 msgid "Name"
 msgid "Name"
 msgstr "名稱"
 msgstr "名稱"
 
 
@@ -913,7 +992,7 @@ msgstr "Nginx"
 msgid "Nginx Access Log Path"
 msgid "Nginx Access Log Path"
 msgstr "Nginx 存取日誌路徑"
 msgstr "Nginx 存取日誌路徑"
 
 
-#: src/views/domain/DomainEdit.vue:222
+#: src/views/domain/DomainEdit.vue:222 src/views/stream/StreamEdit.vue:213
 msgid "Nginx Configuration Parse Error"
 msgid "Nginx Configuration Parse Error"
 msgstr "Nginx 設定解析錯誤"
 msgstr "Nginx 設定解析錯誤"
 
 
@@ -925,7 +1004,7 @@ msgstr "Nginx 控制元件"
 msgid "Nginx Error Log Path"
 msgid "Nginx Error Log Path"
 msgstr "Nginx 錯誤日誌路徑"
 msgstr "Nginx 錯誤日誌路徑"
 
 
-#: src/routes/index.ts:146 src/views/nginx_log/NginxLog.vue:145
+#: src/routes/index.ts:163 src/views/nginx_log/NginxLog.vue:145
 msgid "Nginx Log"
 msgid "Nginx Log"
 msgstr "Nginx 日誌"
 msgstr "Nginx 日誌"
 
 
@@ -941,9 +1020,10 @@ msgstr "Nginx 重啟成功"
 #: src/components/Notification/Notification.vue:84
 #: src/components/Notification/Notification.vue:84
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:351
 #: src/views/domain/DomainList.vue:145
 #: src/views/domain/DomainList.vue:145
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:81
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:91
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/domain/ngx_conf/LocationEditor.vue:74
 #: src/views/notification/Notification.vue:71
 #: src/views/notification/Notification.vue:71
+#: src/views/stream/StreamList.vue:166
 msgid "No"
 msgid "No"
 msgstr "取消"
 msgstr "取消"
 
 
@@ -955,7 +1035,7 @@ msgstr "Node Secret"
 msgid "Not After"
 msgid "Not After"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:232 src/routes/index.ts:234
+#: src/routes/index.ts:249 src/routes/index.ts:251
 msgid "Not Found"
 msgid "Not Found"
 msgstr "找不到頁面"
 msgstr "找不到頁面"
 
 
@@ -973,7 +1053,7 @@ msgstr "備註"
 msgid "Notification"
 msgid "Notification"
 msgstr "憑證"
 msgstr "憑證"
 
 
-#: src/components/Notification/Notification.vue:82 src/routes/index.ts:177
+#: src/components/Notification/Notification.vue:82 src/routes/index.ts:194
 #, fuzzy
 #, fuzzy
 msgid "Notifications"
 msgid "Notifications"
 msgstr "憑證"
 msgstr "憑證"
@@ -1002,10 +1082,13 @@ msgstr "離線"
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/Deploy.vue:23
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/components/RightSettings.vue:51
 #: src/views/domain/DomainList.vue:146
 #: src/views/domain/DomainList.vue:146
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:48
-#: src/views/domain/ngx_conf/NgxServer.vue:83
-#: src/views/domain/ngx_conf/NgxUpstream.vue:27
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:51
+#: src/views/domain/ngx_conf/NgxServer.vue:86
+#: src/views/domain/ngx_conf/NgxUpstream.vue:35
 #: src/views/notification/Notification.vue:72
 #: src/views/notification/Notification.vue:72
+#: src/views/stream/components/Deploy.vue:23
+#: src/views/stream/components/RightSettings.vue:51
+#: src/views/stream/StreamList.vue:167
 msgid "OK"
 msgid "OK"
 msgstr "確定"
 msgstr "確定"
 
 
@@ -1033,10 +1116,12 @@ msgid "OS:"
 msgstr "作業系統:"
 msgstr "作業系統:"
 
 
 #: src/views/domain/components/Deploy.vue:93
 #: src/views/domain/components/Deploy.vue:93
+#: src/views/stream/components/Deploy.vue:93
 msgid "Overwrite"
 msgid "Overwrite"
 msgstr "覆蓋"
 msgstr "覆蓋"
 
 
 #: src/views/domain/components/Deploy.vue:97
 #: src/views/domain/components/Deploy.vue:97
+#: src/views/stream/components/Deploy.vue:97
 msgid "Overwrite exist file"
 msgid "Overwrite exist file"
 msgstr "覆蓋現有檔案"
 msgstr "覆蓋現有檔案"
 
 
@@ -1082,6 +1167,7 @@ msgstr ""
 "求 DNS 供應商的 API。"
 "求 DNS 供應商的 API。"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:40
 #: src/views/domain/components/SiteDuplicate.vue:40
+#: src/views/stream/components/StreamDuplicate.vue:40
 msgid ""
 msgid ""
 "Please input name, this will be used as the filename of the new "
 "Please input name, this will be used as the filename of the new "
 "configuration!"
 "configuration!"
@@ -1105,6 +1191,7 @@ msgid ""
 msgstr ""
 msgstr ""
 
 
 #: src/views/domain/components/SiteDuplicate.vue:47
 #: src/views/domain/components/SiteDuplicate.vue:47
+#: src/views/stream/components/StreamDuplicate.vue:47
 msgid "Please select at least one node!"
 msgid "Please select at least one node!"
 msgstr "請至少選擇一個節點!"
 msgstr "請至少選擇一個節點!"
 
 
@@ -1112,7 +1199,7 @@ msgstr "請至少選擇一個節點!"
 msgid "Pre-release"
 msgid "Pre-release"
 msgstr "預先發布"
 msgstr "預先發布"
 
 
-#: src/routes/index.ts:193 src/views/preference/Preference.vue:85
+#: src/routes/index.ts:210 src/views/preference/Preference.vue:85
 msgid "Preference"
 msgid "Preference"
 msgstr "偏好設定"
 msgstr "偏好設定"
 
 
@@ -1180,15 +1267,11 @@ msgstr "正在重新載入 Nginx"
 msgid "Removed successfully"
 msgid "Removed successfully"
 msgstr "儲存成功"
 msgstr "儲存成功"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:74
+#: src/views/domain/ngx_conf/NgxUpstream.vue:124
 #, fuzzy
 #, fuzzy
 msgid "Rename"
 msgid "Rename"
 msgstr "使用者名稱"
 msgstr "使用者名稱"
 
 
-#: src/views/domain/ngx_conf/NgxUpstream.vue:103
-msgid "Rename Upstream"
-msgstr ""
-
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:43
 #: src/views/certificate/RenewCert.vue:47
 #: src/views/certificate/RenewCert.vue:47
 #, fuzzy
 #, fuzzy
@@ -1238,8 +1321,8 @@ msgstr "執行中"
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/components/ChatGPT/ChatGPT.vue:259
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/certificate/CertificateEditor.vue:214
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
 #: src/views/config/ConfigEdit.vue:98 src/views/domain/DomainEdit.vue:268
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:111
-#: src/views/preference/Preference.vue:113
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:121
+#: src/views/preference/Preference.vue:113 src/views/stream/StreamEdit.vue:258
 msgid "Save"
 msgid "Save"
 msgstr "儲存"
 msgstr "儲存"
 
 
@@ -1248,7 +1331,7 @@ msgid "Save Directive"
 msgstr "儲存指令"
 msgstr "儲存指令"
 
 
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
 #: src/views/config/ConfigEdit.vue:59 src/views/domain/DomainAdd.vue:53
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:41
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:42
 msgid "Save error %{msg}"
 msgid "Save error %{msg}"
 msgstr "儲存錯誤 %{msg}"
 msgstr "儲存錯誤 %{msg}"
 
 
@@ -1264,7 +1347,8 @@ msgstr "儲存成功"
 
 
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/config/ConfigEdit.vue:57 src/views/domain/DomainAdd.vue:41
 #: src/views/domain/DomainEdit.vue:154
 #: src/views/domain/DomainEdit.vue:154
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:39
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:40
+#: src/views/stream/StreamEdit.vue:145
 msgid "Saved successfully"
 msgid "Saved successfully"
 msgstr "儲存成功"
 msgstr "儲存成功"
 
 
@@ -1285,6 +1369,7 @@ msgstr "傳送"
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/components/StdDesign/StdDataDisplay/StdTable.vue:157
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/config/ConfigEdit.vue:42 src/views/domain/DomainList.vue:84
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
 #: src/views/other/Install.vue:72 src/views/preference/Preference.vue:60
+#: src/views/stream/StreamList.vue:116 src/views/stream/StreamList.vue:84
 #: src/views/system/Upgrade.vue:45
 #: src/views/system/Upgrade.vue:45
 msgid "Server error"
 msgid "Server error"
 msgstr "伺服器錯誤"
 msgstr "伺服器錯誤"
@@ -1320,11 +1405,11 @@ msgstr "使用 HTTP01 挑戰提供者"
 msgid "Single Directive"
 msgid "Single Directive"
 msgstr "單一指令"
 msgstr "單一指令"
 
 
-#: src/routes/index.ts:160
+#: src/routes/index.ts:177
 msgid "Site Logs"
 msgid "Site Logs"
 msgstr "網站日誌"
 msgstr "網站日誌"
 
 
-#: src/routes/index.ts:67
+#: src/routes/index.ts:68
 msgid "Sites List"
 msgid "Sites List"
 msgstr "網站列表"
 msgstr "網站列表"
 
 
@@ -1358,7 +1443,7 @@ msgid "Stable"
 msgstr "穩定"
 msgstr "穩定"
 
 
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
 #: src/views/certificate/Certificate.vue:81 src/views/domain/DomainList.vue:25
-#: src/views/environment/Environment.vue:78
+#: src/views/environment/Environment.vue:78 src/views/stream/StreamList.vue:25
 msgid "Status"
 msgid "Status"
 msgstr "狀態"
 msgstr "狀態"
 
 
@@ -1391,7 +1476,7 @@ msgstr ""
 msgid "Switch to light theme"
 msgid "Switch to light theme"
 msgstr ""
 msgstr ""
 
 
-#: src/routes/index.ts:201
+#: src/routes/index.ts:218
 msgid "System"
 msgid "System"
 msgstr "系統"
 msgstr "系統"
 
 
@@ -1400,10 +1485,11 @@ msgid "Table"
 msgstr "表格"
 msgstr "表格"
 
 
 #: src/views/domain/components/SiteDuplicate.vue:142
 #: src/views/domain/components/SiteDuplicate.vue:142
+#: src/views/stream/components/StreamDuplicate.vue:142
 msgid "Target"
 msgid "Target"
 msgstr "目標"
 msgstr "目標"
 
 
-#: src/routes/index.ts:138 src/views/pty/Terminal.vue:95
+#: src/routes/index.ts:155 src/views/pty/Terminal.vue:95
 msgid "Terminal"
 msgid "Terminal"
 msgstr "終端機"
 msgstr "終端機"
 
 
@@ -1452,7 +1538,7 @@ msgstr "此欄位不應為空"
 msgid "Title"
 msgid "Title"
 msgstr ""
 msgstr ""
 
 
-#: src/views/domain/ngx_conf/NgxConfigEditor.vue:43
+#: src/views/domain/ngx_conf/NgxConfigEditor.vue:46
 msgid ""
 msgid ""
 "To make sure the certification auto-renewal can work normally, we need to "
 "To make sure the certification auto-renewal can work normally, we need to "
 "add a location which can proxy the request from authority to backend, and we "
 "add a location which can proxy the request from authority to backend, and we "
@@ -1471,7 +1557,8 @@ msgstr "類型"
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/config/ConfigEdit.vue:123
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/components/RightSettings.vue:87
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
 #: src/views/domain/DomainList.vue:44 src/views/environment/Environment.vue:98
-#: src/views/user/User.vue:40
+#: src/views/stream/components/RightSettings.vue:87
+#: src/views/stream/StreamList.vue:44 src/views/user/User.vue:40
 msgid "Updated at"
 msgid "Updated at"
 msgstr "更新時間"
 msgstr "更新時間"
 
 
@@ -1479,7 +1566,7 @@ msgstr "更新時間"
 msgid "Updated successfully"
 msgid "Updated successfully"
 msgstr "更新成功"
 msgstr "更新成功"
 
 
-#: src/routes/index.ts:212 src/views/system/Upgrade.vue:143
+#: src/routes/index.ts:229 src/views/system/Upgrade.vue:143
 #: src/views/system/Upgrade.vue:235
 #: src/views/system/Upgrade.vue:235
 msgid "Upgrade"
 msgid "Upgrade"
 msgstr "升級"
 msgstr "升級"
@@ -1492,6 +1579,10 @@ msgstr "升級成功"
 msgid "Upgrading Nginx UI, please wait..."
 msgid "Upgrading Nginx UI, please wait..."
 msgstr "正在升級 Nginx UI,請稍候..."
 msgstr "正在升級 Nginx UI,請稍候..."
 
 
+#: src/views/domain/ngx_conf/NgxUpstream.vue:171
+msgid "Upstream Name"
+msgstr ""
+
 #: src/views/dashboard/ServerAnalytic.vue:177
 #: src/views/dashboard/ServerAnalytic.vue:177
 msgid "Uptime:"
 msgid "Uptime:"
 msgstr "運作時間:"
 msgstr "運作時間:"
@@ -1558,7 +1649,7 @@ msgstr "將憑證私鑰寫入磁碟"
 msgid "Writing certificate to disk"
 msgid "Writing certificate to disk"
 msgstr "將憑證寫入磁碟"
 msgstr "將憑證寫入磁碟"
 
 
-#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:80
+#: src/views/domain/ngx_conf/directive/DirectiveEditorItem.vue:90
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 #: src/views/domain/ngx_conf/LocationEditor.vue:73
 msgid "Yes"
 msgid "Yes"
 msgstr "是的"
 msgstr "是的"

+ 17 - 0
app/src/routes/index.ts

@@ -12,6 +12,7 @@ import {
   InfoCircleOutlined,
   InfoCircleOutlined,
   SafetyCertificateOutlined,
   SafetyCertificateOutlined,
   SettingOutlined,
   SettingOutlined,
+  ShareAltOutlined,
   UserOutlined,
   UserOutlined,
 } from '@ant-design/icons-vue'
 } from '@ant-design/icons-vue'
 import NProgress from 'nprogress'
 import NProgress from 'nprogress'
@@ -79,6 +80,22 @@ export const routes: Route[] = [
           },
           },
         }],
         }],
       },
       },
+      {
+        path: 'streams',
+        name: () => $gettext('Manage Streams'),
+        component: () => import('@/views/stream/StreamList.vue'),
+        meta: {
+          icon: ShareAltOutlined,
+        },
+      },
+      {
+        path: 'stream/:name',
+        name: () => $gettext('Edit Stream'),
+        component: () => import('@/views/stream/StreamEdit.vue'),
+        meta: {
+          hiddenInSidebar: true,
+        },
+      },
       {
       {
         path: 'config',
         path: 'config',
         name: () => $gettext('Manage Configs'),
         name: () => $gettext('Manage Configs'),

+ 1 - 1
app/src/views/domain/DomainAdd.vue

@@ -87,7 +87,7 @@ const ngx_directives = computed(() => {
   return ngx_config.servers[0].directives
   return ngx_config.servers[0].directives
 })
 })
 
 
-provide('save_site_config', save)
+provide('save_config', save)
 provide('ngx_directives', ngx_directives)
 provide('ngx_directives', ngx_directives)
 provide('ngx_config', ngx_config)
 provide('ngx_config', ngx_config)
 </script>
 </script>

+ 1 - 1
app/src/views/domain/DomainEdit.vue

@@ -157,7 +157,7 @@ const save = async () => {
   })
   })
 }
 }
 
 
-provide('save_site_config', save)
+provide('save_config', save)
 provide('configText', configText)
 provide('configText', configText)
 provide('ngx_config', ngx_config)
 provide('ngx_config', ngx_config)
 provide('history_chatgpt_record', history_chatgpt_record)
 provide('history_chatgpt_record', history_chatgpt_record)

+ 3 - 3
app/src/views/domain/cert/components/ObtainCert.vue

@@ -33,7 +33,7 @@ const modalClosable = ref(true)
 
 
 provide('data', data)
 provide('data', data)
 
 
-const save_site_config = inject('save_site_config') as () => Promise<void>
+const save_config = inject('save_config') as () => Promise<void>
 const no_server_name = inject('no_server_name') as Ref<boolean>
 const no_server_name = inject('no_server_name') as Ref<boolean>
 const props = inject('props') as Props
 const props = inject('props') as Props
 const issuing_cert = inject('issuing_cert') as Ref<boolean>
 const issuing_cert = inject('issuing_cert') as Ref<boolean>
@@ -53,7 +53,7 @@ const issue_cert = (config_name: string, server_name: string) => {
 async function callback(ssl_certificate: string, ssl_certificate_key: string) {
 async function callback(ssl_certificate: string, ssl_certificate_key: string) {
   directivesMap.value.ssl_certificate[0].params = ssl_certificate
   directivesMap.value.ssl_certificate[0].params = ssl_certificate
   directivesMap.value.ssl_certificate_key[0].params = ssl_certificate_key
   directivesMap.value.ssl_certificate_key[0].params = ssl_certificate_key
-  await save_site_config()
+  await save_config()
   change_auto_cert(true)
   change_auto_cert(true)
   emit('update:auto_cert', true)
   emit('update:auto_cert', true)
 }
 }
@@ -87,7 +87,7 @@ async function onchange(status: boolean) {
     ngx_config.servers.forEach(v => {
     ngx_config.servers.forEach(v => {
       v.locations = v?.locations?.filter(l => l.path !== '/.well-known/acme-challenge')
       v.locations = v?.locations?.filter(l => l.path !== '/.well-known/acme-challenge')
     })
     })
-    await save_site_config()
+    await save_config()
     change_auto_cert(status)
     change_auto_cert(status)
   }
   }
 
 

+ 13 - 9
app/src/views/domain/ngx_conf/NgxConfigEditor.vue

@@ -9,19 +9,22 @@ import type { CertificateInfo } from '@/api/cert'
 import NgxServer from '@/views/domain/ngx_conf/NgxServer.vue'
 import NgxServer from '@/views/domain/ngx_conf/NgxServer.vue'
 import NgxUpstream from '@/views/domain/ngx_conf/NgxUpstream.vue'
 import NgxUpstream from '@/views/domain/ngx_conf/NgxUpstream.vue'
 
 
-const props = defineProps<{
-  autoCert: boolean
+const props = withDefaults(defineProps<{
+  autoCert?: boolean
   enabled: boolean
   enabled: boolean
-  certInfo?: {
-    [key: number]: CertificateInfo
-  }
-}>()
+  certInfo?: Record<number, CertificateInfo>
+  context?: 'http' | 'stream'
+}>(), {
+  autoCert: false,
+  enabled: false,
+  context: 'http',
+})
 
 
 const emit = defineEmits(['callback', 'update:autoCert'])
 const emit = defineEmits(['callback', 'update:autoCert'])
 
 
 const { $gettext } = useGettext()
 const { $gettext } = useGettext()
 
 
-const save_site_config = inject('save_site_config') as () => Promise<void>
+const save_config = inject('save_config') as () => Promise<void>
 
 
 const [modal, ContextHolder] = Modal.useModal()
 const [modal, ContextHolder] = Modal.useModal()
 
 
@@ -57,7 +60,7 @@ function confirm_change_tls(status: boolean) {
 
 
         first.locations.push(...r.locations)
         first.locations.push(...r.locations)
       })
       })
-      await save_site_config()
+      await save_config()
 
 
       change_tls(status)
       change_tls(status)
     },
     },
@@ -170,7 +173,7 @@ const activeKey = ref(['3'])
   <div>
   <div>
     <ContextHolder />
     <ContextHolder />
     <AFormItem
     <AFormItem
-      v-if="!support_ssl"
+      v-if="!support_ssl && context === 'http'"
       :label="$gettext('Enable TLS')"
       :label="$gettext('Enable TLS')"
     >
     >
       <ASwitch @change="confirm_change_tls" />
       <ASwitch @change="confirm_change_tls" />
@@ -205,6 +208,7 @@ const activeKey = ref(['3'])
           v-model:auto-cert="autoCertRef"
           v-model:auto-cert="autoCertRef"
           :enabled="enabled"
           :enabled="enabled"
           :cert-info="certInfo"
           :cert-info="certInfo"
+          :context="context"
         />
         />
       </ACollapsePanel>
       </ACollapsePanel>
     </ACollapse>
     </ACollapse>

+ 10 - 3
app/src/views/domain/ngx_conf/NgxServer.vue

@@ -12,12 +12,15 @@ import DirectiveEditor from '@/views/domain/ngx_conf/directive/DirectiveEditor.v
 import type { NgxConfig, NgxDirective } from '@/api/ngx'
 import type { NgxConfig, NgxDirective } from '@/api/ngx'
 import type { CertificateInfo } from '@/api/cert'
 import type { CertificateInfo } from '@/api/cert'
 
 
-defineProps<{
+withDefaults(defineProps<{
   enabled: boolean
   enabled: boolean
   certInfo?: {
   certInfo?: {
     [key: number]: CertificateInfo
     [key: number]: CertificateInfo
   }
   }
-}>()
+  context: 'http' | 'stream'
+}>(), {
+  context: 'http',
+})
 
 
 const emit = defineEmits(['callback'])
 const emit = defineEmits(['callback'])
 
 
@@ -144,9 +147,13 @@ provide('ngx_directives', ngx_directives)
           </template>
           </template>
           <DirectiveEditor />
           <DirectiveEditor />
           <br>
           <br>
-          <ConfigTemplate :current-server-index="current_server_index" />
+          <ConfigTemplate
+            v-if="context === 'http'"
+            :current-server-index="current_server_index"
+          />
           <br>
           <br>
           <LocationEditor
           <LocationEditor
+            v-if="context === 'http'"
             :current-server-index="current_server_index"
             :current-server-index="current_server_index"
             :locations="v.locations"
             :locations="v.locations"
           />
           />

+ 319 - 0
app/src/views/stream/StreamEdit.vue

@@ -0,0 +1,319 @@
+<script setup lang="ts">
+import { useGettext } from 'vue3-gettext'
+import { message } from 'ant-design-vue'
+import type { Ref } from 'vue'
+import FooterToolBar from '@/components/FooterToolbar/FooterToolBar.vue'
+import CodeEditor from '@/components/CodeEditor/CodeEditor.vue'
+
+import NgxConfigEditor from '@/views/domain/ngx_conf/NgxConfigEditor.vue'
+import type { NgxConfig } from '@/api/ngx'
+import ngx from '@/api/ngx'
+import config from '@/api/config'
+import RightSettings from '@/views/stream/components/RightSettings.vue'
+import type { ChatComplicationMessage } from '@/api/openai'
+import type { Stream } from '@/api/stream'
+import stream from '@/api/stream'
+
+const { $gettext, interpolate } = useGettext()
+
+const route = useRoute()
+const router = useRouter()
+
+const name = ref(route.params.name.toString())
+
+watch(route, () => {
+  name.value = route.params?.name?.toString() ?? ''
+})
+
+const ngx_config: NgxConfig = reactive({
+  name: '',
+  upstreams: [],
+  servers: [],
+})
+
+const enabled = ref(false)
+const configText = ref('')
+const advance_mode_ref = ref(false)
+const saving = ref(false)
+const filename = ref('')
+const parse_error_status = ref(false)
+const parse_error_message = ref('')
+const data = ref({})
+
+init()
+
+const advance_mode = computed({
+  get() {
+    return advance_mode_ref.value || parse_error_status.value
+  },
+  set(v: boolean) {
+    advance_mode_ref.value = v
+  },
+})
+
+const history_chatgpt_record = ref([]) as Ref<ChatComplicationMessage[]>
+
+function handle_response(r: Stream) {
+  if (r.advanced)
+    advance_mode.value = true
+
+  if (r.advanced)
+    advance_mode.value = true
+
+  parse_error_status.value = false
+  parse_error_message.value = ''
+  filename.value = r.name
+  configText.value = r.config
+  enabled.value = r.enabled
+  history_chatgpt_record.value = r.chatgpt_messages
+  data.value = r
+  Object.assign(ngx_config, r.tokenized)
+}
+
+function init() {
+  if (name.value) {
+    stream.get(name.value).then(r => {
+      handle_response(r)
+    }).catch(handle_parse_error)
+  }
+  else {
+    history_chatgpt_record.value = []
+  }
+}
+
+function handle_parse_error(e: { error?: string; message: string }) {
+  console.error(e)
+  if (e?.error === 'nginx_config_syntax_error') {
+    parse_error_status.value = true
+    parse_error_message.value = e.message
+    config.get(`streams-available/${name.value}`).then(r => {
+      configText.value = r.content
+    })
+  }
+  else {
+    message.error($gettext(e?.message ?? 'Server error'))
+  }
+}
+
+function on_mode_change(advanced: boolean) {
+  stream.advance_mode(name.value, { advanced }).then(() => {
+    advance_mode.value = advanced
+    if (advanced) {
+      build_config()
+    }
+    else {
+      // eslint-disable-next-line promise/no-nesting
+      return ngx.tokenize_config(configText.value).then(r => {
+        Object.assign(ngx_config, r)
+        // eslint-disable-next-line promise/no-nesting
+      }).catch(handle_parse_error)
+    }
+  })
+}
+
+async function build_config() {
+  return ngx.build_config(ngx_config).then(r => {
+    configText.value = r.content
+  })
+}
+
+const save = async () => {
+  saving.value = true
+
+  if (!advance_mode.value) {
+    try {
+      await build_config()
+    }
+    catch (e) {
+      saving.value = false
+      message.error($gettext('Failed to save, syntax error(s) was detected in the configuration.'))
+
+      return
+    }
+  }
+
+  return stream.save(name.value, {
+    name: filename.value || name.value,
+    content: configText.value,
+    overwrite: true,
+  }).then(r => {
+    handle_response(r)
+    router.push({
+      path: `/stream/${filename.value}`,
+      query: route.query,
+    })
+    message.success($gettext('Saved successfully'))
+  }).catch(handle_parse_error).finally(() => {
+    saving.value = false
+  })
+}
+
+provide('save_config', save)
+provide('configText', configText)
+provide('ngx_config', ngx_config)
+provide('history_chatgpt_record', history_chatgpt_record)
+provide('enabled', enabled)
+provide('name', name)
+provide('filename', filename)
+provide('data', data)
+</script>
+
+<template>
+  <ARow :gutter="16">
+    <ACol
+      :xs="24"
+      :sm="24"
+      :md="18"
+    >
+      <ACard :bordered="false">
+        <template #title>
+          <span style="margin-right: 10px">{{ interpolate($gettext('Edit %{n}'), { n: name }) }}</span>
+          <ATag
+            v-if="enabled"
+            color="blue"
+          >
+            {{ $gettext('Enabled') }}
+          </ATag>
+          <ATag
+            v-else
+            color="orange"
+          >
+            {{ $gettext('Disabled') }}
+          </ATag>
+        </template>
+        <template #extra>
+          <div class="mode-switch">
+            <div class="switch">
+              <ASwitch
+                size="small"
+                :disabled="parse_error_status"
+                :checked="advance_mode"
+                @change="on_mode_change"
+              />
+            </div>
+            <template v-if="advance_mode">
+              <div>{{ $gettext('Advance Mode') }}</div>
+            </template>
+            <template v-else>
+              <div>{{ $gettext('Basic Mode') }}</div>
+            </template>
+          </div>
+        </template>
+
+        <Transition name="slide-fade">
+          <div
+            v-if="advance_mode"
+            key="advance"
+          >
+            <div
+              v-if="parse_error_status"
+              class="parse-error-alert-wrapper"
+            >
+              <AAlert
+                :message="$gettext('Nginx Configuration Parse Error')"
+                :description="parse_error_message"
+                type="error"
+                show-icon
+              />
+            </div>
+            <div>
+              <CodeEditor v-model:content="configText" />
+            </div>
+          </div>
+
+          <div
+            v-else
+            key="basic"
+            class="domain-edit-container"
+          >
+            <NgxConfigEditor
+              :enabled="enabled"
+              context="stream"
+              @callback="save"
+            />
+          </div>
+        </Transition>
+      </ACard>
+    </ACol>
+
+    <ACol
+      class="col-right"
+      :xs="24"
+      :sm="24"
+      :md="6"
+    >
+      <RightSettings />
+    </ACol>
+
+    <FooterToolBar>
+      <ASpace>
+        <AButton @click="$router.push('/streams')">
+          {{ $gettext('Back') }}
+        </AButton>
+        <AButton
+          type="primary"
+          :loading="saving"
+          @click="save"
+        >
+          {{ $gettext('Save') }}
+        </AButton>
+      </ASpace>
+    </FooterToolBar>
+  </ARow>
+</template>
+
+<style lang="less">
+
+</style>
+
+<style lang="less" scoped>
+.col-right {
+  position: relative;
+}
+
+.ant-card {
+  margin: 10px 0;
+  box-shadow: unset;
+}
+
+.mode-switch {
+  display: flex;
+
+  .switch {
+    display: flex;
+    align-items: center;
+    margin-right: 5px;
+  }
+}
+
+.parse-error-alert-wrapper {
+  margin-bottom: 20px;
+}
+
+.domain-edit-container {
+  max-width: 800px;
+  margin: 0 auto;
+}
+
+.slide-fade-enter-active {
+  transition: all .3s ease-in-out;
+}
+
+.slide-fade-leave-active {
+  transition: all .3s cubic-bezier(1.0, 0.5, 0.8, 1.0);
+}
+
+.slide-fade-enter-from, .slide-fade-enter-to, .slide-fade-leave-to
+  /* .slide-fade-leave-active for below version 2.1.8 */ {
+  transform: translateX(10px);
+  opacity: 0;
+}
+
+.directive-params-wrapper {
+  margin: 10px 0;
+}
+
+.tab-content {
+  padding: 10px;
+}
+</style>

+ 203 - 0
app/src/views/stream/StreamList.vue

@@ -0,0 +1,203 @@
+<script setup lang="tsx">
+import { useGettext } from 'vue3-gettext'
+import { Badge, message } from 'ant-design-vue'
+import StdTable from '@/components/StdDesign/StdDataDisplay/StdTable.vue'
+import type { customRender } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
+import { datetime } from '@/components/StdDesign/StdDataDisplay/StdTableTransformer'
+import stream from '@/api/stream'
+import { input } from '@/components/StdDesign/StdDataEntry'
+import InspectConfig from '@/views/config/InspectConfig.vue'
+import type { Column } from '@/components/StdDesign/types'
+import StreamDuplicate from '@/views/stream/components/StreamDuplicate.vue'
+
+const { $gettext } = useGettext()
+
+const columns: Column[] = [{
+  title: () => $gettext('Name'),
+  dataIndex: 'name',
+  sortable: true,
+  pithy: true,
+  edit: {
+    type: input,
+  },
+  search: true,
+}, {
+  title: () => $gettext('Status'),
+  dataIndex: 'enabled',
+  customRender: (args: customRender) => {
+    const template = []
+    const { text } = args
+    if (text === true || text > 0) {
+      template.push(<Badge status="success"/>)
+      template.push($gettext('Enabled'))
+    }
+    else {
+      template.push(<Badge status="warning"/>)
+      template.push($gettext('Disabled'))
+    }
+
+    return h('div', template)
+  },
+  sortable: true,
+  pithy: true,
+}, {
+  title: () => $gettext('Updated at'),
+  dataIndex: 'modified_at',
+  customRender: datetime,
+  sortable: true,
+  pithy: true,
+}, {
+  title: () => $gettext('Action'),
+  dataIndex: 'action',
+}]
+
+const table = ref()
+
+const inspect_config = ref()
+
+function enable(name: string) {
+  stream.enable(name).then(() => {
+    message.success($gettext('Enabled successfully'))
+    table.value?.get_list()
+    inspect_config.value?.test()
+  }).catch(r => {
+    message.error($gettext('Failed to enable %{msg}', { msg: r.message ?? '' }), 10)
+  })
+}
+
+function disable(name: string) {
+  stream.disable(name).then(() => {
+    message.success($gettext('Disabled successfully'))
+    table.value?.get_list()
+    inspect_config.value?.test()
+  }).catch(r => {
+    message.error($gettext('Failed to disable %{msg}', { msg: r.message ?? '' }))
+  })
+}
+
+function destroy(stream_name: string) {
+  stream.destroy(stream_name).then(() => {
+    table.value.get_list()
+    message.success($gettext('Delete stream: %{stream_name}', { stream_name }))
+    inspect_config.value?.test()
+  }).catch(e => {
+    message.error(e?.message ?? $gettext('Server error'))
+  })
+}
+
+const showDuplicator = ref(false)
+
+const target = ref('')
+
+function handle_click_duplicate(name: string) {
+  showDuplicator.value = true
+  target.value = name
+}
+
+const route = useRoute()
+
+watch(route, () => {
+  inspect_config.value?.test()
+})
+
+const showAddStream = ref(false)
+const name = ref('')
+function add() {
+  showAddStream.value = true
+  name.value = ''
+}
+
+function handleAddStream() {
+  stream.save(name.value, { name: name.value, content: 'server\t{\n\n}' }).then(() => {
+    showAddStream.value = false
+    table.value?.get_list()
+    message.success($gettext('Added successfully'))
+  }).catch(e => {
+    message.error(e?.message ?? $gettext('Server error'))
+  })
+}
+</script>
+
+<template>
+  <ACard :title="$gettext('Manage Streams')">
+    <template #extra>
+      <a @click="add">{{ $gettext('Add') }}</a>
+    </template>
+
+    <InspectConfig ref="inspect_config" />
+
+    <StdTable
+      ref="table"
+      :api="stream"
+      :columns="columns"
+      row-key="name"
+      disable-delete
+      @click-edit="r => $router.push({
+        path: `/stream/${r}`,
+      })"
+    >
+      <template #actions="{ record }">
+        <AButton
+          v-if="record.enabled"
+          type="link"
+          size="small"
+          @click="disable(record.name)"
+        >
+          {{ $gettext('Disable') }}
+        </AButton>
+        <AButton
+          v-else
+          type="link"
+          size="small"
+          @click="enable(record.name)"
+        >
+          {{ $gettext('Enable') }}
+        </AButton>
+        <ADivider type="vertical" />
+        <AButton
+          type="link"
+          size="small"
+          @click="handle_click_duplicate(record.name)"
+        >
+          {{ $gettext('Duplicate') }}
+        </AButton>
+        <ADivider type="vertical" />
+        <APopconfirm
+          :cancel-text="$gettext('No')"
+          :ok-text="$gettext('OK')"
+          :title="$gettext('Are you sure you want to delete?')"
+          :disabled="record.enabled"
+          @confirm="destroy(record.name)"
+        >
+          <AButton
+            type="link"
+            size="small"
+            :disabled="record.enabled"
+          >
+            {{ $gettext('Delete') }}
+          </AButton>
+        </APopconfirm>
+      </template>
+    </StdTable>
+    <AModal
+      v-model:open="showAddStream"
+      :title="$gettext('Add Stream')"
+      @ok="handleAddStream"
+    >
+      <AForm layout="vertical">
+        <AFormItem :label="$gettext('Name')">
+          <AInput v-model:value="name" />
+        </AFormItem>
+      </AForm>
+    </AModal>
+    <StreamDuplicate
+      v-model:visible="showDuplicator"
+      :name="target"
+      @duplicated="() => table.get_list()"
+    />
+  </ACard>
+</template>
+
+<style scoped>
+
+</style>

+ 129 - 0
app/src/views/stream/components/Deploy.vue

@@ -0,0 +1,129 @@
+<script setup lang="ts">
+import { useGettext } from 'vue3-gettext'
+import { InfoCircleOutlined } from '@ant-design/icons-vue'
+import { Modal, notification } from 'ant-design-vue'
+import type { Ref } from 'vue'
+import stream from '@/api/stream'
+import NodeSelector from '@/components/NodeSelector/NodeSelector.vue'
+
+const { $gettext, $ngettext } = useGettext()
+
+const node_map = reactive({})
+const target = ref([])
+const overwrite = ref(false)
+const enabled = ref(false)
+const name = inject('name') as Ref<string>
+const [modal, ContextHolder] = Modal.useModal()
+function deploy() {
+  modal.confirm({
+    title: () => $ngettext('Do you want to deploy this file to remote server?',
+      'Do you want to deploy this file to remote servers?', target.value.length),
+    mask: false,
+    centered: true,
+    okText: $gettext('OK'),
+    cancelText: $gettext('Cancel'),
+    onOk() {
+      target.value.forEach(id => {
+        const node_name = node_map[id]
+
+        // get source content
+        stream.get(name.value).then(r => {
+          stream.save(name.value, {
+            name: name.value,
+            content: r.config,
+            overwrite: overwrite.value,
+            // eslint-disable-next-line promise/no-nesting
+          }, { headers: { 'X-Node-ID': id } }).then(async () => {
+            notification.success({
+              message: $gettext('Deploy successfully'),
+              description:
+                $gettext('Deploy %{conf_name} to %{node_name} successfully',
+                  { conf_name: name.value, node_name }),
+            })
+            if (enabled.value) {
+              // eslint-disable-next-line promise/no-nesting
+              stream.enable(name.value).then(() => {
+                notification.success({
+                  message: $gettext('Enable successfully'),
+                  description:
+                    $gettext('Enable %{conf_name} in %{node_name} successfully',
+                      { conf_name: name.value, node_name }),
+                })
+                // eslint-disable-next-line promise/no-nesting
+              }).catch(e => {
+                notification.error({
+                  message: $gettext('Enable %{conf_name} in %{node_name} failed', {
+                    conf_name: name.value,
+                    node_name,
+                  }),
+                  description: $gettext(e?.message ?? 'Server error'),
+                })
+              })
+            }
+            // eslint-disable-next-line promise/no-nesting
+          }).catch(e => {
+            notification.error({
+              message: $gettext('Deploy %{conf_name} to %{node_name} failed', {
+                conf_name: name.value,
+                node_name,
+              }),
+              description: $gettext(e?.message ?? 'Server error'),
+            })
+          })
+        })
+      })
+    },
+  })
+}
+</script>
+
+<template>
+  <ContextHolder />
+  <NodeSelector
+    v-model:target="target"
+    hidden-local
+    :map="node_map"
+  />
+  <div class="node-deploy-control">
+    <ACheckbox v-model:checked="enabled">
+      {{ $gettext('Enable') }}
+    </ACheckbox>
+    <div class="overwrite">
+      <ACheckbox v-model:checked="overwrite">
+        {{ $gettext('Overwrite') }}
+      </ACheckbox>
+      <ATooltip placement="bottom">
+        <template #title>
+          {{ $gettext('Overwrite exist file') }}
+        </template>
+        <InfoCircleOutlined />
+      </ATooltip>
+    </div>
+
+    <AButton
+      :disabled="target.length === 0"
+      type="primary"
+      ghost
+      @click="deploy"
+    >
+      {{ $gettext('Deploy') }}
+    </AButton>
+  </div>
+</template>
+
+<style scoped lang="less">
+.overwrite {
+  margin-right: 15px;
+
+  span {
+    color: #9b9b9b;
+  }
+}
+
+.node-deploy-control {
+  display: flex;
+  justify-content: flex-end;
+  margin-top: 10px;
+  align-items: center;
+}
+</style>

+ 130 - 0
app/src/views/stream/components/RightSettings.vue

@@ -0,0 +1,130 @@
+<script setup lang="ts">
+import { useGettext } from 'vue3-gettext'
+import { Modal, message } from 'ant-design-vue'
+import type { Ref } from 'vue'
+import type { Stream } from '@/api/stream'
+import stream from '@/api/stream'
+import ChatGPT from '@/components/ChatGPT/ChatGPT.vue'
+import { formatDateTime } from '@/lib/helper'
+import Deploy from '@/views/stream/components/Deploy.vue'
+import { useSettingsStore } from '@/pinia'
+import type { ChatComplicationMessage } from '@/api/openai'
+import type { NgxConfig } from '@/api/ngx'
+
+const settings = useSettingsStore()
+const { $gettext } = useGettext()
+const configText = inject('configText') as Ref<string>
+const ngx_config = inject('ngx_config') as Ref<NgxConfig>
+const enabled = inject('enabled') as Ref<boolean>
+const name = inject('name') as Ref<string>
+const history_chatgpt_record = inject('history_chatgpt_record') as Ref<ChatComplicationMessage[]>
+const filename = inject('filename')
+const data = inject('data') as Ref<Stream>
+
+const [modal, ContextHolder] = Modal.useModal()
+
+const active_key = ref(['1', '2', '3'])
+
+function enable() {
+  stream.enable(name.value).then(() => {
+    message.success($gettext('Enabled successfully'))
+    enabled.value = true
+  }).catch(r => {
+    message.error($gettext('Failed to enable %{msg}', { msg: r.message ?? '' }), 10)
+  })
+}
+
+function disable() {
+  stream.disable(name.value).then(() => {
+    message.success($gettext('Disabled successfully'))
+    enabled.value = false
+  }).catch(r => {
+    message.error($gettext('Failed to disable %{msg}', { msg: r.message ?? '' }))
+  })
+}
+
+function on_change_enabled(checked: boolean) {
+  modal.confirm({
+    title: checked ? $gettext('Do you want to enable this stream?') : $gettext('Do you want to disable this stream?'),
+    mask: false,
+    centered: true,
+    okText: $gettext('OK'),
+    cancelText: $gettext('Cancel'),
+    async onOk() {
+      if (checked)
+        enable()
+      else
+        disable()
+    },
+  })
+}
+
+</script>
+
+<template>
+  <ACard
+    class="right-settings"
+    :bordered="false"
+  >
+    <ContextHolder />
+    <ACollapse
+      v-model:activeKey="active_key"
+      ghost
+    >
+      <ACollapsePanel
+        key="1"
+        :header="$gettext('Basic')"
+      >
+        <AFormItem :label="$gettext('Enabled')">
+          <ASwitch
+            :checked="enabled"
+            @change="on_change_enabled"
+          />
+        </AFormItem>
+        <AFormItem :label="$gettext('Name')">
+          <AInput v-model:value="filename" />
+        </AFormItem>
+        <AFormItem :label="$gettext('Updated at')">
+          {{ formatDateTime(data.modified_at) }}
+        </AFormItem>
+      </ACollapsePanel>
+      <ACollapsePanel
+        v-if="!settings.is_remote"
+        key="2"
+        :header="$gettext('Deploy')"
+      >
+        <Deploy />
+      </ACollapsePanel>
+      <ACollapsePanel
+        key="3"
+        header="ChatGPT"
+      >
+        <ChatGPT
+          v-model:history-messages="history_chatgpt_record"
+          :content="configText"
+          :path="ngx_config.file_name"
+        />
+      </ACollapsePanel>
+    </ACollapse>
+  </ACard>
+</template>
+
+<style scoped lang="less">
+.right-settings {
+  position: sticky;
+  top: 78px;
+
+  :deep(.ant-card-body) {
+    max-height: 100vh;
+    overflow-y: scroll;
+  }
+}
+
+:deep(.ant-collapse-ghost > .ant-collapse-item > .ant-collapse-content > .ant-collapse-content-box) {
+  padding: 0;
+}
+
+:deep(.ant-collapse > .ant-collapse-item > .ant-collapse-header) {
+  padding: 0 0 10px 0;
+}
+</style>

+ 156 - 0
app/src/views/stream/components/StreamDuplicate.vue

@@ -0,0 +1,156 @@
+<script setup lang="ts">
+import { useGettext } from 'vue3-gettext'
+import { Form, message, notification } from 'ant-design-vue'
+import gettext from '@/gettext'
+import stream from '@/api/stream'
+import NodeSelector from '@/components/NodeSelector/NodeSelector.vue'
+import { useSettingsStore } from '@/pinia'
+
+const props = defineProps<{
+  visible: boolean
+  name: string
+}>()
+
+const emit = defineEmits(['update:visible', 'duplicated'])
+
+const { $gettext } = useGettext()
+
+const settings = useSettingsStore()
+
+const show = computed({
+  get() {
+    return props.visible
+  },
+  set(v) {
+    emit('update:visible', v)
+  },
+})
+
+interface Model {
+  name: string // site name
+  target: number[] // ids of deploy targets
+}
+
+const modelRef: Model = reactive({ name: '', target: [] })
+
+const rulesRef = reactive({
+  name: [
+    {
+      required: true,
+      message: () => $gettext('Please input name, '
+        + 'this will be used as the filename of the new configuration!'),
+    },
+  ],
+  target: [
+    {
+      required: true,
+      message: () => $gettext('Please select at least one node!'),
+    },
+  ],
+})
+
+const { validate, validateInfos, clearValidate } = Form.useForm(modelRef, rulesRef)
+
+const loading = ref(false)
+
+const node_map: Record<number, string> = reactive({})
+
+function onSubmit() {
+  validate().then(async () => {
+    loading.value = true
+
+    modelRef.target.forEach(id => {
+      if (id === 0) {
+        // eslint-disable-next-line promise/no-nesting
+        stream.duplicate(props.name, { name: modelRef.name }).then(() => {
+          message.success($gettext('Duplicate to local successfully'))
+          show.value = false
+          emit('duplicated')
+          // eslint-disable-next-line promise/no-nesting
+        }).catch(e => {
+          message.error($gettext(e?.message ?? 'Server error'))
+        })
+      }
+      else {
+        // get source content
+        // eslint-disable-next-line promise/no-nesting
+        stream.get(props.name).then(r => {
+          stream.save(modelRef.name, {
+            name: modelRef.name,
+            content: r.config,
+            // eslint-disable-next-line promise/no-nesting
+          }, { headers: { 'X-Node-ID': id } }).then(() => {
+            notification.success({
+              message: $gettext('Duplicate successfully'),
+              description:
+                $gettext('Duplicate %{conf_name} to %{node_name} successfully',
+                  { conf_name: props.name, node_name: node_map[id] }),
+            })
+            // eslint-disable-next-line promise/no-nesting
+          }).catch(e => {
+            notification.error({
+              message: $gettext('Duplicate failed'),
+              description: $gettext(e?.message ?? 'Server error'),
+            })
+          })
+          if (r.enabled) {
+            // eslint-disable-next-line promise/no-nesting
+            stream.enable(modelRef.name, { headers: { 'X-Node-ID': id } }).then(() => {
+              notification.success({
+                message: $gettext('Enabled successfully'),
+              })
+            })
+          }
+        })
+      }
+    })
+
+    loading.value = false
+  })
+}
+
+watch(() => props.visible, v => {
+  if (v) {
+    modelRef.name = props.name // default with source name
+    modelRef.target = [0]
+    nextTick(() => clearValidate())
+  }
+})
+
+watch(() => gettext.current, () => {
+  clearValidate()
+})
+</script>
+
+<template>
+  <AModal
+    v-model:open="show"
+    :title="$gettext('Duplicate')"
+    :confirm-loading="loading"
+    :mask="null"
+    @ok="onSubmit"
+  >
+    <AForm layout="vertical">
+      <AFormItem
+        :label="$gettext('Name')"
+        v-bind="validateInfos.name"
+      >
+        <AInput v-model:value="modelRef.name" />
+      </AFormItem>
+      <AFormItem
+        v-if="!settings.is_remote"
+        :label="$gettext('Target')"
+        v-bind="validateInfos.target"
+      >
+        <NodeSelector
+          v-model:target="modelRef.target"
+          :map="node_map"
+        />
+      </AFormItem>
+    </AForm>
+  </AModal>
+</template>
+
+<style lang="less" scoped>
+
+</style>

+ 20 - 20
model/chatgpt_log.go

@@ -1,35 +1,35 @@
 package model
 package model
 
 
 import (
 import (
-    "database/sql/driver"
-    "encoding/json"
-    "fmt"
-    "github.com/pkg/errors"
-    "github.com/sashabaranov/go-openai"
+	"database/sql/driver"
+	"encoding/json"
+	"fmt"
+	"github.com/pkg/errors"
+	"github.com/sashabaranov/go-openai"
 )
 )
 
 
-type JSON []openai.ChatCompletionMessage
+type ChatGPTCompletionMessages []openai.ChatCompletionMessage
 
 
-// Scan scan value into Jsonb, implements sql.Scanner interface
-func (j *JSON) Scan(value interface{}) error {
-    bytes, ok := value.([]byte)
-    if !ok {
-        return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
-    }
+// Scan value into Jsonb, implements sql.Scanner interface
+func (j *ChatGPTCompletionMessages) Scan(value interface{}) error {
+	bytes, ok := value.([]byte)
+	if !ok {
+		return errors.New(fmt.Sprint("Failed to unmarshal JSONB value:", value))
+	}
 
 
-    result := make([]openai.ChatCompletionMessage, 0)
-    err := json.Unmarshal(bytes, &result)
-    *j = result
+	result := make([]openai.ChatCompletionMessage, 0)
+	err := json.Unmarshal(bytes, &result)
+	*j = result
 
 
-    return err
+	return err
 }
 }
 
 
 // Value return json value, implement driver.Valuer interface
 // Value return json value, implement driver.Valuer interface
-func (j *JSON) Value() (driver.Value, error) {
-    return json.Marshal(*j)
+func (j *ChatGPTCompletionMessages) Value() (driver.Value, error) {
+	return json.Marshal(*j)
 }
 }
 
 
 type ChatGPTLog struct {
 type ChatGPTLog struct {
-    Name    string `json:"name"`
-    Content JSON   `json:"content" gorm:"serializer:json"`
+	Name    string                    `json:"name"`
+	Content ChatGPTCompletionMessages `json:"content" gorm:"serializer:json"`
 }
 }

+ 1 - 0
model/model.go

@@ -32,6 +32,7 @@ func GenerateAllModel() []any {
 		Cert{},
 		Cert{},
 		ChatGPTLog{},
 		ChatGPTLog{},
 		Site{},
 		Site{},
+		Stream{},
 		DnsCredential{},
 		DnsCredential{},
 		Environment{},
 		Environment{},
 		Notification{},
 		Notification{},

+ 8 - 0
model/stream.go

@@ -0,0 +1,8 @@
+package model
+
+type Stream struct {
+	Model
+	Path            string                    `json:"path"`
+	Advanced        bool                      `json:"advanced"`
+	ChatGPTMessages ChatGPTCompletionMessages `json:"chatgpt_messages" gorm:"serializer:json"`
+}

+ 8 - 0
query/gen.go

@@ -26,6 +26,7 @@ var (
 	Environment   *environment
 	Environment   *environment
 	Notification  *notification
 	Notification  *notification
 	Site          *site
 	Site          *site
+	Stream        *stream
 )
 )
 
 
 func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
 func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
@@ -39,6 +40,7 @@ func SetDefault(db *gorm.DB, opts ...gen.DOOption) {
 	Environment = &Q.Environment
 	Environment = &Q.Environment
 	Notification = &Q.Notification
 	Notification = &Q.Notification
 	Site = &Q.Site
 	Site = &Q.Site
+	Stream = &Q.Stream
 }
 }
 
 
 func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
 func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
@@ -53,6 +55,7 @@ func Use(db *gorm.DB, opts ...gen.DOOption) *Query {
 		Environment:   newEnvironment(db, opts...),
 		Environment:   newEnvironment(db, opts...),
 		Notification:  newNotification(db, opts...),
 		Notification:  newNotification(db, opts...),
 		Site:          newSite(db, opts...),
 		Site:          newSite(db, opts...),
+		Stream:        newStream(db, opts...),
 	}
 	}
 }
 }
 
 
@@ -68,6 +71,7 @@ type Query struct {
 	Environment   environment
 	Environment   environment
 	Notification  notification
 	Notification  notification
 	Site          site
 	Site          site
+	Stream        stream
 }
 }
 
 
 func (q *Query) Available() bool { return q.db != nil }
 func (q *Query) Available() bool { return q.db != nil }
@@ -84,6 +88,7 @@ func (q *Query) clone(db *gorm.DB) *Query {
 		Environment:   q.Environment.clone(db),
 		Environment:   q.Environment.clone(db),
 		Notification:  q.Notification.clone(db),
 		Notification:  q.Notification.clone(db),
 		Site:          q.Site.clone(db),
 		Site:          q.Site.clone(db),
+		Stream:        q.Stream.clone(db),
 	}
 	}
 }
 }
 
 
@@ -107,6 +112,7 @@ func (q *Query) ReplaceDB(db *gorm.DB) *Query {
 		Environment:   q.Environment.replaceDB(db),
 		Environment:   q.Environment.replaceDB(db),
 		Notification:  q.Notification.replaceDB(db),
 		Notification:  q.Notification.replaceDB(db),
 		Site:          q.Site.replaceDB(db),
 		Site:          q.Site.replaceDB(db),
+		Stream:        q.Stream.replaceDB(db),
 	}
 	}
 }
 }
 
 
@@ -120,6 +126,7 @@ type queryCtx struct {
 	Environment   *environmentDo
 	Environment   *environmentDo
 	Notification  *notificationDo
 	Notification  *notificationDo
 	Site          *siteDo
 	Site          *siteDo
+	Stream        *streamDo
 }
 }
 
 
 func (q *Query) WithContext(ctx context.Context) *queryCtx {
 func (q *Query) WithContext(ctx context.Context) *queryCtx {
@@ -133,6 +140,7 @@ func (q *Query) WithContext(ctx context.Context) *queryCtx {
 		Environment:   q.Environment.WithContext(ctx),
 		Environment:   q.Environment.WithContext(ctx),
 		Notification:  q.Notification.WithContext(ctx),
 		Notification:  q.Notification.WithContext(ctx),
 		Site:          q.Site.WithContext(ctx),
 		Site:          q.Site.WithContext(ctx),
+		Stream:        q.Stream.WithContext(ctx),
 	}
 	}
 }
 }
 
 

+ 374 - 0
query/streams.gen.go

@@ -0,0 +1,374 @@
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+// Code generated by gorm.io/gen. DO NOT EDIT.
+
+package query
+
+import (
+	"context"
+	"strings"
+
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+	"gorm.io/gorm/schema"
+
+	"gorm.io/gen"
+	"gorm.io/gen/field"
+
+	"gorm.io/plugin/dbresolver"
+
+	"github.com/0xJacky/Nginx-UI/model"
+)
+
+func newStream(db *gorm.DB, opts ...gen.DOOption) stream {
+	_stream := stream{}
+
+	_stream.streamDo.UseDB(db, opts...)
+	_stream.streamDo.UseModel(&model.Stream{})
+
+	tableName := _stream.streamDo.TableName()
+	_stream.ALL = field.NewAsterisk(tableName)
+	_stream.ID = field.NewInt(tableName, "id")
+	_stream.CreatedAt = field.NewTime(tableName, "created_at")
+	_stream.UpdatedAt = field.NewTime(tableName, "updated_at")
+	_stream.DeletedAt = field.NewField(tableName, "deleted_at")
+	_stream.Path = field.NewString(tableName, "path")
+	_stream.Advanced = field.NewBool(tableName, "advanced")
+	_stream.ChatGPTMessages = field.NewField(tableName, "chat_gpt_messages")
+
+	_stream.fillFieldMap()
+
+	return _stream
+}
+
+type stream struct {
+	streamDo
+
+	ALL             field.Asterisk
+	ID              field.Int
+	CreatedAt       field.Time
+	UpdatedAt       field.Time
+	DeletedAt       field.Field
+	Path            field.String
+	Advanced        field.Bool
+	ChatGPTMessages field.Field
+
+	fieldMap map[string]field.Expr
+}
+
+func (s stream) Table(newTableName string) *stream {
+	s.streamDo.UseTable(newTableName)
+	return s.updateTableName(newTableName)
+}
+
+func (s stream) As(alias string) *stream {
+	s.streamDo.DO = *(s.streamDo.As(alias).(*gen.DO))
+	return s.updateTableName(alias)
+}
+
+func (s *stream) updateTableName(table string) *stream {
+	s.ALL = field.NewAsterisk(table)
+	s.ID = field.NewInt(table, "id")
+	s.CreatedAt = field.NewTime(table, "created_at")
+	s.UpdatedAt = field.NewTime(table, "updated_at")
+	s.DeletedAt = field.NewField(table, "deleted_at")
+	s.Path = field.NewString(table, "path")
+	s.Advanced = field.NewBool(table, "advanced")
+	s.ChatGPTMessages = field.NewField(table, "chat_gpt_messages")
+
+	s.fillFieldMap()
+
+	return s
+}
+
+func (s *stream) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
+	_f, ok := s.fieldMap[fieldName]
+	if !ok || _f == nil {
+		return nil, false
+	}
+	_oe, ok := _f.(field.OrderExpr)
+	return _oe, ok
+}
+
+func (s *stream) fillFieldMap() {
+	s.fieldMap = make(map[string]field.Expr, 7)
+	s.fieldMap["id"] = s.ID
+	s.fieldMap["created_at"] = s.CreatedAt
+	s.fieldMap["updated_at"] = s.UpdatedAt
+	s.fieldMap["deleted_at"] = s.DeletedAt
+	s.fieldMap["path"] = s.Path
+	s.fieldMap["advanced"] = s.Advanced
+	s.fieldMap["chat_gpt_messages"] = s.ChatGPTMessages
+}
+
+func (s stream) clone(db *gorm.DB) stream {
+	s.streamDo.ReplaceConnPool(db.Statement.ConnPool)
+	return s
+}
+
+func (s stream) replaceDB(db *gorm.DB) stream {
+	s.streamDo.ReplaceDB(db)
+	return s
+}
+
+type streamDo struct{ gen.DO }
+
+// FirstByID Where("id=@id")
+func (s streamDo) FirstByID(id int) (result *model.Stream, err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = s.UnderlyingDB().Where(generateSQL.String(), params...).Take(&result) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+// DeleteByID update @@table set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=@id
+func (s streamDo) DeleteByID(id int) (err error) {
+	var params []interface{}
+
+	var generateSQL strings.Builder
+	params = append(params, id)
+	generateSQL.WriteString("update streams set deleted_at=strftime('%Y-%m-%d %H:%M:%S','now') where id=? ")
+
+	var executeSQL *gorm.DB
+	executeSQL = s.UnderlyingDB().Exec(generateSQL.String(), params...) // ignore_security_alert
+	err = executeSQL.Error
+
+	return
+}
+
+func (s streamDo) Debug() *streamDo {
+	return s.withDO(s.DO.Debug())
+}
+
+func (s streamDo) WithContext(ctx context.Context) *streamDo {
+	return s.withDO(s.DO.WithContext(ctx))
+}
+
+func (s streamDo) ReadDB() *streamDo {
+	return s.Clauses(dbresolver.Read)
+}
+
+func (s streamDo) WriteDB() *streamDo {
+	return s.Clauses(dbresolver.Write)
+}
+
+func (s streamDo) Session(config *gorm.Session) *streamDo {
+	return s.withDO(s.DO.Session(config))
+}
+
+func (s streamDo) Clauses(conds ...clause.Expression) *streamDo {
+	return s.withDO(s.DO.Clauses(conds...))
+}
+
+func (s streamDo) Returning(value interface{}, columns ...string) *streamDo {
+	return s.withDO(s.DO.Returning(value, columns...))
+}
+
+func (s streamDo) Not(conds ...gen.Condition) *streamDo {
+	return s.withDO(s.DO.Not(conds...))
+}
+
+func (s streamDo) Or(conds ...gen.Condition) *streamDo {
+	return s.withDO(s.DO.Or(conds...))
+}
+
+func (s streamDo) Select(conds ...field.Expr) *streamDo {
+	return s.withDO(s.DO.Select(conds...))
+}
+
+func (s streamDo) Where(conds ...gen.Condition) *streamDo {
+	return s.withDO(s.DO.Where(conds...))
+}
+
+func (s streamDo) Order(conds ...field.Expr) *streamDo {
+	return s.withDO(s.DO.Order(conds...))
+}
+
+func (s streamDo) Distinct(cols ...field.Expr) *streamDo {
+	return s.withDO(s.DO.Distinct(cols...))
+}
+
+func (s streamDo) Omit(cols ...field.Expr) *streamDo {
+	return s.withDO(s.DO.Omit(cols...))
+}
+
+func (s streamDo) Join(table schema.Tabler, on ...field.Expr) *streamDo {
+	return s.withDO(s.DO.Join(table, on...))
+}
+
+func (s streamDo) LeftJoin(table schema.Tabler, on ...field.Expr) *streamDo {
+	return s.withDO(s.DO.LeftJoin(table, on...))
+}
+
+func (s streamDo) RightJoin(table schema.Tabler, on ...field.Expr) *streamDo {
+	return s.withDO(s.DO.RightJoin(table, on...))
+}
+
+func (s streamDo) Group(cols ...field.Expr) *streamDo {
+	return s.withDO(s.DO.Group(cols...))
+}
+
+func (s streamDo) Having(conds ...gen.Condition) *streamDo {
+	return s.withDO(s.DO.Having(conds...))
+}
+
+func (s streamDo) Limit(limit int) *streamDo {
+	return s.withDO(s.DO.Limit(limit))
+}
+
+func (s streamDo) Offset(offset int) *streamDo {
+	return s.withDO(s.DO.Offset(offset))
+}
+
+func (s streamDo) Scopes(funcs ...func(gen.Dao) gen.Dao) *streamDo {
+	return s.withDO(s.DO.Scopes(funcs...))
+}
+
+func (s streamDo) Unscoped() *streamDo {
+	return s.withDO(s.DO.Unscoped())
+}
+
+func (s streamDo) Create(values ...*model.Stream) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return s.DO.Create(values)
+}
+
+func (s streamDo) CreateInBatches(values []*model.Stream, batchSize int) error {
+	return s.DO.CreateInBatches(values, batchSize)
+}
+
+// Save : !!! underlying implementation is different with GORM
+// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
+func (s streamDo) Save(values ...*model.Stream) error {
+	if len(values) == 0 {
+		return nil
+	}
+	return s.DO.Save(values)
+}
+
+func (s streamDo) First() (*model.Stream, error) {
+	if result, err := s.DO.First(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Stream), nil
+	}
+}
+
+func (s streamDo) Take() (*model.Stream, error) {
+	if result, err := s.DO.Take(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Stream), nil
+	}
+}
+
+func (s streamDo) Last() (*model.Stream, error) {
+	if result, err := s.DO.Last(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Stream), nil
+	}
+}
+
+func (s streamDo) Find() ([]*model.Stream, error) {
+	result, err := s.DO.Find()
+	return result.([]*model.Stream), err
+}
+
+func (s streamDo) FindInBatch(batchSize int, fc func(tx gen.Dao, batch int) error) (results []*model.Stream, err error) {
+	buf := make([]*model.Stream, 0, batchSize)
+	err = s.DO.FindInBatches(&buf, batchSize, func(tx gen.Dao, batch int) error {
+		defer func() { results = append(results, buf...) }()
+		return fc(tx, batch)
+	})
+	return results, err
+}
+
+func (s streamDo) FindInBatches(result *[]*model.Stream, batchSize int, fc func(tx gen.Dao, batch int) error) error {
+	return s.DO.FindInBatches(result, batchSize, fc)
+}
+
+func (s streamDo) Attrs(attrs ...field.AssignExpr) *streamDo {
+	return s.withDO(s.DO.Attrs(attrs...))
+}
+
+func (s streamDo) Assign(attrs ...field.AssignExpr) *streamDo {
+	return s.withDO(s.DO.Assign(attrs...))
+}
+
+func (s streamDo) Joins(fields ...field.RelationField) *streamDo {
+	for _, _f := range fields {
+		s = *s.withDO(s.DO.Joins(_f))
+	}
+	return &s
+}
+
+func (s streamDo) Preload(fields ...field.RelationField) *streamDo {
+	for _, _f := range fields {
+		s = *s.withDO(s.DO.Preload(_f))
+	}
+	return &s
+}
+
+func (s streamDo) FirstOrInit() (*model.Stream, error) {
+	if result, err := s.DO.FirstOrInit(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Stream), nil
+	}
+}
+
+func (s streamDo) FirstOrCreate() (*model.Stream, error) {
+	if result, err := s.DO.FirstOrCreate(); err != nil {
+		return nil, err
+	} else {
+		return result.(*model.Stream), nil
+	}
+}
+
+func (s streamDo) FindByPage(offset int, limit int) (result []*model.Stream, count int64, err error) {
+	result, err = s.Offset(offset).Limit(limit).Find()
+	if err != nil {
+		return
+	}
+
+	if size := len(result); 0 < limit && 0 < size && size < limit {
+		count = int64(size + offset)
+		return
+	}
+
+	count, err = s.Offset(-1).Limit(-1).Count()
+	return
+}
+
+func (s streamDo) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
+	count, err = s.Count()
+	if err != nil {
+		return
+	}
+
+	err = s.Offset(offset).Limit(limit).Scan(result)
+	return
+}
+
+func (s streamDo) Scan(result interface{}) (err error) {
+	return s.DO.Scan(result)
+}
+
+func (s streamDo) Delete(models ...*model.Stream) (result gen.ResultInfo, err error) {
+	return s.DO.Delete(models)
+}
+
+func (s *streamDo) withDO(do gen.Dao) *streamDo {
+	s.DO = *do.(*gen.DO)
+	return s
+}

+ 2 - 0
router/routers.go

@@ -9,6 +9,7 @@ import (
 	"github.com/0xJacky/Nginx-UI/api/notification"
 	"github.com/0xJacky/Nginx-UI/api/notification"
 	"github.com/0xJacky/Nginx-UI/api/openai"
 	"github.com/0xJacky/Nginx-UI/api/openai"
 	"github.com/0xJacky/Nginx-UI/api/sites"
 	"github.com/0xJacky/Nginx-UI/api/sites"
+	"github.com/0xJacky/Nginx-UI/api/streams"
 	"github.com/0xJacky/Nginx-UI/api/system"
 	"github.com/0xJacky/Nginx-UI/api/system"
 	"github.com/0xJacky/Nginx-UI/api/template"
 	"github.com/0xJacky/Nginx-UI/api/template"
 	"github.com/0xJacky/Nginx-UI/api/terminal"
 	"github.com/0xJacky/Nginx-UI/api/terminal"
@@ -49,6 +50,7 @@ func InitRouter() *gin.Engine {
 			user.InitManageUserRouter(g)
 			user.InitManageUserRouter(g)
 			nginx.InitRouter(g)
 			nginx.InitRouter(g)
 			sites.InitRouter(g)
 			sites.InitRouter(g)
+			streams.InitRouter(g)
 			config.InitRouter(g)
 			config.InitRouter(g)
 			template.InitRouter(g)
 			template.InitRouter(g)
 			certificate.InitCertificateRouter(g)
 			certificate.InitCertificateRouter(g)