Merge remote-tracking branch 'origin/master' into clients-patch
This commit is contained in:
		
						commit
						12a5b7c822
					
				|  | @ -53,6 +53,7 @@ Note: | ||||||
| | `WGUI_USERNAME`             | The username for the login page. Used for db initialization only                                                                                             | `admin`                            | | | `WGUI_USERNAME`             | The username for the login page. Used for db initialization only                                                                                             | `admin`                            | | ||||||
| | `WGUI_PASSWORD`             | The password for the user on the login page. Will be hashed automatically. Used for db initialization only                                                   | `admin`                            | | | `WGUI_PASSWORD`             | The password for the user on the login page. Will be hashed automatically. Used for db initialization only                                                   | `admin`                            | | ||||||
| | `WGUI_PASSWORD_HASH`        | The password hash for the user on the login page. (alternative to `WGUI_PASSWORD`). Used for db initialization only                                          | N/A                                | | | `WGUI_PASSWORD_HASH`        | The password hash for the user on the login page. (alternative to `WGUI_PASSWORD`). Used for db initialization only                                          | N/A                                | | ||||||
|  | | `WGUI_FAVICON_FILE_PATH`    | The file path used as website favicon                                                                                                                        | Embedded WireGuard logo            | | ||||||
| | `WGUI_ENDPOINT_ADDRESS`     | The default endpoint address used in global settings                                                                                                         | Resolved to your public ip address | | | `WGUI_ENDPOINT_ADDRESS`     | The default endpoint address used in global settings                                                                                                         | Resolved to your public ip address | | ||||||
| | `WGUI_DNS`                  | The default DNS servers (comma-separated-list) used in the global settings                                                                                   | `1.1.1.1`                          | | | `WGUI_DNS`                  | The default DNS servers (comma-separated-list) used in the global settings                                                                                   | `1.1.1.1`                          | | ||||||
| | `WGUI_MTU`                  | The default MTU used in global settings                                                                                                                      | `1450`                             | | | `WGUI_MTU`                  | The default MTU used in global settings                                                                                                                      | `1450`                             | | ||||||
|  | @ -68,7 +69,7 @@ Note: | ||||||
| | `SMTP_USERNAME`             | The SMTP username                                                                                                                                            | N/A                                | | | `SMTP_USERNAME`             | The SMTP username                                                                                                                                            | N/A                                | | ||||||
| | `SMTP_PASSWORD`             | The SMTP user password                                                                                                                                       | N/A                                | | | `SMTP_PASSWORD`             | The SMTP user password                                                                                                                                       | N/A                                | | ||||||
| | `SMTP_AUTH_TYPE`            | The SMTP authentication type. Possible values: `PLAIN`, `LOGIN`, `NONE`                                                                                      | `NONE`                             | | | `SMTP_AUTH_TYPE`            | The SMTP authentication type. Possible values: `PLAIN`, `LOGIN`, `NONE`                                                                                      | `NONE`                             | | ||||||
| | `SMTP_ENCRYPTION`           | the encryption method. Possible values: `SSL`, `SSLTLS`, `TLS`, `STARTTLS`                                                                                   | `STARTTLS`                         | | | `SMTP_ENCRYPTION`           | the encryption method. Possible values: `NONE`, `SSL`, `SSLTLS`, `TLS`, `STARTTLS`                                                                           | `STARTTLS`                         | | ||||||
| 
 | 
 | ||||||
| ### Defaults for server configuration | ### Defaults for server configuration | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 17 KiB | 
|  | @ -33,6 +33,8 @@ func authType(authType string) mail.AuthType { | ||||||
| 
 | 
 | ||||||
| func encryptionType(encryptionType string) mail.Encryption { | func encryptionType(encryptionType string) mail.Encryption { | ||||||
| 	switch strings.ToUpper(encryptionType) { | 	switch strings.ToUpper(encryptionType) { | ||||||
|  | 	case "NONE": | ||||||
|  | 		return mail.EncryptionNone | ||||||
| 	case "SSL": | 	case "SSL": | ||||||
| 		return mail.EncryptionSSL | 		return mail.EncryptionSSL | ||||||
| 	case "SSLTLS": | 	case "SSLTLS": | ||||||
|  |  | ||||||
|  | @ -6,6 +6,7 @@ import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  | 	"os" | ||||||
| 	"sort" | 	"sort" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -32,6 +33,15 @@ func Health() echo.HandlerFunc { | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func Favicon() echo.HandlerFunc { | ||||||
|  | 	return func(c echo.Context) error { | ||||||
|  | 		if favicon, ok := os.LookupEnv(util.FaviconFilePathEnvVar); ok { | ||||||
|  | 			return c.File(favicon) | ||||||
|  | 		} | ||||||
|  | 		return c.Redirect(http.StatusFound, util.BasePath+"/static/custom/img/favicon.ico") | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // LoginPage handler
 | // LoginPage handler
 | ||||||
| func LoginPage() echo.HandlerFunc { | func LoginPage() echo.HandlerFunc { | ||||||
| 	return func(c echo.Context) error { | 	return func(c echo.Context) error { | ||||||
|  | @ -609,6 +619,8 @@ func Status(db store.IStore) echo.HandlerFunc { | ||||||
| 		LastHandshakeTime time.Time | 		LastHandshakeTime time.Time | ||||||
| 		LastHandshakeRel  time.Duration | 		LastHandshakeRel  time.Duration | ||||||
| 		Connected         bool | 		Connected         bool | ||||||
|  | 		AllocatedIP       string | ||||||
|  | 		Endpoint          string | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	type DeviceVM struct { | 	type DeviceVM struct { | ||||||
|  | @ -656,12 +668,21 @@ func Status(db store.IStore) echo.HandlerFunc { | ||||||
| 			for i := range devices { | 			for i := range devices { | ||||||
| 				devVm := DeviceVM{Name: devices[i].Name} | 				devVm := DeviceVM{Name: devices[i].Name} | ||||||
| 				for j := range devices[i].Peers { | 				for j := range devices[i].Peers { | ||||||
|  | 					var allocatedIPs string | ||||||
|  | 					for _, ip := range devices[i].Peers[j].AllowedIPs { | ||||||
|  | 						if len(allocatedIPs) > 0 { | ||||||
|  | 							allocatedIPs += "</br>" | ||||||
|  | 						} | ||||||
|  | 						allocatedIPs += ip.String() | ||||||
|  | 					} | ||||||
| 					pVm := PeerVM{ | 					pVm := PeerVM{ | ||||||
| 						PublicKey:         devices[i].Peers[j].PublicKey.String(), | 						PublicKey:         devices[i].Peers[j].PublicKey.String(), | ||||||
| 						ReceivedBytes:     devices[i].Peers[j].ReceiveBytes, | 						ReceivedBytes:     devices[i].Peers[j].ReceiveBytes, | ||||||
| 						TransmitBytes:     devices[i].Peers[j].TransmitBytes, | 						TransmitBytes:     devices[i].Peers[j].TransmitBytes, | ||||||
| 						LastHandshakeTime: devices[i].Peers[j].LastHandshakeTime, | 						LastHandshakeTime: devices[i].Peers[j].LastHandshakeTime, | ||||||
| 						LastHandshakeRel:  time.Since(devices[i].Peers[j].LastHandshakeTime), | 						LastHandshakeRel:  time.Since(devices[i].Peers[j].LastHandshakeTime), | ||||||
|  | 						AllocatedIP:       allocatedIPs, | ||||||
|  | 						Endpoint:          devices[i].Peers[j].Endpoint.String(), | ||||||
| 					} | 					} | ||||||
| 					pVm.Connected = pVm.LastHandshakeRel.Minutes() < 3. | 					pVm.Connected = pVm.LastHandshakeRel.Minutes() < 3. | ||||||
| 
 | 
 | ||||||
|  | @ -810,3 +831,13 @@ func ApplyServerConfig(db store.IStore, tmplBox *rice.Box) echo.HandlerFunc { | ||||||
| 		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Applied server config successfully"}) | 		return c.JSON(http.StatusOK, jsonHTTPResponse{true, "Applied server config successfully"}) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | // AboutPage handler
 | ||||||
|  | func AboutPage() echo.HandlerFunc { | ||||||
|  | 	return func(c echo.Context) error { | ||||||
|  | 		return c.Render(http.StatusOK, "about.html", map[string]interface{}{ | ||||||
|  | 			"baseData": model.BaseData{Active: "about", CurrentUser: currentUser(c)}, | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								main.go
								
								
								
								
							
							
						
						
									
										4
									
								
								main.go
								
								
								
								
							|  | @ -61,7 +61,7 @@ func init() { | ||||||
| 	flag.StringVar(&flagSmtpUsername, "smtp-username", util.LookupEnvOrString("SMTP_USERNAME", flagSmtpUsername), "SMTP Username") | 	flag.StringVar(&flagSmtpUsername, "smtp-username", util.LookupEnvOrString("SMTP_USERNAME", flagSmtpUsername), "SMTP Username") | ||||||
| 	flag.StringVar(&flagSmtpPassword, "smtp-password", util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword), "SMTP Password") | 	flag.StringVar(&flagSmtpPassword, "smtp-password", util.LookupEnvOrString("SMTP_PASSWORD", flagSmtpPassword), "SMTP Password") | ||||||
| 	flag.BoolVar(&flagSmtpNoTLSCheck, "smtp-no-tls-check", util.LookupEnvOrBool("SMTP_NO_TLS_CHECK", flagSmtpNoTLSCheck), "Disable TLS verification for SMTP. This is potentially dangerous.") | 	flag.BoolVar(&flagSmtpNoTLSCheck, "smtp-no-tls-check", util.LookupEnvOrBool("SMTP_NO_TLS_CHECK", flagSmtpNoTLSCheck), "Disable TLS verification for SMTP. This is potentially dangerous.") | ||||||
| 	flag.StringVar(&flagSmtpEncryption, "smtp-encryption", util.LookupEnvOrString("SMTP_ENCRYPTION", flagSmtpEncryption), "SMTP Encryption : SSL, SSLTLS, TLS or STARTTLS (by default)") | 	flag.StringVar(&flagSmtpEncryption, "smtp-encryption", util.LookupEnvOrString("SMTP_ENCRYPTION", flagSmtpEncryption), "SMTP Encryption : NONE, SSL, SSLTLS, TLS or STARTTLS (by default)") | ||||||
| 	flag.StringVar(&flagSmtpAuthType, "smtp-auth-type", util.LookupEnvOrString("SMTP_AUTH_TYPE", flagSmtpAuthType), "SMTP Auth Type : PLAIN, LOGIN or NONE.") | 	flag.StringVar(&flagSmtpAuthType, "smtp-auth-type", util.LookupEnvOrString("SMTP_AUTH_TYPE", flagSmtpAuthType), "SMTP Auth Type : PLAIN, LOGIN or NONE.") | ||||||
| 	flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey), "Your sendgrid api key.") | 	flag.StringVar(&flagSendgridApiKey, "sendgrid-api-key", util.LookupEnvOrString("SENDGRID_API_KEY", flagSendgridApiKey), "Your sendgrid api key.") | ||||||
| 	flag.StringVar(&flagEmailFrom, "email-from", util.LookupEnvOrString("EMAIL_FROM_ADDRESS", flagEmailFrom), "'From' email address.") | 	flag.StringVar(&flagEmailFrom, "email-from", util.LookupEnvOrString("EMAIL_FROM_ADDRESS", flagEmailFrom), "'From' email address.") | ||||||
|  | @ -147,7 +147,9 @@ func main() { | ||||||
| 		sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom, util.SmtpEncryption) | 		sendmail = emailer.NewSmtpMail(util.SmtpHostname, util.SmtpPort, util.SmtpUsername, util.SmtpPassword, util.SmtpNoTLSCheck, util.SmtpAuthType, util.EmailFromName, util.EmailFrom, util.SmtpEncryption) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	app.GET(util.BasePath+"/about", handler.AboutPage()) | ||||||
| 	app.GET(util.BasePath+"/_health", handler.Health()) | 	app.GET(util.BasePath+"/_health", handler.Health()) | ||||||
|  | 	app.GET(util.BasePath+"/favicon", handler.Favicon()) | ||||||
| 	app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson) | 	app.POST(util.BasePath+"/new-client", handler.NewClient(db), handler.ValidSession, handler.ContentTypeJson) | ||||||
| 	app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson) | 	app.POST(util.BasePath+"/update-client", handler.UpdateClient(db), handler.ValidSession, handler.ContentTypeJson) | ||||||
| 	app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson) | 	app.POST(util.BasePath+"/email-client", handler.EmailClient(db, sendmail, defaultEmailSubject, defaultEmailContent), handler.ValidSession, handler.ContentTypeJson) | ||||||
|  |  | ||||||
|  | @ -93,6 +93,11 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec | ||||||
| 		log.Fatal(err) | 		log.Fatal(err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	aboutPageString, err := tmplBox.String("about.html") | ||||||
|  | 	if err != nil { | ||||||
|  | 		log.Fatal(err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// create template list
 | 	// create template list
 | ||||||
| 	funcs := template.FuncMap{ | 	funcs := template.FuncMap{ | ||||||
| 		"StringsJoin": strings.Join, | 		"StringsJoin": strings.Join, | ||||||
|  | @ -105,6 +110,7 @@ func New(tmplBox *rice.Box, extraData map[string]string, secret []byte) *echo.Ec | ||||||
| 	templates["global_settings.html"] = template.Must(template.New("global_settings").Funcs(funcs).Parse(tmplBaseString + tmplGlobalSettingsString)) | 	templates["global_settings.html"] = template.Must(template.New("global_settings").Funcs(funcs).Parse(tmplBaseString + tmplGlobalSettingsString)) | ||||||
| 	templates["status.html"] = template.Must(template.New("status").Funcs(funcs).Parse(tmplBaseString + tmplStatusString)) | 	templates["status.html"] = template.Must(template.New("status").Funcs(funcs).Parse(tmplBaseString + tmplStatusString)) | ||||||
| 	templates["wake_on_lan_hosts.html"] = template.Must(template.New("wake_on_lan_hosts").Funcs(funcs).Parse(tmplBaseString + tmplWakeOnLanHostsString)) | 	templates["wake_on_lan_hosts.html"] = template.Must(template.New("wake_on_lan_hosts").Funcs(funcs).Parse(tmplBaseString + tmplWakeOnLanHostsString)) | ||||||
|  | 	templates["about.html"] = template.Must(template.New("about").Funcs(funcs).Parse(tmplBaseString + aboutPageString)) | ||||||
| 
 | 
 | ||||||
| 	e.Logger.SetLevel(log.DEBUG) | 	e.Logger.SetLevel(log.DEBUG) | ||||||
| 	e.Pre(middleware.RemoveTrailingSlash()) | 	e.Pre(middleware.RemoveTrailingSlash()) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,139 @@ | ||||||
|  | {{ define "title"}} | ||||||
|  | About | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "top_css"}} | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "username"}} | ||||||
|  | {{ .username }} | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "page_title"}} | ||||||
|  | About | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "page_content"}} | ||||||
|  | <section class="content"> | ||||||
|  |     <div class="container-fluid"> | ||||||
|  |         <!-- <h5 class="mt-4 mb-2">Global Settings</h5> --> | ||||||
|  |         <div class="row"> | ||||||
|  |             <!-- left column --> | ||||||
|  |             <div class="col-md-6"> | ||||||
|  |                 <div class="card card-success"> | ||||||
|  |                     <div class="card-header"> | ||||||
|  |                         <h3 class="card-title">About Wireguard-UI</h3> | ||||||
|  |                     </div> | ||||||
|  |                     <!-- /.card-header --> | ||||||
|  |                     <div class="card-body"> | ||||||
|  |                         <div class="form-group"> | ||||||
|  |                             <label for="version" class="control-label">Current version</label> | ||||||
|  |                             <input type="text" class="form-control" id="version" value="{{ .appVersion }}" readonly> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="form-group"> | ||||||
|  |                             <label for="currentReleaseDate" class="control-label">Current version release date</label> | ||||||
|  |                             <input type="text" class="form-control" id="currentReleaseDate" readonly> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="form-group"> | ||||||
|  |                             <label for="latestRelease" class="control-label">Latest release</label> | ||||||
|  |                             <input type="text" class="form-control" id="latestRelease" readonly> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="form-group"> | ||||||
|  |                             <label for="latestReleaseDate" class="control-label">Latest release date</label> | ||||||
|  |                             <input type="text" class="form-control" id="latestReleaseDate" readonly> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="form-group"> | ||||||
|  |                             <label for="author" class="control-label">Author</label> | ||||||
|  |                             <div id="author"> | ||||||
|  |                                 <a id="authorLink"> | ||||||
|  |                                     <img id="authorImage" | ||||||
|  |                                          style="width: 50px; height: 50px; border-radius: 50%; border: 1px solid #000;"> | ||||||
|  |                                 </a> | ||||||
|  |                             </div> | ||||||
|  |                         </div> | ||||||
|  |                         <div class="form-group"> | ||||||
|  |                             <label for="contributors" class="control-label">Contributors</label> | ||||||
|  |                             <div id="contributors"></div> | ||||||
|  |                         </div> | ||||||
|  |                         <strong>Copyright © | ||||||
|  |                             <script>document.write(new Date().getFullYear())</script> | ||||||
|  |                             <a href="https://github.com/ngoduykhanh/wireguard-ui">Wireguard UI</a>. | ||||||
|  |                         </strong> All rights reserved. | ||||||
|  | 
 | ||||||
|  |                     </div> | ||||||
|  |                 </div> | ||||||
|  |                 <!-- /.card --> | ||||||
|  |             </div> | ||||||
|  |         </div> | ||||||
|  |         <!-- /.row --> | ||||||
|  |     </div> | ||||||
|  | </section> | ||||||
|  | {{ end }} | ||||||
|  | 
 | ||||||
|  | {{ define "bottom_js"}} | ||||||
|  | <script> | ||||||
|  |     $(document).ready(function () { | ||||||
|  | 
 | ||||||
|  |         $.ajax({ | ||||||
|  |             cache: false, | ||||||
|  |             method: 'GET', | ||||||
|  |             url: 'https://api.github.com/repos/ngoduykhanh/wireguard-ui/releases/tags/' + $("#version").val(), | ||||||
|  |             dataType: 'json', | ||||||
|  |             contentType: "application/json", | ||||||
|  |             success: function (data) { | ||||||
|  |                 $("#currentReleaseDate").attr("value", data.published_at.split("T")[0]); | ||||||
|  | 
 | ||||||
|  |             }, | ||||||
|  |             error: function (jqXHR, exception) { | ||||||
|  |                 $("#currentReleaseDate").attr("value", "Could not find this version on GitHub.com"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  |         $.ajax({ | ||||||
|  |             cache: false, | ||||||
|  |             method: 'GET', | ||||||
|  |             url: 'https://api.github.com/repos/ngoduykhanh/wireguard-ui/releases/latest', | ||||||
|  |             dataType: 'json', | ||||||
|  |             contentType: "application/json", | ||||||
|  |             success: function (data) { | ||||||
|  |                 $("#latestRelease").attr("value", data.tag_name); | ||||||
|  |                 $("#latestReleaseDate").attr("value", data.published_at.split("T")[0]); | ||||||
|  |                 $("#author").attr("value", data.author.login); | ||||||
|  |                 $("#authorImage").attr("src", data.author.avatar_url); | ||||||
|  |                 $("#authorImage").after("<b>  " + data.author.login + "</b>"); | ||||||
|  |                 $("#authorLink").attr("href", data.author.html_url); | ||||||
|  | 
 | ||||||
|  |             }, | ||||||
|  |             error: function (jqXHR, exception) { | ||||||
|  |                 $("#latestRelease").attr("value", "Could not connect to GitHub.com"); | ||||||
|  |                 $("#latestReleaseDate").attr("value", "Could not connect to GitHub.com"); | ||||||
|  |                 $("#author").attr("value", "Could not connect to GitHub.com"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  | 
 | ||||||
|  |         $.ajax({ | ||||||
|  |             cache: false, | ||||||
|  |             method: 'GET', | ||||||
|  |             url: 'https://api.github.com/repos/ngoduykhanh/wireguard-ui/contributors', | ||||||
|  |             dataType: 'json', | ||||||
|  |             contentType: "application/json", | ||||||
|  |             success: function (data) { | ||||||
|  |                 data.forEach(contributor => $("#contributors").append("<a href=\"" + contributor.html_url + "\" title=\"" + contributor.login + "\">" + | ||||||
|  |                     "<img src=\"" + contributor.avatar_url + "\" style=\"width: 50px; height: 50px; border-radius: 50%; border: 1px solid #000; margin: 5px;\"/></a>")); | ||||||
|  |             }, | ||||||
|  |             error: function (jqXHR, exception) { | ||||||
|  |                 $("#contributors").html("<p>Could not connect to GitHub.com</p>"); | ||||||
|  |             } | ||||||
|  |         }); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     $(document).ajaxStop(function () { | ||||||
|  |         if (Date.parse($("#currentReleaseDate").val()) < Date.parse($("#latestReleaseDate").val())) { | ||||||
|  |             $("#currentReleaseDate").after("<p style=\"color:red\">Current version is out of date</p>") | ||||||
|  |         } | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | </script> | ||||||
|  | {{ end }} | ||||||
|  | @ -8,6 +8,8 @@ | ||||||
|     <title>{{template "title" .}}</title> |     <title>{{template "title" .}}</title> | ||||||
|     <!-- Tell the browser to be responsive to screen width --> |     <!-- Tell the browser to be responsive to screen width --> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  |     <!-- Favicon --> | ||||||
|  |     <link rel="icon" href="{{.basePath}}/favicon"> | ||||||
| 
 | 
 | ||||||
|     <!-- Font Awesome --> |     <!-- Font Awesome --> | ||||||
|     <link rel="stylesheet" href="{{.basePath}}/static/plugins/fontawesome-free/css/all.min.css"> |     <link rel="stylesheet" href="{{.basePath}}/static/plugins/fontawesome-free/css/all.min.css"> | ||||||
|  | @ -44,17 +46,17 @@ | ||||||
|             </ul> |             </ul> | ||||||
| 
 | 
 | ||||||
|             <!-- SEARCH FORM --> |             <!-- SEARCH FORM --> | ||||||
| <!--            <form class="form-inline ml-3">--> |             <form class="form-inline ml-3" style="display: none" id="search-form"> | ||||||
| <!--                <div class="input-group input-group-sm">--> |                 <div class="input-group input-group-sm"> | ||||||
| <!--                    <input class="form-control form-control-navbar" type="search" placeholder="Search"--> |                     <input class="form-control form-control-navbar" placeholder="Search" | ||||||
| <!--                        aria-label="Search">--> |                         aria-label="Search" id="search-input"> | ||||||
| <!--                    <div class="input-group-append">--> |                     <div class="input-group-append"> | ||||||
| <!--                        <button class="btn btn-navbar" type="submit">--> |                         <button class="btn-navbar" type="submit" disabled> | ||||||
| <!--                            <i class="fas fa-search"></i>--> |                             <i class="fas fa-search"></i> | ||||||
| <!--                        </button>--> |                         </button> | ||||||
| <!--                    </div>--> |                     </div> | ||||||
| <!--                </div>--> |                 </div> | ||||||
| <!--            </form>--> |             </form> | ||||||
| 
 | 
 | ||||||
|             <!-- Right navbar links --> |             <!-- Right navbar links --> | ||||||
|             <div class="navbar-nav ml-auto"> |             <div class="navbar-nav ml-auto"> | ||||||
|  | @ -141,6 +143,15 @@ | ||||||
|                                 </p> |                                 </p> | ||||||
|                             </a> |                             </a> | ||||||
|                         </li> |                         </li> | ||||||
|  |                         <li class="nav-header">ABOUT</li> | ||||||
|  |                         <li class="nav-item"> | ||||||
|  |                             <a href="{{.basePath}}/about" class="nav-link {{if eq .baseData.Active "about" }}active{{end}}"> | ||||||
|  |                             <i class="nav-icon fas  fa-solid fa-id-card"></i> | ||||||
|  |                             <p> | ||||||
|  |                                 About | ||||||
|  |                             </p> | ||||||
|  |                             </a> | ||||||
|  |                         </li> | ||||||
|                     </ul> |                     </ul> | ||||||
|                 </nav> |                 </nav> | ||||||
|                 <!-- /.sidebar-menu --> |                 <!-- /.sidebar-menu --> | ||||||
|  | @ -289,7 +300,7 @@ | ||||||
|             <!-- /.content --> |             <!-- /.content --> | ||||||
|         </div> |         </div> | ||||||
|         <!-- /.content-wrapper --> |         <!-- /.content-wrapper --> | ||||||
| 
 |         <!-- | ||||||
|         <footer class="main-footer"> |         <footer class="main-footer"> | ||||||
|             <div class="float-right d-none d-sm-block"> |             <div class="float-right d-none d-sm-block"> | ||||||
|                 <b>Version</b> {{ .appVersion }} |                 <b>Version</b> {{ .appVersion }} | ||||||
|  | @ -297,7 +308,7 @@ | ||||||
|             <strong>Copyright © <script>document.write(new Date().getFullYear())</script> <a href="https://github.com/ngoduykhanh/wireguard-ui">Wireguard UI</a>.</strong> All rights |             <strong>Copyright © <script>document.write(new Date().getFullYear())</script> <a href="https://github.com/ngoduykhanh/wireguard-ui">Wireguard UI</a>.</strong> All rights | ||||||
|             reserved. |             reserved. | ||||||
|         </footer> |         </footer> | ||||||
| 
 |         --> | ||||||
|         <!-- Control Sidebar --> |         <!-- Control Sidebar --> | ||||||
|         <aside class="control-sidebar control-sidebar-dark"> |         <aside class="control-sidebar control-sidebar-dark"> | ||||||
|             <!-- Control sidebar content goes here --> |             <!-- Control sidebar content goes here --> | ||||||
|  |  | ||||||
|  | @ -70,7 +70,9 @@ Wireguard Clients | ||||||
|             </div> |             </div> | ||||||
|             <div class="modal-body"> |             <div class="modal-body"> | ||||||
|                 <input type="hidden" id="qr_client_id" name="qr_client_id"> |                 <input type="hidden" id="qr_client_id" name="qr_client_id"> | ||||||
|  |                 <a href="" download="" id="qr_code_a"> | ||||||
|                     <img id="qr_code" class="w-100" style="image-rendering: pixelated;" src="" alt="QR code" /> |                     <img id="qr_code" class="w-100" style="image-rendering: pixelated;" src="" alt="QR code" /> | ||||||
|  |                 </a> | ||||||
|                 <div class="form-group"> |                 <div class="form-group"> | ||||||
|                     <div class="icheck-primary d-inline"> |                     <div class="icheck-primary d-inline"> | ||||||
|                         <input type="checkbox" id="qr_include_fwmark" onchange="regenerateQRCode()"> |                         <input type="checkbox" id="qr_include_fwmark" onchange="regenerateQRCode()"> | ||||||
|  | @ -258,6 +260,28 @@ Wireguard Clients | ||||||
|             populateClientList(); |             populateClientList(); | ||||||
|         }) |         }) | ||||||
| 
 | 
 | ||||||
|  |         // show search bar and override :contains to be case-insensitive | ||||||
|  |         $(document).ready(function () { | ||||||
|  |             $("#search-form").show(); | ||||||
|  |             jQuery.expr[':'].contains = function(a, i, m) { | ||||||
|  |                 return jQuery(a).text().toUpperCase() | ||||||
|  |                     .indexOf(m[3].toUpperCase()) >= 0; | ||||||
|  |             }; | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|  |         // hide all clients and display only the ones that meet the search criteria (name, email, IP) | ||||||
|  |         $('#search-input').keyup(function () { | ||||||
|  |             var query = $(this).val(); | ||||||
|  |             $('.col-lg-4').hide(); | ||||||
|  |             $(".info-box-text").each(function() { | ||||||
|  |                 if($(this).children('i.fa-user').length > 0 || $(this).children('i.fa-envelope').length > 0) | ||||||
|  |                 { | ||||||
|  |                     $(this).filter(':contains("' + query + '")').parent().parent().parent().show(); | ||||||
|  |                 } | ||||||
|  |                 }) | ||||||
|  |             $(".badge-secondary").filter(':contains("' + query + '")').parent().parent().parent().show(); | ||||||
|  |         }) | ||||||
|  | 
 | ||||||
|         // modal_pause_client modal event |         // modal_pause_client modal event | ||||||
|         $("#modal_pause_client").on('show.bs.modal', function (event) { |         $("#modal_pause_client").on('show.bs.modal', function (event) { | ||||||
|             const button = $(event.relatedTarget); |             const button = $(event.relatedTarget); | ||||||
|  | @ -402,6 +426,7 @@ Wireguard Clients | ||||||
|         function regenerateQRCode() { |         function regenerateQRCode() { | ||||||
|             const client_id = $("#qr_client_id").val(); |             const client_id = $("#qr_client_id").val(); | ||||||
|             const QRCodeImg = $("#qr_code"); |             const QRCodeImg = $("#qr_code"); | ||||||
|  |             const QRCodeA = $("#qr_code_a"); | ||||||
|             let include_fwmark = false; |             let include_fwmark = false; | ||||||
|             if ($("#qr_include_fwmark").is(':checked')){ |             if ($("#qr_include_fwmark").is(':checked')){ | ||||||
|                 include_fwmark = true; |                 include_fwmark = true; | ||||||
|  | @ -421,6 +446,8 @@ Wireguard Clients | ||||||
| 
 | 
 | ||||||
|                     $(".modal-title").text("Scan QR Code for " + client.name + " profile"); |                     $(".modal-title").text("Scan QR Code for " + client.name + " profile"); | ||||||
|                     QRCodeImg.attr('src', resp.QRCode).show(); |                     QRCodeImg.attr('src', resp.QRCode).show(); | ||||||
|  |                     QRCodeA.attr('download', resp.Client.name); | ||||||
|  |                     QRCodeA.attr('href', resp.QRCode).show(); | ||||||
|                 }, |                 }, | ||||||
|                 error: function (jqXHR, exception) { |                 error: function (jqXHR, exception) { | ||||||
|                     const responseJson = jQuery.parseJSON(jqXHR.responseText); |                     const responseJson = jQuery.parseJSON(jqXHR.responseText); | ||||||
|  |  | ||||||
|  | @ -91,7 +91,7 @@ Global Settings | ||||||
|                             <dt>2. DNS Servers</dt> |                             <dt>2. DNS Servers</dt> | ||||||
|                             <dd>The DNS servers will be set to client config.</dd> |                             <dd>The DNS servers will be set to client config.</dd> | ||||||
|                             <dt>3. MTU</dt> |                             <dt>3. MTU</dt> | ||||||
|                             <dd>The MTU will be set to server and client config. By default it is <code>1420</code>. You might want |                             <dd>The MTU will be set to server and client config. By default it is <code>1450</code>. You might want | ||||||
|                                 to adjust the MTU size if your connection (e.g PPPoE, 3G, satellite network, etc) has a low MTU.</dd> |                                 to adjust the MTU size if your connection (e.g PPPoE, 3G, satellite network, etc) has a low MTU.</dd> | ||||||
|                             <dd>Leave blank to omit this setting in the configs.</dd> |                             <dd>Leave blank to omit this setting in the configs.</dd> | ||||||
|                             <dt>4. Persistent Keepalive</dt> |                             <dt>4. Persistent Keepalive</dt> | ||||||
|  |  | ||||||
|  | @ -7,6 +7,8 @@ | ||||||
|     <title>WireGuard UI</title> |     <title>WireGuard UI</title> | ||||||
|     <!-- Tell the browser to be responsive to screen width --> |     <!-- Tell the browser to be responsive to screen width --> | ||||||
|     <meta name="viewport" content="width=device-width, initial-scale=1"> |     <meta name="viewport" content="width=device-width, initial-scale=1"> | ||||||
|  |     <!-- Favicon --> | ||||||
|  |     <link rel="icon" href="{{.basePath}}/favicon"> | ||||||
| 
 | 
 | ||||||
|     <!-- Font Awesome --> |     <!-- Font Awesome --> | ||||||
|     <link rel="stylesheet" href="{{.basePath}}/static/plugins/fontawesome-free/css/all.min.css"> |     <link rel="stylesheet" href="{{.basePath}}/static/plugins/fontawesome-free/css/all.min.css"> | ||||||
|  |  | ||||||
|  | @ -41,6 +41,8 @@ Connected Peers | ||||||
|                   <th scope="col">#</th> |                   <th scope="col">#</th> | ||||||
|                   <th scope="col">Name</th> |                   <th scope="col">Name</th> | ||||||
|                   <th scope="col">Email</th> |                   <th scope="col">Email</th> | ||||||
|  |                   <th scope="col">Allocated IPs</th> | ||||||
|  |                   <th scope="col">Endpoint</th> | ||||||
|                   <th scope="col">Public Key</th> |                   <th scope="col">Public Key</th> | ||||||
|                   <th scope="col">Received</th> |                   <th scope="col">Received</th> | ||||||
|                   <th scope="col">Transmitted</th> |                   <th scope="col">Transmitted</th> | ||||||
|  | @ -54,6 +56,8 @@ Connected Peers | ||||||
|                 <th scope="row">{{ $idx }}</th> |                 <th scope="row">{{ $idx }}</th> | ||||||
|                 <td>{{ $peer.Name }}</td> |                 <td>{{ $peer.Name }}</td> | ||||||
|                 <td>{{ $peer.Email }}</td> |                 <td>{{ $peer.Email }}</td> | ||||||
|  |                 <td>{{ $peer.AllocatedIP }}</td> | ||||||
|  |                 <td>{{ $peer.Endpoint }}</td> | ||||||
|                 <td>{{ $peer.PublicKey }}</td> |                 <td>{{ $peer.PublicKey }}</td> | ||||||
|                 <td title="{{ $peer.ReceivedBytes }} Bytes"><script>document.write(bytesToHumanReadable({{ $peer.ReceivedBytes }}))</script></td> |                 <td title="{{ $peer.ReceivedBytes }} Bytes"><script>document.write(bytesToHumanReadable({{ $peer.ReceivedBytes }}))</script></td> | ||||||
|                 <td title="{{ $peer.TransmitBytes }} Bytes"><script>document.write(bytesToHumanReadable({{ $peer.TransmitBytes }}))</script></td> |                 <td title="{{ $peer.TransmitBytes }} Bytes"><script>document.write(bytesToHumanReadable({{ $peer.TransmitBytes }}))</script></td> | ||||||
|  |  | ||||||
|  | @ -34,6 +34,7 @@ const ( | ||||||
| 	UsernameEnvVar                         = "WGUI_USERNAME" | 	UsernameEnvVar                         = "WGUI_USERNAME" | ||||||
| 	PasswordEnvVar                         = "WGUI_PASSWORD" | 	PasswordEnvVar                         = "WGUI_PASSWORD" | ||||||
| 	PasswordHashEnvVar                     = "WGUI_PASSWORD_HASH" | 	PasswordHashEnvVar                     = "WGUI_PASSWORD_HASH" | ||||||
|  | 	FaviconFilePathEnvVar                  = "WGUI_FAVICON_FILE_PATH" | ||||||
| 	EndpointAddressEnvVar                  = "WGUI_ENDPOINT_ADDRESS" | 	EndpointAddressEnvVar                  = "WGUI_ENDPOINT_ADDRESS" | ||||||
| 	DNSEnvVar                              = "WGUI_DNS" | 	DNSEnvVar                              = "WGUI_DNS" | ||||||
| 	MTUEnvVar                              = "WGUI_MTU" | 	MTUEnvVar                              = "WGUI_MTU" | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue