Sfoglia il codice sorgente

Finalized Stream proxy module

Toby Chui 9 mesi fa
parent
commit
28189fb4af
3 ha cambiato i file con 302 aggiunte e 305 eliminazioni
  1. 288 291
      emails.go
  2. 11 11
      web/components/streamprox.html
  3. 3 3
      web/main.css

+ 288 - 291
emails.go

@@ -1,291 +1,288 @@
-package main
-
-import (
-	"bytes"
-	"encoding/gob"
-	"encoding/json"
-	"net/http"
-	"strconv"
-	"strings"
-	"time"
-
-	"github.com/google/uuid"
-	"imuslab.com/zoraxy/mod/email"
-	"imuslab.com/zoraxy/mod/utils"
-)
-
-/*
-	SMTP Settings and Test Email Handlers
-*/
-
-func HandleSMTPSet(w http.ResponseWriter, r *http.Request) {
-	hostname, err := utils.PostPara(r, "hostname")
-	if err != nil {
-		utils.SendErrorResponse(w, "hostname cannot be empty")
-		return
-	}
-
-	portString, err := utils.PostPara(r, "port")
-	if err != nil {
-		utils.SendErrorResponse(w, "port must be a valid integer")
-		return
-	}
-
-	port, err := strconv.Atoi(portString)
-	if err != nil {
-		utils.SendErrorResponse(w, "port must be a valid integer")
-		return
-	}
-
-	username, err := utils.PostPara(r, "username")
-	if err != nil {
-		utils.SendErrorResponse(w, "username cannot be empty")
-		return
-	}
-
-	password, err := utils.PostPara(r, "password")
-	if err != nil {
-		//Empty password. Use old one if exists
-		oldConfig := loadSMTPConfig()
-		if oldConfig.Password == "" {
-			utils.SendErrorResponse(w, "password cannot be empty")
-			return
-		} else {
-			password = oldConfig.Password
-		}
-	}
-
-	senderAddr, err := utils.PostPara(r, "senderAddr")
-	if err != nil {
-		utils.SendErrorResponse(w, "senderAddr cannot be empty")
-		return
-	}
-
-	adminAddr, err := utils.PostPara(r, "adminAddr")
-	if err != nil {
-		utils.SendErrorResponse(w, "adminAddr cannot be empty")
-		return
-	}
-
-	//Set the email sender properties
-	thisEmailSender := email.Sender{
-		Hostname:   strings.TrimSpace(hostname),
-		Port:       port,
-		Username:   strings.TrimSpace(username),
-		Password:   strings.TrimSpace(password),
-		SenderAddr: strings.TrimSpace(senderAddr),
-	}
-
-	//Write this into database
-	setSMTPConfig(&thisEmailSender)
-
-	//Update the current EmailSender
-	EmailSender = &thisEmailSender
-
-	//Set the admin address of password reset
-	setSMTPAdminAddress(adminAddr)
-
-	//Reply ok
-	utils.SendOK(w)
-}
-
-func HandleSMTPGet(w http.ResponseWriter, r *http.Request) {
-	// Create a buffer to store the encoded value
-	var buf bytes.Buffer
-
-	// Encode the original object into the buffer
-	encoder := gob.NewEncoder(&buf)
-	err := encoder.Encode(*EmailSender)
-	if err != nil {
-		utils.SendErrorResponse(w, "Internal encode error")
-		return
-	}
-
-	// Decode the buffer into a new object
-	var copied email.Sender
-	decoder := gob.NewDecoder(&buf)
-	err = decoder.Decode(&copied)
-	if err != nil {
-		utils.SendErrorResponse(w, "Internal decode error")
-		return
-	}
-
-	copied.Password = ""
-
-	js, _ := json.Marshal(copied)
-	utils.SendJSONResponse(w, string(js))
-}
-
-func HandleAdminEmailGet(w http.ResponseWriter, r *http.Request) {
-	js, _ := json.Marshal(loadSMTPAdminAddr())
-	utils.SendJSONResponse(w, string(js))
-}
-
-func HandleTestEmailSend(w http.ResponseWriter, r *http.Request) {
-	adminEmailAccount := loadSMTPAdminAddr()
-	if adminEmailAccount == "" {
-		utils.SendErrorResponse(w, "Management account not set")
-		return
-	}
-
-	err := EmailSender.SendEmail(adminEmailAccount,
-		"SMTP Testing Email | Zoraxy", "This is a test email sent by Zoraxy. Please do not reply to this email.<br>Zoraxy からのテストメールです。このメールには返信しないでください。<br>這是由 Zoraxy 發送的測試電子郵件。請勿回覆此郵件。<br>Ceci est un email de test envoyé par Zoraxy. Merci de ne pas répondre à cet email.<br>Dies ist eine Test-E-Mail, die von Zoraxy gesendet wurde. Bitte antworten Sie nicht auf diese E-Mail.")
-
-	if err != nil {
-		utils.SendErrorResponse(w, err.Error())
-		return
-	}
-
-	utils.SendOK(w)
-}
-
-/*
-	SMTP config
-
-	The following handle SMTP configs
-*/
-
-func setSMTPConfig(config *email.Sender) error {
-	return sysdb.Write("smtp", "config", config)
-}
-
-func loadSMTPConfig() *email.Sender {
-	if sysdb.KeyExists("smtp", "config") {
-		thisEmailSender := email.Sender{
-			Port: 587,
-		}
-		err := sysdb.Read("smtp", "config", &thisEmailSender)
-		if err != nil {
-			return &email.Sender{
-				Port: 587,
-			}
-		}
-		return &thisEmailSender
-	} else {
-		//Not set. Return an empty one
-		return &email.Sender{
-			Port: 587,
-		}
-	}
-}
-
-func setSMTPAdminAddress(adminAddr string) error {
-	return sysdb.Write("smtp", "admin", adminAddr)
-}
-
-// Load SMTP admin address. Return empty string if not set
-func loadSMTPAdminAddr() string {
-	adminAddr := ""
-	if sysdb.KeyExists("smtp", "admin") {
-		err := sysdb.Read("smtp", "admin", &adminAddr)
-		if err != nil {
-			return ""
-		}
-		return adminAddr
-	} else {
-		return ""
-	}
-}
-
-/*
-	Admin Account Reset
-*/
-
-var (
-	accountResetEmailDelay   int64  = 30      //Delay between each account reset email, default 30s
-	tokenValidDuration       int64  = 15 * 60 //Duration of the token, default 15 minutes
-	lastAccountResetEmail    int64  = 0       //Timestamp for last sent account reset email
-	passwordResetAccessToken string = ""      //Access token for resetting password
-)
-
-func HandleAdminAccountResetEmail(w http.ResponseWriter, r *http.Request) {
-	if EmailSender.Username == "" {
-		//Reset account not setup
-		utils.SendErrorResponse(w, "Reset account not setup.")
-		return
-	}
-
-	if loadSMTPAdminAddr() == "" {
-		utils.SendErrorResponse(w, "Reset account not setup.")
-	}
-
-	//Check if the delay expired
-	if lastAccountResetEmail+accountResetEmailDelay > time.Now().Unix() {
-		//Too frequent
-		utils.SendErrorResponse(w, "You cannot send another account reset email in cooldown time")
-		return
-	}
-
-	passwordResetAccessToken = uuid.New().String()
-
-	//SMTP info exists. Send reset account email
-	lastAccountResetEmail = time.Now().Unix()
-	EmailSender.SendEmail(loadSMTPAdminAddr(), "Management Account Reset | Zoraxy",
-		"Enter the following reset token to reset your password on your Zoraxy router.<br>"+passwordResetAccessToken+"<br><br> This is an automated generated email. DO NOT REPLY TO THIS EMAIL.")
-
-	utils.SendOK(w)
-}
-
-func HandleNewPasswordSetup(w http.ResponseWriter, r *http.Request) {
-	if passwordResetAccessToken == "" {
-		//Not initiated
-		utils.SendErrorResponse(w, "Invalid usage")
-		return
-	}
-
-	username, err := utils.PostPara(r, "username")
-	if err != nil {
-		utils.SendErrorResponse(w, "Invalid username given")
-		return
-	}
-	token, err := utils.PostPara(r, "token")
-	if err != nil {
-		utils.SendErrorResponse(w, "Invalid token given")
-		return
-	}
-	newPassword, err := utils.PostPara(r, "newpw")
-	if err != nil {
-		utils.SendErrorResponse(w, "Invalid new password given")
-		return
-	}
-
-	token = strings.TrimSpace(token)
-	username = strings.TrimSpace(username)
-
-	//Validate the token
-	if token != passwordResetAccessToken {
-		utils.SendErrorResponse(w, "Invalid Token")
-		return
-	}
-
-	//Check if time expired
-	if lastAccountResetEmail+tokenValidDuration < time.Now().Unix() {
-		//Expired
-		utils.SendErrorResponse(w, "Token expired")
-		return
-	}
-
-	//Check if user exists
-	if !authAgent.UserExists(username) {
-		//Invalid admin account name
-		utils.SendErrorResponse(w, "Invalid Username")
-		return
-	}
-
-	//Delete the user account
-	authAgent.UnregisterUser(username)
-
-	//Ok. Set the new password
-	err = authAgent.CreateUserAccount(username, newPassword, "")
-	if err != nil {
-		utils.SendErrorResponse(w, err.Error())
-		return
-	}
-
-	if err != nil {
-		utils.SendErrorResponse(w, err.Error())
-		return
-	}
-
-	utils.SendOK(w)
-}
+package main
+
+import (
+	"bytes"
+	"encoding/gob"
+	"encoding/json"
+	"net/http"
+	"strconv"
+	"strings"
+	"time"
+
+	"github.com/google/uuid"
+	"imuslab.com/zoraxy/mod/email"
+	"imuslab.com/zoraxy/mod/utils"
+)
+
+/*
+	SMTP Settings and Test Email Handlers
+*/
+
+func HandleSMTPSet(w http.ResponseWriter, r *http.Request) {
+	hostname, err := utils.PostPara(r, "hostname")
+	if err != nil {
+		utils.SendErrorResponse(w, "hostname cannot be empty")
+		return
+	}
+
+	portString, err := utils.PostPara(r, "port")
+	if err != nil {
+		utils.SendErrorResponse(w, "port must be a valid integer")
+		return
+	}
+
+	port, err := strconv.Atoi(portString)
+	if err != nil {
+		utils.SendErrorResponse(w, "port must be a valid integer")
+		return
+	}
+
+	username, err := utils.PostPara(r, "username")
+	if err != nil {
+		utils.SendErrorResponse(w, "username cannot be empty")
+		return
+	}
+
+	password, err := utils.PostPara(r, "password")
+	if err != nil {
+		//Empty password. Use old one if exists
+		oldConfig := loadSMTPConfig()
+		if oldConfig.Password == "" {
+			utils.SendErrorResponse(w, "password cannot be empty")
+			return
+		} else {
+			password = oldConfig.Password
+		}
+	}
+
+	senderAddr, err := utils.PostPara(r, "senderAddr")
+	if err != nil {
+		utils.SendErrorResponse(w, "senderAddr cannot be empty")
+		return
+	}
+
+	adminAddr, err := utils.PostPara(r, "adminAddr")
+	if err != nil {
+		utils.SendErrorResponse(w, "adminAddr cannot be empty")
+		return
+	}
+
+	//Set the email sender properties
+	thisEmailSender := email.Sender{
+		Hostname:   strings.TrimSpace(hostname),
+		Port:       port,
+		Username:   strings.TrimSpace(username),
+		Password:   strings.TrimSpace(password),
+		SenderAddr: strings.TrimSpace(senderAddr),
+	}
+
+	//Write this into database
+	setSMTPConfig(&thisEmailSender)
+
+	//Update the current EmailSender
+	EmailSender = &thisEmailSender
+
+	//Set the admin address of password reset
+	setSMTPAdminAddress(adminAddr)
+
+	//Reply ok
+	utils.SendOK(w)
+}
+
+func HandleSMTPGet(w http.ResponseWriter, r *http.Request) {
+	// Create a buffer to store the encoded value
+	var buf bytes.Buffer
+
+	// Encode the original object into the buffer
+	encoder := gob.NewEncoder(&buf)
+	err := encoder.Encode(*EmailSender)
+	if err != nil {
+		utils.SendErrorResponse(w, "Internal encode error")
+		return
+	}
+
+	// Decode the buffer into a new object
+	var copied email.Sender
+	decoder := gob.NewDecoder(&buf)
+	err = decoder.Decode(&copied)
+	if err != nil {
+		utils.SendErrorResponse(w, "Internal decode error")
+		return
+	}
+
+	copied.Password = ""
+
+	js, _ := json.Marshal(copied)
+	utils.SendJSONResponse(w, string(js))
+}
+
+func HandleAdminEmailGet(w http.ResponseWriter, r *http.Request) {
+	js, _ := json.Marshal(loadSMTPAdminAddr())
+	utils.SendJSONResponse(w, string(js))
+}
+
+func HandleTestEmailSend(w http.ResponseWriter, r *http.Request) {
+	adminEmailAccount := loadSMTPAdminAddr()
+	if adminEmailAccount == "" {
+		utils.SendErrorResponse(w, "Management account not set")
+		return
+	}
+
+	err := EmailSender.SendEmail(adminEmailAccount,
+		"SMTP Testing Email | Zoraxy", "This is a test email sent by Zoraxy. Please do not reply to this email.<br>Zoraxy からのテストメールです。このメールには返信しないでください。<br>這是由 Zoraxy 發送的測試電子郵件。請勿回覆此郵件。<br>Ceci est un email de test envoyé par Zoraxy. Merci de ne pas répondre à cet email.<br>Dies ist eine Test-E-Mail, die von Zoraxy gesendet wurde. Bitte antworten Sie nicht auf diese E-Mail.")
+
+	if err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}
+
+/*
+	SMTP config
+
+	The following handle SMTP configs
+*/
+
+func setSMTPConfig(config *email.Sender) error {
+	return sysdb.Write("smtp", "config", config)
+}
+
+func loadSMTPConfig() *email.Sender {
+	if sysdb.KeyExists("smtp", "config") {
+		thisEmailSender := email.Sender{
+			Port: 587,
+		}
+		err := sysdb.Read("smtp", "config", &thisEmailSender)
+		if err != nil {
+			return &email.Sender{
+				Port: 587,
+			}
+		}
+		return &thisEmailSender
+	} else {
+		//Not set. Return an empty one
+		return &email.Sender{
+			Port: 587,
+		}
+	}
+}
+
+func setSMTPAdminAddress(adminAddr string) error {
+	return sysdb.Write("smtp", "admin", adminAddr)
+}
+
+// Load SMTP admin address. Return empty string if not set
+func loadSMTPAdminAddr() string {
+	adminAddr := ""
+	if sysdb.KeyExists("smtp", "admin") {
+		err := sysdb.Read("smtp", "admin", &adminAddr)
+		if err != nil {
+			return ""
+		}
+		return adminAddr
+	} else {
+		return ""
+	}
+}
+
+/*
+	Admin Account Reset
+*/
+
+var (
+	accountResetEmailDelay   int64  = 30      //Delay between each account reset email, default 30s
+	tokenValidDuration       int64  = 15 * 60 //Duration of the token, default 15 minutes
+	lastAccountResetEmail    int64  = 0       //Timestamp for last sent account reset email
+	passwordResetAccessToken string = ""      //Access token for resetting password
+)
+
+func HandleAdminAccountResetEmail(w http.ResponseWriter, r *http.Request) {
+	if EmailSender.Username == "" {
+		//Reset account not setup
+		utils.SendErrorResponse(w, "Reset account not setup.")
+		return
+	}
+
+	if loadSMTPAdminAddr() == "" {
+		utils.SendErrorResponse(w, "Reset account not setup.")
+	}
+
+	//Check if the delay expired
+	if lastAccountResetEmail+accountResetEmailDelay > time.Now().Unix() {
+		//Too frequent
+		utils.SendErrorResponse(w, "You cannot send another account reset email in cooldown time")
+		return
+	}
+
+	passwordResetAccessToken = uuid.New().String()
+
+	//SMTP info exists. Send reset account email
+	lastAccountResetEmail = time.Now().Unix()
+	EmailSender.SendEmail(loadSMTPAdminAddr(), "Management Account Reset | Zoraxy",
+		"Enter the following reset token to reset your password on your Zoraxy router.<br>"+passwordResetAccessToken+"<br><br> This is an automated generated email. DO NOT REPLY TO THIS EMAIL.")
+
+	utils.SendOK(w)
+}
+
+func HandleNewPasswordSetup(w http.ResponseWriter, r *http.Request) {
+	if passwordResetAccessToken == "" {
+		//Not initiated
+		utils.SendErrorResponse(w, "Invalid usage")
+		return
+	}
+
+	username, err := utils.PostPara(r, "username")
+	if err != nil {
+		utils.SendErrorResponse(w, "Invalid username given")
+		return
+	}
+	token, err := utils.PostPara(r, "token")
+	if err != nil {
+		utils.SendErrorResponse(w, "Invalid token given")
+		return
+	}
+	newPassword, err := utils.PostPara(r, "newpw")
+	if err != nil {
+		utils.SendErrorResponse(w, "Invalid new password given")
+		return
+	}
+
+	token = strings.TrimSpace(token)
+	username = strings.TrimSpace(username)
+
+	//Validate the token
+	if token != passwordResetAccessToken {
+		utils.SendErrorResponse(w, "Invalid Token")
+		return
+	}
+
+	//Check if time expired
+	if lastAccountResetEmail+tokenValidDuration < time.Now().Unix() {
+		//Expired
+		utils.SendErrorResponse(w, "Token expired")
+		return
+	}
+
+	//Check if user exists
+	if !authAgent.UserExists(username) {
+		//Invalid admin account name
+		utils.SendErrorResponse(w, "Invalid Username")
+		return
+	}
+
+	// Un register the user account
+	if err := authAgent.UnregisterUser(username); err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	//Ok. Set the new password
+	if err := authAgent.CreateUserAccount(username, newPassword, ""); err != nil {
+		utils.SendErrorResponse(w, err.Error())
+		return
+	}
+
+	utils.SendOK(w)
+}

+ 11 - 11
web/components/streamprox.html

@@ -5,8 +5,8 @@
     </div>
     <div class="ui divider"></div>
     <div class="ui basic segment" style="margin-top: 0;">
-        <h4>TCP / UDP Proxy Rules</h4>
-        <p>A list of TCP proxy rules created on this host. To enable them, use the toggle button on the right.</p>
+        <h3>TCP / UDP Proxy Rules</h3>
+        <p>A list of TCP / UDP proxy rules created on this host.</p>
         <div style="overflow-x: auto; ">
             <table id="proxyTable" class="ui celled basic unstackable table">
                 <thead>
@@ -30,7 +30,7 @@
     </div>
     <div class="ui divider"></div>
     <div class="ui basic segment" id="addproxyConfig">
-        <h4>Add or Edit Stream Proxy</h4>
+        <h3>Add or Edit Stream Proxy</h3>
         <p>Create or edit a new stream proxy instance</p>
         <form id="streamProxyForm" class="ui form">
             <div class="field" style="display:none;">
@@ -42,10 +42,10 @@
                 <input type="text" name="name" placeholder="Config Name">
             </div>
             <div class="field">
-                <label>Listening Port / Address with Port</label>
+                <label>Listening Address with Port</label>
                 <input type="text" name="listenAddr" placeholder="">
-                <small>Port to listen on this host. e.g. :25565 or 127.0.0.1:25565. <br>
-                    If you are using Docker, you will need to expose this port to host network.</small>
+                <small>Address to listen on this host. e.g. :25565 or 127.0.0.1:25565. <br>
+                    If you are using Docker, you will also need to expose this port to host network.</small>
             </div>
             <div class="field">
                 <label>Proxy Target Address with Port</label>
@@ -178,10 +178,10 @@
                 proxyConfigs.forEach(function(config) {
                     var runningLogo = 'Stopped';
                     var runningClass = "stopped";
-                    var startButton = `<button onclick="startStreamProx('${config.UUID}');" class="ui basic circular icon button" title="Start Proxy"><i class="green play icon"></i></button>`;
+                    var startButton = `<button onclick="startStreamProx('${config.UUID}');" class="ui basic mini circular icon button" title="Start Proxy"><i class="green play icon"></i></button>`;
                     if (config.Running){
                         runningLogo = 'Running';
-                        startButton = `<button onclick="stopStreamProx('${config.UUID}');" class="ui basic circular icon button" title="Stop Proxy"><i class="red stop icon"></i></button>`;
+                        startButton = `<button onclick="stopStreamProx('${config.UUID}');" class="ui basic mini circular icon button" title="Stop Proxy"><i class="red stop icon"></i></button>`;
                         runningClass = "running"
                     }
 
@@ -194,7 +194,7 @@
                         modeText.push("UDP")
                     }
 
-                    modeText = modeText.join(" | ")
+                    modeText = modeText.join(" & ")
 
                     var thisConfig = encodeURIComponent(JSON.stringify(config));
 
@@ -208,8 +208,8 @@
                     row.append($('<td>').text(config.Timeout));
                     row.append($('<td>').html(`
                         ${startButton}
-                        <button onclick="editTCPProxyConfig('${config.UUID}');" class="ui circular basic small icon button" title="Edit Config"><i class="edit icon"></i></button>
-                        <button onclick="deleteTCPProxyConfig('${config.UUID}');" class="ui circular red basic small icon button" title="Delete Config"><i class="trash icon"></i></button>
+                        <button onclick="editTCPProxyConfig('${config.UUID}');" class="ui circular basic mini icon button" title="Edit Config"><i class="edit icon"></i></button>
+                        <button onclick="deleteTCPProxyConfig('${config.UUID}');" class="ui circular red basic mini icon button" title="Delete Config"><i class="trash icon"></i></button>
                     `));
                     tableBody.append(row);
                 });

+ 3 - 3
web/main.css

@@ -556,17 +556,17 @@ body{
 }
 
 .streamproxConfig.running td:first-child{
-    border-left: 0.6em solid #02cb59 !important;
+    border-left: 0.6em solid #01cb55 !important;
 }
 
 .streamproxConfig.stopped td:first-child{
-    border-left: 0.6em solid #02032a !important;
+    border-left: 0.6em solid #1b1b1b !important;
 }
 
 .streamproxConfig td:first-child .statusText{
     position: absolute;
     bottom: 0.1em;
-    left: 0.1em;
+    right: 0.2em;
     font-size: 1em;
     color:rgb(224, 224, 224);
     opacity: 0.7;