From eccabc05880d6288ac93e8a51fd774e03b995993 Mon Sep 17 00:00:00 2001 From: yusing Date: Mon, 3 Feb 2025 08:55:55 +0800 Subject: [PATCH 01/13] remove incorrectly added pnpn lockfile --- pnpm-lock.yaml | 391 ------------------------------------------------- 1 file changed, 391 deletions(-) delete mode 100644 pnpm-lock.yaml diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml deleted file mode 100644 index 46a851a..0000000 --- a/pnpm-lock.yaml +++ /dev/null @@ -1,391 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - dependencies: - typescript-json-schema: - specifier: ^0.65.1 - version: 0.65.1 - -packages: - - '@cspotcode/source-map-support@0.8.1': - resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} - engines: {node: '>=12'} - - '@jridgewell/resolve-uri@3.1.2': - resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} - engines: {node: '>=6.0.0'} - - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.9': - resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} - - '@tsconfig/node12@1.0.11': - resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} - - '@tsconfig/node14@1.0.3': - resolution: {integrity: sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==} - - '@tsconfig/node16@1.0.4': - resolution: {integrity: sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==} - - '@types/json-schema@7.0.15': - resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - - '@types/node@18.19.74': - resolution: {integrity: sha512-HMwEkkifei3L605gFdV+/UwtpxP6JSzM+xFk2Ia6DNFSwSVBRh9qp5Tgf4lNFOMfPVuU0WnkcWpXZpgn5ufO4A==} - - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} - engines: {node: '>=0.4.0'} - - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} - engines: {node: '>=0.4.0'} - hasBin: true - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - arg@4.1.3: - resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - create-require@1.1.1: - resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} - - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} - engines: {node: '>=0.3.1'} - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - glob@7.2.3: - resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported - - inflight@1.0.6: - resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} - deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - make-error@1.3.6: - resolution: {integrity: sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - path-equal@1.2.5: - resolution: {integrity: sha512-i73IctDr3F2W+bsOWDyyVm/lqsXO47aY9nsFZUjTT/aljSbkxHxxCoyZ9UUrM8jK0JVod+An+rl48RCsvWM+9g==} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - safe-stable-stringify@2.5.0: - resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} - engines: {node: '>=10'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - ts-node@10.9.2: - resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} - hasBin: true - peerDependencies: - '@swc/core': '>=1.2.50' - '@swc/wasm': '>=1.2.50' - '@types/node': '*' - typescript: '>=2.7' - peerDependenciesMeta: - '@swc/core': - optional: true - '@swc/wasm': - optional: true - - typescript-json-schema@0.65.1: - resolution: {integrity: sha512-tuGH7ff2jPaUYi6as3lHyHcKpSmXIqN7/mu50x3HlYn0EHzLpmt3nplZ7EuhUkO0eqDRc9GqWNkfjgBPIS9kxg==} - hasBin: true - - typescript@5.5.4: - resolution: {integrity: sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@5.26.5: - resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - - v8-compile-cache-lib@3.0.1: - resolution: {integrity: sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==} - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yn@3.1.1: - resolution: {integrity: sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==} - engines: {node: '>=6'} - -snapshots: - - '@cspotcode/source-map-support@0.8.1': - dependencies: - '@jridgewell/trace-mapping': 0.3.9 - - '@jridgewell/resolve-uri@3.1.2': {} - - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.9': - dependencies: - '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 - - '@tsconfig/node10@1.0.11': {} - - '@tsconfig/node12@1.0.11': {} - - '@tsconfig/node14@1.0.3': {} - - '@tsconfig/node16@1.0.4': {} - - '@types/json-schema@7.0.15': {} - - '@types/node@18.19.74': - dependencies: - undici-types: 5.26.5 - - acorn-walk@8.3.4: - dependencies: - acorn: 8.14.0 - - acorn@8.14.0: {} - - ansi-regex@5.0.1: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - arg@4.1.3: {} - - balanced-match@1.0.2: {} - - brace-expansion@1.1.11: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - concat-map@0.0.1: {} - - create-require@1.1.1: {} - - diff@4.0.2: {} - - emoji-regex@8.0.0: {} - - escalade@3.2.0: {} - - fs.realpath@1.0.0: {} - - get-caller-file@2.0.5: {} - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - is-fullwidth-code-point@3.0.0: {} - - make-error@1.3.6: {} - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.11 - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - path-equal@1.2.5: {} - - path-is-absolute@1.0.1: {} - - require-directory@2.1.1: {} - - safe-stable-stringify@2.5.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - ts-node@10.9.2(@types/node@18.19.74)(typescript@5.5.4): - dependencies: - '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 - '@tsconfig/node12': 1.0.11 - '@tsconfig/node14': 1.0.3 - '@tsconfig/node16': 1.0.4 - '@types/node': 18.19.74 - acorn: 8.14.0 - acorn-walk: 8.3.4 - arg: 4.1.3 - create-require: 1.1.1 - diff: 4.0.2 - make-error: 1.3.6 - typescript: 5.5.4 - v8-compile-cache-lib: 3.0.1 - yn: 3.1.1 - - typescript-json-schema@0.65.1: - dependencies: - '@types/json-schema': 7.0.15 - '@types/node': 18.19.74 - glob: 7.2.3 - path-equal: 1.2.5 - safe-stable-stringify: 2.5.0 - ts-node: 10.9.2(@types/node@18.19.74)(typescript@5.5.4) - typescript: 5.5.4 - yargs: 17.7.2 - transitivePeerDependencies: - - '@swc/core' - - '@swc/wasm' - - typescript@5.5.4: {} - - undici-types@5.26.5: {} - - v8-compile-cache-lib@3.0.1: {} - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrappy@1.0.2: {} - - y18n@5.0.8: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yn@3.1.1: {} From 4d7422dd90acd6207cf3a333b92aa9e549a94138 Mon Sep 17 00:00:00 2001 From: yusing Date: Tue, 4 Feb 2025 02:34:23 +0800 Subject: [PATCH 02/13] adjusted and simplified default config and compose.yml --- compose.example.yml | 12 ++--- config.example.yml | 117 ++++++++++++-------------------------------- 2 files changed, 37 insertions(+), 92 deletions(-) diff --git a/compose.example.yml b/compose.example.yml index b30d0f5..fd82f65 100755 --- a/compose.example.yml +++ b/compose.example.yml @@ -28,15 +28,13 @@ services: volumes: - /var/run/docker.sock:/var/run/docker.sock - ./config:/app/config + - ./logs:/app/logs - ./error_pages:/app/error_pages - # (Optional) choose one of below to enable https - # 1. use existing certificate + # To use autocert, certs will be stored in "./certs". + # You can also use a docker volume to store it + - ./certs:/app/certs + # remove "./certs:/app/certs" and uncomment below to use existing certificate # - /path/to/certs/cert.crt:/app/certs/cert.crt # - /path/to/certs/priv.key:/app/certs/priv.key - - # 2. use autocert, certs will be stored in ./certs - # you can also use a docker volume to store it - - # - ./certs:/app/certs diff --git a/config.example.yml b/config.example.yml index 062967d..462413f 100644 --- a/config.example.yml +++ b/config.example.yml @@ -1,78 +1,42 @@ # Autocert (choose one below and uncomment to enable) # # 1. use existing cert -# + # autocert: # provider: local -# -# cert_path: certs/cert.crt # optional, uncomment only if you need to change it -# key_path: certs/priv.key # optional, uncomment only if you need to change it -# + # 2. cloudflare -# # autocert: # provider: cloudflare -# email: abc@gmail.com # ACME Email -# domains: # a list of domains for cert registration -# - "*.y.z" # remember to use double quotes to surround wildcard domain +# email: abc@gmail.com # ACME Email +# domains: # a list of domains for cert registration +# - "*.domain.com" +# - "domain.com" # options: -# auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token -# -# 3. other providers, check docs/dns_providers.md for more +# auth_token: c1234565789-abcdefghijklmnopqrst # your zone API token + +# 3. other providers, see https://github.com/yusing/go-proxy/wiki/Supported-DNS%E2%80%9001-Providers#supported-dns-01-providers entrypoint: - middlewares: - # this part blocks all non-LAN HTTP traffic - # remove if you don't want this - - use: CIDRWhitelist - allow: - - "127.0.0.1" - - "10.0.0.0/8" - - "172.16.0.0/12" - - "192.168.0.0/16" - status: 403 - message: "Forbidden" - # end of CIDRWhitelist + # Below define an example of middleware config + # 1. block non local IP connections + # 2. redirect HTTP to HTTPS + # + # middlewares: + # - use: CIDRWhitelist + # allow: + # - "127.0.0.1" + # - "10.0.0.0/8" + # - "172.16.0.0/12" + # - "192.168.0.0/16" + # status: 403 + # message: "Forbidden" + # - use: RedirectHTTP - # this part redirects HTTP to HTTPS - # remove if you don't want this - - use: RedirectHTTP - - # access_log: - # buffer_size: 1024 - # path: /var/log/example.log - # filters: - # status_codes: - # values: - # - 200-299 - # - 101 - # method: - # values: - # - GET - # host: - # values: - # - example.y.z - # headers: - # negative: true - # values: - # - foo=bar - # - baz - # cidr: - # values: - # - 192.168.10.0/24 - # fields: - # headers: - # default: keep - # config: - # foo: redact - # query: - # default: drop - # config: - # foo: keep - # cookies: - # default: redact - # config: - # foo: keep + # below enables access log + access_log: + format: combined + path: /app/logs/entrypoint.log providers: # include files are standalone yaml files under `config/` directory @@ -84,6 +48,7 @@ providers: docker: # $DOCKER_HOST implies environment variable `DOCKER_HOST` or unix:///var/run/docker.sock by default local: $DOCKER_HOST + # explicit only mode # only containers with explicit aliases will be proxied # add "!" after provider name to enable explicit only mode @@ -106,28 +71,10 @@ providers: # - name: discord # provider: webhook # url: https://discord.com/api/webhooks/... - # template: discord - # # payload: | # discord template implies the following - # # { - # # "embeds": [ - # # { - # # "title": $title, - # # "fields": $fields, - # # "color": "$color" - # # } - # # ] - # # } -# if match_domains not defined -# any host = alias+[any domain] will match -# i.e. https://app1.y.z will match alias app1 for any domain y.z -# but https://app1.node1.y.z will only match alias "app.node1" -# -# if match_domains defined -# only host = alias+[one of match_domains] will match -# i.e. match_domains = [node1.my.app, my.site] -# https://app1.my.app, https://app1.my.net, etc. will not match even if app1 exists -# only https://*.node1.my.app and https://*.my.site will match -# + # template: discord # this means use payload template from internal/notif/templates/discord.json + +# Check https://github.com/yusing/go-proxy/wiki/Certificates-and-domain-matching#domain-matching +# for explaination of `match_domains` # # match_domains: # - my.site From 7c56c88dd4c363d159d5aae02857a7aee385a438 Mon Sep 17 00:00:00 2001 From: yusing Date: Tue, 4 Feb 2025 07:02:28 +0800 Subject: [PATCH 03/13] fix server not being restarted after config reload --- internal/config/config.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/config/config.go b/internal/config/config.go index 8ea9ef2..c0fe6bb 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -125,7 +125,7 @@ func Reload() E.Error { // -> replace config -> start new subtasks instance.task.Finish("config changed") instance = newCfg - instance.Start() + instance.Start(StartAllServers) return nil } @@ -184,6 +184,8 @@ type StartServersOptions struct { Proxy, API bool } +var StartAllServers = &StartServersOptions{true, true} + func (cfg *Config) StartServers(opts ...*StartServersOptions) { if len(opts) == 0 { opts = append(opts, &StartServersOptions{}) From 1871ef3d386a8f0d3619147c2d2a69933a5b488c Mon Sep 17 00:00:00 2001 From: yusing Date: Tue, 4 Feb 2025 06:42:21 +0800 Subject: [PATCH 04/13] clearer error message when config reload failed --- internal/config/config.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/internal/config/config.go b/internal/config/config.go index c0fe6bb..c086156 100644 --- a/internal/config/config.go +++ b/internal/config/config.go @@ -103,7 +103,6 @@ func OnConfigChange(ev []events.Event) { } if err := Reload(); err != nil { - logging.Warn().Msg("using last config") // recovered in event queue panic(err) } @@ -118,7 +117,7 @@ func Reload() E.Error { err := newCfg.load() if err != nil { newCfg.task.Finish(err) - return err + return E.New("using last config").With(err) } // cancel all current subtasks -> wait From f997423fd7332f5e37f45347b7cdc274e9f3d99c Mon Sep 17 00:00:00 2001 From: yusing Date: Tue, 4 Feb 2025 05:47:46 +0800 Subject: [PATCH 05/13] fix error formatting --- internal/error/nested_error.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/internal/error/nested_error.go b/internal/error/nested_error.go index e0a5851..e666a4f 100644 --- a/internal/error/nested_error.go +++ b/internal/error/nested_error.go @@ -75,8 +75,10 @@ func (err *nestedError) Error() string { lines := make([]string, 0, 1+len(err.Extras)) if err.Err != nil { lines = append(lines, makeLine(err.Err.Error(), 0)) + lines = append(lines, makeLines(err.Extras, 1)...) + } else { + lines = append(lines, makeLines(err.Extras, 0)...) } - lines = append(lines, makeLines(err.Extras, 1)...) return strutils.JoinLines(lines) } From 043bbd7a1135f76ae3f0b55c5c46b36d62e55f39 Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 6 Feb 2025 00:56:11 +0800 Subject: [PATCH 06/13] readme and docker compose example amendment --- README.md | 39 ++++++++++++++++++++------------------- README_CHT.md | 40 ++++++++++++++++++++-------------------- compose.example.yml | 10 +++++----- 3 files changed, 45 insertions(+), 44 deletions(-) diff --git a/README.md b/README.md index 1238e7a..9f71aba 100755 --- a/README.md +++ b/README.md @@ -1,19 +1,25 @@ +
+ # GoDoxy [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy) +![GitHub last commit](https://img.shields.io/github/last-commit/yusing/go-proxy) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy) +[![](https://dcbadge.limes.pink/api/server/umReR62nRd?style=flat)](https://discord.gg/umReR62nRd) + +A lightweight, simple, and [performant](https://github.com/yusing/go-proxy/wiki/Benchmarks) reverse proxy with WebUI. + +For full documentation, check out **[Wiki](https://github.com/yusing/go-proxy/wiki)** + +**EN** | 中文 + + -[繁體中文文檔請看此](README_CHT.md) + -A lightweight, easy-to-use, and [performant](https://github.com/yusing/go-proxy/wiki/Benchmarks) reverse proxy with a Web UI and dashboard. - -![Screenshot](https://github.com/user-attachments/assets/4bb371f4-6e4c-425c-89b2-b9e962bdd46f) - -_Join our [Discord](https://discord.gg/umReR62nRd) for help and discussions_ +
## Table of content @@ -22,9 +28,8 @@ _Join our [Discord](https://discord.gg/umReR62nRd) for help and discussions_ - [GoDoxy](#godoxy) - [Table of content](#table-of-content) - [Key Features](#key-features) - - [Getting Started](#getting-started) - - [Prerequisites](#prerequisites) - - [Setup](#setup) + - [Prerequisites](#prerequisites) + - [Setup](#setup) - [Manual Setup](#manual-setup) - [Folder structrue](#folder-structrue) - [Use JSON Schema in VSCode](#use-json-schema-in-vscode) @@ -53,18 +58,14 @@ _Join our [Discord](https://discord.gg/umReR62nRd) for help and discussions_ [🔼Back to top](#table-of-content) -## Getting Started - -For full documentation, **[See Wiki](https://github.com/yusing/go-proxy/wiki)** - -### Prerequisites +## Prerequisites Setup DNS Records point to machine which runs `GoDoxy`, e.g. - A Record: `*.y.z` -> `10.0.10.1` - AAAA Record: `*.y.z` -> `::ffff:a00:a01` -### Setup +## Setup 1. Pull the latest docker images @@ -98,7 +99,7 @@ Setup DNS Records point to machine which runs `GoDoxy`, e.g. 5. Start the container `docker compose up -d` -6. You may now do some extra configuration on WebUI `https://gp.y.z` +6. You may now do some extra configuration on WebUI `https://godoxy.domain.com` [🔼Back to top](#table-of-content) diff --git a/README_CHT.md b/README_CHT.md index 0edccd5..00568ba 100644 --- a/README_CHT.md +++ b/README_CHT.md @@ -1,19 +1,25 @@ +
+ # GoDoxy [![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=alert_status)](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy) +![GitHub last commit](https://img.shields.io/github/last-commit/yusing/go-proxy) [![Lines of Code](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=ncloc)](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy) -[![Security Rating](https://sonarcloud.io/api/project_badges/measure?project=yusing_go-proxy&metric=security_rating)](https://sonarcloud.io/summary/new_code?id=yusing_go-proxy) +[![](https://dcbadge.limes.pink/api/server/umReR62nRd?style=flat)](https://discord.gg/umReR62nRd) + +輕量、易用、 [高效能](https://github.com/yusing/go-proxy/wiki/Benchmarks),且帶有主頁和配置面板的反向代理 + +完整文檔請查閱 **[Wiki](https://github.com/yusing/go-proxy/wiki)**(暫未有中文翻譯) + + -[English Documentation](README.md) +EN | **中文** -一個輕量級、易於使用且[高效能](https://github.com/yusing/go-proxy/wiki/Benchmarks)的反向代理,具有網頁介面和儀表板。 + -![截圖](screenshots/webui.png) - -_加入我們的 [Discord](https://discord.gg/umReR62nRd) 獲取幫助和討論_ +
## 目錄 @@ -22,9 +28,8 @@ _加入我們的 [Discord](https://discord.gg/umReR62nRd) 獲取幫助和討論_ - [GoDoxy](#godoxy) - [目錄](#目錄) - [主要特點](#主要特點) - - [入門指南](#入門指南) - - [前置需求](#前置需求) - - [安裝](#安裝) + - [前置需求](#前置需求) + - [安裝](#安裝) - [手動安裝](#手動安裝) - [資料夾結構](#資料夾結構) - [在 VSCode 中使用 JSON Schema](#在-vscode-中使用-json-schema) @@ -43,6 +48,7 @@ _加入我們的 [Discord](https://discord.gg/umReR62nRd) 獲取幫助和討論_ - 容器狀態/配置文件變更時自動熱重載 - **閒置休眠**:在閒置時停止容器,有流量時喚醒(_可選,參見[截圖](#閒置休眠)_) - HTTP(s) 反向代理 +- OpenID Connect 支持 - [HTTP 中介軟體支援](https://github.com/yusing/go-proxy/wiki/Middlewares) - [自訂錯誤頁面支援](https://github.com/yusing/go-proxy/wiki/Middlewares#custom-error-pages) - TCP 和 UDP 埠轉發 @@ -52,18 +58,14 @@ _加入我們的 [Discord](https://discord.gg/umReR62nRd) 獲取幫助和討論_ [🔼回到頂部](#目錄) -## 入門指南 - -完整文檔請參見 **[Wiki](https://github.com/yusing/go-proxy/wiki)** - -### 前置需求 +## 前置需求 設置 DNS 記錄指向運行 `GoDoxy` 的機器,例如: - A 記錄:`*.y.z` -> `10.0.10.1` - AAAA 記錄:`*.y.z` -> `::ffff:a00:a01` -### 安裝 +## 安裝 1. 拉取最新的 Docker 映像 @@ -97,9 +99,7 @@ _加入我們的 [Discord](https://discord.gg/umReR62nRd) 獲取幫助和討論_ 5. 啟動容器 `docker compose up -d` -6. 現在您可以進行額外的配置 - - 使用文字編輯器(如 Visual Studio Code) - - 通過網頁介面 `https://gp.y.z` +6. 大功告成!可前往WebUI `https://gp.domain.com` 進行額外的配置 [🔼回到頂部](#目錄) diff --git a/compose.example.yml b/compose.example.yml index fd82f65..2f3ac17 100755 --- a/compose.example.yml +++ b/compose.example.yml @@ -10,11 +10,11 @@ services: - app # modify below to fit your needs labels: - proxy.aliases: gp - proxy.#1.port: 3000 - # proxy.#1.middlewares.cidr_whitelist.status: 403 - # proxy.#1.middlewares.cidr_whitelist.message: IP not allowed - # proxy.#1.middlewares.cidr_whitelist.allow: | + proxy.aliases: godoxy + proxy.godoxy.port: 3000 + # proxy.godoxy.middlewares.cidr_whitelist.status: 403 + # proxy.godoxy.middlewares.cidr_whitelist.message: IP not allowed + # proxy.godoxy.middlewares.cidr_whitelist.allow: | # - 127.0.0.1 # - 10.0.0.0/8 # - 192.168.0.0/16 From 688f38943d2428605e4a518b90f992c98218b90b Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 6 Feb 2025 01:58:45 +0800 Subject: [PATCH 07/13] fix single line yaml list treated as comma seperated list --- internal/utils/serialization.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/utils/serialization.go b/internal/utils/serialization.go index 37922f4..548ee5f 100644 --- a/internal/utils/serialization.go +++ b/internal/utils/serialization.go @@ -428,7 +428,7 @@ func ConvertString(src string, dst reflect.Value) (convertible bool, convErr E.E src = strings.TrimSpace(src) isMultiline := strings.ContainsRune(src, '\n') // one liner is comma separated list - if !isMultiline { + if !isMultiline && src[0] != '-' { values := strutils.CommaSeperatedList(src) dst.Set(reflect.MakeSlice(dst.Type(), len(values), len(values))) errs := E.NewBuilder("invalid slice values") From 2cd1f22e68978f218ffcdf55cb6ef64e89a5d17b Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 6 Feb 2025 02:33:30 +0800 Subject: [PATCH 08/13] add test for the previous commit --- internal/utils/serialization_test.go | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/internal/utils/serialization_test.go b/internal/utils/serialization_test.go index 23696e9..24ac451 100644 --- a/internal/utils/serialization_test.go +++ b/internal/utils/serialization_test.go @@ -186,6 +186,13 @@ func TestStringToSlice(t *testing.T) { ExpectNoError(t, err) ExpectDeepEqual(t, dst, []string{"a", "b", "c"}) }) + t.Run("single-line-yaml-like", func(t *testing.T) { + dst := make([]string, 0) + convertible, err := ConvertString("- a", reflect.ValueOf(&dst)) + ExpectTrue(t, convertible) + ExpectNoError(t, err) + ExpectDeepEqual(t, dst, []string{"a"}) + }) } func BenchmarkStringToSlice(b *testing.B) { From 1549b56866ecbcf78edfd66383de009ecb16530f Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 6 Feb 2025 03:12:34 +0800 Subject: [PATCH 09/13] README: move auth docs to wiki --- README.md | 22 +++------------------- README_CHT.md | 22 +++------------------- 2 files changed, 6 insertions(+), 38 deletions(-) diff --git a/README.md b/README.md index 9f71aba..d19cad7 100755 --- a/README.md +++ b/README.md @@ -79,27 +79,11 @@ Setup DNS Records point to machine which runs `GoDoxy`, e.g. docker run --rm -v .:/setup ghcr.io/yusing/go-proxy /app/godoxy setup ``` -3. _(Optional)_ setup WebUI login (skip if you use OIDC) +3. _(Optional)_ setup `docker-socket-proxy` other docker nodes (see [Multi docker nodes setup](https://github.com/yusing/go-proxy/wiki/Configurations#multi-docker-nodes-setup)) then add them inside `config.yml` - - set random JWT secret +4. Start the container `docker compose up -d` - ```shell - sed -i "s|API_JWT_SECRET=.*|API_JWT_SECRET=$(openssl rand -base64 32)|g" .env - ``` - - - change username and password for WebUI authentication - ```shell - USERNAME=admin - PASSWORD=some-password - sed -i "s|API_USERNAME=.*|API_USERNAME=${USERNAME}|g" .env - sed -i "s|API_PASSWORD=.*|API_PASSWORD=${PASSWORD}|g" .env - ``` - -4. _(Optional)_ setup `docker-socket-proxy` other docker nodes (see [Multi docker nodes setup](https://github.com/yusing/go-proxy/wiki/Configurations#multi-docker-nodes-setup)) then add them inside `config.yml` - -5. Start the container `docker compose up -d` - -6. You may now do some extra configuration on WebUI `https://godoxy.domain.com` +5. You may now do some extra configuration on WebUI `https://godoxy.domain.com` [🔼Back to top](#table-of-content) diff --git a/README_CHT.md b/README_CHT.md index 00568ba..ba97ae0 100644 --- a/README_CHT.md +++ b/README_CHT.md @@ -79,27 +79,11 @@ docker run --rm -v .:/setup ghcr.io/yusing/go-proxy /app/godoxy setup ``` -3. _(可選)_ 設置網頁介面登入 +3. _(可選)_ 設置其他 Docker 節點的 `docker-socket-proxy`(參見 [多 Docker 節點設置](https://github.com/yusing/go-proxy/wiki/Configurations#multi-docker-nodes-setup)),然後在 `config.yml` 中添加它們 - - 設置隨機 JWT 密鑰 +4. 啟動容器 `docker compose up -d` - ```shell - sed -i "s|API_JWT_SECRET=.*|API_JWT_SECRET=$(openssl rand -base64 32)|g" .env - ``` - - - 更改網頁介面認證的使用者名稱和密碼 - ```shell - USERNAME=admin - PASSWORD=some-password - sed -i "s|API_USERNAME=.*|API_USERNAME=${USERNAME}|g" .env - sed -i "s|API_PASSWORD=.*|API_PASSWORD=${PASSWORD}|g" .env - ``` - -4. _(可選)_ 設置其他 Docker 節點的 `docker-socket-proxy`(參見 [多 Docker 節點設置](https://github.com/yusing/go-proxy/wiki/Configurations#multi-docker-nodes-setup)),然後在 `config.yml` 中添加它們 - -5. 啟動容器 `docker compose up -d` - -6. 大功告成!可前往WebUI `https://gp.domain.com` 進行額外的配置 +5. 大功告成!可前往WebUI `https://gp.domain.com` 進行額外的配置 [🔼回到頂部](#目錄) From c9ddf3d1658931370e79b948609462498bb4f40c Mon Sep 17 00:00:00 2001 From: Yuzerion Date: Thu, 6 Feb 2025 04:44:19 +0800 Subject: [PATCH 10/13] Create FUNDING.yml --- .github/FUNDING.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..a0814e6 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1,15 @@ +# These are supported funding model platforms + +github: yusing # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] +patreon: # Replace with a single Patreon username +open_collective: # Replace with a single Open Collective username +ko_fi: # Replace with a single Ko-fi username +tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel +community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry +liberapay: # Replace with a single Liberapay username +issuehunt: # Replace with a single IssueHunt username +lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry +polar: # Replace with a single Polar username +buy_me_a_coffee: yusingwysq # Replace with a single Buy Me a Coffee username +thanks_dev: # Replace with a single thanks.dev username +custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] From 693bf688643b8fb012a97fc4c75e7fcbdd18811b Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 6 Feb 2025 05:12:49 +0800 Subject: [PATCH 11/13] rules: updated help message, make values optional, fixes tests --- internal/net/http/methods.go | 28 ++++++------- internal/route/rules/errors.go | 7 ++-- internal/route/rules/help.go | 7 ++-- internal/route/rules/on.go | 68 ++++++++++++++++++++++++-------- internal/route/rules/on_test.go | 55 +++++++++++++++++++++----- internal/route/rules/validate.go | 12 ++++++ 6 files changed, 130 insertions(+), 47 deletions(-) diff --git a/internal/net/http/methods.go b/internal/net/http/methods.go index a46923d..caca564 100644 --- a/internal/net/http/methods.go +++ b/internal/net/http/methods.go @@ -2,19 +2,19 @@ package http import "net/http" -var validMethods = map[string]struct{}{ - http.MethodGet: {}, - http.MethodHead: {}, - http.MethodPost: {}, - http.MethodPut: {}, - http.MethodPatch: {}, - http.MethodDelete: {}, - http.MethodConnect: {}, - http.MethodOptions: {}, - http.MethodTrace: {}, -} - func IsMethodValid(method string) bool { - _, ok := validMethods[method] - return ok + switch method { + case http.MethodGet, + http.MethodHead, + http.MethodPost, + http.MethodPut, + http.MethodPatch, + http.MethodDelete, + http.MethodConnect, + http.MethodOptions, + http.MethodTrace: + return true + default: + return false + } } diff --git a/internal/route/rules/errors.go b/internal/route/rules/errors.go index 6a9e852..4bd3151 100644 --- a/internal/route/rules/errors.go +++ b/internal/route/rules/errors.go @@ -11,7 +11,8 @@ var ( ErrInvalidCommandSequence = E.New("invalid command sequence") ErrInvalidSetTarget = E.New("invalid `rule.set` target") - ErrExpectNoArg = ErrInvalidArguments.Withf("expect no arg") - ErrExpectOneArg = ErrInvalidArguments.Withf("expect 1 arg") - ErrExpectTwoArgs = ErrInvalidArguments.Withf("expect 2 args") + ErrExpectNoArg = E.New("expect no arg") + ErrExpectOneArg = E.New("expect 1 arg") + ErrExpectTwoArgs = E.New("expect 2 args") + ErrExpectKVOptionalV = E.New("expect 'key' or 'key value'") ) diff --git a/internal/route/rules/help.go b/internal/route/rules/help.go index cdff5c9..9b222b7 100644 --- a/internal/route/rules/help.go +++ b/internal/route/rules/help.go @@ -20,9 +20,8 @@ func (h *Help) String() string { sb.WriteString(h.command) sb.WriteString(" ") for arg := range h.args { - sb.WriteRune('<') - sb.WriteString(arg) - sb.WriteString("> ") + sb.WriteString(strings.ToUpper(arg)) + sb.WriteRune(' ') } if h.description != "" { sb.WriteString("\n\t") @@ -32,7 +31,7 @@ func (h *Help) String() string { sb.WriteRune('\n') for arg, desc := range h.args { sb.WriteRune('\t') - sb.WriteString(arg) + sb.WriteString(strings.ToUpper(arg)) sb.WriteString(": ") sb.WriteString(desc) sb.WriteRune('\n') diff --git a/internal/route/rules/on.go b/internal/route/rules/on.go index f2c64c8..e0b1837 100644 --- a/internal/route/rules/on.go +++ b/internal/route/rules/on.go @@ -34,15 +34,25 @@ var checkers = map[string]struct { help: Help{ command: OnHeader, args: map[string]string{ - "key": "the header key", - "value": "the header value", + "key": "the header key", + "[value]": "the header value", }, }, - validate: toStrTuple, + validate: toKVOptionalV, builder: func(args any) CheckFunc { k, v := args.(*StrTuple).Unpack() + if v == "" { + return func(cached Cache, r *http.Request) bool { + return len(r.Header[k]) > 0 + } + } return func(cached Cache, r *http.Request) bool { - return r.Header.Get(k) == v + for _, vv := range r.Header[k] { + if v == vv { + return true + } + } + return false } }, }, @@ -50,13 +60,18 @@ var checkers = map[string]struct { help: Help{ command: OnQuery, args: map[string]string{ - "key": "the query key", - "value": "the query value", + "key": "the query key", + "[value]": "the query value", }, }, - validate: toStrTuple, + validate: toKVOptionalV, builder: func(args any) CheckFunc { k, v := args.(*StrTuple).Unpack() + if v == "" { + return func(cached Cache, r *http.Request) bool { + return len(cached.GetQueries(r)[k]) > 0 + } + } return func(cached Cache, r *http.Request) bool { queries := cached.GetQueries(r)[k] for _, query := range queries { @@ -72,13 +87,24 @@ var checkers = map[string]struct { help: Help{ command: OnCookie, args: map[string]string{ - "key": "the cookie key", - "value": "the cookie value", + "key": "the cookie key", + "[value]": "the cookie value", }, }, - validate: toStrTuple, + validate: toKVOptionalV, builder: func(args any) CheckFunc { k, v := args.(*StrTuple).Unpack() + if v == "" { + return func(cached Cache, r *http.Request) bool { + cookies := cached.GetCookies(r) + for _, cookie := range cookies { + if cookie.Name == k { + return true + } + } + return false + } + } return func(cached Cache, r *http.Request) bool { cookies := cached.GetCookies(r) for _, cookie := range cookies { @@ -95,13 +121,18 @@ var checkers = map[string]struct { help: Help{ command: OnForm, args: map[string]string{ - "key": "the form key", - "value": "the form value", + "key": "the form key", + "[value]": "the form value", }, }, - validate: toStrTuple, + validate: toKVOptionalV, builder: func(args any) CheckFunc { k, v := args.(*StrTuple).Unpack() + if v == "" { + return func(cached Cache, r *http.Request) bool { + return r.FormValue(k) != "" + } + } return func(cached Cache, r *http.Request) bool { return r.FormValue(k) == v } @@ -111,13 +142,18 @@ var checkers = map[string]struct { help: Help{ command: OnPostForm, args: map[string]string{ - "key": "the form key", - "value": "the form value", + "key": "the form key", + "[value]": "the form value", }, }, - validate: toStrTuple, + validate: toKVOptionalV, builder: func(args any) CheckFunc { k, v := args.(*StrTuple).Unpack() + if v == "" { + return func(cached Cache, r *http.Request) bool { + return r.PostFormValue(k) != "" + } + } return func(cached Cache, r *http.Request) bool { return r.PostFormValue(k) == v } diff --git a/internal/route/rules/on_test.go b/internal/route/rules/on_test.go index acbe384..c5bdc8e 100644 --- a/internal/route/rules/on_test.go +++ b/internal/route/rules/on_test.go @@ -15,25 +15,50 @@ func TestParseOn(t *testing.T) { }{ // header { - name: "header_valid", + name: "header_valid_kv", input: "header Connection Upgrade", wantErr: nil, }, { - name: "header_invalid", + name: "header_valid_k", input: "header Connection", - wantErr: ErrInvalidArguments, + wantErr: nil, + }, + { + name: "header_missing_arg", + input: "header", + wantErr: ErrExpectKVOptionalV, }, // query { - name: "query_valid", + name: "query_valid_kv", input: "query key value", wantErr: nil, }, { - name: "query_invalid", + name: "query_valid_k", input: "query key", - wantErr: ErrInvalidArguments, + wantErr: nil, + }, + { + name: "query_missing_arg", + input: "query", + wantErr: ErrExpectKVOptionalV, + }, + { + name: "cookie_valid_kv", + input: "cookie key value", + wantErr: nil, + }, + { + name: "cookie_valid_k", + input: "cookie key", + wantErr: nil, + }, + { + name: "cookie_missing_arg", + input: "cookie", + wantErr: ErrExpectKVOptionalV, }, // method { @@ -43,9 +68,14 @@ func TestParseOn(t *testing.T) { }, { name: "method_invalid", - input: "method", + input: "method invalid", wantErr: ErrInvalidArguments, }, + { + name: "method_missing_arg", + input: "method", + wantErr: ErrExpectOneArg, + }, // path { name: "path_valid", @@ -53,9 +83,9 @@ func TestParseOn(t *testing.T) { wantErr: nil, }, { - name: "path_invalid", + name: "path_missing_arg", input: "path", - wantErr: ErrInvalidArguments, + wantErr: ErrExpectOneArg, }, // remote { @@ -65,9 +95,14 @@ func TestParseOn(t *testing.T) { }, { name: "remote_invalid", - input: "remote", + input: "remote abcd", wantErr: ErrInvalidArguments, }, + { + name: "remote_missing_arg", + input: "remote", + wantErr: ErrExpectOneArg, + }, { name: "unknown_target", input: "unknown", diff --git a/internal/route/rules/validate.go b/internal/route/rules/validate.go index 818ce96..e894309 100644 --- a/internal/route/rules/validate.go +++ b/internal/route/rules/validate.go @@ -36,6 +36,18 @@ func toStrTuple(args []string) (any, E.Error) { return &StrTuple{args[0], args[1]}, nil } +// toKVOptionalV returns *StrTuple that value is optional. +func toKVOptionalV(args []string) (any, E.Error) { + switch len(args) { + case 1: + return &StrTuple{args[0], ""}, nil + case 2: + return &StrTuple{args[0], args[1]}, nil + default: + return nil, ErrExpectKVOptionalV + } +} + // validateURL returns types.URL with the URL validated. func validateURL(args []string) (any, E.Error) { if len(args) != 1 { From af7c59b5c2c094882d0aa947f792c277a7672e55 Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 6 Feb 2025 05:50:03 +0800 Subject: [PATCH 12/13] add tests for rules.on --- internal/route/rules/on_test.go | 154 ++++++++++++++++++++++++++++++++ 1 file changed, 154 insertions(+) diff --git a/internal/route/rules/on_test.go b/internal/route/rules/on_test.go index c5bdc8e..fb7af45 100644 --- a/internal/route/rules/on_test.go +++ b/internal/route/rules/on_test.go @@ -1,10 +1,15 @@ package rules import ( + "encoding/base64" + "fmt" + "net/http" + "net/url" "testing" E "github.com/yusing/go-proxy/internal/error" . "github.com/yusing/go-proxy/internal/utils/testing" + "golang.org/x/crypto/bcrypt" ) func TestParseOn(t *testing.T) { @@ -122,3 +127,152 @@ func TestParseOn(t *testing.T) { }) } } + +type testCorrectness struct { + name string + checker string + input *http.Request + want bool +} + +func genCorrectnessTestCases(field string, genRequest func(k, v string) *http.Request) []testCorrectness { + return []testCorrectness{ + { + name: field + "_match", + checker: field + " foo bar", + input: genRequest("foo", "bar"), + want: true, + }, + { + name: field + "_no_match", + checker: field + " foo baz", + input: genRequest("foo", "bar"), + want: false, + }, + { + name: field + "_exists", + checker: field + " foo", + input: genRequest("foo", "abcd"), + want: true, + }, + { + name: field + "_not_exists", + checker: field + " foo", + input: genRequest("bar", "abcd"), + want: false, + }, + } +} + +func TestOnCorrectness(t *testing.T) { + tests := []testCorrectness{ + { + name: "method_match", + checker: "method GET", + input: &http.Request{Method: http.MethodGet}, + want: true, + }, + { + name: "method_no_match", + checker: "method GET", + input: &http.Request{Method: http.MethodPost}, + want: false, + }, + { + name: "path_exact_match", + checker: "path /example", + input: &http.Request{ + URL: &url.URL{Path: "/example"}, + }, + want: true, + }, + { + name: "path_wildcard_match", + checker: "path /example/*", + input: &http.Request{ + URL: &url.URL{Path: "/example/123"}, + }, + want: true, + }, + { + name: "remote_match", + checker: "remote 192.168.1.0/24", + input: &http.Request{ + RemoteAddr: "192.168.1.5", + }, + want: true, + }, + { + name: "remote_no_match", + checker: "remote 192.168.1.0/24", + input: &http.Request{ + RemoteAddr: "192.168.2.5", + }, + want: false, + }, + { + name: "basic_auth_correct", + checker: "basic_auth user " + string(E.Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost))), + input: &http.Request{ + Header: http.Header{ + "Authorization": {"Basic " + base64.StdEncoding.EncodeToString([]byte("user:password"))}, // "user:password" + }, + }, + want: true, + }, + { + name: "basic_auth_incorrect", + checker: "basic_auth user " + string(E.Must(bcrypt.GenerateFromPassword([]byte("password"), bcrypt.DefaultCost))), + input: &http.Request{ + Header: http.Header{ + "Authorization": {"Basic " + base64.StdEncoding.EncodeToString([]byte("user:incorrect"))}, // "user:wrong" + }, + }, + want: false, + }, + } + + tests = append(tests, genCorrectnessTestCases("header", func(k, v string) *http.Request { + return &http.Request{ + Header: http.Header{k: []string{v}}} + })...) + tests = append(tests, genCorrectnessTestCases("query", func(k, v string) *http.Request { + return &http.Request{ + URL: &url.URL{ + RawQuery: fmt.Sprintf("%s=%s", k, v), + }, + } + })...) + tests = append(tests, genCorrectnessTestCases("cookie", func(k, v string) *http.Request { + return &http.Request{ + Header: http.Header{ + "Cookie": {fmt.Sprintf("%s=%s", k, v)}, + }, + } + })...) + tests = append(tests, genCorrectnessTestCases("form", func(k, v string) *http.Request { + return &http.Request{ + Form: url.Values{ + k: []string{v}, + }, + } + })...) + tests = append(tests, genCorrectnessTestCases("postform", func(k, v string) *http.Request { + return &http.Request{ + PostForm: url.Values{ + k: []string{v}, + }, + } + })...) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + on, err := parseOn(tt.checker) + ExpectNoError(t, err) + got := on.Check(Cache{}, tt.input) + if tt.want != got { + t.Errorf("want %v, got %v", tt.want, got) + } + }) + } +} From 4d47eb0e9160acf9004906c7a5fbc128c6dbb5f9 Mon Sep 17 00:00:00 2001 From: yusing Date: Thu, 6 Feb 2025 05:59:21 +0800 Subject: [PATCH 13/13] update compose example --- compose.example.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/compose.example.yml b/compose.example.yml index 2f3ac17..0c4d68c 100755 --- a/compose.example.yml +++ b/compose.example.yml @@ -12,9 +12,10 @@ services: labels: proxy.aliases: godoxy proxy.godoxy.port: 3000 - # proxy.godoxy.middlewares.cidr_whitelist.status: 403 - # proxy.godoxy.middlewares.cidr_whitelist.message: IP not allowed - # proxy.godoxy.middlewares.cidr_whitelist.allow: | + # proxy.godoxy.middlewares.cidr_whitelist: | + # status: 403 + # message: IP not allowed + # allow: # - 127.0.0.1 # - 10.0.0.0/8 # - 192.168.0.0/16