소스 검색

auto update script executed

Toby Chui 1 년 전
부모
커밋
064cd93f5d
15개의 변경된 파일1773개의 추가작업 그리고 892개의 파일을 삭제
  1. 195 0
      accesslist.go
  2. 8 0
      api.go
  3. 0 102
      blacklist.go
  4. 1 2
      deprecated/upnp.html
  5. 2 2
      main.go
  6. 15 0
      mod/dynamicproxy/Server.go
  7. 149 58
      mod/geodb/geodb.go
  8. 98 0
      mod/geodb/netutils.go
  9. 1265 0
      web/components/access.html
  10. 0 714
      web/components/blacklist.html
  11. 11 5
      web/components/cert.html
  12. 2 3
      web/components/stats.html
  13. 3 3
      web/forbidden.html
  14. 3 3
      web/index.html
  15. 21 0
      web/main.css

+ 195 - 0
accesslist.go

@@ -0,0 +1,195 @@
+package main
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"imuslab.com/zoraxy/mod/utils"
+)
+
+/*
+	accesslist.go
+
+	This script file is added to extend the
+	reverse proxy function to include
+	banning / whitelist a specific IP address or country code
+*/
+
+/*
+	Blacklist Related
+*/
+
+// List a of blacklisted ip address or country code
+func handleListBlacklisted(w http.ResponseWriter, r *http.Request) {
+	bltype, err := utils.GetPara(r, "type")
+	if err != nil {
+		bltype = "country"
+	}
+
+	resulst := []string{}
+	if bltype == "country" {
+		resulst = geodbStore.GetAllBlacklistedCountryCode()
+	} else if bltype == "ip" {
+		resulst = geodbStore.GetAllBlacklistedIp()
+	}
+
+	js, _ := json.Marshal(resulst)
+	utils.SendJSONResponse(w, string(js))
+
+}
+
+func handleCountryBlacklistAdd(w http.ResponseWriter, r *http.Request) {
+	countryCode, err := utils.PostPara(r, "cc")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty country code")
+		return
+	}
+
+	geodbStore.AddCountryCodeToBlackList(countryCode)
+
+	utils.SendOK(w)
+}
+
+func handleCountryBlacklistRemove(w http.ResponseWriter, r *http.Request) {
+	countryCode, err := utils.PostPara(r, "cc")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty country code")
+		return
+	}
+
+	geodbStore.RemoveCountryCodeFromBlackList(countryCode)
+
+	utils.SendOK(w)
+}
+
+func handleIpBlacklistAdd(w http.ResponseWriter, r *http.Request) {
+	ipAddr, err := utils.PostPara(r, "ip")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty ip address")
+		return
+	}
+
+	geodbStore.AddIPToBlackList(ipAddr)
+}
+
+func handleIpBlacklistRemove(w http.ResponseWriter, r *http.Request) {
+	ipAddr, err := utils.PostPara(r, "ip")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty ip address")
+		return
+	}
+
+	geodbStore.RemoveIPFromBlackList(ipAddr)
+
+	utils.SendOK(w)
+}
+
+func handleBlacklistEnable(w http.ResponseWriter, r *http.Request) {
+	enable, err := utils.PostPara(r, "enable")
+	if err != nil {
+		//Return the current enabled state
+		currentEnabled := geodbStore.BlacklistEnabled
+		js, _ := json.Marshal(currentEnabled)
+		utils.SendJSONResponse(w, string(js))
+	} else {
+		if enable == "true" {
+			geodbStore.ToggleBlacklist(true)
+		} else if enable == "false" {
+			geodbStore.ToggleBlacklist(false)
+		} else {
+			utils.SendErrorResponse(w, "invalid enable state: only true and false is accepted")
+			return
+		}
+
+		utils.SendOK(w)
+	}
+}
+
+/*
+	Whitelist Related
+*/
+
+func handleListWhitelisted(w http.ResponseWriter, r *http.Request) {
+	bltype, err := utils.GetPara(r, "type")
+	if err != nil {
+		bltype = "country"
+	}
+
+	resulst := []string{}
+	if bltype == "country" {
+		resulst = geodbStore.GetAllWhitelistedCountryCode()
+	} else if bltype == "ip" {
+		resulst = geodbStore.GetAllWhitelistedIp()
+	}
+
+	js, _ := json.Marshal(resulst)
+	utils.SendJSONResponse(w, string(js))
+
+}
+
+func handleCountryWhitelistAdd(w http.ResponseWriter, r *http.Request) {
+	countryCode, err := utils.PostPara(r, "cc")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty country code")
+		return
+	}
+
+	geodbStore.AddCountryCodeToWhitelist(countryCode)
+
+	utils.SendOK(w)
+}
+
+func handleCountryWhitelistRemove(w http.ResponseWriter, r *http.Request) {
+	countryCode, err := utils.PostPara(r, "cc")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty country code")
+		return
+	}
+
+	geodbStore.RemoveCountryCodeFromWhitelist(countryCode)
+
+	utils.SendOK(w)
+}
+
+func handleIpWhitelistAdd(w http.ResponseWriter, r *http.Request) {
+	ipAddr, err := utils.PostPara(r, "ip")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty ip address")
+		return
+	}
+
+	geodbStore.AddIPToWhiteList(ipAddr)
+}
+
+func handleIpWhitelistRemove(w http.ResponseWriter, r *http.Request) {
+	ipAddr, err := utils.PostPara(r, "ip")
+	if err != nil {
+		utils.SendErrorResponse(w, "invalid or empty ip address")
+		return
+	}
+
+	geodbStore.RemoveIPFromWhiteList(ipAddr)
+
+	utils.SendOK(w)
+}
+
+func handleWhitelistEnable(w http.ResponseWriter, r *http.Request) {
+	enable, err := utils.PostPara(r, "enable")
+	if err != nil {
+		//Return the current enabled state
+		currentEnabled := geodbStore.WhitelistEnabled
+		js, _ := json.Marshal(currentEnabled)
+		utils.SendJSONResponse(w, string(js))
+	} else {
+		if enable == "true" {
+			geodbStore.ToggleWhitelist(true)
+		} else if enable == "false" {
+			geodbStore.ToggleWhitelist(false)
+		} else {
+			utils.SendErrorResponse(w, "invalid enable state: only true and false is accepted")
+			return
+		}
+
+		utils.SendOK(w)
+	}
+}

+ 8 - 0
api.go

@@ -71,6 +71,14 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/blacklist/ip/remove", handleIpBlacklistRemove)
 	authRouter.HandleFunc("/api/blacklist/enable", handleBlacklistEnable)
 
+	//Whitelist APIs
+	authRouter.HandleFunc("/api/whitelist/list", handleListWhitelisted)
+	authRouter.HandleFunc("/api/whitelist/country/add", handleCountryWhitelistAdd)
+	authRouter.HandleFunc("/api/whitelist/country/remove", handleCountryWhitelistRemove)
+	authRouter.HandleFunc("/api/whitelist/ip/add", handleIpWhitelistAdd)
+	authRouter.HandleFunc("/api/whitelist/ip/remove", handleIpWhitelistRemove)
+	authRouter.HandleFunc("/api/whitelist/enable", handleWhitelistEnable)
+
 	//Statistic & uptime monitoring API
 	authRouter.HandleFunc("/api/stats/summary", statisticCollector.HandleTodayStatLoad)
 	authRouter.HandleFunc("/api/stats/countries", HandleCountryDistrSummary)

+ 0 - 102
blacklist.go

@@ -1,102 +0,0 @@
-package main
-
-import (
-	"encoding/json"
-	"net/http"
-
-	"imuslab.com/zoraxy/mod/utils"
-)
-
-/*
-	blacklist.go
-
-	This script file is added to extend the
-	reverse proxy function to include
-	banning a specific IP address or country code
-*/
-
-//List a of blacklisted ip address or country code
-func handleListBlacklisted(w http.ResponseWriter, r *http.Request) {
-	bltype, err := utils.GetPara(r, "type")
-	if err != nil {
-		bltype = "country"
-	}
-
-	resulst := []string{}
-	if bltype == "country" {
-		resulst = geodbStore.GetAllBlacklistedCountryCode()
-	} else if bltype == "ip" {
-		resulst = geodbStore.GetAllBlacklistedIp()
-	}
-
-	js, _ := json.Marshal(resulst)
-	utils.SendJSONResponse(w, string(js))
-
-}
-
-func handleCountryBlacklistAdd(w http.ResponseWriter, r *http.Request) {
-	countryCode, err := utils.PostPara(r, "cc")
-	if err != nil {
-		utils.SendErrorResponse(w, "invalid or empty country code")
-		return
-	}
-
-	geodbStore.AddCountryCodeToBlackList(countryCode)
-
-	utils.SendOK(w)
-}
-
-func handleCountryBlacklistRemove(w http.ResponseWriter, r *http.Request) {
-	countryCode, err := utils.PostPara(r, "cc")
-	if err != nil {
-		utils.SendErrorResponse(w, "invalid or empty country code")
-		return
-	}
-
-	geodbStore.RemoveCountryCodeFromBlackList(countryCode)
-
-	utils.SendOK(w)
-}
-
-func handleIpBlacklistAdd(w http.ResponseWriter, r *http.Request) {
-	ipAddr, err := utils.PostPara(r, "ip")
-	if err != nil {
-		utils.SendErrorResponse(w, "invalid or empty ip address")
-		return
-	}
-
-	geodbStore.AddIPToBlackList(ipAddr)
-}
-
-func handleIpBlacklistRemove(w http.ResponseWriter, r *http.Request) {
-	ipAddr, err := utils.PostPara(r, "ip")
-	if err != nil {
-		utils.SendErrorResponse(w, "invalid or empty ip address")
-		return
-	}
-
-	geodbStore.RemoveIPFromBlackList(ipAddr)
-
-	utils.SendOK(w)
-}
-
-func handleBlacklistEnable(w http.ResponseWriter, r *http.Request) {
-	enable, err := utils.PostPara(r, "enable")
-	if err != nil {
-		//Return the current enabled state
-		currentEnabled := geodbStore.Enabled
-		js, _ := json.Marshal(currentEnabled)
-		utils.SendJSONResponse(w, string(js))
-	} else {
-		if enable == "true" {
-			geodbStore.ToggleBlacklist(true)
-		} else if enable == "false" {
-			geodbStore.ToggleBlacklist(false)
-		} else {
-			utils.SendErrorResponse(w, "invalid enable state: only true and false is accepted")
-			return
-		}
-
-		utils.SendOK(w)
-	}
-}

+ 1 - 2
deprecated/upnp.html

@@ -139,8 +139,7 @@
     function updateFordwardTable(){
       $.get("/api/upnp/list", function(data){
         renderForwardTable(data);
-        console.log(data);
-      })
+      });
     }
 
     function renderForwardTable(data){

+ 2 - 2
main.go

@@ -40,7 +40,7 @@ var (
 	name        = "Zoraxy"
 	version     = "2.6"
 	nodeUUID    = "generic"
-	development = false //Set this to false to use embedded web fs
+	development = true //Set this to false to use embedded web fs
 
 	/*
 		Binary Embedding File System
@@ -56,7 +56,7 @@ var (
 	authAgent          *auth.AuthAgent         //Authentication agent
 	tlsCertManager     *tlscert.Manager        //TLS / SSL management
 	redirectTable      *redirection.RuleTable  //Handle special redirection rule sets
-	geodbStore         *geodb.Store            //GeoIP database
+	geodbStore         *geodb.Store            //GeoIP database, also handle black list and whitelist features
 	netstatBuffers     *netstat.NetStatBuffers //Realtime graph buffers
 	statisticCollector *statistic.Collector    //Collecting statistic from visitors
 	uptimeMonitor      *uptime.Monitor         //Uptime monitor service worker

+ 15 - 0
mod/dynamicproxy/Server.go

@@ -25,6 +25,7 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	/*
 		General Access Check
 	*/
+
 	//Check if this ip is in blacklist
 	clientIpAddr := geodb.GetRequesterIP(r)
 	if h.Parent.Option.GeodbStore.IsBlacklisted(clientIpAddr) {
@@ -40,6 +41,20 @@ func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	//Check if this ip is in whitelist
+	if !h.Parent.Option.GeodbStore.IsWhitelisted(clientIpAddr) {
+		w.Header().Set("Content-Type", "text/html; charset=utf-8")
+		w.WriteHeader(http.StatusForbidden)
+		template, err := os.ReadFile("./web/forbidden.html")
+		if err != nil {
+			w.Write([]byte("403 - Forbidden"))
+		} else {
+			w.Write(template)
+		}
+		h.logRequest(r, false, 403, "whitelist", "")
+		return
+	}
+
 	/*
 		Redirection Routing
 	*/

+ 149 - 58
mod/geodb/geodb.go

@@ -3,7 +3,6 @@ package geodb
 import (
 	_ "embed"
 	"log"
-	"net"
 	"net/http"
 	"strings"
 
@@ -14,8 +13,9 @@ import (
 var geoipv4 []byte //Original embedded csv file
 
 type Store struct {
-	Enabled bool
-	geodb   [][]string //Parsed geodb list
+	BlacklistEnabled bool
+	WhitelistEnabled bool
+	geodb            [][]string //Parsed geodb list
 	//geoipCache sync.Map
 	geotrie *trie
 	sysdb   *database.Database
@@ -33,6 +33,7 @@ func NewGeoDb(sysdb *database.Database) (*Store, error) {
 	}
 
 	blacklistEnabled := false
+	whitelistEnabled := false
 	if sysdb != nil {
 		err = sysdb.NewTable("blacklist-cn")
 		if err != nil {
@@ -44,18 +45,31 @@ func NewGeoDb(sysdb *database.Database) (*Store, error) {
 			return nil, err
 		}
 
-		err = sysdb.NewTable("blacklist")
+		err = sysdb.NewTable("whitelist-cn")
 		if err != nil {
 			return nil, err
 		}
-		sysdb.Read("blacklist", "enabled", &blacklistEnabled)
+
+		err = sysdb.NewTable("whitelist-ip")
+		if err != nil {
+			return nil, err
+		}
+
+		err = sysdb.NewTable("blackwhitelist")
+		if err != nil {
+			return nil, err
+		}
+
+		sysdb.Read("blackwhitelist", "blacklistEnabled", &blacklistEnabled)
+		sysdb.Read("blackwhitelist", "whitelistEnabled", &whitelistEnabled)
 	} else {
 		log.Println("Database pointer set to nil: Entering debug mode")
 	}
 
 	return &Store{
-		Enabled: blacklistEnabled,
-		geodb:   parsedGeoData,
+		BlacklistEnabled: blacklistEnabled,
+		WhitelistEnabled: whitelistEnabled,
+		geodb:            parsedGeoData,
 		//geoipCache: sync.Map{},
 		geotrie: constrctTrieTree(parsedGeoData),
 		sysdb:   sysdb,
@@ -63,8 +77,13 @@ func NewGeoDb(sysdb *database.Database) (*Store, error) {
 }
 
 func (s *Store) ToggleBlacklist(enabled bool) {
-	s.sysdb.Write("blacklist", "enabled", enabled)
-	s.Enabled = enabled
+	s.sysdb.Write("blackwhitelist", "blacklistEnabled", enabled)
+	s.BlacklistEnabled = enabled
+}
+
+func (s *Store) ToggleWhitelist(enabled bool) {
+	s.sysdb.Write("blackwhitelist", "whitelistEnabled", enabled)
+	s.WhitelistEnabled = enabled
 }
 
 func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error) {
@@ -79,6 +98,10 @@ func (s *Store) Close() {
 
 }
 
+/*
+	Country code based black / white list
+*/
+
 func (s *Store) AddCountryCodeToBlackList(countryCode string) {
 	countryCode = strings.ToLower(countryCode)
 	s.sysdb.Write("blacklist-cn", countryCode, true)
@@ -89,6 +112,16 @@ func (s *Store) RemoveCountryCodeFromBlackList(countryCode string) {
 	s.sysdb.Delete("blacklist-cn", countryCode)
 }
 
+func (s *Store) AddCountryCodeToWhitelist(countryCode string) {
+	countryCode = strings.ToLower(countryCode)
+	s.sysdb.Write("whitelist-cn", countryCode, true)
+}
+
+func (s *Store) RemoveCountryCodeFromWhitelist(countryCode string) {
+	countryCode = strings.ToLower(countryCode)
+	s.sysdb.Delete("whitelist-cn", countryCode)
+}
+
 func (s *Store) IsCountryCodeBlacklisted(countryCode string) bool {
 	countryCode = strings.ToLower(countryCode)
 	var isBlacklisted bool = false
@@ -96,6 +129,13 @@ func (s *Store) IsCountryCodeBlacklisted(countryCode string) bool {
 	return isBlacklisted
 }
 
+func (s *Store) IsCountryCodeWhitelisted(countryCode string) bool {
+	countryCode = strings.ToLower(countryCode)
+	var isWhitelisted bool = false
+	s.sysdb.Read("whitelist-cn", countryCode, &isWhitelisted)
+	return isWhitelisted
+}
+
 func (s *Store) GetAllBlacklistedCountryCode() []string {
 	bannedCountryCodes := []string{}
 	entries, err := s.sysdb.ListTable("blacklist-cn")
@@ -110,6 +150,24 @@ func (s *Store) GetAllBlacklistedCountryCode() []string {
 	return bannedCountryCodes
 }
 
+func (s *Store) GetAllWhitelistedCountryCode() []string {
+	whitelistedCountryCode := []string{}
+	entries, err := s.sysdb.ListTable("whitelist-cn")
+	if err != nil {
+		return whitelistedCountryCode
+	}
+	for _, keypairs := range entries {
+		ip := string(keypairs[0])
+		whitelistedCountryCode = append(whitelistedCountryCode, ip)
+	}
+
+	return whitelistedCountryCode
+}
+
+/*
+	IP based black / whitelist
+*/
+
 func (s *Store) AddIPToBlackList(ipAddr string) {
 	s.sysdb.Write("blacklist-ip", ipAddr, true)
 }
@@ -118,6 +176,14 @@ func (s *Store) RemoveIPFromBlackList(ipAddr string) {
 	s.sysdb.Delete("blacklist-ip", ipAddr)
 }
 
+func (s *Store) AddIPToWhiteList(ipAddr string) {
+	s.sysdb.Write("whitelist-ip", ipAddr, true)
+}
+
+func (s *Store) RemoveIPFromWhiteList(ipAddr string) {
+	s.sysdb.Delete("whitelist-ip", ipAddr)
+}
+
 func (s *Store) IsIPBlacklisted(ipAddr string) bool {
 	var isBlacklisted bool = false
 	s.sysdb.Read("blacklist-ip", ipAddr, &isBlacklisted)
@@ -142,6 +208,30 @@ func (s *Store) IsIPBlacklisted(ipAddr string) bool {
 	return false
 }
 
+func (s *Store) IsIPWhitelisted(ipAddr string) bool {
+	var isBlacklisted bool = false
+	s.sysdb.Read("whitelist-ip", ipAddr, &isBlacklisted)
+	if isBlacklisted {
+		return true
+	}
+
+	//Check for IP wildcard and CIRD rules
+	AllBlacklistedIps := s.GetAllBlacklistedIp()
+	for _, blacklistRule := range AllBlacklistedIps {
+		wildcardMatch := MatchIpWildcard(ipAddr, blacklistRule)
+		if wildcardMatch {
+			return true
+		}
+
+		cidrMatch := MatchIpCIDR(ipAddr, blacklistRule)
+		if cidrMatch {
+			return true
+		}
+	}
+
+	return false
+}
+
 func (s *Store) GetAllBlacklistedIp() []string {
 	bannedIps := []string{}
 	entries, err := s.sysdb.ListTable("blacklist-ip")
@@ -157,9 +247,27 @@ func (s *Store) GetAllBlacklistedIp() []string {
 	return bannedIps
 }
 
-// Check if a IP address is blacklisted, in either country or IP blacklist
+func (s *Store) GetAllWhitelistedIp() []string {
+	whitelistedIp := []string{}
+	entries, err := s.sysdb.ListTable("whitelist-ip")
+	if err != nil {
+		return whitelistedIp
+	}
+
+	for _, keypairs := range entries {
+		ip := string(keypairs[0])
+		whitelistedIp = append(whitelistedIp, ip)
+	}
+
+	return whitelistedIp
+}
+
+/*
+Check if a IP address is blacklisted, in either country or IP blacklist
+IsBlacklisted default return is false (allow access)
+*/
 func (s *Store) IsBlacklisted(ipAddr string) bool {
-	if !s.Enabled {
+	if !s.BlacklistEnabled {
 		//Blacklist not enabled. Always return false
 		return false
 	}
@@ -185,66 +293,49 @@ func (s *Store) IsBlacklisted(ipAddr string) bool {
 	return false
 }
 
-func (s *Store) GetRequesterCountryISOCode(r *http.Request) string {
-	ipAddr := GetRequesterIP(r)
+/*
+IsWhitelisted check if a given IP address is in the current
+server's white list.
+
+Note that the Whitelist default result is true even
+when encountered error
+*/
+func (s *Store) IsWhitelisted(ipAddr string) bool {
+	if !s.WhitelistEnabled {
+		//Whitelist not enabled. Always return true (allow access)
+		return true
+	}
+
 	if ipAddr == "" {
-		return ""
+		//Unable to get the target IP address, assume ok
+		return true
 	}
+
 	countryCode, err := s.ResolveCountryCodeFromIP(ipAddr)
 	if err != nil {
-		return ""
+		return true
 	}
 
-	return countryCode.CountryIsoCode
-}
-
-// Utilities function
-func GetRequesterIP(r *http.Request) string {
-	ip := r.Header.Get("X-Forwarded-For")
-	if ip == "" {
-		ip = r.Header.Get("X-Real-IP")
-		if ip == "" {
-			ip = strings.Split(r.RemoteAddr, ":")[0]
-		}
-	}
-	return ip
-}
-
-// Match the IP address with a wildcard string
-func MatchIpWildcard(ipAddress, wildcard string) bool {
-	// Split IP address and wildcard into octets
-	ipOctets := strings.Split(ipAddress, ".")
-	wildcardOctets := strings.Split(wildcard, ".")
-
-	// Check that both have 4 octets
-	if len(ipOctets) != 4 || len(wildcardOctets) != 4 {
-		return false
+	if s.IsCountryCodeWhitelisted(countryCode.CountryIsoCode) {
+		return true
 	}
 
-	// Check each octet to see if it matches the wildcard or is an exact match
-	for i := 0; i < 4; i++ {
-		if wildcardOctets[i] == "*" {
-			continue
-		}
-		if ipOctets[i] != wildcardOctets[i] {
-			return false
-		}
+	if s.IsIPWhitelisted(ipAddr) {
+		return true
 	}
 
-	return true
+	return false
 }
 
-// Match ip address with CIDR
-func MatchIpCIDR(ip string, cidr string) bool {
-	// parse the CIDR string
-	_, cidrnet, err := net.ParseCIDR(cidr)
+func (s *Store) GetRequesterCountryISOCode(r *http.Request) string {
+	ipAddr := GetRequesterIP(r)
+	if ipAddr == "" {
+		return ""
+	}
+	countryCode, err := s.ResolveCountryCodeFromIP(ipAddr)
 	if err != nil {
-		return false
+		return ""
 	}
 
-	// parse the IP address
-	ipAddr := net.ParseIP(ip)
-
-	// check if the IP address is within the CIDR range
-	return cidrnet.Contains(ipAddr)
+	return countryCode.CountryIsoCode
 }

+ 98 - 0
mod/geodb/netutils.go

@@ -0,0 +1,98 @@
+package geodb
+
+import (
+	"net"
+	"net/http"
+	"strings"
+)
+
+// Utilities function
+func GetRequesterIP(r *http.Request) string {
+	ip := r.Header.Get("X-Forwarded-For")
+	if ip == "" {
+		ip = r.Header.Get("X-Real-IP")
+		if ip == "" {
+			ip = strings.Split(r.RemoteAddr, ":")[0]
+		}
+	}
+	return ip
+}
+
+// Match the IP address with a wildcard string
+func MatchIpWildcard(ipAddress, wildcard string) bool {
+	// Split IP address and wildcard into octets
+	ipOctets := strings.Split(ipAddress, ".")
+	wildcardOctets := strings.Split(wildcard, ".")
+
+	// Check that both have 4 octets
+	if len(ipOctets) != 4 || len(wildcardOctets) != 4 {
+		return false
+	}
+
+	// Check each octet to see if it matches the wildcard or is an exact match
+	for i := 0; i < 4; i++ {
+		if wildcardOctets[i] == "*" {
+			continue
+		}
+		if ipOctets[i] != wildcardOctets[i] {
+			return false
+		}
+	}
+
+	return true
+}
+
+// Match ip address with CIDR
+func MatchIpCIDR(ip string, cidr string) bool {
+	// parse the CIDR string
+	_, cidrnet, err := net.ParseCIDR(cidr)
+	if err != nil {
+		return false
+	}
+
+	// parse the IP address
+	ipAddr := net.ParseIP(ip)
+
+	// check if the IP address is within the CIDR range
+	return cidrnet.Contains(ipAddr)
+}
+
+// Check if a ip is private IP range
+func IsPrivateIP(ipStr string) bool {
+	ip := net.ParseIP(ipStr)
+	if ip == nil {
+		return false
+	}
+
+	// Check for IPv4 private address ranges
+	if ip.To4() != nil {
+		privateIPv4Ranges := []string{
+			"10.0.0.0/8",     // 10.0.0.0 - 10.255.255.255
+			"172.16.0.0/12",  // 172.16.0.0 - 172.31.255.255
+			"192.168.0.0/16", // 192.168.0.0 - 192.168.255.255
+			"169.254.0.0/16", // 169.254.0.0 - 169.254.255.255 (link-local addresses)
+		}
+
+		for _, network := range privateIPv4Ranges {
+			_, privateNet, _ := net.ParseCIDR(network)
+			if privateNet.Contains(ip) {
+				return true
+			}
+		}
+	} else {
+		// Check for IPv6 private address ranges
+		privateIPv6Ranges := []string{
+			"fc00::/7",  // Unique local addresses
+			"fe80::/10", // Link-local addresses
+		}
+
+		for _, network := range privateIPv6Ranges {
+			_, privateNet, _ := net.ParseCIDR(network)
+			if privateNet.Contains(ip) {
+				return true
+			}
+		}
+	}
+
+	return false
+}

+ 1265 - 0
web/components/access.html

@@ -0,0 +1,1265 @@
+<div class="standardContainer">
+    <div class="ui basic segment">
+        <h2>Access Control</h2>
+        <p>Setup blacklist or whitelist based on estimated IP geographic location or IP address</p>
+    </div>
+
+<div class="ui top attached tabular menu">
+    <a class="accesscontrol item active" data-tab="tab_blacklist"><i class="ui red circle times icon"></i> Blacklist</a>
+    <a class="accesscontrol item" data-tab="tab_whitelist"><i class="ui green check circle icon"></i> Whitelist</a>
+    <a class="accesscontrol item" data-tab="tab_quickban"><i class="ui red ban icon"></i> Quick Ban</a>
+</div>
+
+<!-- Blacklist Conguration Menu-->
+<div class="ui bottom attached tab segment active" data-tab="tab_blacklist">
+    <h2>Blacklist</h2>
+    <p>Limit access from the following country or IP address<br>
+    <small>Tips: If you only want a few regions to access your site, use whitelist instead.</small></p>
+    <div class="ui divider"></div>
+    <div class="ui toggle checkbox">
+        <input type="checkbox" id="enableBlacklist">
+        <label>Enable Blacklist</label>
+    </div>
+    <div class="toggleSucc" style="float: right; display:none; color: #2abd4d;" >
+        <i class="ui green checkmark icon"></i> Setting Saved
+    </div>
+    <h4>Country Blacklist</h4>
+        <p><i class="yellow exclamation triangle icon"></i>
+        This will block all requests from the selected country. The requester's location is estimated from their IP address and may not be 100% accurate.</p>
+
+    <div class="ui form">
+        <div class="field">
+            <label>Select Country</label>
+            <div id="countrySelector" class="ui fluid search selection dropdown">
+                <input type="hidden" name="country">
+                <i class="dropdown icon"></i>
+                <div class="default text">Select Country</div>
+                <div class="menu">
+                <div class="item" data-value="af"><i class="af flag"></i>Afghanistan</div>
+                <div class="item" data-value="ax"><i class="ax flag"></i>Aland Islands</div>
+                <div class="item" data-value="al"><i class="al flag"></i>Albania</div>
+                <div class="item" data-value="dz"><i class="dz flag"></i>Algeria</div>
+                <div class="item" data-value="as"><i class="as flag"></i>American Samoa</div>
+                <div class="item" data-value="ad"><i class="ad flag"></i>Andorra</div>
+                <div class="item" data-value="ao"><i class="ao flag"></i>Angola</div>
+                <div class="item" data-value="ai"><i class="ai flag"></i>Anguilla</div>
+                <div class="item" data-value="ag"><i class="ag flag"></i>Antigua</div>
+                <div class="item" data-value="ar"><i class="ar flag"></i>Argentina</div>
+                <div class="item" data-value="am"><i class="am flag"></i>Armenia</div>
+                <div class="item" data-value="aw"><i class="aw flag"></i>Aruba</div>
+                <div class="item" data-value="au"><i class="au flag"></i>Australia</div>
+                <div class="item" data-value="at"><i class="at flag"></i>Austria</div>
+                <div class="item" data-value="az"><i class="az flag"></i>Azerbaijan</div>
+                <div class="item" data-value="bs"><i class="bs flag"></i>Bahamas</div>
+                <div class="item" data-value="bh"><i class="bh flag"></i>Bahrain</div>
+                <div class="item" data-value="bd"><i class="bd flag"></i>Bangladesh</div>
+                <div class="item" data-value="bb"><i class="bb flag"></i>Barbados</div>
+                <div class="item" data-value="by"><i class="by flag"></i>Belarus</div>
+                <div class="item" data-value="be"><i class="be flag"></i>Belgium</div>
+                <div class="item" data-value="bz"><i class="bz flag"></i>Belize</div>
+                <div class="item" data-value="bj"><i class="bj flag"></i>Benin</div>
+                <div class="item" data-value="bm"><i class="bm flag"></i>Bermuda</div>
+                <div class="item" data-value="bt"><i class="bt flag"></i>Bhutan</div>
+                <div class="item" data-value="bo"><i class="bo flag"></i>Bolivia</div>
+                <div class="item" data-value="ba"><i class="ba flag"></i>Bosnia</div>
+                <div class="item" data-value="bw"><i class="bw flag"></i>Botswana</div>
+                <div class="item" data-value="bv"><i class="bv flag"></i>Bouvet Island</div>
+                <div class="item" data-value="br"><i class="br flag"></i>Brazil</div>
+                <div class="item" data-value="vg"><i class="vg flag"></i>British Virgin Islands</div>
+                <div class="item" data-value="bn"><i class="bn flag"></i>Brunei</div>
+                <div class="item" data-value="bg"><i class="bg flag"></i>Bulgaria</div>
+                <div class="item" data-value="bf"><i class="bf flag"></i>Burkina Faso</div>
+                <div class="item" data-value="mm"><i class="mm flag"></i>Burma</div>
+                <div class="item" data-value="bi"><i class="bi flag"></i>Burundi</div>
+                <div class="item" data-value="tc"><i class="tc flag"></i>Caicos Islands</div>
+                <div class="item" data-value="kh"><i class="kh flag"></i>Cambodia</div>
+                <div class="item" data-value="cm"><i class="cm flag"></i>Cameroon</div>
+                <div class="item" data-value="ca"><i class="ca flag"></i>Canada</div>
+                <div class="item" data-value="cv"><i class="cv flag"></i>Cape Verde</div>
+                <div class="item" data-value="ky"><i class="ky flag"></i>Cayman Islands</div>
+                <div class="item" data-value="cf"><i class="cf flag"></i>Central African Republic</div>
+                <div class="item" data-value="td"><i class="td flag"></i>Chad</div>
+                <div class="item" data-value="cl"><i class="cl flag"></i>Chile</div>
+                <div class="item" data-value="cn"><i class="cn flag"></i>China</div>
+                <div class="item" data-value="cx"><i class="cx flag"></i>Christmas Island</div>
+                <div class="item" data-value="cc"><i class="cc flag"></i>Cocos Islands</div>
+                <div class="item" data-value="co"><i class="co flag"></i>Colombia</div>
+                <div class="item" data-value="km"><i class="km flag"></i>Comoros</div>
+                <div class="item" data-value="cg"><i class="cg flag"></i>Congo Brazzaville</div>
+                <div class="item" data-value="cd"><i class="cd flag"></i>Congo</div>
+                <div class="item" data-value="ck"><i class="ck flag"></i>Cook Islands</div>
+                <div class="item" data-value="cr"><i class="cr flag"></i>Costa Rica</div>
+                <div class="item" data-value="ci"><i class="ci flag"></i>Cote Divoire</div>
+                <div class="item" data-value="hr"><i class="hr flag"></i>Croatia</div>
+                <div class="item" data-value="cu"><i class="cu flag"></i>Cuba</div>
+                <div class="item" data-value="cy"><i class="cy flag"></i>Cyprus</div>
+                <div class="item" data-value="cz"><i class="cz flag"></i>Czech Republic</div>
+                <div class="item" data-value="dk"><i class="dk flag"></i>Denmark</div>
+                <div class="item" data-value="dj"><i class="dj flag"></i>Djibouti</div>
+                <div class="item" data-value="dm"><i class="dm flag"></i>Dominica</div>
+                <div class="item" data-value="do"><i class="do flag"></i>Dominican Republic</div>
+                <div class="item" data-value="ec"><i class="ec flag"></i>Ecuador</div>
+                <div class="item" data-value="eg"><i class="eg flag"></i>Egypt</div>
+                <div class="item" data-value="sv"><i class="sv flag"></i>El Salvador</div>
+                <div class="item" data-value="gb"><i class="gb flag"></i>England</div>
+                <div class="item" data-value="gq"><i class="gq flag"></i>Equatorial Guinea</div>
+                <div class="item" data-value="er"><i class="er flag"></i>Eritrea</div>
+                <div class="item" data-value="ee"><i class="ee flag"></i>Estonia</div>
+                <div class="item" data-value="et"><i class="et flag"></i>Ethiopia</div>
+                <div class="item" data-value="eu"><i class="eu flag"></i>European Union</div>
+                <div class="item" data-value="fk"><i class="fk flag"></i>Falkland Islands</div>
+                <div class="item" data-value="fo"><i class="fo flag"></i>Faroe Islands</div>
+                <div class="item" data-value="fj"><i class="fj flag"></i>Fiji</div>
+                <div class="item" data-value="fi"><i class="fi flag"></i>Finland</div>
+                <div class="item" data-value="fr"><i class="fr flag"></i>France</div>
+                <div class="item" data-value="gf"><i class="gf flag"></i>French Guiana</div>
+                <div class="item" data-value="pf"><i class="pf flag"></i>French Polynesia</div>
+                <div class="item" data-value="tf"><i class="tf flag"></i>French Territories</div>
+                <div class="item" data-value="ga"><i class="ga flag"></i>Gabon</div>
+                <div class="item" data-value="gm"><i class="gm flag"></i>Gambia</div>
+                <div class="item" data-value="ge"><i class="ge flag"></i>Georgia</div>
+                <div class="item" data-value="de"><i class="de flag"></i>Germany</div>
+                <div class="item" data-value="gh"><i class="gh flag"></i>Ghana</div>
+                <div class="item" data-value="gi"><i class="gi flag"></i>Gibraltar</div>
+                <div class="item" data-value="gr"><i class="gr flag"></i>Greece</div>
+                <div class="item" data-value="gl"><i class="gl flag"></i>Greenland</div>
+                <div class="item" data-value="gd"><i class="gd flag"></i>Grenada</div>
+                <div class="item" data-value="gp"><i class="gp flag"></i>Guadeloupe</div>
+                <div class="item" data-value="gu"><i class="gu flag"></i>Guam</div>
+                <div class="item" data-value="gt"><i class="gt flag"></i>Guatemala</div>
+                <div class="item" data-value="gw"><i class="gw flag"></i>Guinea-Bissau</div>
+                <div class="item" data-value="gn"><i class="gn flag"></i>Guinea</div>
+                <div class="item" data-value="gy"><i class="gy flag"></i>Guyana</div>
+                <div class="item" data-value="ht"><i class="ht flag"></i>Haiti</div>
+                <div class="item" data-value="hm"><i class="hm flag"></i>Heard Island</div>
+                <div class="item" data-value="hn"><i class="hn flag"></i>Honduras</div>
+                <div class="item" data-value="hk"><i class="hk flag"></i>Hong Kong</div>
+                <div class="item" data-value="hu"><i class="hu flag"></i>Hungary</div>
+                <div class="item" data-value="is"><i class="is flag"></i>Iceland</div>
+                <div class="item" data-value="in"><i class="in flag"></i>India</div>
+                <div class="item" data-value="io"><i class="io flag"></i>Indian Ocean Territory</div>
+                <div class="item" data-value="id"><i class="id flag"></i>Indonesia</div>
+                <div class="item" data-value="ir"><i class="ir flag"></i>Iran</div>
+                <div class="item" data-value="iq"><i class="iq flag"></i>Iraq</div>
+                <div class="item" data-value="ie"><i class="ie flag"></i>Ireland</div>
+                <div class="item" data-value="il"><i class="il flag"></i>Israel</div>
+                <div class="item" data-value="it"><i class="it flag"></i>Italy</div>
+                <div class="item" data-value="jm"><i class="jm flag"></i>Jamaica</div>
+                <div class="item" data-value="jp"><i class="jp flag"></i>Japan</div>
+                <div class="item" data-value="jo"><i class="jo flag"></i>Jordan</div>
+                <div class="item" data-value="kz"><i class="kz flag"></i>Kazakhstan</div>
+                <div class="item" data-value="ke"><i class="ke flag"></i>Kenya</div>
+                <div class="item" data-value="ki"><i class="ki flag"></i>Kiribati</div>
+                <div class="item" data-value="kw"><i class="kw flag"></i>Kuwait</div>
+                <div class="item" data-value="kg"><i class="kg flag"></i>Kyrgyzstan</div>
+                <div class="item" data-value="la"><i class="la flag"></i>Laos</div>
+                <div class="item" data-value="lv"><i class="lv flag"></i>Latvia</div>
+                <div class="item" data-value="lb"><i class="lb flag"></i>Lebanon</div>
+                <div class="item" data-value="ls"><i class="ls flag"></i>Lesotho</div>
+                <div class="item" data-value="lr"><i class="lr flag"></i>Liberia</div>
+                <div class="item" data-value="ly"><i class="ly flag"></i>Libya</div>
+                <div class="item" data-value="li"><i class="li flag"></i>Liechtenstein</div>
+                <div class="item" data-value="lt"><i class="lt flag"></i>Lithuania</div>
+                <div class="item" data-value="lu"><i class="lu flag"></i>Luxembourg</div>
+                <div class="item" data-value="mo"><i class="mo flag"></i>Macau</div>
+                <div class="item" data-value="mk"><i class="mk flag"></i>Macedonia</div>
+                <div class="item" data-value="mg"><i class="mg flag"></i>Madagascar</div>
+                <div class="item" data-value="mw"><i class="mw flag"></i>Malawi</div>
+                <div class="item" data-value="my"><i class="my flag"></i>Malaysia</div>
+                <div class="item" data-value="mv"><i class="mv flag"></i>Maldives</div>
+                <div class="item" data-value="ml"><i class="ml flag"></i>Mali</div>
+                <div class="item" data-value="mt"><i class="mt flag"></i>Malta</div>
+                <div class="item" data-value="mh"><i class="mh flag"></i>Marshall Islands</div>
+                <div class="item" data-value="mq"><i class="mq flag"></i>Martinique</div>
+                <div class="item" data-value="mr"><i class="mr flag"></i>Mauritania</div>
+                <div class="item" data-value="mu"><i class="mu flag"></i>Mauritius</div>
+                <div class="item" data-value="yt"><i class="yt flag"></i>Mayotte</div>
+                <div class="item" data-value="mx"><i class="mx flag"></i>Mexico</div>
+                <div class="item" data-value="fm"><i class="fm flag"></i>Micronesia</div>
+                <div class="item" data-value="md"><i class="md flag"></i>Moldova</div>
+                <div class="item" data-value="mc"><i class="mc flag"></i>Monaco</div>
+                <div class="item" data-value="mn"><i class="mn flag"></i>Mongolia</div>
+                <div class="item" data-value="me"><i class="me flag"></i>Montenegro</div>
+                <div class="item" data-value="ms"><i class="ms flag"></i>Montserrat</div>
+                <div class="item" data-value="ma"><i class="ma flag"></i>Morocco</div>
+                <div class="item" data-value="mz"><i class="mz flag"></i>Mozambique</div>
+                <div class="item" data-value="na"><i class="na flag"></i>Namibia</div>
+                <div class="item" data-value="nr"><i class="nr flag"></i>Nauru</div>
+                <div class="item" data-value="np"><i class="np flag"></i>Nepal</div>
+                <div class="item" data-value="an"><i class="an flag"></i>Netherlands Antilles</div>
+                <div class="item" data-value="nl"><i class="nl flag"></i>Netherlands</div>
+                <div class="item" data-value="nc"><i class="nc flag"></i>New Caledonia</div>
+                <div class="item" data-value="pg"><i class="pg flag"></i>New Guinea</div>
+                <div class="item" data-value="nz"><i class="nz flag"></i>New Zealand</div>
+                <div class="item" data-value="ni"><i class="ni flag"></i>Nicaragua</div>
+                <div class="item" data-value="ne"><i class="ne flag"></i>Niger</div>
+                <div class="item" data-value="ng"><i class="ng flag"></i>Nigeria</div>
+                <div class="item" data-value="nu"><i class="nu flag"></i>Niue</div>
+                <div class="item" data-value="nf"><i class="nf flag"></i>Norfolk Island</div>
+                <div class="item" data-value="kp"><i class="kp flag"></i>North Korea</div>
+                <div class="item" data-value="mp"><i class="mp flag"></i>Northern Mariana Islands</div>
+                <div class="item" data-value="no"><i class="no flag"></i>Norway</div>
+                <div class="item" data-value="om"><i class="om flag"></i>Oman</div>
+                <div class="item" data-value="pk"><i class="pk flag"></i>Pakistan</div>
+                <div class="item" data-value="pw"><i class="pw flag"></i>Palau</div>
+                <div class="item" data-value="ps"><i class="ps flag"></i>Palestine</div>
+                <div class="item" data-value="pa"><i class="pa flag"></i>Panama</div>
+                <div class="item" data-value="py"><i class="py flag"></i>Paraguay</div>
+                <div class="item" data-value="pe"><i class="pe flag"></i>Peru</div>
+                <div class="item" data-value="ph"><i class="ph flag"></i>Philippines</div>
+                <div class="item" data-value="pn"><i class="pn flag"></i>Pitcairn Islands</div>
+                <div class="item" data-value="pl"><i class="pl flag"></i>Poland</div>
+                <div class="item" data-value="pt"><i class="pt flag"></i>Portugal</div>
+                <div class="item" data-value="pr"><i class="pr flag"></i>Puerto Rico</div>
+                <div class="item" data-value="qa"><i class="qa flag"></i>Qatar</div>
+                <div class="item" data-value="re"><i class="re flag"></i>Reunion</div>
+                <div class="item" data-value="ro"><i class="ro flag"></i>Romania</div>
+                <div class="item" data-value="ru"><i class="ru flag"></i>Russia</div>
+                <div class="item" data-value="rw"><i class="rw flag"></i>Rwanda</div>
+                <div class="item" data-value="sh"><i class="sh flag"></i>Saint Helena</div>
+                <div class="item" data-value="kn"><i class="kn flag"></i>Saint Kitts and Nevis</div>
+                <div class="item" data-value="lc"><i class="lc flag"></i>Saint Lucia</div>
+                <div class="item" data-value="pm"><i class="pm flag"></i>Saint Pierre</div>
+                <div class="item" data-value="vc"><i class="vc flag"></i>Saint Vincent</div>
+                <div class="item" data-value="ws"><i class="ws flag"></i>Samoa</div>
+                <div class="item" data-value="sm"><i class="sm flag"></i>San Marino</div>
+                <div class="item" data-value="gs"><i class="gs flag"></i>Sandwich Islands</div>
+                <div class="item" data-value="st"><i class="st flag"></i>Sao Tome</div>
+                <div class="item" data-value="sa"><i class="sa flag"></i>Saudi Arabia</div>
+                <div class="item" data-value="sn"><i class="sn flag"></i>Senegal</div>
+                <div class="item" data-value="cs"><i class="cs flag"></i>Serbia</div>
+                <div class="item" data-value="rs"><i class="rs flag"></i>Serbia</div>
+                <div class="item" data-value="sc"><i class="sc flag"></i>Seychelles</div>
+                <div class="item" data-value="sl"><i class="sl flag"></i>Sierra Leone</div>
+                <div class="item" data-value="sg"><i class="sg flag"></i>Singapore</div>
+                <div class="item" data-value="sk"><i class="sk flag"></i>Slovakia</div>
+                <div class="item" data-value="si"><i class="si flag"></i>Slovenia</div>
+                <div class="item" data-value="sb"><i class="sb flag"></i>Solomon Islands</div>
+                <div class="item" data-value="so"><i class="so flag"></i>Somalia</div>
+                <div class="item" data-value="za"><i class="za flag"></i>South Africa</div>
+                <div class="item" data-value="kr"><i class="kr flag"></i>South Korea</div>
+                <div class="item" data-value="es"><i class="es flag"></i>Spain</div>
+                <div class="item" data-value="lk"><i class="lk flag"></i>Sri Lanka</div>
+                <div class="item" data-value="sd"><i class="sd flag"></i>Sudan</div>
+                <div class="item" data-value="sr"><i class="sr flag"></i>Suriname</div>
+                <div class="item" data-value="sj"><i class="sj flag"></i>Svalbard</div>
+                <div class="item" data-value="sz"><i class="sz flag"></i>Swaziland</div>
+                <div class="item" data-value="se"><i class="se flag"></i>Sweden</div>
+                <div class="item" data-value="ch"><i class="ch flag"></i>Switzerland</div>
+                <div class="item" data-value="sy"><i class="sy flag"></i>Syria</div>
+                <div class="item" data-value="tw"><i class="tw flag"></i>Taiwan</div>
+                <div class="item" data-value="tj"><i class="tj flag"></i>Tajikistan</div>
+                <div class="item" data-value="tz"><i class="tz flag"></i>Tanzania</div>
+                <div class="item" data-value="th"><i class="th flag"></i>Thailand</div>
+                <div class="item" data-value="tl"><i class="tl flag"></i>Timorleste</div>
+                <div class="item" data-value="tg"><i class="tg flag"></i>Togo</div>
+                <div class="item" data-value="tk"><i class="tk flag"></i>Tokelau</div>
+                <div class="item" data-value="to"><i class="to flag"></i>Tonga</div>
+                <div class="item" data-value="tt"><i class="tt flag"></i>Trinidad</div>
+                <div class="item" data-value="tn"><i class="tn flag"></i>Tunisia</div>
+                <div class="item" data-value="tr"><i class="tr flag"></i>Turkey</div>
+                <div class="item" data-value="tm"><i class="tm flag"></i>Turkmenistan</div>
+                <div class="item" data-value="tv"><i class="tv flag"></i>Tuvalu</div>
+                <div class="item" data-value="ug"><i class="ug flag"></i>Uganda</div>
+                <div class="item" data-value="ua"><i class="ua flag"></i>Ukraine</div>
+                <div class="item" data-value="ae"><i class="ae flag"></i>United Arab Emirates</div>
+                <div class="item" data-value="us"><i class="us flag"></i>United States</div>
+                <div class="item" data-value="uy"><i class="uy flag"></i>Uruguay</div>
+                <div class="item" data-value="um"><i class="um flag"></i>Us Minor Islands</div>
+                <div class="item" data-value="vi"><i class="vi flag"></i>Us Virgin Islands</div>
+                <div class="item" data-value="uz"><i class="uz flag"></i>Uzbekistan</div>
+                <div class="item" data-value="vu"><i class="vu flag"></i>Vanuatu</div>
+                <div class="item" data-value="va"><i class="va flag"></i>Vatican City</div>
+                <div class="item" data-value="ve"><i class="ve flag"></i>Venezuela</div>
+                <div class="item" data-value="vn"><i class="vn flag"></i>Vietnam</div>
+                <div class="item" data-value="wf"><i class="wf flag"></i>Wallis and Futuna</div>
+                <div class="item" data-value="eh"><i class="eh flag"></i>Western Sahara</div>
+                <div class="item" data-value="ye"><i class="ye flag"></i>Yemen</div>
+                <div class="item" data-value="zm"><i class="zm flag"></i>Zambia</div>
+                <div class="item" data-value="zw"><i class="zw flag"></i>Zimbabwe</div>
+            </div>
+            </div>
+        </div>
+        <button class="ui basic red button" id="ban-btn" onclick="addCountryToBlacklist();"><i class="ui red ban icon"></i> Blacklist Country</button>
+    </div>
+    <table class="ui unstackable basic celled table">
+        <thead>
+            <tr>
+                <th>ISO Code</th>
+                <th>Remove</th>
+            </tr>
+        </thead>
+        <tbody id="banned-list">
+
+        </tbody>
+    </table>
+
+    <h4>IP Blacklist</h4>
+    <p>Black a certain IP or IP range</p>
+    <div class="ui form">
+        <div class="field">
+        <label>IP Address</label>
+        <input id="ipAddressInput" type="text" placeholder="IP Address">
+        </div>
+        <button id="addIpButton" onclick="addIpBlacklist();" class="ui basic red icon button">
+        <i class="ban icon"></i> Blacklist IP
+        </button>
+    </div>
+    <div class="ui message">
+        <i class="ui info circle icon"></i> IP Address support the following formats
+        <div class="ui bulleted list">
+            <div class="item">Fixed IP Address (e.g. 192.128.4.100)</div>
+            <div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
+            <div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
+        </div>
+    </div>
+    <table class="ui unstackable basic celled table">
+        <thead>
+        <tr>
+            <th>IP Address</th>
+            <th>Remove</th>
+        </tr>
+        </thead>
+        <tbody id="blacklistIpTable">
+
+        </tbody>
+    </table>
+</div>
+
+<!-- Whitelist Config Menu-->
+<div class="ui bottom attached tab segment" data-tab="tab_whitelist">
+    <h2>Whitelist</h2>
+    <p>Enable access from the following countries or IP. <br>
+        Whitelist has lower priority than blacklist if both whitelist and blacklist are enabled <br>
+        <small>Tips: You can use blacklist to fine tune whitelist coverage</small></p>
+    <div class="ui divider"></div>
+    <div class="ui toggle checkbox">
+        <input type="checkbox" id="enableWhitelist">
+        <label>Enable Whitelist</label>
+    </div>
+    <div class="toggleSucc" style="float: right; display:none; color: #2abd4d;" >
+        <i class="ui green checkmark icon"></i> Setting Saved
+    </div>
+    <h4>Country Whitelist</h4>
+        <p><i class="yellow exclamation triangle icon"></i>
+        This will allow all requests from the selected country. The requester's location is estimated from their IP address and may not be 100% accurate.</p>
+
+    <div class="ui form">
+        <div class="field">
+            <label>Select Country</label>
+            <div id="countrySelectorWhitelist" class="ui fluid search selection dropdown">
+                <input type="hidden" name="country">
+                <i class="dropdown icon"></i>
+                <div class="default text">Select Country</div>
+                <div class="menu">
+                <div class="item" data-value="af"><i class="af flag"></i>Afghanistan</div>
+                <div class="item" data-value="ax"><i class="ax flag"></i>Aland Islands</div>
+                <div class="item" data-value="al"><i class="al flag"></i>Albania</div>
+                <div class="item" data-value="dz"><i class="dz flag"></i>Algeria</div>
+                <div class="item" data-value="as"><i class="as flag"></i>American Samoa</div>
+                <div class="item" data-value="ad"><i class="ad flag"></i>Andorra</div>
+                <div class="item" data-value="ao"><i class="ao flag"></i>Angola</div>
+                <div class="item" data-value="ai"><i class="ai flag"></i>Anguilla</div>
+                <div class="item" data-value="ag"><i class="ag flag"></i>Antigua</div>
+                <div class="item" data-value="ar"><i class="ar flag"></i>Argentina</div>
+                <div class="item" data-value="am"><i class="am flag"></i>Armenia</div>
+                <div class="item" data-value="aw"><i class="aw flag"></i>Aruba</div>
+                <div class="item" data-value="au"><i class="au flag"></i>Australia</div>
+                <div class="item" data-value="at"><i class="at flag"></i>Austria</div>
+                <div class="item" data-value="az"><i class="az flag"></i>Azerbaijan</div>
+                <div class="item" data-value="bs"><i class="bs flag"></i>Bahamas</div>
+                <div class="item" data-value="bh"><i class="bh flag"></i>Bahrain</div>
+                <div class="item" data-value="bd"><i class="bd flag"></i>Bangladesh</div>
+                <div class="item" data-value="bb"><i class="bb flag"></i>Barbados</div>
+                <div class="item" data-value="by"><i class="by flag"></i>Belarus</div>
+                <div class="item" data-value="be"><i class="be flag"></i>Belgium</div>
+                <div class="item" data-value="bz"><i class="bz flag"></i>Belize</div>
+                <div class="item" data-value="bj"><i class="bj flag"></i>Benin</div>
+                <div class="item" data-value="bm"><i class="bm flag"></i>Bermuda</div>
+                <div class="item" data-value="bt"><i class="bt flag"></i>Bhutan</div>
+                <div class="item" data-value="bo"><i class="bo flag"></i>Bolivia</div>
+                <div class="item" data-value="ba"><i class="ba flag"></i>Bosnia</div>
+                <div class="item" data-value="bw"><i class="bw flag"></i>Botswana</div>
+                <div class="item" data-value="bv"><i class="bv flag"></i>Bouvet Island</div>
+                <div class="item" data-value="br"><i class="br flag"></i>Brazil</div>
+                <div class="item" data-value="vg"><i class="vg flag"></i>British Virgin Islands</div>
+                <div class="item" data-value="bn"><i class="bn flag"></i>Brunei</div>
+                <div class="item" data-value="bg"><i class="bg flag"></i>Bulgaria</div>
+                <div class="item" data-value="bf"><i class="bf flag"></i>Burkina Faso</div>
+                <div class="item" data-value="mm"><i class="mm flag"></i>Burma</div>
+                <div class="item" data-value="bi"><i class="bi flag"></i>Burundi</div>
+                <div class="item" data-value="tc"><i class="tc flag"></i>Caicos Islands</div>
+                <div class="item" data-value="kh"><i class="kh flag"></i>Cambodia</div>
+                <div class="item" data-value="cm"><i class="cm flag"></i>Cameroon</div>
+                <div class="item" data-value="ca"><i class="ca flag"></i>Canada</div>
+                <div class="item" data-value="cv"><i class="cv flag"></i>Cape Verde</div>
+                <div class="item" data-value="ky"><i class="ky flag"></i>Cayman Islands</div>
+                <div class="item" data-value="cf"><i class="cf flag"></i>Central African Republic</div>
+                <div class="item" data-value="td"><i class="td flag"></i>Chad</div>
+                <div class="item" data-value="cl"><i class="cl flag"></i>Chile</div>
+                <div class="item" data-value="cn"><i class="cn flag"></i>China</div>
+                <div class="item" data-value="cx"><i class="cx flag"></i>Christmas Island</div>
+                <div class="item" data-value="cc"><i class="cc flag"></i>Cocos Islands</div>
+                <div class="item" data-value="co"><i class="co flag"></i>Colombia</div>
+                <div class="item" data-value="km"><i class="km flag"></i>Comoros</div>
+                <div class="item" data-value="cg"><i class="cg flag"></i>Congo Brazzaville</div>
+                <div class="item" data-value="cd"><i class="cd flag"></i>Congo</div>
+                <div class="item" data-value="ck"><i class="ck flag"></i>Cook Islands</div>
+                <div class="item" data-value="cr"><i class="cr flag"></i>Costa Rica</div>
+                <div class="item" data-value="ci"><i class="ci flag"></i>Cote Divoire</div>
+                <div class="item" data-value="hr"><i class="hr flag"></i>Croatia</div>
+                <div class="item" data-value="cu"><i class="cu flag"></i>Cuba</div>
+                <div class="item" data-value="cy"><i class="cy flag"></i>Cyprus</div>
+                <div class="item" data-value="cz"><i class="cz flag"></i>Czech Republic</div>
+                <div class="item" data-value="dk"><i class="dk flag"></i>Denmark</div>
+                <div class="item" data-value="dj"><i class="dj flag"></i>Djibouti</div>
+                <div class="item" data-value="dm"><i class="dm flag"></i>Dominica</div>
+                <div class="item" data-value="do"><i class="do flag"></i>Dominican Republic</div>
+                <div class="item" data-value="ec"><i class="ec flag"></i>Ecuador</div>
+                <div class="item" data-value="eg"><i class="eg flag"></i>Egypt</div>
+                <div class="item" data-value="sv"><i class="sv flag"></i>El Salvador</div>
+                <div class="item" data-value="gb"><i class="gb flag"></i>England</div>
+                <div class="item" data-value="gq"><i class="gq flag"></i>Equatorial Guinea</div>
+                <div class="item" data-value="er"><i class="er flag"></i>Eritrea</div>
+                <div class="item" data-value="ee"><i class="ee flag"></i>Estonia</div>
+                <div class="item" data-value="et"><i class="et flag"></i>Ethiopia</div>
+                <div class="item" data-value="eu"><i class="eu flag"></i>European Union</div>
+                <div class="item" data-value="fk"><i class="fk flag"></i>Falkland Islands</div>
+                <div class="item" data-value="fo"><i class="fo flag"></i>Faroe Islands</div>
+                <div class="item" data-value="fj"><i class="fj flag"></i>Fiji</div>
+                <div class="item" data-value="fi"><i class="fi flag"></i>Finland</div>
+                <div class="item" data-value="fr"><i class="fr flag"></i>France</div>
+                <div class="item" data-value="gf"><i class="gf flag"></i>French Guiana</div>
+                <div class="item" data-value="pf"><i class="pf flag"></i>French Polynesia</div>
+                <div class="item" data-value="tf"><i class="tf flag"></i>French Territories</div>
+                <div class="item" data-value="ga"><i class="ga flag"></i>Gabon</div>
+                <div class="item" data-value="gm"><i class="gm flag"></i>Gambia</div>
+                <div class="item" data-value="ge"><i class="ge flag"></i>Georgia</div>
+                <div class="item" data-value="de"><i class="de flag"></i>Germany</div>
+                <div class="item" data-value="gh"><i class="gh flag"></i>Ghana</div>
+                <div class="item" data-value="gi"><i class="gi flag"></i>Gibraltar</div>
+                <div class="item" data-value="gr"><i class="gr flag"></i>Greece</div>
+                <div class="item" data-value="gl"><i class="gl flag"></i>Greenland</div>
+                <div class="item" data-value="gd"><i class="gd flag"></i>Grenada</div>
+                <div class="item" data-value="gp"><i class="gp flag"></i>Guadeloupe</div>
+                <div class="item" data-value="gu"><i class="gu flag"></i>Guam</div>
+                <div class="item" data-value="gt"><i class="gt flag"></i>Guatemala</div>
+                <div class="item" data-value="gw"><i class="gw flag"></i>Guinea-Bissau</div>
+                <div class="item" data-value="gn"><i class="gn flag"></i>Guinea</div>
+                <div class="item" data-value="gy"><i class="gy flag"></i>Guyana</div>
+                <div class="item" data-value="ht"><i class="ht flag"></i>Haiti</div>
+                <div class="item" data-value="hm"><i class="hm flag"></i>Heard Island</div>
+                <div class="item" data-value="hn"><i class="hn flag"></i>Honduras</div>
+                <div class="item" data-value="hk"><i class="hk flag"></i>Hong Kong</div>
+                <div class="item" data-value="hu"><i class="hu flag"></i>Hungary</div>
+                <div class="item" data-value="is"><i class="is flag"></i>Iceland</div>
+                <div class="item" data-value="in"><i class="in flag"></i>India</div>
+                <div class="item" data-value="io"><i class="io flag"></i>Indian Ocean Territory</div>
+                <div class="item" data-value="id"><i class="id flag"></i>Indonesia</div>
+                <div class="item" data-value="ir"><i class="ir flag"></i>Iran</div>
+                <div class="item" data-value="iq"><i class="iq flag"></i>Iraq</div>
+                <div class="item" data-value="ie"><i class="ie flag"></i>Ireland</div>
+                <div class="item" data-value="il"><i class="il flag"></i>Israel</div>
+                <div class="item" data-value="it"><i class="it flag"></i>Italy</div>
+                <div class="item" data-value="jm"><i class="jm flag"></i>Jamaica</div>
+                <div class="item" data-value="jp"><i class="jp flag"></i>Japan</div>
+                <div class="item" data-value="jo"><i class="jo flag"></i>Jordan</div>
+                <div class="item" data-value="kz"><i class="kz flag"></i>Kazakhstan</div>
+                <div class="item" data-value="ke"><i class="ke flag"></i>Kenya</div>
+                <div class="item" data-value="ki"><i class="ki flag"></i>Kiribati</div>
+                <div class="item" data-value="kw"><i class="kw flag"></i>Kuwait</div>
+                <div class="item" data-value="kg"><i class="kg flag"></i>Kyrgyzstan</div>
+                <div class="item" data-value="la"><i class="la flag"></i>Laos</div>
+                <div class="item" data-value="lv"><i class="lv flag"></i>Latvia</div>
+                <div class="item" data-value="lb"><i class="lb flag"></i>Lebanon</div>
+                <div class="item" data-value="ls"><i class="ls flag"></i>Lesotho</div>
+                <div class="item" data-value="lr"><i class="lr flag"></i>Liberia</div>
+                <div class="item" data-value="ly"><i class="ly flag"></i>Libya</div>
+                <div class="item" data-value="li"><i class="li flag"></i>Liechtenstein</div>
+                <div class="item" data-value="lt"><i class="lt flag"></i>Lithuania</div>
+                <div class="item" data-value="lu"><i class="lu flag"></i>Luxembourg</div>
+                <div class="item" data-value="mo"><i class="mo flag"></i>Macau</div>
+                <div class="item" data-value="mk"><i class="mk flag"></i>Macedonia</div>
+                <div class="item" data-value="mg"><i class="mg flag"></i>Madagascar</div>
+                <div class="item" data-value="mw"><i class="mw flag"></i>Malawi</div>
+                <div class="item" data-value="my"><i class="my flag"></i>Malaysia</div>
+                <div class="item" data-value="mv"><i class="mv flag"></i>Maldives</div>
+                <div class="item" data-value="ml"><i class="ml flag"></i>Mali</div>
+                <div class="item" data-value="mt"><i class="mt flag"></i>Malta</div>
+                <div class="item" data-value="mh"><i class="mh flag"></i>Marshall Islands</div>
+                <div class="item" data-value="mq"><i class="mq flag"></i>Martinique</div>
+                <div class="item" data-value="mr"><i class="mr flag"></i>Mauritania</div>
+                <div class="item" data-value="mu"><i class="mu flag"></i>Mauritius</div>
+                <div class="item" data-value="yt"><i class="yt flag"></i>Mayotte</div>
+                <div class="item" data-value="mx"><i class="mx flag"></i>Mexico</div>
+                <div class="item" data-value="fm"><i class="fm flag"></i>Micronesia</div>
+                <div class="item" data-value="md"><i class="md flag"></i>Moldova</div>
+                <div class="item" data-value="mc"><i class="mc flag"></i>Monaco</div>
+                <div class="item" data-value="mn"><i class="mn flag"></i>Mongolia</div>
+                <div class="item" data-value="me"><i class="me flag"></i>Montenegro</div>
+                <div class="item" data-value="ms"><i class="ms flag"></i>Montserrat</div>
+                <div class="item" data-value="ma"><i class="ma flag"></i>Morocco</div>
+                <div class="item" data-value="mz"><i class="mz flag"></i>Mozambique</div>
+                <div class="item" data-value="na"><i class="na flag"></i>Namibia</div>
+                <div class="item" data-value="nr"><i class="nr flag"></i>Nauru</div>
+                <div class="item" data-value="np"><i class="np flag"></i>Nepal</div>
+                <div class="item" data-value="an"><i class="an flag"></i>Netherlands Antilles</div>
+                <div class="item" data-value="nl"><i class="nl flag"></i>Netherlands</div>
+                <div class="item" data-value="nc"><i class="nc flag"></i>New Caledonia</div>
+                <div class="item" data-value="pg"><i class="pg flag"></i>New Guinea</div>
+                <div class="item" data-value="nz"><i class="nz flag"></i>New Zealand</div>
+                <div class="item" data-value="ni"><i class="ni flag"></i>Nicaragua</div>
+                <div class="item" data-value="ne"><i class="ne flag"></i>Niger</div>
+                <div class="item" data-value="ng"><i class="ng flag"></i>Nigeria</div>
+                <div class="item" data-value="nu"><i class="nu flag"></i>Niue</div>
+                <div class="item" data-value="nf"><i class="nf flag"></i>Norfolk Island</div>
+                <div class="item" data-value="kp"><i class="kp flag"></i>North Korea</div>
+                <div class="item" data-value="mp"><i class="mp flag"></i>Northern Mariana Islands</div>
+                <div class="item" data-value="no"><i class="no flag"></i>Norway</div>
+                <div class="item" data-value="om"><i class="om flag"></i>Oman</div>
+                <div class="item" data-value="pk"><i class="pk flag"></i>Pakistan</div>
+                <div class="item" data-value="pw"><i class="pw flag"></i>Palau</div>
+                <div class="item" data-value="ps"><i class="ps flag"></i>Palestine</div>
+                <div class="item" data-value="pa"><i class="pa flag"></i>Panama</div>
+                <div class="item" data-value="py"><i class="py flag"></i>Paraguay</div>
+                <div class="item" data-value="pe"><i class="pe flag"></i>Peru</div>
+                <div class="item" data-value="ph"><i class="ph flag"></i>Philippines</div>
+                <div class="item" data-value="pn"><i class="pn flag"></i>Pitcairn Islands</div>
+                <div class="item" data-value="pl"><i class="pl flag"></i>Poland</div>
+                <div class="item" data-value="pt"><i class="pt flag"></i>Portugal</div>
+                <div class="item" data-value="pr"><i class="pr flag"></i>Puerto Rico</div>
+                <div class="item" data-value="qa"><i class="qa flag"></i>Qatar</div>
+                <div class="item" data-value="re"><i class="re flag"></i>Reunion</div>
+                <div class="item" data-value="ro"><i class="ro flag"></i>Romania</div>
+                <div class="item" data-value="ru"><i class="ru flag"></i>Russia</div>
+                <div class="item" data-value="rw"><i class="rw flag"></i>Rwanda</div>
+                <div class="item" data-value="sh"><i class="sh flag"></i>Saint Helena</div>
+                <div class="item" data-value="kn"><i class="kn flag"></i>Saint Kitts and Nevis</div>
+                <div class="item" data-value="lc"><i class="lc flag"></i>Saint Lucia</div>
+                <div class="item" data-value="pm"><i class="pm flag"></i>Saint Pierre</div>
+                <div class="item" data-value="vc"><i class="vc flag"></i>Saint Vincent</div>
+                <div class="item" data-value="ws"><i class="ws flag"></i>Samoa</div>
+                <div class="item" data-value="sm"><i class="sm flag"></i>San Marino</div>
+                <div class="item" data-value="gs"><i class="gs flag"></i>Sandwich Islands</div>
+                <div class="item" data-value="st"><i class="st flag"></i>Sao Tome</div>
+                <div class="item" data-value="sa"><i class="sa flag"></i>Saudi Arabia</div>
+                <div class="item" data-value="sn"><i class="sn flag"></i>Senegal</div>
+                <div class="item" data-value="cs"><i class="cs flag"></i>Serbia</div>
+                <div class="item" data-value="rs"><i class="rs flag"></i>Serbia</div>
+                <div class="item" data-value="sc"><i class="sc flag"></i>Seychelles</div>
+                <div class="item" data-value="sl"><i class="sl flag"></i>Sierra Leone</div>
+                <div class="item" data-value="sg"><i class="sg flag"></i>Singapore</div>
+                <div class="item" data-value="sk"><i class="sk flag"></i>Slovakia</div>
+                <div class="item" data-value="si"><i class="si flag"></i>Slovenia</div>
+                <div class="item" data-value="sb"><i class="sb flag"></i>Solomon Islands</div>
+                <div class="item" data-value="so"><i class="so flag"></i>Somalia</div>
+                <div class="item" data-value="za"><i class="za flag"></i>South Africa</div>
+                <div class="item" data-value="kr"><i class="kr flag"></i>South Korea</div>
+                <div class="item" data-value="es"><i class="es flag"></i>Spain</div>
+                <div class="item" data-value="lk"><i class="lk flag"></i>Sri Lanka</div>
+                <div class="item" data-value="sd"><i class="sd flag"></i>Sudan</div>
+                <div class="item" data-value="sr"><i class="sr flag"></i>Suriname</div>
+                <div class="item" data-value="sj"><i class="sj flag"></i>Svalbard</div>
+                <div class="item" data-value="sz"><i class="sz flag"></i>Swaziland</div>
+                <div class="item" data-value="se"><i class="se flag"></i>Sweden</div>
+                <div class="item" data-value="ch"><i class="ch flag"></i>Switzerland</div>
+                <div class="item" data-value="sy"><i class="sy flag"></i>Syria</div>
+                <div class="item" data-value="tw"><i class="tw flag"></i>Taiwan</div>
+                <div class="item" data-value="tj"><i class="tj flag"></i>Tajikistan</div>
+                <div class="item" data-value="tz"><i class="tz flag"></i>Tanzania</div>
+                <div class="item" data-value="th"><i class="th flag"></i>Thailand</div>
+                <div class="item" data-value="tl"><i class="tl flag"></i>Timorleste</div>
+                <div class="item" data-value="tg"><i class="tg flag"></i>Togo</div>
+                <div class="item" data-value="tk"><i class="tk flag"></i>Tokelau</div>
+                <div class="item" data-value="to"><i class="to flag"></i>Tonga</div>
+                <div class="item" data-value="tt"><i class="tt flag"></i>Trinidad</div>
+                <div class="item" data-value="tn"><i class="tn flag"></i>Tunisia</div>
+                <div class="item" data-value="tr"><i class="tr flag"></i>Turkey</div>
+                <div class="item" data-value="tm"><i class="tm flag"></i>Turkmenistan</div>
+                <div class="item" data-value="tv"><i class="tv flag"></i>Tuvalu</div>
+                <div class="item" data-value="ug"><i class="ug flag"></i>Uganda</div>
+                <div class="item" data-value="ua"><i class="ua flag"></i>Ukraine</div>
+                <div class="item" data-value="ae"><i class="ae flag"></i>United Arab Emirates</div>
+                <div class="item" data-value="us"><i class="us flag"></i>United States</div>
+                <div class="item" data-value="uy"><i class="uy flag"></i>Uruguay</div>
+                <div class="item" data-value="um"><i class="um flag"></i>Us Minor Islands</div>
+                <div class="item" data-value="vi"><i class="vi flag"></i>Us Virgin Islands</div>
+                <div class="item" data-value="uz"><i class="uz flag"></i>Uzbekistan</div>
+                <div class="item" data-value="vu"><i class="vu flag"></i>Vanuatu</div>
+                <div class="item" data-value="va"><i class="va flag"></i>Vatican City</div>
+                <div class="item" data-value="ve"><i class="ve flag"></i>Venezuela</div>
+                <div class="item" data-value="vn"><i class="vn flag"></i>Vietnam</div>
+                <div class="item" data-value="wf"><i class="wf flag"></i>Wallis and Futuna</div>
+                <div class="item" data-value="eh"><i class="eh flag"></i>Western Sahara</div>
+                <div class="item" data-value="ye"><i class="ye flag"></i>Yemen</div>
+                <div class="item" data-value="zm"><i class="zm flag"></i>Zambia</div>
+                <div class="item" data-value="zw"><i class="zw flag"></i>Zimbabwe</div>
+            </div>
+            </div>
+        </div>
+        <button class="ui basic green button" id="whitelist-btn" onclick="addCountryToWhitelist();"><i class="ui green add icon"></i> Whitelist Country</button>
+    </div>
+    <table class="ui unstackable basic celled table">
+        <thead>
+            <tr>
+                <th>ISO Code</th>
+                <th>Remove</th>
+            </tr>
+        </thead>
+        <tbody id="whitelistCountryList">
+
+        </tbody>
+    </table>
+
+    <h4>IP Whitelist</h4>
+    <p>Whitelist a certain IP or IP range</p>
+    <div class="ui form">
+        <div class="field">
+        <label>IP Address</label>
+        <input id="ipAddressInputWhitelist" type="text" placeholder="IP Address">
+        </div>
+        <button id="addIpButton" onclick="addIpWhitelist();" class="ui basic green button">
+            <i class="green add icon"></i> Whitelist IP
+        </button>
+    </div>
+    <div class="ui message">
+        <i class="ui info circle icon"></i> IP Address support the following formats
+        <div class="ui bulleted list">
+            <div class="item">Fixed IP Address (e.g. 192.128.4.100)</div>
+            <div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
+            <div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
+        </div>
+    </div>
+    <table class="ui unstackable basic celled table">
+        <thead>
+        <tr>
+            <th>IP Address</th>
+            <th>Remove</th>
+        </tr>
+        </thead>
+        <tbody id="whitelistIpTable">
+
+        </tbody>
+    </table>
+</div>
+<!-- Quick ban list-->
+<div class="ui bottom attached tab segment" data-tab="tab_quickban">
+    <h2>Quick Ban List</h2>
+    <button style="margin-top: -1em;" onclick="initBlacklistQuickBanTable();" class="ui green small right floated circular basic icon button"><i class="ui refresh icon"></i></button>
+    <p>You can perform one-click IP ban on the list below if you observe unusual traffic from an unknown origin<br>
+    Blacklist must be enabled in order to use this feature.</p>
+    <div class="ui divider"></div>
+    <table class="ui celled unstackable very compact table" id="ipTable">
+        <thead>
+            <tr>
+            <th>IP</th>
+            <th>Access Count</th>
+            <th>Blacklist</th>
+            </tr>
+        </thead>
+        <tbody>
+        </tbody>
+    </table>
+    <div class="pagination"></div>
+</div>
+<div class="ui yellow message">
+    <i class="info circle icon"></i> Access checking and validation will slightly increase proxy latency. <br>
+    Enabling this feature on servers with low end hardware is not recommended.
+</div>
+</div>
+<script>
+    /*
+        Access Control
+    */
+    $(".dropdown").dropdown();
+    $('.menu .accesscontrol.item').tab();
+    $('.menu .accesscontrol.item').addClass("activated");
+
+    /*
+        Table Init
+    */
+
+    //Blacklist country table
+    function initBannedCountryList(){
+        $.get("/api/blacklist/list?type=country", function(data) {
+            let bannedListHtml = '';
+            data.forEach((countryCode) => {
+                bannedListHtml += `
+                <tr>
+                    <td><i class="${countryCode} flag"></i> ${getCountryName(countryCode)} (${countryCode.toUpperCase()})</td>
+                    <td><button class="ui red basic mini icon button" onclick="removeFromBannedList('${countryCode}')"><i class="trash icon"></i></button></td>
+                </tr>
+                `;
+            });
+            $('#banned-list').html(bannedListHtml);
+            filterCountries(data, "#countrySelector .menu .item");
+            if (data.length === 0) {
+                $('#banned-list').append(`
+                    <tr>
+                        <td colspan="2">
+                            <i class="green check circle icon"></i>
+                            There are no blacklisted countries
+                        </td>
+                    </tr>
+                `);
+            }
+
+        });
+    }
+    initBannedCountryList();
+
+    //Blacklist ip table
+    function initIpBanTable(){
+        $.get('/api/blacklist/list?type=ip', function(data) {
+            $('#blacklistIpTable').html("");
+            if (data.length === 0) {
+                $('#blacklistIpTable').append(`
+                <tr>
+                    <td colspan="2"><i class="green check circle icon"></i>There are no blacklisted IP addresses</td>
+                </tr>
+                `);
+            } else {
+                $.each(data, function(index, ip) {
+                    let icon = "globe icon";
+                    if (isLAN(ip)){
+                        icon = "desktop icon";
+                    }else if (isHomeAddr(ip)){
+                        icon = "home icon";
+                    }
+                    $('#blacklistIpTable').append(`
+                        <tr class="blacklistItem" ip="${encodeURIComponent(ip)}">
+                            <td><i class="${icon}"></i> ${ip}</td>
+                            <td><button class="ui icon basic mini red button" onclick="removeIpBlacklist('${ip}');"><i class="trash alternate icon"></i></button></td>
+                        </tr>
+                    `);
+                });
+            }
+
+            initBlacklistQuickBanTable();
+        });
+    }
+    initIpBanTable();
+
+    //Init blacklist state
+    function initBlacklistEnableState(){
+        $.get('/api/blacklist/enable', function(data){
+            if (data == true){
+                $('#enableBlacklist').parent().checkbox("set checked");
+            }else{
+                $("#ipTable").addClass("disabled");
+            }
+
+            //Register on change event
+            $("#enableBlacklist").on("change", function(){
+                enableBlacklist();
+            })
+        });
+    }
+    initBlacklistEnableState();
+
+    //Whitelist country table
+    function initWhitelistCountryList(){
+        $.get("/api/whitelist/list?type=country", function(data) {
+            let bannedListHtml = '';
+            data.forEach((countryCode) => {
+                bannedListHtml += `
+                <tr>
+                    <td><i class="${countryCode} flag"></i> ${getCountryName(countryCode)} (${countryCode.toUpperCase()})</td>
+                    <td><button class="ui red basic mini icon button" onclick="removeFromWhiteList('${countryCode}')"><i class="trash icon"></i></button></td>
+                </tr>
+                `;
+            });
+            $('#whitelistCountryList').html(bannedListHtml);
+            filterCountries(data, "#countrySelectorWhitelist .menu .item");
+            if (data.length === 0) {
+                $('#whitelistCountryList').append(`
+                    <tr>
+                        <td colspan="2">
+                            <i class="green check circle icon"></i>
+                            There are no whitelisted countries
+                        </td>
+                    </tr>
+                `);
+            }
+
+        });
+    }
+    initWhitelistCountryList();
+
+    //Whitelist ip table
+    function initIpWhitelistTable(){
+        $.get('/api/whitelist/list?type=ip', function(data) {
+            $('#whitelistIpTable').html("");
+            if (data.length === 0) {
+                $('#whitelistIpTable').append(`
+                <tr>
+                    <td colspan="2"><i class="green check circle icon"></i>There are no whitelisted IP addresses</td>
+                </tr>
+                `);
+            } else {
+                $.each(data, function(index, ip) {
+                    let icon = "globe icon";
+                    if (isLAN(ip)){
+                        icon = "desktop icon";
+                    }else if (isHomeAddr(ip)){
+                        icon = "home icon";
+                    }
+                    $('#whitelistIpTable').append(`
+                        <tr class="whitelistItem" ip="${encodeURIComponent(ip)}">
+                            <td><i class="${icon}"></i> ${ip}</td>
+                            <td><button class="ui icon basic mini red button" onclick="removeIpWhitelist('${ip}');"><i class="trash alternate icon"></i></button></td>
+                        </tr>
+                    `);
+                });
+            }
+
+        });
+    }
+    initIpWhitelistTable();
+
+    //Init whitelist state
+    function initWhitelistEnableState(){
+        $.get('/api/whitelist/enable', function(data){
+            if (data == true){
+                $('#enableWhitelist').parent().checkbox("set checked");
+            }
+
+            //Register on change event
+            $("#enableWhitelist").on("change", function(){
+                enableWhitelist();
+            })
+        });
+    }
+    initWhitelistEnableState();
+
+    /*
+        Blacklist API Calls
+    */
+    function enableBlacklist() {
+        var isChecked = $('#enableBlacklist').is(':checked');
+        $.ajax({
+            type: 'POST',
+            url: '/api/blacklist/enable',
+            data: { enable: isChecked },
+            success: function(data){
+                if (isChecked){
+                    $("#ipTable").removeClass("disabled");
+                }else{
+                    $("#ipTable").addClass("disabled");
+                }
+                
+                $(".toggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
+            }
+        });
+    }
+
+    function addCountryToBlacklist() {
+        var countryCode = $("#countrySelector").dropdown("get value").toLowerCase();
+        $('#countrySelector').dropdown('clear');
+        $.ajax({
+            type: "POST",
+            url: "/api/blacklist/country/add",
+            data: { cc: countryCode },
+            success: function(response) {
+                if (response.error != undefined){
+                    alert(response.error);
+                }
+                initBannedCountryList();
+            },
+            error: function(xhr, status, error) {
+            // handle error response
+            }
+        });
+    }
+
+    function removeFromBannedList(countryCode){
+        if (confirm("Confirm removing " + getCountryName(countryCode) + " from blacklist?")){
+            countryCode = countryCode.toLowerCase(); 
+            $.ajax({
+                url: "/api/blacklist/country/remove",
+                method: "POST",
+                data: { cc: countryCode },
+                success: function(response) {
+                    if (response.error != undefined){
+                        alert(response.error);
+                    }
+                    initBannedCountryList();
+                },
+                error: function(xhr, status, error) {
+                    console.error("Error removing country from blacklist: " + error);
+                    // Handle error response
+                }
+            });
+        }
+    }
+
+    function addIpBlacklist(){
+        let targetIp = $("#ipAddressInput").val().trim();
+        if (targetIp == ""){
+            alert("IP address is empty")
+            return
+        }
+        if (!isValidIpFilter(targetIp)){
+            if (!confirm("This doesn't seems like a valid IP address. Continue anyway?")){
+                return;
+            }
+        }
+
+        $.ajax({
+            url: "/api/blacklist/ip/add",
+            type: "POST",
+            data: {ip: targetIp.toLowerCase()},
+            success: function(response) {
+                if (response.error !== undefined) {
+                    msgbox(response.error, false, 6000);
+                } else {
+                    initIpBanTable();
+                }
+
+                $("#ipAddressInput").val("");
+                $("#ipAddressInput").parent().remvoeClass("error");
+            },
+            error: function() {
+                alert("Failed to add IP address to blacklist");
+            }
+        });
+    }
+
+    function removeIpBlacklist(ipaddr){
+        if (confirm("Confirm remove blacklist for " + ipaddr + " ?")){
+            $.ajax({
+                url: "/api/blacklist/ip/remove",
+                type: "POST",
+                data: {ip: ipaddr.toLowerCase()},
+                success: function(response) {
+                    if (response.error !== undefined) {
+                        msgbox(response.error, false, 6000);
+                    } else {
+                        initIpBanTable();
+                    }
+                },
+                error: function() {
+                    alert("Failed to remove IP address from blacklist");
+                }
+                
+            });
+        }
+    }
+
+    /* 
+        Whitelist APIs
+    */
+    function enableWhitelist() {
+        var isChecked = $('#enableWhitelist').is(':checked');
+        $.ajax({
+            type: 'POST',
+            url: '/api/whitelist/enable',
+            data: { enable: isChecked },
+            success: function(data){
+                $(".toggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
+            }
+        });
+    }
+
+    function addCountryToWhitelist() {
+        var countryCode = $("#countrySelectorWhitelist").dropdown("get value").toLowerCase();
+        $('#countrySelectorWhitelist').dropdown('clear');
+        $.ajax({
+            type: "POST",
+            url: "/api/whitelist/country/add",
+            data: { cc: countryCode },
+            success: function(response) {
+                if (response.error != undefined){
+                    alert(response.error);
+                }
+                initWhitelistCountryList();
+            },
+            error: function(xhr, status, error) {
+                // handle error response
+            }
+        });
+    }
+
+    function removeFromWhiteList(countryCode){
+        if (confirm("Confirm removing " + getCountryName(countryCode) + " from whitelist?")){
+            countryCode = countryCode.toLowerCase(); 
+            $.ajax({
+                url: "/api/whitelist/country/remove",
+                method: "POST",
+                data: { cc: countryCode },
+                success: function(response) {
+                    if (response.error != undefined){
+                        alert(response.error);
+                    }
+                    initWhitelistCountryList();
+                },
+                error: function(xhr, status, error) {
+                    console.error("Error removing country from blacklist: " + error);
+                    // Handle error response
+                }
+            });
+        }
+    }
+
+    function addIpWhitelist(){
+        let targetIp = $("#ipAddressInputWhitelist").val().trim();
+        if (targetIp == ""){
+            alert("IP address is empty")
+            return
+        }
+        if (!isValidIpFilter(targetIp)){
+            if (!confirm("This doesn't seems like a valid IP address. Continue anyway?")){
+                return;
+            }
+        }
+
+        $.ajax({
+            url: "/api/whitelist/ip/add",
+            type: "POST",
+            data: {ip: targetIp.toLowerCase()},
+            success: function(response) {
+                if (response.error !== undefined) {
+                    msgbox(response.error, false, 6000);
+                } else {
+                    initIpWhitelistTable();
+                }
+
+                $("#ipAddressInputWhitelist").val("");
+                $("#ipAddressInputWhitelist").parent().remvoeClass("error");
+            },
+            error: function() {
+                alert("Failed to add IP address to whitelist");
+            }
+        });
+    }
+
+    function removeIpWhitelist(ipaddr){
+        if (confirm("Confirm remove whitelist for " + ipaddr + " ?")){
+            $.ajax({
+                url: "/api/whitelist/ip/remove",
+                type: "POST",
+                data: {ip: ipaddr.toLowerCase()},
+                success: function(response) {
+                    if (response.error !== undefined) {
+                        msgbox(response.error, false, 6000);
+                    } else {
+                        initIpWhitelistTable();
+                    }
+                },
+                error: function() {
+                    alert("Failed to remove IP address from whitelist");
+                }
+                
+            });
+        }
+    }
+
+    /*
+        Common Utilities
+    */
+    function filterCountries(codesToShow, selector="#countrySelector .menu .item") {
+        // get all items in the dropdown
+        const items = document.querySelectorAll(selector);
+        // loop through all items
+        items.forEach(item => {
+            // get the value of the item (i.e. the country code)
+            const code = item.dataset.value;
+            // if the code is in the array of codes to show, show the item
+            if (codesToShow.includes(code)) {
+                item.style.display = 'none';
+            }
+            // otherwise, hide the item
+            else {
+                item.style.display = 'block';
+            }
+        });
+    }
+
+    //Get the country name from the dropdown
+    function getCountryName(countryCode) {
+        return $('#countrySelector .item[data-value="' + countryCode.toLowerCase() + '"]').text().trim();
+    }
+
+    //Check if a input is a valid IP address, wildcard of a IP address or a CIDR string
+    function isValidIpFilter(input) {
+        // Check if input is a valid IP address
+        const isValidIp = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/.test(input);
+
+        if (isValidIp) {
+            return true;
+        }
+
+        // Check if input is a wildcard IP address
+        const isValidWildcardIp = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])$/.test(input);
+
+        if (isValidWildcardIp) {
+            return true;
+        }
+
+        // Check if input is a valid CIDR address string
+        const isValidCidr = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|[1-2][0-9]|3[0-2])$/.test(input);
+
+        if (isValidCidr) {
+            return true;
+        }
+
+        // Input is not a valid IP address, wildcard IP address, or CIDR address string
+        return false;
+    }
+
+    //Do dynamic checking to see if the IP looks like a real ip
+    $("#ipAddressInput").on("input", function() {
+        $(this).val($(this).val().trim());
+            var ipAddress = $(this).val();
+        if (!isValidIpFilter(ipAddress)) {
+            $(this).parent().addClass("error");
+        } else {
+            $(this).parent().removeClass("error");
+        }
+    });
+
+    $("#ipAddressInputWhitelist").on("input", function() {
+        $(this).val($(this).val().trim());
+            var ipAddress = $(this).val();
+        if (!isValidIpFilter(ipAddress)) {
+            $(this).parent().addClass("error");
+        } else {
+            $(this).parent().removeClass("error");
+        }
+    });
+
+    function isLAN(ipAddress) {
+        function ip2long(ipAddress) {
+            // Convert the IP address to a 32-bit integer
+            const parts = ipAddress.split(".");
+            return (
+                (parseInt(parts[0]) << 24) |
+                (parseInt(parts[1]) << 16) |
+                (parseInt(parts[2]) << 8) |
+                parseInt(parts[3])
+            );
+        }
+
+        // Define the LAN IP address ranges
+        const LAN_RANGES = [
+            { start: "10.0.0.0", end: "10.255.255.255" },
+            { start: "172.16.0.0", end: "172.31.255.255" },
+            { start: "192.168.0.0", end: "192.168.255.255" }
+        ];
+
+        // Check if the IP address is within any of the LAN ranges
+        for (let i = 0; i < LAN_RANGES.length; i++) {
+            const rangeStart = ip2long(LAN_RANGES[i].start);
+            const rangeEnd = ip2long(LAN_RANGES[i].end);
+            const ipAddressLong = ip2long(ipAddress);
+
+            if (ipAddressLong >= rangeStart && ipAddressLong <= rangeEnd) {
+            return true;
+            }
+        }
+
+        return false;
+    }
+
+    function isHomeAddr(ipAddress) {
+        const specialIpAddresses = ['0.0.0.0', '127.0.0.1', '::1'];
+        return specialIpAddresses.includes(ipAddress);
+    }
+
+
+    
+    //Load the summary to ip access table
+    function initBlacklistQuickBanTable(){
+        $.get("/api/stats/summary", function(data){
+            initIpAccessTable(data.RequestClientIp);
+        })
+    }
+    initBlacklistQuickBanTable();
+
+    var blacklist_entriesPerPage = 30;
+    var blacklist_currentPage = 1;
+    var blacklist_totalPages = 0;
+
+    function initIpAccessTable(ipAccessCounts){
+        blacklist_totalPages = Math.ceil(Object.keys(ipAccessCounts).length / blacklist_entriesPerPage);
+
+        function sortkv(obj) {
+            var sortable = [];
+            for (var key in obj) {
+                if (obj.hasOwnProperty(key)) {
+                sortable.push([key, obj[key]]);
+                }
+            }
+            sortable.sort(function(a, b) {
+                return b[1] - a[1];
+            });
+            var sortedObj = {};
+            sortable.forEach(function(item) {
+                sortedObj[item[0]] = item[1];
+            });
+            return sortedObj;
+        }
+
+        ipAccessCounts = sortkv(ipAccessCounts);
+
+        function renderTable() {
+            var tableBody = $("#ipTable tbody");
+            tableBody.empty();
+
+            var startIndex = (blacklist_currentPage - 1) * blacklist_entriesPerPage;
+            var endIndex = startIndex + blacklist_entriesPerPage;
+            var slicedEntries = Object.entries(ipAccessCounts).slice(startIndex, endIndex);
+
+            slicedEntries.forEach(function([ip, accessCount]) {
+                var row = $("<tr>").appendTo(tableBody);
+                $("<td>").text(ip).appendTo(row);
+                $("<td>").text(accessCount).appendTo(row);
+                if (ipInBlacklist(ip)){
+                    $("<td>").html(`<button class="ui basic green tiny icon button" title"Unban IP" onclick="handleUnban('${ip}');"><i class="green check icon"></i></button>`).appendTo(row);
+                }else{
+                    $("<td>").html(`<button class="ui basic red tiny icon button" title"Ban IP" onclick="handleBanIp('${ip}');"><i class="red ban icon"></i></button>`).appendTo(row);
+                }
+            });
+        }
+
+        function renderPagination() {
+            var paginationDiv = $(".pagination");
+            paginationDiv.empty();
+
+            for (var i = 1; i <= blacklist_totalPages; i++) {
+                var button = $("<button>").text(i).addClass("ui small basic compact button");
+                if (i === blacklist_currentPage) {
+                    button.addClass("disabled");
+                }
+                button.click(function() {
+                    blacklist_currentPage = parseInt($(this).text());
+                    renderTable();
+                    renderPagination();
+                });
+                button.appendTo(paginationDiv);
+            }
+        }
+
+        renderTable();
+        renderPagination();
+    }
+
+    function ipInBlacklist(targetIp){
+        let inBlacklist = false;
+        $(".blacklistItem").each(function(){
+            if ($(this).attr("ip") == encodeURIComponent(targetIp)){
+                inBlacklist = true;
+            }   
+        });
+        return inBlacklist;
+    }
+
+    function handleBanIp(targetIp){
+        $("#ipAddressInput").val(targetIp);
+        addIpBlacklist();
+    }
+
+    function handleUnban(targetIp){
+        removeIpBlacklist(targetIp);
+    }
+</script>

+ 0 - 714
web/components/blacklist.html

@@ -1,714 +0,0 @@
-<div class="standardContainer">
-    <div class="ui basic segment">
-        <h2>Blacklist</h2>
-        <p>Setup blacklist based on estimated IP geographic location or IP address</p>
-    </div>
-
-<div class="ui divider"></div>
-<div class="ui toggle checkbox">
-    <input type="checkbox" id="enableBlacklist">
-    <label>Enable Blacklist</label>
-</div>
-<div id="toggleSucc" style="float: right; display:none; color: #2abd4d;" >
-    <i class="ui green checkmark icon"></i> Setting Saved
-</div>
-<div class="ui message">
-    <i class="info circle icon"></i> Blacklist function require complex checking logic to validate each incoming request. Not recommend enabling this feature on servers with low end hardware.
-</div>
-<h4>Country Blacklist</h4>
-    <p><i class="yellow exclamation triangle icon"></i>
-    This will block all requests from the selected country. The requester's location is estimated from their IP address and may not be 100% accurate.</p>
-
-<div class="ui form">
-    <div class="field">
-        <label>Select Country</label>
-        <div id="countrySelector" class="ui fluid search selection dropdown">
-            <input type="hidden" name="country">
-            <i class="dropdown icon"></i>
-            <div class="default text">Select Country</div>
-            <div class="menu">
-            <div class="item" data-value="af"><i class="af flag"></i>Afghanistan</div>
-            <div class="item" data-value="ax"><i class="ax flag"></i>Aland Islands</div>
-            <div class="item" data-value="al"><i class="al flag"></i>Albania</div>
-            <div class="item" data-value="dz"><i class="dz flag"></i>Algeria</div>
-            <div class="item" data-value="as"><i class="as flag"></i>American Samoa</div>
-            <div class="item" data-value="ad"><i class="ad flag"></i>Andorra</div>
-            <div class="item" data-value="ao"><i class="ao flag"></i>Angola</div>
-            <div class="item" data-value="ai"><i class="ai flag"></i>Anguilla</div>
-            <div class="item" data-value="ag"><i class="ag flag"></i>Antigua</div>
-            <div class="item" data-value="ar"><i class="ar flag"></i>Argentina</div>
-            <div class="item" data-value="am"><i class="am flag"></i>Armenia</div>
-            <div class="item" data-value="aw"><i class="aw flag"></i>Aruba</div>
-            <div class="item" data-value="au"><i class="au flag"></i>Australia</div>
-            <div class="item" data-value="at"><i class="at flag"></i>Austria</div>
-            <div class="item" data-value="az"><i class="az flag"></i>Azerbaijan</div>
-            <div class="item" data-value="bs"><i class="bs flag"></i>Bahamas</div>
-            <div class="item" data-value="bh"><i class="bh flag"></i>Bahrain</div>
-            <div class="item" data-value="bd"><i class="bd flag"></i>Bangladesh</div>
-            <div class="item" data-value="bb"><i class="bb flag"></i>Barbados</div>
-            <div class="item" data-value="by"><i class="by flag"></i>Belarus</div>
-            <div class="item" data-value="be"><i class="be flag"></i>Belgium</div>
-            <div class="item" data-value="bz"><i class="bz flag"></i>Belize</div>
-            <div class="item" data-value="bj"><i class="bj flag"></i>Benin</div>
-            <div class="item" data-value="bm"><i class="bm flag"></i>Bermuda</div>
-            <div class="item" data-value="bt"><i class="bt flag"></i>Bhutan</div>
-            <div class="item" data-value="bo"><i class="bo flag"></i>Bolivia</div>
-            <div class="item" data-value="ba"><i class="ba flag"></i>Bosnia</div>
-            <div class="item" data-value="bw"><i class="bw flag"></i>Botswana</div>
-            <div class="item" data-value="bv"><i class="bv flag"></i>Bouvet Island</div>
-            <div class="item" data-value="br"><i class="br flag"></i>Brazil</div>
-            <div class="item" data-value="vg"><i class="vg flag"></i>British Virgin Islands</div>
-            <div class="item" data-value="bn"><i class="bn flag"></i>Brunei</div>
-            <div class="item" data-value="bg"><i class="bg flag"></i>Bulgaria</div>
-            <div class="item" data-value="bf"><i class="bf flag"></i>Burkina Faso</div>
-            <div class="item" data-value="mm"><i class="mm flag"></i>Burma</div>
-            <div class="item" data-value="bi"><i class="bi flag"></i>Burundi</div>
-            <div class="item" data-value="tc"><i class="tc flag"></i>Caicos Islands</div>
-            <div class="item" data-value="kh"><i class="kh flag"></i>Cambodia</div>
-            <div class="item" data-value="cm"><i class="cm flag"></i>Cameroon</div>
-            <div class="item" data-value="ca"><i class="ca flag"></i>Canada</div>
-            <div class="item" data-value="cv"><i class="cv flag"></i>Cape Verde</div>
-            <div class="item" data-value="ky"><i class="ky flag"></i>Cayman Islands</div>
-            <div class="item" data-value="cf"><i class="cf flag"></i>Central African Republic</div>
-            <div class="item" data-value="td"><i class="td flag"></i>Chad</div>
-            <div class="item" data-value="cl"><i class="cl flag"></i>Chile</div>
-            <div class="item" data-value="cn"><i class="cn flag"></i>China</div>
-            <div class="item" data-value="cx"><i class="cx flag"></i>Christmas Island</div>
-            <div class="item" data-value="cc"><i class="cc flag"></i>Cocos Islands</div>
-            <div class="item" data-value="co"><i class="co flag"></i>Colombia</div>
-            <div class="item" data-value="km"><i class="km flag"></i>Comoros</div>
-            <div class="item" data-value="cg"><i class="cg flag"></i>Congo Brazzaville</div>
-            <div class="item" data-value="cd"><i class="cd flag"></i>Congo</div>
-            <div class="item" data-value="ck"><i class="ck flag"></i>Cook Islands</div>
-            <div class="item" data-value="cr"><i class="cr flag"></i>Costa Rica</div>
-            <div class="item" data-value="ci"><i class="ci flag"></i>Cote Divoire</div>
-            <div class="item" data-value="hr"><i class="hr flag"></i>Croatia</div>
-            <div class="item" data-value="cu"><i class="cu flag"></i>Cuba</div>
-            <div class="item" data-value="cy"><i class="cy flag"></i>Cyprus</div>
-            <div class="item" data-value="cz"><i class="cz flag"></i>Czech Republic</div>
-            <div class="item" data-value="dk"><i class="dk flag"></i>Denmark</div>
-            <div class="item" data-value="dj"><i class="dj flag"></i>Djibouti</div>
-            <div class="item" data-value="dm"><i class="dm flag"></i>Dominica</div>
-            <div class="item" data-value="do"><i class="do flag"></i>Dominican Republic</div>
-            <div class="item" data-value="ec"><i class="ec flag"></i>Ecuador</div>
-            <div class="item" data-value="eg"><i class="eg flag"></i>Egypt</div>
-            <div class="item" data-value="sv"><i class="sv flag"></i>El Salvador</div>
-            <div class="item" data-value="gb"><i class="gb flag"></i>England</div>
-            <div class="item" data-value="gq"><i class="gq flag"></i>Equatorial Guinea</div>
-            <div class="item" data-value="er"><i class="er flag"></i>Eritrea</div>
-            <div class="item" data-value="ee"><i class="ee flag"></i>Estonia</div>
-            <div class="item" data-value="et"><i class="et flag"></i>Ethiopia</div>
-            <div class="item" data-value="eu"><i class="eu flag"></i>European Union</div>
-            <div class="item" data-value="fk"><i class="fk flag"></i>Falkland Islands</div>
-            <div class="item" data-value="fo"><i class="fo flag"></i>Faroe Islands</div>
-            <div class="item" data-value="fj"><i class="fj flag"></i>Fiji</div>
-            <div class="item" data-value="fi"><i class="fi flag"></i>Finland</div>
-            <div class="item" data-value="fr"><i class="fr flag"></i>France</div>
-            <div class="item" data-value="gf"><i class="gf flag"></i>French Guiana</div>
-            <div class="item" data-value="pf"><i class="pf flag"></i>French Polynesia</div>
-            <div class="item" data-value="tf"><i class="tf flag"></i>French Territories</div>
-            <div class="item" data-value="ga"><i class="ga flag"></i>Gabon</div>
-            <div class="item" data-value="gm"><i class="gm flag"></i>Gambia</div>
-            <div class="item" data-value="ge"><i class="ge flag"></i>Georgia</div>
-            <div class="item" data-value="de"><i class="de flag"></i>Germany</div>
-            <div class="item" data-value="gh"><i class="gh flag"></i>Ghana</div>
-            <div class="item" data-value="gi"><i class="gi flag"></i>Gibraltar</div>
-            <div class="item" data-value="gr"><i class="gr flag"></i>Greece</div>
-            <div class="item" data-value="gl"><i class="gl flag"></i>Greenland</div>
-            <div class="item" data-value="gd"><i class="gd flag"></i>Grenada</div>
-            <div class="item" data-value="gp"><i class="gp flag"></i>Guadeloupe</div>
-            <div class="item" data-value="gu"><i class="gu flag"></i>Guam</div>
-            <div class="item" data-value="gt"><i class="gt flag"></i>Guatemala</div>
-            <div class="item" data-value="gw"><i class="gw flag"></i>Guinea-Bissau</div>
-            <div class="item" data-value="gn"><i class="gn flag"></i>Guinea</div>
-            <div class="item" data-value="gy"><i class="gy flag"></i>Guyana</div>
-            <div class="item" data-value="ht"><i class="ht flag"></i>Haiti</div>
-            <div class="item" data-value="hm"><i class="hm flag"></i>Heard Island</div>
-            <div class="item" data-value="hn"><i class="hn flag"></i>Honduras</div>
-            <div class="item" data-value="hk"><i class="hk flag"></i>Hong Kong</div>
-            <div class="item" data-value="hu"><i class="hu flag"></i>Hungary</div>
-            <div class="item" data-value="is"><i class="is flag"></i>Iceland</div>
-            <div class="item" data-value="in"><i class="in flag"></i>India</div>
-            <div class="item" data-value="io"><i class="io flag"></i>Indian Ocean Territory</div>
-            <div class="item" data-value="id"><i class="id flag"></i>Indonesia</div>
-            <div class="item" data-value="ir"><i class="ir flag"></i>Iran</div>
-            <div class="item" data-value="iq"><i class="iq flag"></i>Iraq</div>
-            <div class="item" data-value="ie"><i class="ie flag"></i>Ireland</div>
-            <div class="item" data-value="il"><i class="il flag"></i>Israel</div>
-            <div class="item" data-value="it"><i class="it flag"></i>Italy</div>
-            <div class="item" data-value="jm"><i class="jm flag"></i>Jamaica</div>
-            <div class="item" data-value="jp"><i class="jp flag"></i>Japan</div>
-            <div class="item" data-value="jo"><i class="jo flag"></i>Jordan</div>
-            <div class="item" data-value="kz"><i class="kz flag"></i>Kazakhstan</div>
-            <div class="item" data-value="ke"><i class="ke flag"></i>Kenya</div>
-            <div class="item" data-value="ki"><i class="ki flag"></i>Kiribati</div>
-            <div class="item" data-value="kw"><i class="kw flag"></i>Kuwait</div>
-            <div class="item" data-value="kg"><i class="kg flag"></i>Kyrgyzstan</div>
-            <div class="item" data-value="la"><i class="la flag"></i>Laos</div>
-            <div class="item" data-value="lv"><i class="lv flag"></i>Latvia</div>
-            <div class="item" data-value="lb"><i class="lb flag"></i>Lebanon</div>
-            <div class="item" data-value="ls"><i class="ls flag"></i>Lesotho</div>
-            <div class="item" data-value="lr"><i class="lr flag"></i>Liberia</div>
-            <div class="item" data-value="ly"><i class="ly flag"></i>Libya</div>
-            <div class="item" data-value="li"><i class="li flag"></i>Liechtenstein</div>
-            <div class="item" data-value="lt"><i class="lt flag"></i>Lithuania</div>
-            <div class="item" data-value="lu"><i class="lu flag"></i>Luxembourg</div>
-            <div class="item" data-value="mo"><i class="mo flag"></i>Macau</div>
-            <div class="item" data-value="mk"><i class="mk flag"></i>Macedonia</div>
-            <div class="item" data-value="mg"><i class="mg flag"></i>Madagascar</div>
-            <div class="item" data-value="mw"><i class="mw flag"></i>Malawi</div>
-            <div class="item" data-value="my"><i class="my flag"></i>Malaysia</div>
-            <div class="item" data-value="mv"><i class="mv flag"></i>Maldives</div>
-            <div class="item" data-value="ml"><i class="ml flag"></i>Mali</div>
-            <div class="item" data-value="mt"><i class="mt flag"></i>Malta</div>
-            <div class="item" data-value="mh"><i class="mh flag"></i>Marshall Islands</div>
-            <div class="item" data-value="mq"><i class="mq flag"></i>Martinique</div>
-            <div class="item" data-value="mr"><i class="mr flag"></i>Mauritania</div>
-            <div class="item" data-value="mu"><i class="mu flag"></i>Mauritius</div>
-            <div class="item" data-value="yt"><i class="yt flag"></i>Mayotte</div>
-            <div class="item" data-value="mx"><i class="mx flag"></i>Mexico</div>
-            <div class="item" data-value="fm"><i class="fm flag"></i>Micronesia</div>
-            <div class="item" data-value="md"><i class="md flag"></i>Moldova</div>
-            <div class="item" data-value="mc"><i class="mc flag"></i>Monaco</div>
-            <div class="item" data-value="mn"><i class="mn flag"></i>Mongolia</div>
-            <div class="item" data-value="me"><i class="me flag"></i>Montenegro</div>
-            <div class="item" data-value="ms"><i class="ms flag"></i>Montserrat</div>
-            <div class="item" data-value="ma"><i class="ma flag"></i>Morocco</div>
-            <div class="item" data-value="mz"><i class="mz flag"></i>Mozambique</div>
-            <div class="item" data-value="na"><i class="na flag"></i>Namibia</div>
-            <div class="item" data-value="nr"><i class="nr flag"></i>Nauru</div>
-            <div class="item" data-value="np"><i class="np flag"></i>Nepal</div>
-            <div class="item" data-value="an"><i class="an flag"></i>Netherlands Antilles</div>
-            <div class="item" data-value="nl"><i class="nl flag"></i>Netherlands</div>
-            <div class="item" data-value="nc"><i class="nc flag"></i>New Caledonia</div>
-            <div class="item" data-value="pg"><i class="pg flag"></i>New Guinea</div>
-            <div class="item" data-value="nz"><i class="nz flag"></i>New Zealand</div>
-            <div class="item" data-value="ni"><i class="ni flag"></i>Nicaragua</div>
-            <div class="item" data-value="ne"><i class="ne flag"></i>Niger</div>
-            <div class="item" data-value="ng"><i class="ng flag"></i>Nigeria</div>
-            <div class="item" data-value="nu"><i class="nu flag"></i>Niue</div>
-            <div class="item" data-value="nf"><i class="nf flag"></i>Norfolk Island</div>
-            <div class="item" data-value="kp"><i class="kp flag"></i>North Korea</div>
-            <div class="item" data-value="mp"><i class="mp flag"></i>Northern Mariana Islands</div>
-            <div class="item" data-value="no"><i class="no flag"></i>Norway</div>
-            <div class="item" data-value="om"><i class="om flag"></i>Oman</div>
-            <div class="item" data-value="pk"><i class="pk flag"></i>Pakistan</div>
-            <div class="item" data-value="pw"><i class="pw flag"></i>Palau</div>
-            <div class="item" data-value="ps"><i class="ps flag"></i>Palestine</div>
-            <div class="item" data-value="pa"><i class="pa flag"></i>Panama</div>
-            <div class="item" data-value="py"><i class="py flag"></i>Paraguay</div>
-            <div class="item" data-value="pe"><i class="pe flag"></i>Peru</div>
-            <div class="item" data-value="ph"><i class="ph flag"></i>Philippines</div>
-            <div class="item" data-value="pn"><i class="pn flag"></i>Pitcairn Islands</div>
-            <div class="item" data-value="pl"><i class="pl flag"></i>Poland</div>
-            <div class="item" data-value="pt"><i class="pt flag"></i>Portugal</div>
-            <div class="item" data-value="pr"><i class="pr flag"></i>Puerto Rico</div>
-            <div class="item" data-value="qa"><i class="qa flag"></i>Qatar</div>
-            <div class="item" data-value="re"><i class="re flag"></i>Reunion</div>
-            <div class="item" data-value="ro"><i class="ro flag"></i>Romania</div>
-            <div class="item" data-value="ru"><i class="ru flag"></i>Russia</div>
-            <div class="item" data-value="rw"><i class="rw flag"></i>Rwanda</div>
-            <div class="item" data-value="sh"><i class="sh flag"></i>Saint Helena</div>
-            <div class="item" data-value="kn"><i class="kn flag"></i>Saint Kitts and Nevis</div>
-            <div class="item" data-value="lc"><i class="lc flag"></i>Saint Lucia</div>
-            <div class="item" data-value="pm"><i class="pm flag"></i>Saint Pierre</div>
-            <div class="item" data-value="vc"><i class="vc flag"></i>Saint Vincent</div>
-            <div class="item" data-value="ws"><i class="ws flag"></i>Samoa</div>
-            <div class="item" data-value="sm"><i class="sm flag"></i>San Marino</div>
-            <div class="item" data-value="gs"><i class="gs flag"></i>Sandwich Islands</div>
-            <div class="item" data-value="st"><i class="st flag"></i>Sao Tome</div>
-            <div class="item" data-value="sa"><i class="sa flag"></i>Saudi Arabia</div>
-            <div class="item" data-value="sn"><i class="sn flag"></i>Senegal</div>
-            <div class="item" data-value="cs"><i class="cs flag"></i>Serbia</div>
-            <div class="item" data-value="rs"><i class="rs flag"></i>Serbia</div>
-            <div class="item" data-value="sc"><i class="sc flag"></i>Seychelles</div>
-            <div class="item" data-value="sl"><i class="sl flag"></i>Sierra Leone</div>
-            <div class="item" data-value="sg"><i class="sg flag"></i>Singapore</div>
-            <div class="item" data-value="sk"><i class="sk flag"></i>Slovakia</div>
-            <div class="item" data-value="si"><i class="si flag"></i>Slovenia</div>
-            <div class="item" data-value="sb"><i class="sb flag"></i>Solomon Islands</div>
-            <div class="item" data-value="so"><i class="so flag"></i>Somalia</div>
-            <div class="item" data-value="za"><i class="za flag"></i>South Africa</div>
-            <div class="item" data-value="kr"><i class="kr flag"></i>South Korea</div>
-            <div class="item" data-value="es"><i class="es flag"></i>Spain</div>
-            <div class="item" data-value="lk"><i class="lk flag"></i>Sri Lanka</div>
-            <div class="item" data-value="sd"><i class="sd flag"></i>Sudan</div>
-            <div class="item" data-value="sr"><i class="sr flag"></i>Suriname</div>
-            <div class="item" data-value="sj"><i class="sj flag"></i>Svalbard</div>
-            <div class="item" data-value="sz"><i class="sz flag"></i>Swaziland</div>
-            <div class="item" data-value="se"><i class="se flag"></i>Sweden</div>
-            <div class="item" data-value="ch"><i class="ch flag"></i>Switzerland</div>
-            <div class="item" data-value="sy"><i class="sy flag"></i>Syria</div>
-            <div class="item" data-value="tw"><i class="tw flag"></i>Taiwan</div>
-            <div class="item" data-value="tj"><i class="tj flag"></i>Tajikistan</div>
-            <div class="item" data-value="tz"><i class="tz flag"></i>Tanzania</div>
-            <div class="item" data-value="th"><i class="th flag"></i>Thailand</div>
-            <div class="item" data-value="tl"><i class="tl flag"></i>Timorleste</div>
-            <div class="item" data-value="tg"><i class="tg flag"></i>Togo</div>
-            <div class="item" data-value="tk"><i class="tk flag"></i>Tokelau</div>
-            <div class="item" data-value="to"><i class="to flag"></i>Tonga</div>
-            <div class="item" data-value="tt"><i class="tt flag"></i>Trinidad</div>
-            <div class="item" data-value="tn"><i class="tn flag"></i>Tunisia</div>
-            <div class="item" data-value="tr"><i class="tr flag"></i>Turkey</div>
-            <div class="item" data-value="tm"><i class="tm flag"></i>Turkmenistan</div>
-            <div class="item" data-value="tv"><i class="tv flag"></i>Tuvalu</div>
-            <div class="item" data-value="ug"><i class="ug flag"></i>Uganda</div>
-            <div class="item" data-value="ua"><i class="ua flag"></i>Ukraine</div>
-            <div class="item" data-value="ae"><i class="ae flag"></i>United Arab Emirates</div>
-            <div class="item" data-value="us"><i class="us flag"></i>United States</div>
-            <div class="item" data-value="uy"><i class="uy flag"></i>Uruguay</div>
-            <div class="item" data-value="um"><i class="um flag"></i>Us Minor Islands</div>
-            <div class="item" data-value="vi"><i class="vi flag"></i>Us Virgin Islands</div>
-            <div class="item" data-value="uz"><i class="uz flag"></i>Uzbekistan</div>
-            <div class="item" data-value="vu"><i class="vu flag"></i>Vanuatu</div>
-            <div class="item" data-value="va"><i class="va flag"></i>Vatican City</div>
-            <div class="item" data-value="ve"><i class="ve flag"></i>Venezuela</div>
-            <div class="item" data-value="vn"><i class="vn flag"></i>Vietnam</div>
-            <div class="item" data-value="wf"><i class="wf flag"></i>Wallis and Futuna</div>
-            <div class="item" data-value="eh"><i class="eh flag"></i>Western Sahara</div>
-            <div class="item" data-value="ye"><i class="ye flag"></i>Yemen</div>
-            <div class="item" data-value="zm"><i class="zm flag"></i>Zambia</div>
-            <div class="item" data-value="zw"><i class="zw flag"></i>Zimbabwe</div>
-        </div>
-        </div>
-    </div>
-    <button class="ui basic red button" id="ban-btn" onclick="addCountryToBlacklist();"><i class="ui red ban icon"></i> Blacklist Country</button>
-</div>
-<table class="ui unstackable basic celled table">
-    <thead>
-        <tr>
-            <th>ISO Code</th>
-            <th>Remove</th>
-        </tr>
-    </thead>
-    <tbody id="banned-list">
-
-    </tbody>
-</table>
-
-<h4>IP Blacklist</h4>
-<p>Black a certain IP or IP range</p>
-<div class="ui form">
-    <div class="field">
-      <label>IP Address</label>
-      <input id="ipAddressInput" type="text" placeholder="IP Address">
-    </div>
-    <button id="addIpButton" onclick="addIpBlacklist();" class="ui basic red icon button">
-      <i class="ban icon"></i> Blacklist IP
-    </button>
-  </div>
-  <div class="ui message">
-    <i class="ui info circle icon"></i> IP Address support the following formats
-    <div class="ui bulleted list">
-        <div class="item">Fixed IP Address (e.g. 192.128.4.100)</div>
-        <div class="item">IP Wildcard (e.g. 172.164.*.*)</div>
-        <div class="item">CIDR String (e.g. 128.32.0.1/16)</div>
-    </div>
-  </div>
-  <table class="ui unstackable basic celled table">
-    <thead>
-      <tr>
-        <th>IP Address</th>
-        <th>Remove</th>
-      </tr>
-    </thead>
-    <tbody id="blacklistIpTable">
-
-    </tbody>
-  </table>
-  <div class="ui divider"></div>
-  <h4>Visitor IP list</h4>
-  <button style="margin-top: -1em;" onclick="initBlacklistQuickBanTable();" class="ui green small right floated circular basic icon button"><i class="ui refresh icon"></i></button>
-  <p>Observe strange traffic on your sites? Ban them in the list below.</p>
- 
-  <table class="ui celled unstackable table" id="ipTable">
-    <thead>
-      <tr>
-        <th>IP</th>
-        <th>Access Count</th>
-        <th>Blacklist</th>
-      </tr>
-    </thead>
-    <tbody>
-    </tbody>
-  </table>
-  <div class="pagination"></div>
-</div>
-<script>
-    $(".dropdown").dropdown();
-
-    function filterCountries(codesToShow) {
-        // get all items in the dropdown
-        const items = document.querySelectorAll('.ui.fluid.search.selection.dropdown .menu .item');
-        // loop through all items
-        items.forEach(item => {
-            // get the value of the item (i.e. the country code)
-            const code = item.dataset.value;
-            // if the code is in the array of codes to show, show the item
-            if (codesToShow.includes(code)) {
-                item.style.display = 'none';
-            }
-            // otherwise, hide the item
-            else {
-            item.style.display = 'block';
-            }
-        });
-    }
-
-    //Get the country name from the dropdown
-    function getCountryName(countryCode) {
-        return $('#countrySelector .item[data-value="' + countryCode.toLowerCase() + '"]').text().trim();
-    }
-
-    function initBannedCountryList(){
-        $.get("/api/blacklist/list?type=country", function(data) {
-            let bannedListHtml = '';
-            data.forEach((countryCode) => {
-                bannedListHtml += `
-                <tr>
-                    <td><i class="${countryCode} flag"></i> ${getCountryName(countryCode)} (${countryCode.toUpperCase()})</td>
-                    <td><button class="ui red basic mini icon button" onclick="removeFromBannedList('${countryCode}')"><i class="trash icon"></i></button></td>
-                </tr>
-                `;
-            });
-            $('#banned-list').html(bannedListHtml);
-            filterCountries(data);
-            if (data.length === 0) {
-                $('#banned-list').append(`
-                    <tr>
-                        <td colspan="2">
-                            <i class="green check circle icon"></i>
-                            There are no blacklisted countries
-                        </td>
-                    </tr>
-                `);
-            }
-
-        });
-    }
-    initBannedCountryList();
-
-    function addCountryToBlacklist() {
-        var countryCode = $("#countrySelector").dropdown("get value").toLowerCase();
-        $('#countrySelector').dropdown('clear');
-        $.ajax({
-            type: "POST",
-            url: "/api/blacklist/country/add",
-            data: { cc: countryCode },
-            success: function(response) {
-                if (response.error != undefined){
-                    alert(response.error);
-                }
-                initBannedCountryList();
-            },
-            error: function(xhr, status, error) {
-            // handle error response
-            }
-        });
-    }
-
-    function removeFromBannedList(countryCode){
-        if (confirm("Confirm removing " + getCountryName(countryCode) + " from blacklist?")){
-            countryCode = countryCode.toLowerCase(); 
-            $.ajax({
-                url: "/api/blacklist/country/remove",
-                method: "POST",
-                data: { cc: countryCode },
-                success: function(response) {
-                    if (response.error != undefined){
-                        alert(response.error);
-                    }
-                    initBannedCountryList();
-                },
-                error: function(xhr, status, error) {
-                    console.error("Error removing country from blacklist: " + error);
-                    // Handle error response
-                }
-            });
-        }
-    }
-
-    function initIpBanTable(){
-        $.get('/api/blacklist/list?type=ip', function(data) {
-            $('#blacklistIpTable').html("");
-            if (data.length === 0) {
-                $('#blacklistIpTable').append(`
-                <tr>
-                    <td colspan="2"><i class="green check circle icon"></i>There are no blacklisted IP addresses</td>
-                </tr>
-                `);
-            } else {
-                $.each(data, function(index, ip) {
-                    let icon = "globe icon";
-                    if (isLAN(ip)){
-                        icon = "desktop icon";
-                    }else if (isHomeAddr(ip)){
-                        icon = "home icon";
-                    }
-                    $('#blacklistIpTable').append(`
-                        <tr class="blacklistItem" ip="${encodeURIComponent(ip)}">
-                            <td><i class="${icon}"></i> ${ip}</td>
-                            <td><button class="ui icon basic mini red button" onclick="removeIpBlacklist('${ip}');"><i class="trash alternate icon"></i></button></td>
-                        </tr>
-                    `);
-                });
-            }
-
-            initBlacklistQuickBanTable();
-        });
-    }
-    initIpBanTable();
-
-    //Check if a input is a valid IP address, wildcard of a IP address or a CIDR string
-    function isValidIpFilter(input) {
-        // Check if input is a valid IP address
-        const isValidIp = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])$/.test(input);
-
-        if (isValidIp) {
-            return true;
-        }
-
-        // Check if input is a wildcard IP address
-        const isValidWildcardIp = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])\.([01]?[0-9]?[0-9]|\*|\*\/[0-9]|[01]?[0-9]?[0-9]-[01]?[0-9]?[0-9]|\[\d+,\d+\])$/.test(input);
-
-        if (isValidWildcardIp) {
-            return true;
-        }
-
-        // Check if input is a valid CIDR address string
-        const isValidCidr = /^([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\.([01]?[0-9]?[0-9]|2[0-4][0-9]|25[0-5])\/([0-9]|[1-2][0-9]|3[0-2])$/.test(input);
-
-        if (isValidCidr) {
-            return true;
-        }
-
-        // Input is not a valid IP address, wildcard IP address, or CIDR address string
-        return false;
-    }
-
-    $("#ipAddressInput").on("input", function() {
-        $(this).val($(this).val().trim());
-            var ipAddress = $(this).val();
-        if (!isValidIpFilter(ipAddress)) {
-            $(this).parent().addClass("error");
-        } else {
-            $(this).parent().removeClass("error");
-        }
-    });
-
-    function isLAN(ipAddress) {
-        function ip2long(ipAddress) {
-            // Convert the IP address to a 32-bit integer
-            const parts = ipAddress.split(".");
-            return (
-                (parseInt(parts[0]) << 24) |
-                (parseInt(parts[1]) << 16) |
-                (parseInt(parts[2]) << 8) |
-                parseInt(parts[3])
-            );
-        }
-
-        // Define the LAN IP address ranges
-        const LAN_RANGES = [
-            { start: "10.0.0.0", end: "10.255.255.255" },
-            { start: "172.16.0.0", end: "172.31.255.255" },
-            { start: "192.168.0.0", end: "192.168.255.255" }
-        ];
-
-        // Check if the IP address is within any of the LAN ranges
-        for (let i = 0; i < LAN_RANGES.length; i++) {
-            const rangeStart = ip2long(LAN_RANGES[i].start);
-            const rangeEnd = ip2long(LAN_RANGES[i].end);
-            const ipAddressLong = ip2long(ipAddress);
-
-            if (ipAddressLong >= rangeStart && ipAddressLong <= rangeEnd) {
-            return true;
-            }
-        }
-
-        return false;
-    }
-
-    function isHomeAddr(ipAddress) {
-        const specialIpAddresses = ['0.0.0.0', '127.0.0.1', '::1'];
-        return specialIpAddresses.includes(ipAddress);
-    }
-       
-
-
-    function addIpBlacklist(){
-        let targetIp = $("#ipAddressInput").val().trim();
-        if (targetIp == ""){
-            alert("IP address is empty")
-            return
-        }
-        if (!isValidIpFilter(targetIp)){
-            if (!confirm("This doesn't seems like a valid IP address. Continue anyway?")){
-                return;
-            }
-        }
-
-        $.ajax({
-            url: "/api/blacklist/ip/add",
-            type: "POST",
-            data: {ip: targetIp.toLowerCase()},
-            success: function(response) {
-                if (response.error !== undefined) {
-                    alert(response.error);
-                } else {
-                    initIpBanTable();
-                }
-
-                $("#ipAddressInput").val("");
-                $("#ipAddressInput").parent().remvoeClass("error");
-            },
-            error: function() {
-                alert("Failed to add IP address to blacklist.");
-            }
-        });
-    }
-
-    function removeIpBlacklist(ipaddr){
-        if (confirm("Confirm remove blacklist for " + ipaddr + " ?")){
-            $.ajax({
-                url: "/api/blacklist/ip/remove",
-                type: "POST",
-                data: {ip: ipaddr.toLowerCase()},
-                success: function(response) {
-                    if (response.error !== undefined) {
-                        alert(response.error);
-                    } else {
-                        initIpBanTable();
-                    }
-                },
-                error: function() {
-                    alert("Failed to remove IP address from blacklist.");
-                }
-                
-            });
-        }
-    }
-
-    //function to check for blacklist enable
-    function enableBlacklist() {
-        var isChecked = $('#enableBlacklist').is(':checked');
-        $.ajax({
-            type: 'POST',
-            url: '/api/blacklist/enable',
-            data: { enable: isChecked },
-            success: function(data){
-                $("#toggleSucc").stop().finish().fadeIn("fast").delay(3000).fadeOut("fast");
-            }
-        });
-    }
-
-    function initBlacklistEnableState(){
-        $.get('/api/blacklist/enable', function(data){
-            if (data == true){
-                $('#enableBlacklist').parent().checkbox("set checked");
-            }
-
-            //Register on change event
-            $("#enableBlacklist").on("change", function(){
-                enableBlacklist();
-            })
-        });
-    }
-    initBlacklistEnableState();
-
-    //Load the summary to ip access table
-    function initBlacklistQuickBanTable(){
-        $.get("/api/stats/summary", function(data){
-            initIpAccessTable(data.RequestClientIp);
-        })
-    }
-    initBlacklistQuickBanTable();
-
-    var blacklist_entriesPerPage = 30;
-    var blacklist_currentPage = 1;
-    var blacklist_totalPages = 0;
-
-    function initIpAccessTable(ipAccessCounts){
-        blacklist_totalPages = Math.ceil(Object.keys(ipAccessCounts).length / blacklist_entriesPerPage);
-
-        function sortkv(obj) {
-            var sortable = [];
-            for (var key in obj) {
-                if (obj.hasOwnProperty(key)) {
-                sortable.push([key, obj[key]]);
-                }
-            }
-            sortable.sort(function(a, b) {
-                return b[1] - a[1];
-            });
-            var sortedObj = {};
-            sortable.forEach(function(item) {
-                sortedObj[item[0]] = item[1];
-            });
-            return sortedObj;
-        }
-
-        ipAccessCounts = sortkv(ipAccessCounts);
-
-        function renderTable() {
-            var tableBody = $("#ipTable tbody");
-            tableBody.empty();
-
-            var startIndex = (blacklist_currentPage - 1) * blacklist_entriesPerPage;
-            var endIndex = startIndex + blacklist_entriesPerPage;
-            var slicedEntries = Object.entries(ipAccessCounts).slice(startIndex, endIndex);
-
-            slicedEntries.forEach(function([ip, accessCount]) {
-                var row = $("<tr>").appendTo(tableBody);
-                $("<td>").text(ip).appendTo(row);
-                $("<td>").text(accessCount).appendTo(row);
-                    if (ipInBlacklist(ip)){
-                        $("<td>").html(`<button class="ui basic green small icon button" title"Unban IP" onclick="handleUnban('${ip}');"><i class="green check icon"></i></button>`).appendTo(row);
-                    }else{
-                        $("<td>").html(`<button class="ui basic red small icon button" title"Ban IP" onclick="handleBanIp('${ip}');"><i class="red ban icon"></i></button>`).appendTo(row);
-                    }
-                
-            });
-        }
-
-        function renderPagination() {
-            var paginationDiv = $(".pagination");
-            paginationDiv.empty();
-
-            for (var i = 1; i <= blacklist_totalPages; i++) {
-                var button = $("<button>").text(i).addClass("ui small basic compact button");
-                if (i === blacklist_currentPage) {
-                    button.addClass("disabled");
-                }
-                button.click(function() {
-                    blacklist_currentPage = parseInt($(this).text());
-                    renderTable();
-                    renderPagination();
-                });
-                button.appendTo(paginationDiv);
-            }
-        }
-
-        renderTable();
-        renderPagination();
-    }
-
-    function ipInBlacklist(targetIp){
-        let inBlacklist = false;
-        $(".blacklistItem").each(function(){
-            if ($(this).attr("ip") == encodeURIComponent(targetIp)){
-                inBlacklist = true;
-            }   
-        });
-        return inBlacklist;
-    }
-
-    function handleBanIp(targetIp){
-        $("#ipAddressInput").val(targetIp);
-        addIpBlacklist();
-    }
-
-    function handleUnban(targetIp){
-        removeIpBlacklist(targetIp);
-    }
-</script>

+ 11 - 5
web/components/cert.html

@@ -107,12 +107,18 @@
             }else{
                 data.forEach(entry => {
                     $("#certifiedDomainList").append(`<tr>
-                    <td>${entry.Domain}</td>
-                    <td>${entry.LastModifiedDate}</td>
-                    <td>${entry.ExpireDate}</td>
-                    <td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td>
+                        <td>${entry.Domain}</td>
+                        <td>${entry.LastModifiedDate}</td>
+                        <td>${entry.ExpireDate}</td>
+                        <td><button title="Delete key-pair" class="ui mini basic red icon button" onclick="deleteCertificate('${entry.Domain}');"><i class="ui red trash icon"></i></button></td>
                     </tr>`);
-                })
+                });
+
+                if (data.length == 0){
+                    $("#certifiedDomainList").append(`<tr>
+                        <td colspan="4"><i class="ui times circle icon"></i> No valid keypairs found</td>
+                    </tr>`);
+                }
             }
         })
     }

+ 2 - 3
web/components/stats.html

@@ -212,7 +212,7 @@
             }else{
                 //Two dates are given and they are not identical
                 loadStatisticByRange(startdate, enddate);
-                console.log(startdate, enddate);
+                //console.log(startdate, enddate);
             }
     }
 
@@ -270,7 +270,7 @@
 
    function loadStatisticByRange(startdate, endDate){
         $.getJSON("/api/analytic/loadRange?start=" + startdate + "&end=" + endDate, function(data){
-            console.log(data);
+            //console.log(data);
             //Destroy all the previous charts
            
             statisticCharts.forEach(function(thisChart){
@@ -368,7 +368,6 @@
 
    function renderRefererTable(refererList){
         const sortedEntries = Object.entries(refererList).sort(([, valueA], [, valueB]) => valueB - valueA);
-        console.log(sortedEntries);
         $("#stats_RefererTable").html("");
         let endStop = 100;
         if (sortedEntries.length < 100){

+ 3 - 3
web/forbidden.html

@@ -1,11 +1,11 @@
 <html>
     <head>
+        <!-- Zoraxy Forbidden Template -->
         <meta charset="UTF-8">
         <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=no">
         <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.css">
         <script type="text/javascript" src="https://code.jquery.com/jquery-3.6.4.min.js"></script>
-        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.js
-        "></script>
+        <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/semantic-ui/2.5.0/semantic.min.js"></script>
         <title>Forbidden</title>
         <style>
             #msg{
@@ -39,7 +39,7 @@
                 <h3 style="margin-top: 1em;">403 - Forbidden</h3>
                 <div class="ui divider"></div>
                 <p>You do not have permission to view this directory or page. <br>
-                    This might be caused by the site admin has blacklisted your country or IP address</p>
+                    This might cause by the region limit setting of this site.</p>
                 <div class="ui divider"></div>
                 <div style="text-align: left;">
                     <small>Request time: <span id="reqtime"></span></small><br>

+ 3 - 3
web/index.html

@@ -55,8 +55,8 @@
                     <a class="item" tag="redirectset">
                         <i class="simplistic level up alternate icon"></i> Redirection
                     </a>
-                    <a class="item" tag="blacklist">
-                        <i class="simplistic ban icon"></i> Blacklist
+                    <a class="item" tag="access">
+                        <i class="simplistic ban icon"></i> Access Control
                     </a>
                     <div class="ui divider menudivider">Bridging</div>
                     <a class="item" tag="gan">
@@ -109,7 +109,7 @@
                 <div id="redirectset" class="functiontab" target="redirection.html"></div>
 
                 <!-- Blacklist -->
-                <div id="blacklist" class="functiontab" target="blacklist.html"></div>
+                <div id="access" class="functiontab" target="access.html"></div>
 
                 <!-- Global Area Networking -->
                 <div id="gan" class="functiontab" target="gan.html"></div>

+ 21 - 0
web/main.css

@@ -398,6 +398,27 @@ body{
     }
   }
 
+/*
+    Access Control
+*/
+
+.ui.very.compact.table th{
+    padding-top: 0.2em;
+    padding-bottom: 0.2em;
+}
+
+
+.ui.very.compact.table td{
+    padding-top: 0.1em;
+    padding-bottom: 0.1em;
+}
+
+#ipTable.disabled{
+    opacity: 0.5;
+    pointer-events: none;
+    user-select: none;
+}
+
 /*
     Uptime Monitor
 */