Browse Source

auto update script executed

Toby Chui 1 year ago
parent
commit
82b90293e4

+ 9 - 0
api.go

@@ -92,4 +92,13 @@ func initAPIs() {
 	authRouter.HandleFunc("/api/redirect/list", handleListRedirectionRules)
 	authRouter.HandleFunc("/api/redirect/add", handleAddRedirectionRule)
 	authRouter.HandleFunc("/api/redirect/delete", handleDeleteRedirectionRule)
+
+	//Blacklist APIs
+	authRouter.HandleFunc("/api/blacklist/list", handleListBlacklisted)
+	authRouter.HandleFunc("/api/blacklist/country/add", handleCountryBlacklistAdd)
+	authRouter.HandleFunc("/api/blacklist/country/remove", handleCountryBlacklistRemove)
+	authRouter.HandleFunc("/api/blacklist/ip/add", handleIpBlacklistAdd)
+	authRouter.HandleFunc("/api/blacklist/ip/remove", handleIpBlacklistRemove)
+
+	//If you got APIs to add, append them here
 }

+ 81 - 0
blacklist.go

@@ -0,0 +1,81 @@
+package main
+
+import (
+	"encoding/json"
+	"net/http"
+
+	"imuslab.com/arozos/ReverseProxy/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)
+}

+ 66 - 0
mod/dynamicproxy/Server.go

@@ -0,0 +1,66 @@
+package dynamicproxy
+
+import (
+	"net/http"
+	"os"
+	"strings"
+
+	"imuslab.com/arozos/ReverseProxy/mod/geodb"
+)
+
+/*
+	Server.go
+
+	Main server for dynamic proxy core
+*/
+
+func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	//Check if this ip is in blacklist
+	clientIpAddr := geodb.GetRequesterIP(r)
+	if h.Parent.Option.GeodbStore.IsBlacklisted(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)
+		}
+
+		return
+	}
+
+	//Check if this is a redirection url
+	if h.Parent.Option.RedirectRuleTable.IsRedirectable(r) {
+		h.Parent.Option.RedirectRuleTable.HandleRedirect(w, r)
+		return
+	}
+
+	//Extract request host to see if it is virtual directory or subdomain
+	domainOnly := r.Host
+	if strings.Contains(r.Host, ":") {
+		hostPath := strings.Split(r.Host, ":")
+		domainOnly = hostPath[0]
+	}
+
+	if strings.Contains(r.Host, ".") {
+		//This might be a subdomain. See if there are any subdomain proxy router for this
+		//Remove the port if any
+
+		sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly)
+		if sep != nil {
+			h.subdomainRequest(w, r, sep)
+			return
+		}
+	}
+
+	//Clean up the request URI
+	proxyingPath := strings.TrimSpace(r.RequestURI)
+
+	targetProxyEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath)
+	if targetProxyEndpoint != nil {
+		h.proxyRequest(w, r, targetProxyEndpoint)
+	} else {
+		h.proxyRequest(w, r, h.Parent.Root)
+	}
+}

+ 27 - 70
mod/dynamicproxy/dynamicproxy.go

@@ -9,41 +9,37 @@ import (
 	"net/http"
 	"net/url"
 	"strconv"
-	"strings"
 	"sync"
 	"time"
 
 	"imuslab.com/arozos/ReverseProxy/mod/dynamicproxy/dpcore"
 	"imuslab.com/arozos/ReverseProxy/mod/dynamicproxy/redirection"
+	"imuslab.com/arozos/ReverseProxy/mod/geodb"
 	"imuslab.com/arozos/ReverseProxy/mod/reverseproxy"
 	"imuslab.com/arozos/ReverseProxy/mod/tlscert"
 )
 
 /*
-Allow users to setup manual proxying for specific path
+	Zoraxy Dynamic Proxy
 */
-type Router struct {
-	ListenPort             int
-	ProxyEndpoints         *sync.Map
-	SubdomainEndpoint      *sync.Map
-	Running                bool
-	Root                   *ProxyEndpoint
-	tlsCertManager         *tlscert.Manager
-	mux                    http.Handler
-	TlsManager             *tlscert.Manager
-	useTLS                 bool
-	useHttpToHttpsRedirect bool
-	server                 *http.Server
-	tlsListener            net.Listener
-	redirectionRuleTable   *redirection.RuleTable
-}
-
 type RouterOption struct {
 	Port               int
 	UseTls             bool
 	ForceHttpsRedirect bool
 	TlsManager         *tlscert.Manager
 	RedirectRuleTable  *redirection.RuleTable
+	GeodbStore         *geodb.Store
+}
+
+type Router struct {
+	Option            *RouterOption
+	ProxyEndpoints    *sync.Map
+	SubdomainEndpoint *sync.Map
+	Running           bool
+	Root              *ProxyEndpoint
+	mux               http.Handler
+	server            *http.Server
+	tlsListener       net.Listener
 }
 
 type ProxyEndpoint struct {
@@ -68,15 +64,11 @@ func NewDynamicProxy(option RouterOption) (*Router, error) {
 	proxyMap := sync.Map{}
 	domainMap := sync.Map{}
 	thisRouter := Router{
-		ListenPort:             option.Port,
-		ProxyEndpoints:         &proxyMap,
-		SubdomainEndpoint:      &domainMap,
-		Running:                false,
-		tlsCertManager:         option.TlsManager,
-		useTLS:                 option.UseTls,
-		useHttpToHttpsRedirect: option.ForceHttpsRedirect,
-		redirectionRuleTable:   option.RedirectRuleTable,
-		server:                 nil,
+		Option:            &option,
+		ProxyEndpoints:    &proxyMap,
+		SubdomainEndpoint: &domainMap,
+		Running:           false,
+		server:            nil,
 	}
 
 	thisRouter.mux = &ProxyHandler{
@@ -89,13 +81,13 @@ func NewDynamicProxy(option RouterOption) (*Router, error) {
 // Update TLS setting in runtime. Will restart the proxy server
 // if it is already running in the background
 func (router *Router) UpdateTLSSetting(tlsEnabled bool) {
-	router.useTLS = tlsEnabled
+	router.Option.UseTls = tlsEnabled
 	router.Restart()
 }
 
 // Update https redirect, which will require updates
 func (router *Router) UpdateHttpToHttpsRedirectSetting(useRedirect bool) {
-	router.useHttpToHttpsRedirect = useRedirect
+	router.Option.ForceHttpsRedirect = useRedirect
 	router.Restart()
 }
 
@@ -111,21 +103,21 @@ func (router *Router) StartProxyService() error {
 	}
 
 	config := &tls.Config{
-		GetCertificate: router.tlsCertManager.GetCert,
+		GetCertificate: router.Option.TlsManager.GetCert,
 	}
 
-	if router.useTLS {
+	if router.Option.UseTls {
 		//Serve with TLS mode
-		ln, err := tls.Listen("tcp", ":"+strconv.Itoa(router.ListenPort), config)
+		ln, err := tls.Listen("tcp", ":"+strconv.Itoa(router.Option.Port), config)
 		if err != nil {
 			log.Println(err)
 			return err
 		}
 		router.tlsListener = ln
-		router.server = &http.Server{Addr: ":" + strconv.Itoa(router.ListenPort), Handler: router.mux}
+		router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux}
 		router.Running = true
 
-		if router.ListenPort == 443 && router.useHttpToHttpsRedirect {
+		if router.Option.Port == 443 && router.Option.ForceHttpsRedirect {
 			//Add a 80 to 443 redirector
 			httpServer := &http.Server{
 				Addr: ":80",
@@ -163,7 +155,7 @@ func (router *Router) StartProxyService() error {
 	} else {
 		//Serve with non TLS mode
 		router.tlsListener = nil
-		router.server = &http.Server{Addr: ":" + strconv.Itoa(router.ListenPort), Handler: router.mux}
+		router.server = &http.Server{Addr: ":" + strconv.Itoa(router.Option.Port), Handler: router.mux}
 		router.Running = true
 		log.Println("Reverse proxy service started in the background (Plain HTTP mode)")
 		go func() {
@@ -299,38 +291,3 @@ func (router *Router) SetRootProxy(proxyLocation string, requireTLS bool) error
 	router.Root = &rootEndpoint
 	return nil
 }
-
-// Do all the main routing in here
-func (h *ProxyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	//Check if this is a redirection url
-	if h.Parent.redirectionRuleTable.IsRedirectable(r) {
-		h.Parent.redirectionRuleTable.HandleRedirect(w, r)
-		return
-	}
-	domainOnly := r.Host
-	if strings.Contains(r.Host, ":") {
-		hostPath := strings.Split(r.Host, ":")
-		domainOnly = hostPath[0]
-	}
-
-	if strings.Contains(r.Host, ".") {
-		//This might be a subdomain. See if there are any subdomain proxy router for this
-		//Remove the port if any
-
-		sep := h.Parent.getSubdomainProxyEndpointFromHostname(domainOnly)
-		if sep != nil {
-			h.subdomainRequest(w, r, sep)
-			return
-		}
-	}
-
-	//Clean up the request URI
-	proxyingPath := strings.TrimSpace(r.RequestURI)
-
-	targetProxyEndpoint := h.Parent.getTargetProxyEndpointFromRequestURI(proxyingPath)
-	if targetProxyEndpoint != nil {
-		h.proxyRequest(w, r, targetProxyEndpoint)
-	} else {
-		h.proxyRequest(w, r, h.Parent.Root)
-	}
-}

+ 143 - 0
mod/geodb/geodb.go

@@ -2,6 +2,8 @@ package geodb
 
 import (
 	"net"
+	"net/http"
+	"strings"
 
 	"github.com/oschwald/geoip2-golang"
 	"imuslab.com/arozos/ReverseProxy/mod/database"
@@ -23,6 +25,16 @@ func NewGeoDb(sysdb *database.Database, dbfile string) (*Store, error) {
 		return nil, err
 	}
 
+	err = sysdb.NewTable("blacklist-cn")
+	if err != nil {
+		return nil, err
+	}
+
+	err = sysdb.NewTable("blacklist-ip")
+	if err != nil {
+		return nil, err
+	}
+
 	return &Store{
 		geodb: db,
 		sysdb: sysdb,
@@ -45,3 +57,134 @@ func (s *Store) ResolveCountryCodeFromIP(ipstring string) (*CountryInfo, error)
 func (s *Store) Close() {
 	s.geodb.Close()
 }
+
+func (s *Store) AddCountryCodeToBlackList(countryCode string) {
+	s.sysdb.Write("blacklist-cn", countryCode, true)
+}
+
+func (s *Store) RemoveCountryCodeFromBlackList(countryCode string) {
+	s.sysdb.Delete("blacklist-cn", countryCode)
+}
+
+func (s *Store) IsCountryCodeBlacklisted(countryCode string) bool {
+	var isBlacklisted bool = false
+	s.sysdb.Read("blacklist-cn", countryCode, &isBlacklisted)
+	return isBlacklisted
+}
+
+func (s *Store) GetAllBlacklistedCountryCode() []string {
+	bannedCountryCodes := []string{}
+	entries, err := s.sysdb.ListTable("blacklist-cn")
+	if err != nil {
+		return bannedCountryCodes
+	}
+	for _, keypairs := range entries {
+		ip := string(keypairs[0])
+		bannedCountryCodes = append(bannedCountryCodes, ip)
+	}
+
+	return bannedCountryCodes
+}
+
+func (s *Store) AddIPToBlackList(ipAddr string) {
+	s.sysdb.Write("blacklist-ip", ipAddr, true)
+}
+
+func (s *Store) RemoveIPFromBlackList(ipAddr string) {
+	s.sysdb.Delete("blacklist-ip", ipAddr)
+}
+
+func (s *Store) IsIPBlacklisted(ipAddr string) bool {
+	var isBlacklisted bool = false
+	s.sysdb.Read("blacklist-ip", ipAddr, &isBlacklisted)
+	return isBlacklisted
+}
+
+func (s *Store) GetAllBlacklistedIp() []string {
+	bannedIps := []string{}
+	entries, err := s.sysdb.ListTable("blacklist-ip")
+	if err != nil {
+		return bannedIps
+	}
+
+	for _, keypairs := range entries {
+		ip := string(keypairs[0])
+		bannedIps = append(bannedIps, ip)
+	}
+
+	return bannedIps
+}
+
+//Check if a IP address is blacklisted, in either country or IP blacklist
+func (s *Store) IsBlacklisted(ipAddr string) bool {
+	if ipAddr == "" {
+		//Unable to get the target IP address
+		return false
+	}
+
+	countryCode, err := s.ResolveCountryCodeFromIP(ipAddr)
+	if err != nil {
+		return false
+	}
+
+	if s.IsCountryCodeBlacklisted(countryCode.CountryIsoCode) {
+		return true
+	}
+
+	if s.IsIPBlacklisted(ipAddr) {
+		return true
+	}
+
+	return false
+}
+
+//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)
+}

+ 3 - 2
reverseproxy.go

@@ -50,6 +50,7 @@ func ReverseProxtInit() {
 		ForceHttpsRedirect: forceHttpsRedirect,
 		TlsManager:         tlsCertManager,
 		RedirectRuleTable:  redirectTable,
+		GeodbStore:         geodbStore,
 	})
 	if err != nil {
 		log.Println(err.Error())
@@ -282,11 +283,11 @@ func HandleIncomingPortSet(w http.ResponseWriter, r *http.Request) {
 	//Stop and change the setting of the reverse proxy service
 	if dynamicProxyRouter.Running {
 		dynamicProxyRouter.StopProxyService()
-		dynamicProxyRouter.ListenPort = newIncomingPortInt
+		dynamicProxyRouter.Option.Port = newIncomingPortInt
 		dynamicProxyRouter.StartProxyService()
 	} else {
 		//Only change setting but not starting the proxy service
-		dynamicProxyRouter.ListenPort = newIncomingPortInt
+		dynamicProxyRouter.Option.Port = newIncomingPortInt
 	}
 
 	sysdb.Write("settings", "inbound", newIncomingPortInt)

+ 553 - 0
web/components/blacklist.html

@@ -0,0 +1,553 @@
+
+<h3><i class="ui ban icon"></i> Blacklist</h3>
+<p>Setup blacklist based on estimated IP geographic location or IP address</p>
+<div class="ui divider"></div>
+<h4>Country Blacklist</h4>
+<div class="ui yellow message">
+    <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.
+</div>
+
+<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 celled table">
+    <thead>
+        <tr>
+            <th>ISO Code</th>
+            <th>Remove</th>
+        </tr>
+    </thead>
+    <tbody id="banned-list">
+
+    </tbody>
+</table>
+<div class="ui divider"></div>
+
+<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>
+  
+  <table class="ui celled table">
+    <thead>
+      <tr>
+        <th>IP Address</th>
+        <th>Remove</th>
+      </tr>
+    </thead>
+    <tbody id="blacklistIpTable">
+
+    </tbody>
+  </table>
+<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>
+                        <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>
+                    `);
+                });
+            }
+        });
+    }
+    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.");
+                }
+            });
+        }
+    }
+</script>

+ 38 - 8
web/components/status.html

@@ -1,18 +1,48 @@
 <div class="ui stackable four column grid">
         <div class="column">
             <div id="serverstatus" class="ui green inverted segment">
-                <h3 class="ui header">
+                <h4 class="ui header">
                     <i class="exchange icon"></i>
                     <div class="content">
                       <span id="statusTitle">Offline</span>
-                      <div class="sub header" id="statusText">Reverse proxy server is offline</div>
+                      <div style="color: white;" class="sub header" id="statusText">Reverse proxy server is offline</div>
                     </div>
-                </h3>
+                </h4>
+            </div>
+        </div>
+        <div class="column">
+            <div id="connections" class="ui blue inverted segment">
+                <h4 class="ui header">
+                    <i class="exchange icon"></i>
+                    <div class="content">
+                      <span></span>
+                      <div class="sub header"></div>
+                    </div>
+                </h4>
+            </div>
+        </div>
+        <div class="column">
+            <div id="connections" class="ui yellow inverted segment">
+                <h4 class="ui header">
+                    <i class="exchange icon"></i>
+                    <div class="content">
+                      <span></span>
+                      <div class="sub header"></div>
+                    </div>
+                </h4>
+            </div>
+        </div>
+        <div class="column">
+            <div id="connections" class="ui pink inverted segment">
+                <h4 class="ui header">
+                    <i class="exchange icon"></i>
+                    <div class="content">
+                      <span></span>
+                      <div class="sub header"></div>
+                    </div>
+                </h4>
             </div>
         </div>
-        <div class="column">Column 2</div>
-        <div class="column">Column 3</div>
-        <div class="column">Column 4</div>
 </div>
 
 <div class="ui divider"></div>
@@ -49,7 +79,7 @@
                 $("#stopbtn").removeClass("disabled");
                 $("#serverstatus").addClass("green");
                 $("#statusTitle").text("Online");
-                $("#statusText").text("Reverse proxying request on port: " + data.ListenPort);
+                $("#statusText").text("Reverse proxying request on port: " + data.Option.Port);
             }else{
                 $("#startbtn").removeClass("disabled");
                 $("#stopbtn").addClass("disabled");
@@ -57,7 +87,7 @@
                 $("#statusText").text("Reverse proxy server is offline");
                 $("#serverstatus").removeClass("green");
             }
-            $("#incomingPort").val(data.ListenPort);
+            $("#incomingPort").val(data.Option.Port);
         });
     }
 

+ 55 - 0
web/forbidden.html

@@ -0,0 +1,55 @@
+<html>
+    <head>
+        <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>
+        <title>Forbidden</title>
+        <style>
+            #msg{
+                position: absolute;
+                top: calc(50% - 150px);
+                left: calc(50% - 250px);
+                width: 500px;
+                height: 300px;
+                text-align: center;
+            }
+
+            #footer{
+                position: fixed;
+                padding: 2em;
+                padding-left: 5em;
+                padding-right: 5em;
+                bottom: 0px;
+                left: 0px;
+                width: 100%;
+            }   
+
+            small{
+                word-break: break-word;
+            }
+        </style>
+    </head>
+    <body>
+        <div id="msg">
+            <h1 style="font-size: 6em; margin-bottom: 0px;"><i class="red ban icon"></i></h1>
+            <div>
+                <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>
+                <div class="ui divider"></div>
+                <div style="text-align: left;">
+                    <small>Request time: <span id="reqtime"></span></small><br>
+                    <small id="reqURLDisplay">Request URI: <span id="requrl"></span></small>
+                </div>
+            </div>
+        </div>
+        <script>
+            $("#reqtime").text(new Date().toLocaleString(undefined, {year: 'numeric', month: '2-digit', day: '2-digit', weekday:"long", hour: '2-digit', hour12: false, minute:'2-digit', second:'2-digit'}));
+            $("#requrl").text(window.location.href);
+        </script>
+    </body>
+</html>

+ 7 - 0
web/index.html

@@ -108,6 +108,10 @@
                     <a class="item" tag="redirectset">
                         <i class="level up alternate icon"></i> Redirection
                     </a>
+                    <a class="item" tag="blacklist">
+                        <i class="ban icon"></i> Blacklist
+                    </a>
+                    <!-- Add more components here -->
                 </div>
             </div>
             <div class="contentWindow">
@@ -133,6 +137,9 @@
 
                 <!-- Redirections -->
                 <div id="redirectset" class="functiontab" target="redirection.html"></div>
+
+                <!-- Blacklist -->
+                <div id="blacklist" class="functiontab" target="blacklist.html"></div>
             </div>
             </div>
         </div>