mirror of
https://github.com/tobychui/zoraxy.git
synced 2025-08-10 07:07:51 +02:00
Added load balancer (wip)
+ Added support for multiple upstreams + Added load balancer + Added upstream abstraction in endpoint + Added load balancer structure + Added breaking change auto-updater + Added uptime monitor proxy type definitions + Added upstream editor UI + Fixed charset bug in many snippets HTML files
This commit is contained in:
44
src/mod/update/update.go
Normal file
44
src/mod/update/update.go
Normal file
@@ -0,0 +1,44 @@
|
||||
package update
|
||||
|
||||
/*
|
||||
Update.go
|
||||
|
||||
This module handle cross version updates that contains breaking changes
|
||||
update command should always exit after the update is completed
|
||||
*/
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
v308 "imuslab.com/zoraxy/mod/update/v308"
|
||||
)
|
||||
|
||||
// Run config update. Version numbers are int. For example
|
||||
// to update 3.0.7 to 3.0.8, use RunConfigUpdate(307, 308)
|
||||
// This function support cross versions updates (e.g. 307 -> 310)
|
||||
func RunConfigUpdate(fromVersion int, toVersion int) {
|
||||
for i := fromVersion; i < toVersion; i++ {
|
||||
oldVersion := fromVersion
|
||||
newVersion := fromVersion + 1
|
||||
fmt.Println("Updating from v", oldVersion, " to v", newVersion)
|
||||
runUpdateRoutineWithVersion(oldVersion, newVersion)
|
||||
}
|
||||
fmt.Println("Update completed")
|
||||
}
|
||||
|
||||
func GetVersionIntFromVersionNumber(version string) int {
|
||||
versionNumberOnly := strings.ReplaceAll(version, ".", "")
|
||||
versionInt, _ := strconv.Atoi(versionNumberOnly)
|
||||
return versionInt
|
||||
}
|
||||
|
||||
func runUpdateRoutineWithVersion(fromVersion int, toVersion int) {
|
||||
if fromVersion == 307 && toVersion == 308 {
|
||||
err := v308.UpdateFrom307To308()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
138
src/mod/update/v308/typedef307.go
Normal file
138
src/mod/update/v308/typedef307.go
Normal file
@@ -0,0 +1,138 @@
|
||||
package v308
|
||||
|
||||
/*
|
||||
v307 type definations
|
||||
|
||||
This file wrap up the self-contained data structure
|
||||
for v3.0.7 structure and allow automatic updates
|
||||
for future releases if required
|
||||
*/
|
||||
|
||||
type v307PermissionsPolicy struct {
|
||||
Accelerometer []string `json:"accelerometer"`
|
||||
AmbientLightSensor []string `json:"ambient_light_sensor"`
|
||||
Autoplay []string `json:"autoplay"`
|
||||
Battery []string `json:"battery"`
|
||||
Camera []string `json:"camera"`
|
||||
CrossOriginIsolated []string `json:"cross_origin_isolated"`
|
||||
DisplayCapture []string `json:"display_capture"`
|
||||
DocumentDomain []string `json:"document_domain"`
|
||||
EncryptedMedia []string `json:"encrypted_media"`
|
||||
ExecutionWhileNotRendered []string `json:"execution_while_not_rendered"`
|
||||
ExecutionWhileOutOfView []string `json:"execution_while_out_of_viewport"`
|
||||
Fullscreen []string `json:"fullscreen"`
|
||||
Geolocation []string `json:"geolocation"`
|
||||
Gyroscope []string `json:"gyroscope"`
|
||||
KeyboardMap []string `json:"keyboard_map"`
|
||||
Magnetometer []string `json:"magnetometer"`
|
||||
Microphone []string `json:"microphone"`
|
||||
Midi []string `json:"midi"`
|
||||
NavigationOverride []string `json:"navigation_override"`
|
||||
Payment []string `json:"payment"`
|
||||
PictureInPicture []string `json:"picture_in_picture"`
|
||||
PublicKeyCredentialsGet []string `json:"publickey_credentials_get"`
|
||||
ScreenWakeLock []string `json:"screen_wake_lock"`
|
||||
SyncXHR []string `json:"sync_xhr"`
|
||||
USB []string `json:"usb"`
|
||||
WebShare []string `json:"web_share"`
|
||||
XRSpatialTracking []string `json:"xr_spatial_tracking"`
|
||||
ClipboardRead []string `json:"clipboard_read"`
|
||||
ClipboardWrite []string `json:"clipboard_write"`
|
||||
Gamepad []string `json:"gamepad"`
|
||||
SpeakerSelection []string `json:"speaker_selection"`
|
||||
ConversionMeasurement []string `json:"conversion_measurement"`
|
||||
FocusWithoutUserActivation []string `json:"focus_without_user_activation"`
|
||||
HID []string `json:"hid"`
|
||||
IdleDetection []string `json:"idle_detection"`
|
||||
InterestCohort []string `json:"interest_cohort"`
|
||||
Serial []string `json:"serial"`
|
||||
SyncScript []string `json:"sync_script"`
|
||||
TrustTokenRedemption []string `json:"trust_token_redemption"`
|
||||
Unload []string `json:"unload"`
|
||||
WindowPlacement []string `json:"window_placement"`
|
||||
VerticalScroll []string `json:"vertical_scroll"`
|
||||
}
|
||||
|
||||
// Auth credential for basic auth on certain endpoints
|
||||
type v307BasicAuthCredentials struct {
|
||||
Username string
|
||||
PasswordHash string
|
||||
}
|
||||
|
||||
// Auth credential for basic auth on certain endpoints
|
||||
type v307BasicAuthUnhashedCredentials struct {
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// Paths to exclude in basic auth enabled proxy handler
|
||||
type v307BasicAuthExceptionRule struct {
|
||||
PathPrefix string
|
||||
}
|
||||
|
||||
// Header injection direction type
|
||||
type v307HeaderDirection int
|
||||
|
||||
const (
|
||||
HeaderDirection_ZoraxyToUpstream v307HeaderDirection = 0 //Inject (or remove) header to request out-going from Zoraxy to backend server
|
||||
HeaderDirection_ZoraxyToDownstream v307HeaderDirection = 1 //Inject (or remove) header to request out-going from Zoraxy to client (e.g. browser)
|
||||
)
|
||||
|
||||
// User defined headers to add into a proxy endpoint
|
||||
type v307UserDefinedHeader struct {
|
||||
Direction v307HeaderDirection
|
||||
Key string
|
||||
Value string
|
||||
IsRemove bool //Instead of set, remove this key instead
|
||||
}
|
||||
|
||||
// The original proxy endpoint structure from v3.0.7
|
||||
type v307ProxyEndpoint struct {
|
||||
ProxyType int //The type of this proxy, see const def
|
||||
RootOrMatchingDomain string //Matching domain for host, also act as key
|
||||
MatchingDomainAlias []string //A list of domains that alias to this rule
|
||||
Domain string //Domain or IP to proxy to
|
||||
|
||||
//TLS/SSL Related
|
||||
RequireTLS bool //Target domain require TLS
|
||||
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
|
||||
SkipCertValidations bool //Set to true to accept self signed certs
|
||||
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
|
||||
|
||||
//Virtual Directories
|
||||
VirtualDirectories []*v307VirtualDirectoryEndpoint
|
||||
|
||||
//Custom Headers
|
||||
UserDefinedHeaders []*v307UserDefinedHeader //Custom headers to append when proxying requests from this endpoint
|
||||
HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers
|
||||
EnablePermissionPolicyHeader bool //Enable injection of permission policy header
|
||||
PermissionPolicy *v307PermissionsPolicy //Permission policy header
|
||||
|
||||
//Authentication
|
||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||
BasicAuthCredentials []*v307BasicAuthCredentials //Basic auth credentials
|
||||
BasicAuthExceptionRules []*v307BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
|
||||
|
||||
// Rate Limiting
|
||||
RequireRateLimit bool
|
||||
RateLimit int64 // Rate limit in requests per second
|
||||
|
||||
//Access Control
|
||||
AccessFilterUUID string //Access filter ID
|
||||
|
||||
Disabled bool //If the rule is disabled
|
||||
|
||||
//Fallback routing logic (Special Rule Sets Only)
|
||||
DefaultSiteOption int //Fallback routing logic options
|
||||
DefaultSiteValue string //Fallback routing target, optional
|
||||
}
|
||||
|
||||
// A Virtual Directory endpoint, provide a subset of ProxyEndpoint for better
|
||||
// program structure than directly using ProxyEndpoint
|
||||
type v307VirtualDirectoryEndpoint struct {
|
||||
MatchingPath string //Matching prefix of the request path, also act as key
|
||||
Domain string //Domain or IP to proxy to
|
||||
RequireTLS bool //Target domain require TLS
|
||||
SkipCertValidations bool //Set to true to accept self signed certs
|
||||
Disabled bool //If the rule is enabled
|
||||
}
|
63
src/mod/update/v308/typedef308.go
Normal file
63
src/mod/update/v308/typedef308.go
Normal file
@@ -0,0 +1,63 @@
|
||||
package v308
|
||||
|
||||
/*
|
||||
v308 type definations
|
||||
|
||||
This file wrap up the self-contained data structure
|
||||
for v3.0.8 structure and allow automatic updates
|
||||
for future releases if required
|
||||
|
||||
Some struct are identical as v307 and hence it is not redefined here
|
||||
*/
|
||||
|
||||
/* Upstream or Origin Server */
|
||||
type v308Upstream struct {
|
||||
//Upstream Proxy Configs
|
||||
OriginIpOrDomain string //Target IP address or domain name with port
|
||||
RequireTLS bool //Require TLS connection
|
||||
SkipCertValidations bool //Set to true to accept self signed certs
|
||||
SkipWebSocketOriginCheck bool //Skip origin check on websocket upgrade connections
|
||||
|
||||
//Load balancing configs
|
||||
Weight int //Prirotiy of fallback, set all to 0 for round robin
|
||||
MaxConn int //Maxmium connection to this server
|
||||
}
|
||||
|
||||
// A proxy endpoint record, a general interface for handling inbound routing
|
||||
type v308ProxyEndpoint struct {
|
||||
ProxyType int //The type of this proxy, see const def
|
||||
RootOrMatchingDomain string //Matching domain for host, also act as key
|
||||
MatchingDomainAlias []string //A list of domains that alias to this rule
|
||||
ActiveOrigins []*v308Upstream //Activated Upstream or origin servers IP or domain to proxy to
|
||||
InactiveOrigins []*v308Upstream //Disabled Upstream or origin servers IP or domain to proxy to
|
||||
UseStickySession bool //Use stick session for load balancing
|
||||
Disabled bool //If the rule is disabled
|
||||
|
||||
//Inbound TLS/SSL Related
|
||||
BypassGlobalTLS bool //Bypass global TLS setting options if TLS Listener enabled (parent.tlsListener != nil)
|
||||
|
||||
//Virtual Directories
|
||||
VirtualDirectories []*v307VirtualDirectoryEndpoint
|
||||
|
||||
//Custom Headers
|
||||
UserDefinedHeaders []*v307UserDefinedHeader //Custom headers to append when proxying requests from this endpoint
|
||||
HSTSMaxAge int64 //HSTS max age, set to 0 for disable HSTS headers
|
||||
EnablePermissionPolicyHeader bool //Enable injection of permission policy header
|
||||
PermissionPolicy *v307PermissionsPolicy //Permission policy header
|
||||
|
||||
//Authentication
|
||||
RequireBasicAuth bool //Set to true to request basic auth before proxy
|
||||
BasicAuthCredentials []*v307BasicAuthCredentials //Basic auth credentials
|
||||
BasicAuthExceptionRules []*v307BasicAuthExceptionRule //Path to exclude in a basic auth enabled proxy target
|
||||
|
||||
// Rate Limiting
|
||||
RequireRateLimit bool
|
||||
RateLimit int64 // Rate limit in requests per second
|
||||
|
||||
//Access Control
|
||||
AccessFilterUUID string //Access filter ID
|
||||
|
||||
//Fallback routing logic (Special Rule Sets Only)
|
||||
DefaultSiteOption int //Fallback routing logic options
|
||||
DefaultSiteValue string //Fallback routing target, optional
|
||||
}
|
132
src/mod/update/v308/v308.go
Normal file
132
src/mod/update/v308/v308.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package v308
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
/*
|
||||
|
||||
v3.0.7 update to v3.0.8
|
||||
|
||||
This update function
|
||||
*/
|
||||
|
||||
// Update proxy config files from v3.0.7 to v3.0.8
|
||||
func UpdateFrom307To308() error {
|
||||
|
||||
//Load the configs
|
||||
oldConfigFiles, err := filepath.Glob("./conf/proxy/*.config")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
//Backup all the files
|
||||
err = os.MkdirAll("./conf/proxy.old/", 0775)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, oldConfigFile := range oldConfigFiles {
|
||||
// Extract the file name from the path
|
||||
fileName := filepath.Base(oldConfigFile)
|
||||
// Construct the backup file path
|
||||
backupFile := filepath.Join("./conf/proxy.old/", fileName)
|
||||
|
||||
// Copy the file to the backup directory
|
||||
err := copyFile(oldConfigFile, backupFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
//read the config into the old struct
|
||||
for _, oldConfigFile := range oldConfigFiles {
|
||||
configContent, err := os.ReadFile(oldConfigFile)
|
||||
if err != nil {
|
||||
log.Println("Unable to read config file "+filepath.Base(oldConfigFile), err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
thisOldConfigStruct := v307ProxyEndpoint{}
|
||||
err = json.Unmarshal(configContent, &thisOldConfigStruct)
|
||||
if err != nil {
|
||||
log.Println("Unable to parse file "+filepath.Base(oldConfigFile), err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
//Convert the old config to new config
|
||||
newProxyStructure := convertV307ToV308(thisOldConfigStruct)
|
||||
js, _ := json.MarshalIndent(newProxyStructure, "", " ")
|
||||
err = os.WriteFile(oldConfigFile, js, 0775)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func convertV307ToV308(old v307ProxyEndpoint) v308ProxyEndpoint {
|
||||
// Create a new v308ProxyEndpoint instance
|
||||
|
||||
matchingDomainsSlice := old.MatchingDomainAlias
|
||||
if matchingDomainsSlice == nil {
|
||||
matchingDomainsSlice = []string{}
|
||||
}
|
||||
|
||||
newEndpoint := v308ProxyEndpoint{
|
||||
ProxyType: old.ProxyType,
|
||||
RootOrMatchingDomain: old.RootOrMatchingDomain,
|
||||
MatchingDomainAlias: matchingDomainsSlice,
|
||||
ActiveOrigins: []*v308Upstream{{ // Mapping Domain field to v308Upstream struct
|
||||
OriginIpOrDomain: old.Domain,
|
||||
RequireTLS: old.RequireTLS,
|
||||
SkipCertValidations: old.SkipCertValidations,
|
||||
SkipWebSocketOriginCheck: old.SkipWebSocketOriginCheck,
|
||||
Weight: 1,
|
||||
MaxConn: 0,
|
||||
}},
|
||||
InactiveOrigins: []*v308Upstream{},
|
||||
UseStickySession: false,
|
||||
Disabled: old.Disabled,
|
||||
BypassGlobalTLS: old.BypassGlobalTLS,
|
||||
VirtualDirectories: old.VirtualDirectories,
|
||||
UserDefinedHeaders: old.UserDefinedHeaders,
|
||||
HSTSMaxAge: old.HSTSMaxAge,
|
||||
EnablePermissionPolicyHeader: old.EnablePermissionPolicyHeader,
|
||||
PermissionPolicy: old.PermissionPolicy,
|
||||
RequireBasicAuth: old.RequireBasicAuth,
|
||||
BasicAuthCredentials: old.BasicAuthCredentials,
|
||||
BasicAuthExceptionRules: old.BasicAuthExceptionRules,
|
||||
RequireRateLimit: old.RequireRateLimit,
|
||||
RateLimit: old.RateLimit,
|
||||
AccessFilterUUID: old.AccessFilterUUID,
|
||||
DefaultSiteOption: old.DefaultSiteOption,
|
||||
DefaultSiteValue: old.DefaultSiteValue,
|
||||
}
|
||||
|
||||
return newEndpoint
|
||||
}
|
||||
|
||||
// Helper function to copy files
|
||||
func copyFile(src, dst string) error {
|
||||
sourceFile, err := os.Open(src)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer sourceFile.Close()
|
||||
|
||||
destinationFile, err := os.Create(dst)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destinationFile.Close()
|
||||
|
||||
_, err = io.Copy(destinationFile, sourceFile)
|
||||
return err
|
||||
}
|
Reference in New Issue
Block a user