♻️ RoadSign v2 #6
| @@ -3,9 +3,8 @@ | ||||
|         { | ||||
|             "type": "go", | ||||
|             "name": "Run RoadSign", | ||||
|             "goExecPath": "C:\\Tools\\Scoop\\shims\\go.exe", | ||||
|             "buildParams": ["code.smartsheep.studio/goatworks/roadsign/pkg/cmd"], | ||||
|             "goExecPath": "/opt/homebrew/bin/go", | ||||
|             "buildParams": ["code.smartsheep.studio/goatworks/roadsign/pkg/cmd/server"], | ||||
|         }, | ||||
|          | ||||
|     ] | ||||
| } | ||||
							
								
								
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										5
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,2 +1,3 @@ | ||||
| /config | ||||
| /letsencrypt | ||||
| /letsencrypt | ||||
|  | ||||
| .DS_Store | ||||
							
								
								
									
										6
									
								
								.idea/RoadSign.iml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										6
									
								
								.idea/RoadSign.iml
									
									
									
										generated
									
									
									
								
							| @@ -1,9 +1,15 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="WEB_MODULE" version="4"> | ||||
|   <component name="FacetManager"> | ||||
|     <facet type="Python" name="Python facet"> | ||||
|       <configuration sdkName="Python 3.9" /> | ||||
|     </facet> | ||||
|   </component> | ||||
|   <component name="Go" enabled="true" /> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$MODULE_DIR$" /> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|     <orderEntry type="library" name="Python 3.9 interpreter library" level="application" /> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										6
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								.idea/misc.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="Black"> | ||||
|     <option name="sdkName" value="Python 3.9" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										31
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								README.md
									
									
									
									
									
								
							| @@ -5,15 +5,19 @@ A blazing fast reverse proxy with a lot of shining features. | ||||
| ## Features | ||||
|  | ||||
| 1. Reverse proxy | ||||
| 2. Static file hosting | ||||
| 3. ~~Analytics and Metrics~~ | ||||
| 4. Integrate with CI/CD | ||||
| 5. Webhook integration | ||||
| 6. ~~Web management panel~~ | ||||
| 7. One-liner CLI | ||||
| 8. **Blazing fast ⚡** | ||||
| 2. HTTP2 Support | ||||
| 3. WebSocket Support | ||||
| 4. Static File Hosting | ||||
| 5. Low Configuration | ||||
| 6. Analytics and Metrics | ||||
| 7. Integrate with CI/CD | ||||
| 8. Web management panel (Work in progres for v2, available in v1) | ||||
| 9. One-liner CLI | ||||
| 10. Open-source and free | ||||
| 11. **Blazing fast ⚡** | ||||
|  | ||||
| > Deleted item means under construction, check out our roadmap! | ||||
| > [!IMPORTANT] | ||||
| > Currently roadsign haven't supported for server-side events. We are working on it. | ||||
|  | ||||
| ### How fast is it? | ||||
|  | ||||
| @@ -42,7 +46,7 @@ We strongly recommend you install RoadSign via docker compose. | ||||
| version: "3" | ||||
| services: | ||||
|   roadsign: | ||||
|     image: code.smartsheep.studio/goatworks/roadsign:nightly | ||||
|     image: xsheep2010/roadsign:nightly | ||||
|     restart: always | ||||
|     volumes: | ||||
|       - "./certs:/certs" # Optional, use for storage certificates | ||||
| @@ -55,12 +59,11 @@ services: | ||||
|       - "81:81" | ||||
| ``` | ||||
|  | ||||
| After that, you can manage your roadsign instance with RoadSign CLI aka. RDS CLI. | ||||
| After that, you can manage your roadsign instance with RoadSign CLI aka. RDC. | ||||
| To install it, run this command. (Make sure you have golang toolchain on your computer) | ||||
|  | ||||
| ```shell | ||||
| go install -buildvcs code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds@latest | ||||
| # Tips: Add `buildvsc` flag to provide more detail compatibility check. | ||||
| go install code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc@latest | ||||
| ``` | ||||
|  | ||||
| ## Usage | ||||
| @@ -73,7 +76,7 @@ After configure, you need sync your config to remote server. Before that, add a | ||||
| rds cli with this command. | ||||
|  | ||||
| ```shell | ||||
| rds connect <id> <url> <password> | ||||
| rdc connect <id> <url> <password> | ||||
| # ID will allow you find this server in after commands. | ||||
| # URL is to your roadsign server sideload api. | ||||
| # Password is your roadsign server credential. | ||||
| @@ -85,7 +88,7 @@ rds connect <id> <url> <password> | ||||
| Then, sync your local config to remote. | ||||
|  | ||||
| ```shell | ||||
| rds sync <server id> <site id> <config file> | ||||
| rdc sync <server id> <region id> <config file> | ||||
| # Server ID is your server added by last command. | ||||
| # Site ID is your new site id or old site id if you need update it. | ||||
| # Config File is your local config file path. | ||||
|   | ||||
							
								
								
									
										31
									
								
								config/example.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								config/example.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| id = "example-region" | ||||
|  | ||||
| [[locations]] | ||||
| id = "example-websocket" | ||||
| host = ["localhost:8000"] | ||||
| path = ["/ws"] | ||||
| [[locations.destinations]] | ||||
| id = "example-websocket-destination" | ||||
| uri = "http://localhost:8765" | ||||
|  | ||||
| [[locations]] | ||||
| id = "example-warden" | ||||
| host = ["localhost:4321"] | ||||
| path = ["/"] | ||||
| [[locations.destinations]] | ||||
| id = "example-warden-destination" | ||||
| uri = "http://localhost:4321" | ||||
|  | ||||
| [[applications]] | ||||
| id = "example-warden-app" | ||||
| workdir = "test/data/warden" | ||||
| command = ["node", "dist/server/entry.mjs"] | ||||
| environment = ["PUBLIC_CMS=https://smartsheep.studio"] | ||||
|  | ||||
| [[locations]] | ||||
| id = "example-static" | ||||
| host = ["localhost:8000"] | ||||
| path = ["/roadsign"] | ||||
| [[locations.destinations]] | ||||
| id = "example-static-destination" | ||||
| uri = "files://test/data" | ||||
							
								
								
									
										24
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										24
									
								
								go.mod
									
									
									
									
									
								
							| @@ -3,29 +3,32 @@ module code.smartsheep.studio/goatworks/roadsign | ||||
| go 1.21.4 | ||||
|  | ||||
| require ( | ||||
| 	github.com/gofiber/fiber/v2 v2.51.0 | ||||
| 	github.com/google/uuid v1.4.0 | ||||
| 	github.com/fasthttp/websocket v1.5.7 | ||||
| 	github.com/gofiber/fiber/v2 v2.52.0 | ||||
| 	github.com/google/uuid v1.6.0 | ||||
| 	github.com/json-iterator/go v1.1.12 | ||||
| 	github.com/rs/zerolog v1.31.0 | ||||
| 	github.com/samber/lo v1.38.1 | ||||
| 	github.com/saracen/fastzip v0.1.11 | ||||
| 	github.com/spf13/viper v1.17.0 | ||||
| 	github.com/urfave/cli/v2 v2.26.0 | ||||
| 	github.com/valyala/fasthttp v1.50.0 | ||||
| 	github.com/valyala/fasthttp v1.51.0 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/russross/blackfriday/v2 v2.1.0 // indirect | ||||
| 	github.com/saracen/zipextra v0.0.0-20220303013732-0187cb0159ea // indirect | ||||
| 	github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee // indirect | ||||
| 	github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect | ||||
| 	golang.org/x/net v0.20.0 // indirect | ||||
| 	golang.org/x/sync v0.5.0 // indirect | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/andybalholm/brotli v1.0.5 // indirect | ||||
| 	github.com/andybalholm/brotli v1.1.0 // indirect | ||||
| 	github.com/fsnotify/fsnotify v1.6.0 // indirect | ||||
| 	github.com/hashicorp/hcl v1.0.0 // indirect | ||||
| 	github.com/klauspost/compress v1.17.4 // indirect | ||||
| @@ -34,13 +37,13 @@ require ( | ||||
| 	github.com/mattn/go-isatty v0.0.20 // indirect | ||||
| 	github.com/mattn/go-runewidth v0.0.15 // indirect | ||||
| 	github.com/mitchellh/mapstructure v1.5.0 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.1.0 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.1.1 | ||||
| 	github.com/philhofer/fwd v1.1.2 // indirect | ||||
| 	github.com/rivo/uniseg v0.2.0 // indirect | ||||
| 	github.com/rivo/uniseg v0.4.4 // indirect | ||||
| 	github.com/sagikazarmark/locafero v0.3.0 // indirect | ||||
| 	github.com/sagikazarmark/slog-shim v0.1.0 // indirect | ||||
| 	github.com/sourcegraph/conc v0.3.0 // indirect | ||||
| 	github.com/spf13/afero v1.10.0 // indirect | ||||
| 	github.com/spf13/afero v1.11.0 // indirect | ||||
| 	github.com/spf13/cast v1.5.1 // indirect | ||||
| 	github.com/spf13/pflag v1.0.5 // indirect | ||||
| 	github.com/subosito/gotenv v1.6.0 // indirect | ||||
| @@ -50,9 +53,8 @@ require ( | ||||
| 	go.uber.org/atomic v1.9.0 // indirect | ||||
| 	go.uber.org/multierr v1.9.0 // indirect | ||||
| 	golang.org/x/exp v0.0.0-20230905200255-921286631fa9 // indirect | ||||
| 	golang.org/x/sys v0.15.0 // indirect | ||||
| 	golang.org/x/text v0.13.0 // indirect | ||||
| 	golang.org/x/sys v0.16.0 // indirect | ||||
| 	golang.org/x/text v0.14.0 // indirect | ||||
| 	gopkg.in/ini.v1 v1.67.0 // indirect | ||||
| 	gopkg.in/yaml.v2 v2.4.0 | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										457
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										457
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,53 +1,5 @@ | ||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||
| cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= | ||||
| cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= | ||||
| cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= | ||||
| cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= | ||||
| cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= | ||||
| cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= | ||||
| cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= | ||||
| cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= | ||||
| cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= | ||||
| cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= | ||||
| cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= | ||||
| cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= | ||||
| cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= | ||||
| cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= | ||||
| cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= | ||||
| cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= | ||||
| cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= | ||||
| cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= | ||||
| cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= | ||||
| cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= | ||||
| cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= | ||||
| cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= | ||||
| cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= | ||||
| cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= | ||||
| cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= | ||||
| cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= | ||||
| cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= | ||||
| cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= | ||||
| cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= | ||||
| cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= | ||||
| cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= | ||||
| cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= | ||||
| cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= | ||||
| cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= | ||||
| cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= | ||||
| dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= | ||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||
| github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= | ||||
| github.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs= | ||||
| github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig= | ||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||
| github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= | ||||
| github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= | ||||
| github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= | ||||
| github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||||
| github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= | ||||
| github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= | ||||
| github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= | ||||
| github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= | ||||
| github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= | ||||
| github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= | ||||
| @@ -55,100 +7,28 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= | ||||
| github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= | ||||
| github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= | ||||
| github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= | ||||
| github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= | ||||
| github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= | ||||
| github.com/fasthttp/websocket v1.5.7 h1:0a6o2OfeATvtGgoMKleURhLT6JqWPg7fYfWnH4KHau4= | ||||
| github.com/fasthttp/websocket v1.5.7/go.mod h1:bC4fxSono9czeXHQUVKxsC0sNjbm7lPJR04GDFqClfU= | ||||
| github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY= | ||||
| github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0= | ||||
| github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= | ||||
| github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= | ||||
| github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= | ||||
| github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= | ||||
| github.com/gofiber/fiber/v2 v2.51.0 h1:JNACcZy5e2tGApWB2QrRpenTWn0fq0hkFm6k0C86gKQ= | ||||
| github.com/gofiber/fiber/v2 v2.51.0/go.mod h1:xaQRZQJGqnKOQnbQw+ltvku3/h8QxvNi8o6JiJ7Ll0U= | ||||
| github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= | ||||
| github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= | ||||
| github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= | ||||
| github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= | ||||
| github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= | ||||
| github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= | ||||
| github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= | ||||
| github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | ||||
| github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= | ||||
| github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= | ||||
| github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= | ||||
| github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= | ||||
| github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= | ||||
| github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= | ||||
| github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= | ||||
| github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= | ||||
| github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= | ||||
| github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= | ||||
| github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= | ||||
| github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= | ||||
| github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/gofiber/fiber/v2 v2.52.0 h1:S+qXi7y+/Pgvqq4DrSmREGiFwtB7Bu6+QFLuIHYw/UE= | ||||
| github.com/gofiber/fiber/v2 v2.52.0/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ= | ||||
| github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= | ||||
| github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= | ||||
| github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= | ||||
| github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= | ||||
| github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= | ||||
| github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= | ||||
| github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= | ||||
| github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= | ||||
| github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= | ||||
| github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= | ||||
| github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= | ||||
| github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||
| github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= | ||||
| github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= | ||||
| github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= | ||||
| github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | ||||
| github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= | ||||
| github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= | ||||
| github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= | ||||
| github.com/klauspost/compress v1.17.4 h1:Ej5ixsIri7BrIjBkRZLTo6ghwrEtHFk7ijlczPW4fZ4= | ||||
| github.com/klauspost/compress v1.17.4/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= | ||||
| github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= | ||||
| github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= | ||||
| github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= | ||||
| github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= | ||||
| github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= | ||||
| github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= | ||||
| github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= | ||||
| github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= | ||||
| github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= | ||||
| @@ -168,19 +48,17 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= | ||||
| github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= | ||||
| github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4= | ||||
| github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= | ||||
| github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI= | ||||
| github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= | ||||
| github.com/philhofer/fwd v1.1.2 h1:bnDivRJ1EWPjUIRXV5KfORO897HTbpFAQddBdE8t7Gw= | ||||
| github.com/philhofer/fwd v1.1.2/go.mod h1:qkPdfjR2SIEbspLqpe1tO4n5yICnr2DY7mqEx2tUTP0= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= | ||||
| github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= | ||||
| github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY= | ||||
| github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= | ||||
| github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= | ||||
| github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis= | ||||
| github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= | ||||
| github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= | ||||
| github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= | ||||
| github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= | ||||
| @@ -198,10 +76,12 @@ github.com/saracen/fastzip v0.1.11 h1:NnExbTEJbya7148cov09BCxwfur9tQ5BQ1QyQH6Xle | ||||
| github.com/saracen/fastzip v0.1.11/go.mod h1:/lN5BiU451/OZMS+hfhVsSDj/RNrxYmO9EYxCtMrFrY= | ||||
| github.com/saracen/zipextra v0.0.0-20220303013732-0187cb0159ea h1:8czYLkvzZRE+AElIQeDffQdgR+CC3wKEFILYU/1PeX4= | ||||
| github.com/saracen/zipextra v0.0.0-20220303013732-0187cb0159ea/go.mod h1:hnzuad9d2wdd3z8fC6UouHQK5qZxqv3F/E6MMzXc7q0= | ||||
| github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee h1:8Iv5m6xEo1NR1AvpV+7XmhI4r39LGNzwUL4YpMuL5vk= | ||||
| github.com/savsgio/gotils v0.0.0-20230208104028-c358bd845dee/go.mod h1:qwtSXrKuJh/zsFQ12yEE89xfCrGKK63Rr7ctU/uCo4g= | ||||
| github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo= | ||||
| github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0= | ||||
| github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= | ||||
| github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= | ||||
| github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= | ||||
| github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= | ||||
| github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA= | ||||
| github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48= | ||||
| github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= | ||||
| @@ -212,9 +92,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+ | ||||
| github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= | ||||
| github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= | ||||
| github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= | ||||
| github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= | ||||
| github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= | ||||
| github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= | ||||
| @@ -227,163 +104,36 @@ github.com/urfave/cli/v2 v2.26.0 h1:3f3AMg3HpThFNT4I++TKOejZO8yU55t3JnnSr4S4QEI= | ||||
| github.com/urfave/cli/v2 v2.26.0/go.mod h1:8qnjx1vcq5s2/wpsqoZFndg2CE5tNFyrTvS6SinrnYQ= | ||||
| github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= | ||||
| github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= | ||||
| github.com/valyala/fasthttp v1.50.0 h1:H7fweIlBm0rXLs2q0XbalvJ6r0CUPFWK3/bB4N13e9M= | ||||
| github.com/valyala/fasthttp v1.50.0/go.mod h1:k2zXd82h/7UZc3VOdJ2WaUqt1uZ/XpXAfE9i+HBC3lA= | ||||
| github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1SqA= | ||||
| github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g= | ||||
| github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= | ||||
| github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= | ||||
| github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU= | ||||
| github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8= | ||||
| github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= | ||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||||
| go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= | ||||
| go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= | ||||
| go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= | ||||
| go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= | ||||
| go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE= | ||||
| go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= | ||||
| go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI= | ||||
| go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ= | ||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||
| golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= | ||||
| golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= | ||||
| golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= | ||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||
| golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= | ||||
| golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= | ||||
| golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= | ||||
| golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= | ||||
| golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= | ||||
| golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | ||||
| golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | ||||
| golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= | ||||
| golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= | ||||
| golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= | ||||
| golang.org/x/exp v0.0.0-20230905200255-921286631fa9 h1:GoHiUyI/Tp2nVkLI2mCxVkOjsbSXD66ic0XW0js0R9g= | ||||
| golang.org/x/exp v0.0.0-20230905200255-921286631fa9/go.mod h1:S2oDrQGGwySpoQPVqRShND87VCbxmc6bL1Yd2oYrm6k= | ||||
| golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= | ||||
| golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= | ||||
| golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= | ||||
| golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= | ||||
| golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= | ||||
| golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= | ||||
| golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= | ||||
| golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= | ||||
| golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= | ||||
| golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= | ||||
| golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= | ||||
| golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= | ||||
| golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||||
| golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= | ||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||
| golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= | ||||
| golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||
| golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||
| golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= | ||||
| golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= | ||||
| golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= | ||||
| golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||
| golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE= | ||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||
| golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||
| golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= | ||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo= | ||||
| golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||
| golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= | ||||
| golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||
| golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| @@ -392,186 +142,27 @@ golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBc | ||||
| golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= | ||||
| golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU= | ||||
| golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||
| golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= | ||||
| golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA= | ||||
| golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||
| golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||
| golang.org/x/text v0.13.0 h1:ablQoSUd0tRdKxZewP80B+BaqeKJuVhuRxj/dkrun3k= | ||||
| golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= | ||||
| golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= | ||||
| golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= | ||||
| golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | ||||
| golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= | ||||
| golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= | ||||
| golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= | ||||
| golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= | ||||
| golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= | ||||
| golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||
| golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= | ||||
| golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= | ||||
| golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= | ||||
| golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= | ||||
| golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= | ||||
| golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= | ||||
| golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= | ||||
| golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= | ||||
| golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= | ||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||
| golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= | ||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= | ||||
| google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= | ||||
| google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= | ||||
| google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= | ||||
| google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= | ||||
| google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= | ||||
| google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= | ||||
| google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= | ||||
| google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= | ||||
| google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= | ||||
| google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= | ||||
| google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= | ||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||
| google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= | ||||
| google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||
| google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= | ||||
| google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||
| google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= | ||||
| google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= | ||||
| google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= | ||||
| google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= | ||||
| google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= | ||||
| google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= | ||||
| google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= | ||||
| google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= | ||||
| google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= | ||||
| google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= | ||||
| google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= | ||||
| google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= | ||||
| google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= | ||||
| google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= | ||||
| google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= | ||||
| google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= | ||||
| google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= | ||||
| google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= | ||||
| google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= | ||||
| google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= | ||||
| google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= | ||||
| google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= | ||||
| google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= | ||||
| google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= | ||||
| google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= | ||||
| google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= | ||||
| google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= | ||||
| google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= | ||||
| gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= | ||||
| gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= | ||||
| gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= | ||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||
| gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= | ||||
| gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||
| honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= | ||||
| honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= | ||||
| honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | ||||
| honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= | ||||
| rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= | ||||
| rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= | ||||
| rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= | ||||
|   | ||||
| @@ -17,7 +17,7 @@ type CliConnection struct { | ||||
| } | ||||
| 
 | ||||
| func (v CliConnection) CheckConnectivity() error { | ||||
| 	client := fiber.Get(v.Url + "/cgi/connectivity") | ||||
| 	client := fiber.Get(v.Url + "/cgi/metadata") | ||||
| 	client.BasicAuth("RoadSign CLI", v.Credential) | ||||
| 
 | ||||
| 	if status, data, err := client.Bytes(); len(err) > 0 { | ||||
| @@ -2,17 +2,16 @@ package deploy | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| 	"io" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/conn" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign" | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| 
 | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc/conn" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/urfave/cli/v2" | ||||
| 	"gopkg.in/yaml.v2" | ||||
| ) | ||||
| 
 | ||||
| var DeployCommands = []*cli.Command{ | ||||
| @@ -58,7 +57,6 @@ var DeployCommands = []*cli.Command{ | ||||
| 	}, | ||||
| 	{ | ||||
| 		Name:      "sync", | ||||
| 		Aliases:   []string{"sc"}, | ||||
| 		ArgsUsage: "<server> <site> <configuration path>", | ||||
| 		Action: func(ctx *cli.Context) error { | ||||
| 			if ctx.Args().Len() < 3 { | ||||
| @@ -72,19 +70,18 @@ var DeployCommands = []*cli.Command{ | ||||
| 				return fmt.Errorf("couldn't connect server: %s", err.Error()) | ||||
| 			} | ||||
| 
 | ||||
| 			var site sign.SiteConfig | ||||
| 			var raw []byte | ||||
| 			if file, err := os.Open(ctx.Args().Get(2)); err != nil { | ||||
| 				return err | ||||
| 			} else { | ||||
| 				raw, _ := io.ReadAll(file) | ||||
| 				yaml.Unmarshal(raw, &site) | ||||
| 				raw, _ = io.ReadAll(file) | ||||
| 			} | ||||
| 
 | ||||
| 			url := fmt.Sprintf("/webhooks/sync/%s", ctx.Args().Get(1)) | ||||
| 			client := fiber.Put(server.Url+url). | ||||
| 				JSONEncoder(jsoniter.ConfigCompatibleWithStandardLibrary.Marshal). | ||||
| 				JSONDecoder(jsoniter.ConfigCompatibleWithStandardLibrary.Unmarshal). | ||||
| 				JSON(site). | ||||
| 				Body(raw). | ||||
| 				BasicAuth("RoadSign CLI", server.Credential) | ||||
| 
 | ||||
| 			if status, data, err := client.Bytes(); len(err) > 0 { | ||||
| @@ -4,8 +4,8 @@ import ( | ||||
| 	"os" | ||||
| 
 | ||||
| 	roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/conn" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds/deploy" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc/conn" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rdc/deploy" | ||||
| 	"github.com/rs/zerolog" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/spf13/viper" | ||||
| @@ -21,7 +21,7 @@ func main() { | ||||
| 	// Configure settings | ||||
| 	viper.AddConfigPath("$HOME") | ||||
| 	viper.SetConfigName(".roadsignrc") | ||||
| 	viper.SetConfigType("yaml") | ||||
| 	viper.SetConfigType("toml") | ||||
| 
 | ||||
| 	// Load settings | ||||
| 	if err := viper.ReadInConfig(); err != nil { | ||||
| @@ -8,8 +8,8 @@ import ( | ||||
|  | ||||
| 	roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/hypertext" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sideload" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/rs/zerolog" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| @@ -26,7 +26,7 @@ func main() { | ||||
| 	viper.AddConfigPath(".") | ||||
| 	viper.AddConfigPath("..") | ||||
| 	viper.SetConfigName("settings") | ||||
| 	viper.SetConfigType("yaml") | ||||
| 	viper.SetConfigType("toml") | ||||
|  | ||||
| 	// Load settings | ||||
| 	if err := viper.ReadInConfig(); err != nil { | ||||
| @@ -43,20 +43,15 @@ func main() { | ||||
| 		log.Warn().Msgf("RoadSign auto generated api credential is %s", credential) | ||||
| 	} | ||||
|  | ||||
| 	// Load & init sign | ||||
| 	if err := sign.ReadInConfig(viper.GetString("paths.configs")); err != nil { | ||||
| 	// Load & init navigator | ||||
| 	if err := navi.ReadInConfig(viper.GetString("paths.configs")); err != nil { | ||||
| 		log.Panic().Err(err).Msg("An error occurred when loading configurations.") | ||||
| 	} else { | ||||
| 		log.Info().Int("count", len(sign.App.Sites)).Msg("All configuration has been loaded.") | ||||
| 		log.Info().Int("count", len(navi.R.Regions)).Msg("All configuration has been loaded.") | ||||
| 	} | ||||
|  | ||||
| 	// Preheat processes | ||||
| 	go func() { | ||||
| 		log.Info().Msg("Preheating processes...") | ||||
| 		sign.App.PreheatProcesses(func(total int, success int) { | ||||
| 			log.Info().Int("requested", total).Int("succeed", success).Msgf("Preheat processes completed!") | ||||
| 		}) | ||||
| 	}() | ||||
| 	// Init warden | ||||
| 	navi.InitializeWarden(navi.R.Regions) | ||||
|  | ||||
| 	// Init hypertext server | ||||
| 	hypertext.RunServer( | ||||
|   | ||||
| @@ -1,9 +1,11 @@ | ||||
| package hypertext | ||||
|  | ||||
| import ( | ||||
| 	"github.com/spf13/viper" | ||||
| 	"math/rand" | ||||
| 	"regexp" | ||||
|  | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
| @@ -16,16 +18,16 @@ func UseProxies(app *fiber.App) { | ||||
| 		headers := ctx.GetReqHeaders() | ||||
|  | ||||
| 		// Filtering sites | ||||
| 		for _, site := range sign.App.Sites { | ||||
| 		for _, region := range navi.R.Regions { | ||||
| 			// Matching rules | ||||
| 			for _, rule := range site.Rules { | ||||
| 				if !lo.Contains(rule.Host, host) { | ||||
| 			for _, location := range region.Locations { | ||||
| 				if !lo.Contains(location.Host, host) { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				if !func() bool { | ||||
| 					flag := false | ||||
| 					for _, pattern := range rule.Path { | ||||
| 					for _, pattern := range location.Path { | ||||
| 						if ok, _ := regexp.MatchString(pattern, path); ok { | ||||
| 							flag = true | ||||
| 							break | ||||
| @@ -38,7 +40,7 @@ func UseProxies(app *fiber.App) { | ||||
|  | ||||
| 				// Filter query strings | ||||
| 				flag := true | ||||
| 				for rk, rv := range rule.Queries { | ||||
| 				for rk, rv := range location.Queries { | ||||
| 					for ik, iv := range queries { | ||||
| 						if rk != ik && rv != iv { | ||||
| 							flag = false | ||||
| @@ -54,7 +56,7 @@ func UseProxies(app *fiber.App) { | ||||
| 				} | ||||
|  | ||||
| 				// Filter headers | ||||
| 				for rk, rv := range rule.Headers { | ||||
| 				for rk, rv := range location.Headers { | ||||
| 					for ik, iv := range headers { | ||||
| 						if rk == ik { | ||||
| 							for _, ov := range iv { | ||||
| @@ -76,9 +78,12 @@ func UseProxies(app *fiber.App) { | ||||
| 					continue | ||||
| 				} | ||||
|  | ||||
| 				idx := rand.Intn(len(location.Destinations)) | ||||
| 				dest := location.Destinations[idx] | ||||
|  | ||||
| 				// Passing all the rules means the site is what we are looking for. | ||||
| 				// Let us respond to our client! | ||||
| 				return makeResponse(ctx, site) | ||||
| 				return makeResponse(ctx, region, &location, &dest) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| @@ -89,23 +94,46 @@ func UseProxies(app *fiber.App) { | ||||
| 	}) | ||||
| } | ||||
|  | ||||
| func makeResponse(ctx *fiber.Ctx, site *sign.SiteConfig) error { | ||||
| func makeResponse(ctx *fiber.Ctx, region *navi.Region, location *navi.Location, dest *navi.Destination) error { | ||||
| 	uri := ctx.Request().URI().String() | ||||
|  | ||||
| 	// Modify request | ||||
| 	for _, transformer := range site.Transformers { | ||||
| 	for _, transformer := range dest.Transformers { | ||||
| 		if err := transformer.TransformRequest(ctx); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Forward | ||||
| 	err := sign.App.Forward(ctx, site) | ||||
| 	err := navi.R.Forward(ctx, dest) | ||||
|  | ||||
| 	// Modify response | ||||
| 	for _, transformer := range site.Transformers { | ||||
| 	for _, transformer := range dest.Transformers { | ||||
| 		if err := transformer.TransformResponse(ctx); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Collect trace | ||||
| 	if viper.GetBool("telemetry.capture_traces") { | ||||
| 		var message string | ||||
| 		if err != nil { | ||||
| 			message = err.Error() | ||||
| 		} | ||||
|  | ||||
| 		go navi.R.AddTrace(navi.RoadTrace{ | ||||
| 			Region:      region.ID, | ||||
| 			Location:    location.ID, | ||||
| 			Destination: dest.ID, | ||||
| 			Uri:         uri, | ||||
| 			IpAddress:   ctx.IP(), | ||||
| 			UserAgent:   ctx.Get(fiber.HeaderUserAgent), | ||||
| 			Error:       navi.RoadTraceError{ | ||||
| 				IsNull: err == nil, | ||||
| 				Message: message, | ||||
| 			}, | ||||
| 		}) | ||||
| 	} | ||||
|  | ||||
| 	return err | ||||
| } | ||||
|   | ||||
| @@ -14,4 +14,4 @@ func init() { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| var AppVersion = "1.2.1" | ||||
| var AppVersion = "2.0.0-delta1" | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| package sign | ||||
| package navi | ||||
| 
 | ||||
| import ( | ||||
| 	"io" | ||||
| @@ -6,32 +6,39 @@ import ( | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"gopkg.in/yaml.v2" | ||||
| 	"github.com/spf13/viper" | ||||
| 
 | ||||
| 	"github.com/pelletier/go-toml/v2" | ||||
| ) | ||||
| 
 | ||||
| var App *RoadApp | ||||
| var R *RoadApp | ||||
| 
 | ||||
| func ReadInConfig(root string) error { | ||||
| 	instance := &RoadApp{ | ||||
| 		Sites: []*SiteConfig{}, | ||||
| 		Regions: make([]*Region, 0), | ||||
| 		Traces:  make([]RoadTrace, 0, viper.GetInt("performance.traces_limit")), | ||||
| 	} | ||||
| 
 | ||||
| 	if err := filepath.Walk(root, func(fp string, info os.FileInfo, err error) error { | ||||
| 		var site SiteConfig | ||||
| 	if err := filepath.Walk(root, func(fp string, info os.FileInfo, _ error) error { | ||||
| 		var region Region | ||||
| 		if info.IsDir() { | ||||
| 			return nil | ||||
| 		} else if !strings.HasSuffix(info.Name(), ".toml") { | ||||
| 			return nil | ||||
| 		} else if file, err := os.OpenFile(fp, os.O_RDONLY, 0755); err != nil { | ||||
| 			return err | ||||
| 		} else if data, err := io.ReadAll(file); err != nil { | ||||
| 			return err | ||||
| 		} else if err := yaml.Unmarshal(data, &site); err != nil { | ||||
| 		} else if err := toml.Unmarshal(data, ®ion); err != nil { | ||||
| 			return err | ||||
| 		} else { | ||||
| 			defer file.Close() | ||||
| 
 | ||||
| 			// Extract file name as site id | ||||
| 			site.ID = strings.SplitN(filepath.Base(fp), ".", 2)[0] | ||||
| 			instance.Sites = append(instance.Sites, &site) | ||||
| 			if region.Disabled { | ||||
| 				return nil | ||||
| 			} | ||||
| 
 | ||||
| 			instance.Regions = append(instance.Regions, ®ion) | ||||
| 		} | ||||
| 
 | ||||
| 		return nil | ||||
| @@ -39,7 +46,7 @@ func ReadInConfig(root string) error { | ||||
| 		return err | ||||
| 	} | ||||
| 
 | ||||
| 	App = instance | ||||
| 	R = instance | ||||
| 
 | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										25
									
								
								pkg/navi/metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								pkg/navi/metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| package navi | ||||
|  | ||||
| import "github.com/spf13/viper" | ||||
|  | ||||
| type RoadTrace struct { | ||||
| 	Region      string         `json:"region"` | ||||
| 	Location    string         `json:"location"` | ||||
| 	Destination string         `json:"destination"` | ||||
| 	Uri         string         `json:"uri"` | ||||
| 	IpAddress   string         `json:"ip_address"` | ||||
| 	UserAgent   string         `json:"user_agent"` | ||||
| 	Error       RoadTraceError `json:"error"` | ||||
| } | ||||
|  | ||||
| type RoadTraceError struct { | ||||
| 	IsNull  bool   `json:"is_null"` | ||||
| 	Message string `json:"message"` | ||||
| } | ||||
|  | ||||
| func (v *RoadApp) AddTrace(trace RoadTrace) { | ||||
| 	v.Traces = append(v.Traces, trace) | ||||
| 	if len(v.Traces) > viper.GetInt("performance.traces_limit") { | ||||
| 		v.Traces = v.Traces[1:] | ||||
| 	} | ||||
| } | ||||
| @@ -1,33 +1,110 @@ | ||||
| package sign | ||||
| package navi | ||||
| 
 | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"fmt" | ||||
| 	"github.com/fasthttp/websocket" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/proxy" | ||||
| 	"github.com/gofiber/fiber/v2/utils" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"github.com/valyala/fasthttp" | ||||
| 	"io/fs" | ||||
| 	"net/http" | ||||
| 	"path/filepath" | ||||
| 	"strconv" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| 
 | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/proxy" | ||||
| 	"github.com/gofiber/fiber/v2/utils" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"github.com/valyala/fasthttp" | ||||
| ) | ||||
| 
 | ||||
| func makeHypertextResponse(c *fiber.Ctx, upstream *UpstreamInstance) error { | ||||
| func makeUnifiedResponse(c *fiber.Ctx, dest *Destination) error { | ||||
| 	if websocket.FastHTTPIsWebSocketUpgrade(c.Context()) { | ||||
| 		// Handle websocket | ||||
| 		return makeWebsocketResponse(c, dest) | ||||
| 	} else { | ||||
| 		_, queries := dest.GetRawUri() | ||||
| 		if len(queries.Get("sse")) > 0 { | ||||
| 			// Handle server-side event | ||||
| 			return makeSeverSideEventResponse(c, dest) | ||||
| 		} else { | ||||
| 			// Handle normal http request | ||||
| 			return makeHypertextResponse(c, dest) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| 
 | ||||
| func makeHypertextResponse(c *fiber.Ctx, dest *Destination) error { | ||||
| 	timeout := time.Duration(viper.GetInt64("performance.network_timeout")) * time.Millisecond | ||||
| 	return proxy.Do(c, upstream.MakeURI(c), &fasthttp.Client{ | ||||
| 	return proxy.Do(c, dest.MakeUri(c), &fasthttp.Client{ | ||||
| 		ReadTimeout:  timeout, | ||||
| 		WriteTimeout: timeout, | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func makeFileResponse(c *fiber.Ctx, upstream *UpstreamInstance) error { | ||||
| 	uri, queries := upstream.GetRawURI() | ||||
| var wsUpgrader = websocket.FastHTTPUpgrader{} | ||||
| 
 | ||||
| func makeWebsocketResponse(c *fiber.Ctx, dest *Destination) error { | ||||
| 	uri := dest.MakeWebsocketUri(c) | ||||
| 
 | ||||
| 	// Upgrade connection | ||||
| 	return wsUpgrader.Upgrade(c.Context(), func(conn *websocket.Conn) { | ||||
| 		// Dial the destination | ||||
| 		remote, _, err := websocket.DefaultDialer.Dial(uri, nil) | ||||
| 		if err != nil { | ||||
| 			return | ||||
| 		} | ||||
| 		defer remote.Close() | ||||
| 
 | ||||
| 		// Read messages from remote | ||||
| 		disconnect := make(chan struct{}) | ||||
| 		signal := make(chan struct { | ||||
| 			head int | ||||
| 			data []byte | ||||
| 		}) | ||||
| 		go func() { | ||||
| 			defer close(disconnect) | ||||
| 			for { | ||||
| 				mode, message, err := remote.ReadMessage() | ||||
| 				if err != nil { | ||||
| 					log.Warn().Err(err).Msg("An error occurred during the websocket proxying...") | ||||
| 					return | ||||
| 				} else { | ||||
| 					signal <- struct { | ||||
| 						head int | ||||
| 						data []byte | ||||
| 					}{head: mode, data: message} | ||||
| 				} | ||||
| 			} | ||||
| 		}() | ||||
| 
 | ||||
| 		// Relay the destination websocket to client | ||||
| 		for { | ||||
| 			select { | ||||
| 			case <-disconnect: | ||||
| 			case val := <-signal: | ||||
| 				if err := conn.WriteMessage(val.head, val.data); err != nil { | ||||
| 					return | ||||
| 				} | ||||
| 			default: | ||||
| 				if head, data, err := conn.ReadMessage(); err != nil { | ||||
| 					return | ||||
| 				} else { | ||||
| 					remote.WriteMessage(head, data) | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	}) | ||||
| } | ||||
| 
 | ||||
| func makeSeverSideEventResponse(c *fiber.Ctx, dest *Destination) error { | ||||
| 	// TODO Impl SSE with https://github.com/gofiber/recipes/blob/master/sse/main.go | ||||
| 	return fiber.NewError(fiber.StatusNotImplemented, "Server-side-events was not available now.") | ||||
| } | ||||
| 
 | ||||
| func makeFileResponse(c *fiber.Ctx, dest *Destination) error { | ||||
| 	uri, queries := dest.GetRawUri() | ||||
| 	root := http.Dir(uri) | ||||
| 
 | ||||
| 	method := c.Method() | ||||
							
								
								
									
										25
									
								
								pkg/navi/route.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								pkg/navi/route.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| package navi | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi/transformers" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| type RoadApp struct { | ||||
| 	Regions []*Region   `json:"regions"` | ||||
| 	Traces  []RoadTrace `json:"traces"` | ||||
| } | ||||
|  | ||||
| func (v *RoadApp) Forward(ctx *fiber.Ctx, dest *Destination) error { | ||||
| 	switch dest.GetType() { | ||||
| 	case DestinationHypertext: | ||||
| 		return makeUnifiedResponse(ctx, dest) | ||||
| 	case DestinationStaticFile: | ||||
| 		return makeFileResponse(ctx, dest) | ||||
| 	default: | ||||
| 		return fiber.ErrBadGateway | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type RequestTransformerConfig = transformers.TransformerConfig | ||||
							
								
								
									
										87
									
								
								pkg/navi/struct.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								pkg/navi/struct.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,87 @@ | ||||
| package navi | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
|  | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi/transformers" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/warden" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| type Region struct { | ||||
| 	ID           string               `json:"id" toml:"id"` | ||||
| 	Disabled     bool                 `json:"disabled" toml:"disabled"` | ||||
| 	Locations    []Location           `json:"locations" toml:"locations"` | ||||
| 	Applications []warden.Application `json:"applications" toml:"applications"` | ||||
| } | ||||
|  | ||||
| type Location struct { | ||||
| 	ID           string              `json:"id" toml:"id"` | ||||
| 	Host         []string            `json:"host" toml:"host"` | ||||
| 	Path         []string            `json:"path" toml:"path"` | ||||
| 	Queries      map[string]string   `json:"queries" toml:"queries"` | ||||
| 	Headers      map[string][]string `json:"headers" toml:"headers"` | ||||
| 	Destinations []Destination       `json:"destinations" toml:"destinations"` | ||||
| } | ||||
|  | ||||
| type DestinationType = int8 | ||||
|  | ||||
| const ( | ||||
| 	DestinationHypertext = DestinationType(iota) | ||||
| 	DestinationStaticFile | ||||
| 	DestinationUnknown | ||||
| ) | ||||
|  | ||||
| type Destination struct { | ||||
| 	ID           string                           `json:"id" toml:"id"` | ||||
| 	Uri          string                           `json:"uri" toml:"uri"` | ||||
| 	Transformers []transformers.TransformerConfig `json:"transformers" toml:"transformers"` | ||||
| } | ||||
|  | ||||
| func (v *Destination) GetProtocol() string { | ||||
| 	return strings.SplitN(v.Uri, "://", 2)[0] | ||||
| } | ||||
|  | ||||
| func (v *Destination) GetType() DestinationType { | ||||
| 	protocol := v.GetProtocol() | ||||
| 	switch protocol { | ||||
| 	case "http", "https": | ||||
| 		return DestinationHypertext | ||||
| 	case "file", "files": | ||||
| 		return DestinationStaticFile | ||||
| 	} | ||||
| 	return DestinationUnknown | ||||
| } | ||||
|  | ||||
| func (v *Destination) GetRawUri() (string, url.Values) { | ||||
| 	uri := strings.SplitN(v.Uri, "://", 2)[1] | ||||
| 	data := strings.SplitN(uri, "?", 2) | ||||
| 	data = append(data, " ") // Make data array least have two element | ||||
| 	qs, _ := url.ParseQuery(data[1]) | ||||
|  | ||||
| 	return data[0], qs | ||||
| } | ||||
|  | ||||
| func (v *Destination) MakeUri(ctx *fiber.Ctx) string { | ||||
| 	var queries []string | ||||
| 	for k, v := range ctx.Queries() { | ||||
| 		parsed, _ := url.QueryUnescape(v) | ||||
| 		value := url.QueryEscape(parsed) | ||||
| 		queries = append(queries, fmt.Sprintf("%s=%s", k, value)) | ||||
| 	} | ||||
|  | ||||
| 	path := string(ctx.Request().URI().Path()) | ||||
| 	hash := string(ctx.Request().URI().Hash()) | ||||
| 	uri, _ := v.GetRawUri() | ||||
|  | ||||
| 	return uri + path + | ||||
| 		lo.Ternary(len(queries) > 0, "?"+strings.Join(queries, "&"), "") + | ||||
| 		lo.Ternary(len(hash) > 0, "#"+hash, "") | ||||
| } | ||||
|  | ||||
| func (v *Destination) MakeWebsocketUri(ctx *fiber.Ctx) string { | ||||
| 	return strings.Replace(v.MakeUri(ctx), "http", "ws", 1) | ||||
| } | ||||
| @@ -5,13 +5,13 @@ import ( | ||||
| 	"github.com/valyala/fasthttp" | ||||
| ) | ||||
| 
 | ||||
| var CompressResponse = RequestTransformer{ | ||||
| var CompressResponse = Transformer{ | ||||
| 	ModifyResponse: func(options any, ctx *fiber.Ctx) error { | ||||
| 		opts := DeserializeOptions[struct { | ||||
| 			Level int `json:"level" yaml:"level"` | ||||
| 			Level int `json:"level" toml:"level"` | ||||
| 		}](options) | ||||
| 
 | ||||
| 		var fctx = func(c *fasthttp.RequestCtx) {} | ||||
| 		fctx := func(c *fasthttp.RequestCtx) {} | ||||
| 		var compressor fasthttp.RequestHandler | ||||
| 		switch opts.Level { | ||||
| 		// Best Speed Mode | ||||
| @@ -9,17 +9,17 @@ import ( | ||||
| 
 | ||||
| var json = jsoniter.ConfigCompatibleWithStandardLibrary | ||||
| 
 | ||||
| type RequestTransformer struct { | ||||
| type Transformer struct { | ||||
| 	ModifyRequest  func(options any, ctx *fiber.Ctx) error | ||||
| 	ModifyResponse func(options any, ctx *fiber.Ctx) error | ||||
| } | ||||
| 
 | ||||
| type RequestTransformerConfig struct { | ||||
| 	Type    string `json:"type" yaml:"type"` | ||||
| 	Options any    `json:"options" yaml:"options"` | ||||
| type TransformerConfig struct { | ||||
| 	Type    string `json:"type" toml:"type"` | ||||
| 	Options any    `json:"options" toml:"options"` | ||||
| } | ||||
| 
 | ||||
| func (v *RequestTransformerConfig) TransformRequest(ctx *fiber.Ctx) error { | ||||
| func (v *TransformerConfig) TransformRequest(ctx *fiber.Ctx) error { | ||||
| 	for k, f := range Transformers { | ||||
| 		if k == v.Type { | ||||
| 			if f.ModifyRequest != nil { | ||||
| @@ -31,7 +31,7 @@ func (v *RequestTransformerConfig) TransformRequest(ctx *fiber.Ctx) error { | ||||
| 	return nil | ||||
| } | ||||
| 
 | ||||
| func (v *RequestTransformerConfig) TransformResponse(ctx *fiber.Ctx) error { | ||||
| func (v *TransformerConfig) TransformResponse(ctx *fiber.Ctx) error { | ||||
| 	for k, f := range Transformers { | ||||
| 		if k == v.Type { | ||||
| 			if f.ModifyResponse != nil { | ||||
| @@ -55,7 +55,7 @@ func DeserializeOptions[T any](data any) T { | ||||
| // Map of Transformers | ||||
| // Every transformer need to be mapped here so that they can get work. | ||||
| 
 | ||||
| var Transformers = map[string]RequestTransformer{ | ||||
| var Transformers = map[string]Transformer{ | ||||
| 	"replacePath":      ReplacePath, | ||||
| 	"compressResponse": CompressResponse, | ||||
| } | ||||
| @@ -1,18 +1,19 @@ | ||||
| package transformers | ||||
| 
 | ||||
| import ( | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"regexp" | ||||
| 	"strings" | ||||
| 
 | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| var ReplacePath = RequestTransformer{ | ||||
| var ReplacePath = Transformer{ | ||||
| 	ModifyRequest: func(options any, ctx *fiber.Ctx) error { | ||||
| 		opts := DeserializeOptions[struct { | ||||
| 			Pattern string `json:"pattern" yaml:"pattern"` | ||||
| 			Value   string `json:"value" yaml:"value"` | ||||
| 			Repl    string `json:"repl" yaml:"repl"` // Use when complex mode(regexp) enabled | ||||
| 			Complex bool   `json:"complex" yaml:"complex"` | ||||
| 			Pattern string `json:"pattern" toml:"pattern"` | ||||
| 			Value   string `json:"value" toml:"value"` | ||||
| 			Repl    string `json:"repl" toml:"repl"` // Use when complex mode(regexp) enabled | ||||
| 			Complex bool   `json:"complex" toml:"complex"` | ||||
| 		}](options) | ||||
| 		path := string(ctx.Request().URI().Path()) | ||||
| 		if !opts.Complex { | ||||
							
								
								
									
										17
									
								
								pkg/navi/warden.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								pkg/navi/warden.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| package navi | ||||
|  | ||||
| import "code.smartsheep.studio/goatworks/roadsign/pkg/warden" | ||||
|  | ||||
| func InitializeWarden(regions []*Region) { | ||||
| 	for _, region := range regions { | ||||
| 		for _, application := range region.Applications { | ||||
| 			warden.InstancePool = append(warden.InstancePool, &warden.AppInstance{ | ||||
| 				Manifest: application, | ||||
| 			}) | ||||
| 		} | ||||
| 	} | ||||
| 	 | ||||
| 	for _, instance := range warden.InstancePool { | ||||
| 		instance.Wake() | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										26
									
								
								pkg/sideload/applications.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								pkg/sideload/applications.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,26 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/warden" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| func getApplications(c *fiber.Ctx) error { | ||||
| 	applications := lo.FlatMap(navi.R.Regions, func(item *navi.Region, idx int) []warden.Application { | ||||
| 		return item.Applications | ||||
| 	}) | ||||
|  | ||||
| 	return c.JSON(applications) | ||||
| } | ||||
|  | ||||
| func getApplicationLogs(c *fiber.Ctx) error { | ||||
| 	if instance, ok := lo.Find(warden.InstancePool, func(item *warden.AppInstance) bool { | ||||
| 		return item.Manifest.ID == c.Params("id") | ||||
| 	}); !ok { | ||||
| 		return fiber.NewError(fiber.StatusNotFound) | ||||
| 	} else { | ||||
| 		return c.SendString(instance.Logs()) | ||||
| 	} | ||||
| } | ||||
| @@ -5,7 +5,7 @@ import ( | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
| 
 | ||||
| func responseConnectivity(c *fiber.Ctx) error { | ||||
| func getMetadata(c *fiber.Ctx) error { | ||||
| 	return c.Status(fiber.StatusOK).JSON(fiber.Map{ | ||||
| 		"server":  "RoadSign", | ||||
| 		"version": roadsign.AppVersion, | ||||
							
								
								
									
										10
									
								
								pkg/sideload/metrics.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								pkg/sideload/metrics.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| ) | ||||
|  | ||||
| func getTraces(c *fiber.Ctx) error { | ||||
| 	return c.JSON(navi.R.Traces) | ||||
| } | ||||
| @@ -1,29 +0,0 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| func getProcesses(c *fiber.Ctx) error { | ||||
| 	processes := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { | ||||
| 		return item.Processes | ||||
| 	}) | ||||
|  | ||||
| 	return c.JSON(processes) | ||||
| } | ||||
|  | ||||
| func getProcessLog(c *fiber.Ctx) error { | ||||
| 	processes := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { | ||||
| 		return item.Processes | ||||
| 	}) | ||||
|  | ||||
| 	if target, ok := lo.Find(processes, func(item *sign.ProcessInstance) bool { | ||||
| 		return item.ID == c.Params("id") | ||||
| 	}); !ok { | ||||
| 		return fiber.NewError(fiber.StatusNotFound) | ||||
| 	} else { | ||||
| 		return c.SendString(target.GetLogs()) | ||||
| 	} | ||||
| } | ||||
| @@ -1,11 +1,12 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/warden" | ||||
| 	"context" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/google/uuid" | ||||
| 	"github.com/samber/lo" | ||||
| @@ -14,23 +15,23 @@ import ( | ||||
|  | ||||
| func doPublish(c *fiber.Ctx) error { | ||||
| 	var workdir string | ||||
| 	var site *sign.SiteConfig | ||||
| 	var upstream *sign.UpstreamInstance | ||||
| 	var process *sign.ProcessInstance | ||||
| 	for _, item := range sign.App.Sites { | ||||
| 	var destination *navi.Destination | ||||
| 	var application *warden.Application | ||||
| 	for _, item := range navi.R.Regions { | ||||
| 		if item.ID == c.Params("site") { | ||||
| 			site = item | ||||
| 			for _, stream := range item.Upstreams { | ||||
| 				if stream.ID == c.Params("slug") { | ||||
| 					upstream = stream | ||||
| 					workdir, _ = stream.GetRawURI() | ||||
| 					break | ||||
| 			for _, location := range item.Locations { | ||||
| 				for _, dest := range location.Destinations { | ||||
| 					if dest.ID == c.Params("slug") { | ||||
| 						destination = &dest | ||||
| 						workdir, _ = dest.GetRawUri() | ||||
| 						break | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 			for _, proc := range item.Processes { | ||||
| 				if proc.ID == c.Params("slug") { | ||||
| 					process = proc | ||||
| 					workdir = proc.Workdir | ||||
| 			for _, app := range item.Applications { | ||||
| 				if app.ID == c.Params("slug") { | ||||
| 					application = &app | ||||
| 					workdir = app.Workdir | ||||
| 					break | ||||
| 				} | ||||
| 			} | ||||
| @@ -38,14 +39,15 @@ func doPublish(c *fiber.Ctx) error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if upstream == nil && process == nil { | ||||
| 		return fiber.ErrNotFound | ||||
| 	} else if upstream != nil && upstream.GetType() != sign.UpstreamTypeFile { | ||||
| 	var instance *warden.AppInstance | ||||
| 	if application != nil { | ||||
| 		if instance = warden.GetFromPool(application.ID); instance != nil { | ||||
| 			instance.Stop() | ||||
| 		} | ||||
| 	} else if destination != nil && destination.GetType() != navi.DestinationStaticFile { | ||||
| 		return fiber.ErrUnprocessableEntity | ||||
| 	} | ||||
|  | ||||
| 	for _, process := range site.Processes { | ||||
| 		process.StopProcess() | ||||
| 	} else { | ||||
| 		return fiber.ErrNotFound | ||||
| 	} | ||||
|  | ||||
| 	if c.Query("overwrite", "yes") == "yes" { | ||||
| @@ -81,5 +83,9 @@ func doPublish(c *fiber.Ctx) error { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if instance != nil { | ||||
| 		instance.Wake() | ||||
| 	} | ||||
|  | ||||
| 	return c.SendStatus(fiber.StatusOK) | ||||
| } | ||||
|   | ||||
							
								
								
									
										67
									
								
								pkg/sideload/regions.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								pkg/sideload/regions.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,67 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/warden" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/pelletier/go-toml/v2" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/spf13/viper" | ||||
| ) | ||||
|  | ||||
| func getRegions(c *fiber.Ctx) error { | ||||
| 	return c.JSON(navi.R.Regions) | ||||
| } | ||||
|  | ||||
| func getRegionConfig(c *fiber.Ctx) error { | ||||
| 	fp := filepath.Join(viper.GetString("paths.configs"), c.Params("id")) | ||||
|  | ||||
| 	var err error | ||||
| 	var data []byte | ||||
| 	if data, err = os.ReadFile(fp + ".toml"); err != nil { | ||||
| 		return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 	} | ||||
|  | ||||
| 	return c.Type("toml").SendString(string(data)) | ||||
| } | ||||
|  | ||||
| func doSync(c *fiber.Ctx) error { | ||||
| 	req := string(c.Body()) | ||||
|  | ||||
| 	id := c.Params("slug") | ||||
| 	path := filepath.Join(viper.GetString("paths.configs"), fmt.Sprintf("%s.toml", id)) | ||||
|  | ||||
| 	if file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755); err != nil { | ||||
| 		return fiber.NewError(fiber.ErrInternalServerError.Code, err.Error()) | ||||
| 	} else { | ||||
| 		raw, _ := toml.Marshal(req) | ||||
| 		file.Write(raw) | ||||
| 		defer file.Close() | ||||
| 	} | ||||
| 	 | ||||
| 	var rebootQueue []*warden.AppInstance | ||||
| 	if region, ok := lo.Find(navi.R.Regions, func(item *navi.Region) bool { | ||||
| 		return item.ID == id | ||||
| 	}); ok { | ||||
| 		for _, application := range region.Applications { | ||||
| 			if instance := warden.GetFromPool(application.ID); instance != nil { | ||||
| 				instance.Stop() | ||||
| 				rebootQueue = append(rebootQueue, instance) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Reload | ||||
| 	navi.ReadInConfig(viper.GetString("paths.configs")) | ||||
| 	 | ||||
| 	// Reboot | ||||
| 	for _, instance := range rebootQueue { | ||||
| 		instance.Wake() | ||||
| 	} | ||||
|  | ||||
| 	return c.SendStatus(fiber.StatusOK) | ||||
| } | ||||
| @@ -1,11 +1,12 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sideload/view" | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
|  | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sideload/view" | ||||
| 	"github.com/gofiber/fiber/v2/middleware/filesystem" | ||||
| 	jsoniter "github.com/json-iterator/go" | ||||
| 	"net/http" | ||||
|  | ||||
| 	roadsign "code.smartsheep.studio/goatworks/roadsign/pkg" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| @@ -51,18 +52,19 @@ func InitSideload() *fiber.App { | ||||
|  | ||||
| 	cgi := app.Group("/cgi").Name("CGI") | ||||
| 	{ | ||||
| 		cgi.All("/connectivity", responseConnectivity) | ||||
| 		cgi.Get("/statistics", getStatistics) | ||||
| 		cgi.Get("/sites", getSites) | ||||
| 		cgi.Get("/sites/cfg/:id", getSiteConfig) | ||||
| 		cgi.Get("/processes", getProcesses) | ||||
| 		cgi.Get("/processes/logs/:id", getProcessLog) | ||||
| 		cgi.Get("/metadata", getMetadata) | ||||
| 		cgi.Get("/traces", getTraces) | ||||
| 		cgi.Get("/stats", getStats) | ||||
| 		cgi.Get("/regions", getRegions) | ||||
| 		cgi.Get("/regions/cfg/:id", getRegionConfig) | ||||
| 		cgi.Get("/applications", getApplications) | ||||
| 		cgi.Get("/applications/logs/:id", getApplicationLogs) | ||||
| 	} | ||||
|  | ||||
| 	webhooks := app.Group("/webhooks").Name("WebHooks") | ||||
| 	{ | ||||
| 		webhooks.Put("/publish/:site/:slug", doPublish) | ||||
| 		webhooks.Put("/sync/:slug", doSyncSite) | ||||
| 		webhooks.Put("/sync/:slug", doSync) | ||||
| 	} | ||||
|  | ||||
| 	return app | ||||
|   | ||||
| @@ -1,63 +0,0 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"path/filepath" | ||||
|  | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| 	"github.com/spf13/viper" | ||||
| 	"gopkg.in/yaml.v2" | ||||
| ) | ||||
|  | ||||
| func getSites(c *fiber.Ctx) error { | ||||
| 	return c.JSON(sign.App.Sites) | ||||
| } | ||||
|  | ||||
| func getSiteConfig(c *fiber.Ctx) error { | ||||
| 	fp := filepath.Join(viper.GetString("paths.configs"), c.Params("id")) | ||||
|  | ||||
| 	var err error | ||||
| 	var data []byte | ||||
| 	if data, err = os.ReadFile(fp + ".yml"); err != nil { | ||||
| 		if data, err = os.ReadFile(fp + ".yaml"); err != nil { | ||||
| 			return fiber.NewError(fiber.StatusNotFound, err.Error()) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return c.Type("yaml").SendString(string(data)) | ||||
| } | ||||
|  | ||||
| func doSyncSite(c *fiber.Ctx) error { | ||||
| 	var req sign.SiteConfig | ||||
|  | ||||
| 	if err := c.BodyParser(&req); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
|  | ||||
| 	id := c.Params("slug") | ||||
| 	path := filepath.Join(viper.GetString("paths.configs"), fmt.Sprintf("%s.yaml", id)) | ||||
|  | ||||
| 	if file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0755); err != nil { | ||||
| 		return fiber.NewError(fiber.ErrInternalServerError.Code, err.Error()) | ||||
| 	} else { | ||||
| 		raw, _ := yaml.Marshal(req) | ||||
| 		file.Write(raw) | ||||
| 		defer file.Close() | ||||
| 	} | ||||
| 	if site, ok := lo.Find(sign.App.Sites, func(item *sign.SiteConfig) bool { | ||||
| 		return item.ID == id | ||||
| 	}); ok { | ||||
| 		for _, process := range site.Processes { | ||||
| 			process.StopProcess() | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Reload | ||||
| 	sign.ReadInConfig(viper.GetString("paths.configs")) | ||||
| 	sign.App.PreheatProcesses() | ||||
|  | ||||
| 	return c.SendStatus(fiber.StatusOK) | ||||
| } | ||||
| @@ -1,28 +1,27 @@ | ||||
| package sideload | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/navi" | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/warden" | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| func getStatistics(c *fiber.Ctx) error { | ||||
| 	upstreams := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.UpstreamInstance { | ||||
| 		return item.Upstreams | ||||
| func getStats(c *fiber.Ctx) error { | ||||
| 	locations := lo.FlatMap(navi.R.Regions, func(item *navi.Region, idx int) []navi.Location { | ||||
| 		return item.Locations | ||||
| 	}) | ||||
| 	processes := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { | ||||
| 		return item.Processes | ||||
| 	destinations := lo.FlatMap(locations, func(item navi.Location, idx int) []navi.Destination { | ||||
| 		return item.Destinations | ||||
| 	}) | ||||
| 	unhealthy := lo.FlatMap(sign.App.Sites, func(item *sign.SiteConfig, idx int) []*sign.ProcessInstance { | ||||
| 		return lo.Filter(item.Processes, func(item *sign.ProcessInstance, idx int) bool { | ||||
| 			return item.Status != sign.ProcessStarted | ||||
| 		}) | ||||
| 	applications := lo.FlatMap(navi.R.Regions, func(item *navi.Region, idx int) []warden.Application { | ||||
| 		return item.Applications | ||||
| 	}) | ||||
|  | ||||
| 	return c.JSON(fiber.Map{ | ||||
| 		"sites":     len(sign.App.Sites), | ||||
| 		"upstreams": len(upstreams), | ||||
| 		"processes": len(processes), | ||||
| 		"status":    len(unhealthy) == 0, | ||||
| 		"regions":      len(navi.R.Regions), | ||||
| 		"locations":    len(locations), | ||||
| 		"destinations": len(destinations), | ||||
| 		"applications": len(applications), | ||||
| 	}) | ||||
| } | ||||
|   | ||||
| @@ -1,3 +0,0 @@ | ||||
| /dist | ||||
| /node_modules | ||||
| /*.lock | ||||
| @@ -1,15 +0,0 @@ | ||||
| /* eslint-env node */ | ||||
| require('@rushstack/eslint-patch/modern-module-resolution') | ||||
|  | ||||
| module.exports = { | ||||
|   root: true, | ||||
|   'extends': [ | ||||
|     'plugin:vue/vue3-essential', | ||||
|     'eslint:recommended', | ||||
|     '@vue/eslint-config-typescript', | ||||
|     '@vue/eslint-config-prettier/skip-formatting' | ||||
|   ], | ||||
|   parserOptions: { | ||||
|     ecmaVersion: 'latest' | ||||
|   } | ||||
| } | ||||
							
								
								
									
										9
									
								
								pkg/sideload/view/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										9
									
								
								pkg/sideload/view/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -8,23 +8,20 @@ pnpm-debug.log* | ||||
| lerna-debug.log* | ||||
|  | ||||
| node_modules | ||||
| .DS_Store | ||||
| dist | ||||
| dist-ssr | ||||
| coverage | ||||
| *.local | ||||
|  | ||||
| /cypress/videos/ | ||||
| /cypress/screenshots/ | ||||
|  | ||||
| # Editor directories and files | ||||
| .vscode/* | ||||
| !.vscode/extensions.json | ||||
| .idea | ||||
| .DS_Store | ||||
| *.suo | ||||
| *.ntvs* | ||||
| *.njsproj | ||||
| *.sln | ||||
| *.sw? | ||||
|  | ||||
| *.tsbuildinfo | ||||
| *.lock | ||||
| package-lock.json | ||||
| @@ -1,8 +0,0 @@ | ||||
| { | ||||
|   "$schema": "https://json.schemastore.org/prettierrc", | ||||
|   "semi": false, | ||||
|   "tabWidth": 2, | ||||
|   "singleQuote": false, | ||||
|   "printWidth": 120, | ||||
|   "trailingComma": "none" | ||||
| } | ||||
							
								
								
									
										8
									
								
								pkg/sideload/view/.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										8
									
								
								pkg/sideload/view/.vscode/extensions.json
									
									
									
									
										vendored
									
									
								
							| @@ -1,8 +0,0 @@ | ||||
| { | ||||
|   "recommendations": [ | ||||
|     "Vue.volar", | ||||
|     "Vue.vscode-typescript-vue-plugin", | ||||
|     "dbaeumer.vscode-eslint", | ||||
|     "esbenp.prettier-vscode" | ||||
|   ] | ||||
| } | ||||
| @@ -1,46 +0,0 @@ | ||||
| # @roadsign/sideload-ui | ||||
|  | ||||
| This template should help get you started developing with Vue 3 in Vite. | ||||
|  | ||||
| ## Recommended IDE Setup | ||||
|  | ||||
| [VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin). | ||||
|  | ||||
| ## Type Support for `.vue` Imports in TS | ||||
|  | ||||
| TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin) to make the TypeScript language service aware of `.vue` types. | ||||
|  | ||||
| If the standalone TypeScript plugin doesn't feel fast enough to you, Volar has also implemented a [Take Over Mode](https://github.com/johnsoncodehk/volar/discussions/471#discussioncomment-1361669) that is more performant. You can enable it by the following steps: | ||||
|  | ||||
| 1. Disable the built-in TypeScript Extension | ||||
|     1) Run `Extensions: Show Built-in Extensions` from VSCode's command palette | ||||
|     2) Find `TypeScript and JavaScript Language Features`, right click and select `Disable (Workspace)` | ||||
| 2. Reload the VSCode window by running `Developer: Reload Window` from the command palette. | ||||
|  | ||||
| ## Customize configuration | ||||
|  | ||||
| See [Vite Configuration Reference](https://vitejs.dev/config/). | ||||
|  | ||||
| ## Project Setup | ||||
|  | ||||
| ```sh | ||||
| yarn | ||||
| ``` | ||||
|  | ||||
| ### Compile and Hot-Reload for Development | ||||
|  | ||||
| ```sh | ||||
| yarn dev | ||||
| ``` | ||||
|  | ||||
| ### Type-Check, Compile and Minify for Production | ||||
|  | ||||
| ```sh | ||||
| yarn build | ||||
| ``` | ||||
|  | ||||
| ### Lint with [ESLint](https://eslint.org/) | ||||
|  | ||||
| ```sh | ||||
| yarn lint | ||||
| ``` | ||||
| @@ -1,13 +1,13 @@ | ||||
| <!DOCTYPE html> | ||||
| <!doctype html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8"> | ||||
|     <link rel="icon" href="/favicon.ico"> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|     <title>RoadSign</title> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <link rel="icon" type="image/svg+xml" href="/favicon.svg" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>RoadSign Sideload</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div id="app"></div> | ||||
|     <script type="module" src="/src/main.ts"></script> | ||||
|     <div id="root"></div> | ||||
|     <script type="module" src="/src/index.tsx"></script> | ||||
|   </body> | ||||
| </html> | ||||
|   | ||||
| @@ -1,44 +1,24 @@ | ||||
| { | ||||
|   "name": "@roadsign/sideload-ui", | ||||
|   "version": "0.0.0", | ||||
|   "name": "roadsign-sideload", | ||||
|   "private": true, | ||||
|   "version": "0.0.0", | ||||
|   "type": "module", | ||||
|   "scripts": { | ||||
|     "dev": "vite", | ||||
|     "build": "run-p type-check \"build-only {@}\" --", | ||||
|     "preview": "vite preview", | ||||
|     "build-only": "vite build", | ||||
|     "type-check": "vue-tsc --build --force", | ||||
|     "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore", | ||||
|     "format": "prettier --write src/" | ||||
|     "build": "tsc && vite build", | ||||
|     "preview": "vite preview" | ||||
|   }, | ||||
|   "dependencies": { | ||||
|     "@guolao/vue-monaco-editor": "^1.4.1", | ||||
|     "highlight.js": "^11.9.0", | ||||
|     "js-yaml": "^4.1.0", | ||||
|     "pinia": "^2.1.7", | ||||
|     "vue": "^3.3.11", | ||||
|     "vue-router": "^4.2.5" | ||||
|     "@solidjs/router": "^0.10.10", | ||||
|     "solid-js": "^1.8.7" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@rushstack/eslint-patch": "^1.3.3", | ||||
|     "@tsconfig/node18": "^18.2.2", | ||||
|     "@types/js-yaml": "^4.0.9", | ||||
|     "@types/node": "^18.19.3", | ||||
|     "@vicons/carbon": "^0.12.0", | ||||
|     "@vitejs/plugin-vue": "^4.5.2", | ||||
|     "@vue/eslint-config-prettier": "^8.0.0", | ||||
|     "@vue/eslint-config-typescript": "^12.0.0", | ||||
|     "@vue/tsconfig": "^0.5.0", | ||||
|     "eslint": "^8.49.0", | ||||
|     "eslint-plugin-vue": "^9.17.0", | ||||
|     "naive-ui": "^2.36.0", | ||||
|     "npm-run-all2": "^6.1.1", | ||||
|     "prettier": "^3.0.3", | ||||
|     "typescript": "~5.3.0", | ||||
|     "unocss": "^0.58.2", | ||||
|     "vfonts": "^0.0.3", | ||||
|     "vite": "^5.0.10", | ||||
|     "vue-tsc": "^1.8.25" | ||||
|     "autoprefixer": "^10.4.17", | ||||
|     "daisyui": "^4.6.0", | ||||
|     "postcss": "^8.4.33", | ||||
|     "tailwindcss": "^3.4.1", | ||||
|     "typescript": "^5.2.2", | ||||
|     "vite": "^5.0.8", | ||||
|     "vite-plugin-solid": "^2.8.0" | ||||
|   } | ||||
| } | ||||
|   | ||||
							
								
								
									
										6
									
								
								pkg/sideload/view/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								pkg/sideload/view/postcss.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| export default { | ||||
|   plugins: { | ||||
|     tailwindcss: {}, | ||||
|     autoprefixer: {}, | ||||
|   }, | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| @import "vfonts/IBMPlexSans.css"; | ||||
| @import "vfonts/IBMPlexMono.css"; | ||||
|  | ||||
| a { | ||||
|     color: #3f7ee8; | ||||
| } | ||||
| @@ -1,135 +0,0 @@ | ||||
| <template> | ||||
|   <div class="flex gap-[4px]"> | ||||
|     <n-button size="small" @click="publishing = true"> | ||||
|       <template #icon> | ||||
|         <n-icon :component="CloudUpload" /> | ||||
|       </template> | ||||
|     </n-button> | ||||
|     <n-button size="small" @click="editConfig()"> | ||||
|       <template #icon> | ||||
|         <n-icon :component="Edit" /> | ||||
|       </template> | ||||
|     </n-button> | ||||
|  | ||||
|     <n-modal | ||||
|       v-model:show="publishing" | ||||
|       class="w-[720px]" | ||||
|       preset="card" | ||||
|       title="Publish Artifacts" | ||||
|       segmented | ||||
|       closable | ||||
|     > | ||||
|       We are sorry about this tool isn't completed yet. <br> | ||||
|       For now, you can use our <b>Wonderful Command Line Tool —— RDS</b> <br> | ||||
|       Learn more on our <a href="https://wiki.smartsheep.studio/roadsign/index.html" target="_blank">official wiki</a>. | ||||
|       <br> | ||||
|       <br> | ||||
|       Install it by this command below | ||||
|       <n-code code="go install code.smartsheep.studio/goatworks/roadsign/pkg/cmd/rds@latest" /> | ||||
|       <br> | ||||
|       Then connect your rds client to this server | ||||
|       <n-code :code="`rds connect <name> ${host} <credentials>`" /> | ||||
|       <br> | ||||
|       After that you can publish your stuff (You need to compress them to zip archive before publish) | ||||
|       <n-code :code="`rds deploy <name> ${props.id} <upstream id or process id>`" /> | ||||
|     </n-modal> | ||||
|  | ||||
|     <n-modal | ||||
|       v-model:show="editing" | ||||
|       class="w-[720px]" | ||||
|       content-style="padding: 0" | ||||
|       preset="card" | ||||
|       title="Edit Configuration" | ||||
|       segmented | ||||
|       closable | ||||
|     > | ||||
|       <div class="relative h-[540px]"> | ||||
|         <vue-monaco-editor | ||||
|           v-model:value="config" | ||||
|           :options="{ automaticLayout: true, minimap: { enabled: false } }" | ||||
|           language="yaml" | ||||
|         /> | ||||
|  | ||||
|         <div class="fab"> | ||||
|           <n-tooltip placement="left"> | ||||
|             <template #trigger> | ||||
|               <n-button | ||||
|                 circle | ||||
|                 type="primary" | ||||
|                 size="large" | ||||
|                 class="shadow-lg" | ||||
|                 :loading="submitting" | ||||
|                 @click="syncConfig()" | ||||
|               > | ||||
|                 <template #icon> | ||||
|                   <n-icon :component="Save" /> | ||||
|                 </template> | ||||
|               </n-button> | ||||
|             </template> | ||||
|             This operation will restart all processes related. Service may interrupted for some while. | ||||
|           </n-tooltip> | ||||
|         </div> | ||||
|       </div> | ||||
|     </n-modal> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NButton, NCode, NIcon, NModal, NTooltip, useMessage } from "naive-ui" | ||||
| import { CloudUpload, Edit, Save } from "@vicons/carbon" | ||||
| import { ref } from "vue" | ||||
| import { VueMonacoEditor } from "@guolao/vue-monaco-editor" | ||||
| import * as yaml from "js-yaml" | ||||
|  | ||||
| const message = useMessage() | ||||
|  | ||||
| const props = defineProps<{ id: string, rules: any[], upstreams: any[], processes: any[] }>() | ||||
| const emits = defineEmits(["reload"]) | ||||
| const host = location.protocol + "//" + location.host | ||||
|  | ||||
| const submitting = ref(false) | ||||
|  | ||||
| const publishing = ref(false) | ||||
| const editing = ref(false) | ||||
|  | ||||
| const config = ref<string | undefined>(undefined) | ||||
|  | ||||
| async function editConfig() { | ||||
|   const resp = await fetch(`/cgi/sites/cfg/${props.id}`) | ||||
|   config.value = await resp.text() | ||||
|   editing.value = true | ||||
| } | ||||
|  | ||||
| async function syncConfig() { | ||||
|   if (config.value == null) return | ||||
|  | ||||
|   let content | ||||
|   try { | ||||
|     content = yaml.load(config.value) | ||||
|   } catch (e: any) { | ||||
|     message.warning(`Your configuration has some issue: ${e.message}`) | ||||
|     return | ||||
|   } | ||||
|  | ||||
|   submitting.value = true | ||||
|   const resp = await fetch(`/webhooks/sync/${props.id}`, { | ||||
|     method: "PUT", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify(content) | ||||
|   }) | ||||
|   if (resp.status != 200) { | ||||
|     message.error(`Something went wrong... ${await resp.text()}`) | ||||
|   } else { | ||||
|     emits("reload") | ||||
|   } | ||||
|   submitting.value = false | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .fab { | ||||
|   position: absolute; | ||||
|   bottom: 16px; | ||||
|   right: 24px; | ||||
| } | ||||
| </style> | ||||
| @@ -1,110 +0,0 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-button circle size="small" type="primary" @click="creating = true"> | ||||
|       <template #icon> | ||||
|         <n-icon :component="Add" /> | ||||
|       </template> | ||||
|     </n-button> | ||||
|  | ||||
|     <n-modal | ||||
|       v-model:show="creating" | ||||
|       class="w-[720px]" | ||||
|       content-style="padding: 0" | ||||
|       preset="card" | ||||
|       title="Create Site" | ||||
|       segmented | ||||
|       closable | ||||
|     > | ||||
|       <div class="py-4 px-5 border border-solid border-b border-[#eee]"> | ||||
|         <n-input | ||||
|           v-model:value="data.id" | ||||
|           placeholder="Will be the file name of this file" | ||||
|         /> | ||||
|       </div> | ||||
|  | ||||
|       <div class="relative mt-[4px] h-[540px]"> | ||||
|         <vue-monaco-editor | ||||
|           v-model:value="data.content" | ||||
|           :options="{ automaticLayout: true, minimap: { enabled: false } }" | ||||
|           language="yaml" | ||||
|         /> | ||||
|  | ||||
|         <div class="fab"> | ||||
|           <n-tooltip placement="left"> | ||||
|             <template #trigger> | ||||
|               <n-button | ||||
|                 circle | ||||
|                 type="primary" | ||||
|                 size="large" | ||||
|                 class="shadow-lg" | ||||
|                 :loading="submitting" | ||||
|                 @click="submit()" | ||||
|               > | ||||
|                 <template #icon> | ||||
|                   <n-icon :component="Checkmark" /> | ||||
|                 </template> | ||||
|               </n-button> | ||||
|             </template> | ||||
|             This operation will publish this site right away. | ||||
|           </n-tooltip> | ||||
|         </div> | ||||
|       </div> | ||||
|     </n-modal> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NButton, NIcon, NInput, NModal, NTooltip, useMessage } from "naive-ui" | ||||
| import { Add, Checkmark } from "@vicons/carbon" | ||||
| import { VueMonacoEditor } from "@guolao/vue-monaco-editor" | ||||
| import { ref } from "vue" | ||||
| import * as yaml from "js-yaml" | ||||
|  | ||||
| const message = useMessage() | ||||
|  | ||||
| const emits = defineEmits(["reload"]) | ||||
|  | ||||
| const submitting = ref(false) | ||||
| const creating = ref(false) | ||||
|  | ||||
| const data = ref<any>({}) | ||||
|  | ||||
| async function submit() { | ||||
|   let content | ||||
|   try { | ||||
|     content = yaml.load(data.value.content) | ||||
|   } catch (e: any) { | ||||
|     message.warning(`Your configuration has some issue: ${e.message}`) | ||||
|     return | ||||
|   } | ||||
|  | ||||
|   submitting.value = true | ||||
|   const resp = await fetch(`/webhooks/sync/${data.value.id}`, { | ||||
|     method: "PUT", | ||||
|     headers: { "Content-Type": "application/json" }, | ||||
|     body: JSON.stringify(content) | ||||
|   }) | ||||
|   if (resp.status != 200) { | ||||
|     message.error(`Something went wrong... ${await resp.text()}`) | ||||
|   } else { | ||||
|     reset() | ||||
|     emits("reload") | ||||
|     message.success("Your site has been created! 🎉") | ||||
|     creating.value = false | ||||
|   } | ||||
|   submitting.value = false | ||||
| } | ||||
|  | ||||
| function reset() { | ||||
|   data.value.id = "" | ||||
|   data.value.content = "" | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .fab { | ||||
|   position: absolute; | ||||
|   bottom: 16px; | ||||
|   right: 24px; | ||||
| } | ||||
| </style> | ||||
| @@ -1,36 +0,0 @@ | ||||
| <template> | ||||
|   <div class="flex flex-col gap-1"> | ||||
|  | ||||
|     <div> | ||||
|       <div class="font-bold">Rules</div> | ||||
|       <n-code :hljs="hljs" :code="parseData(props.rules)" language="json" /> | ||||
|     </div> | ||||
|  | ||||
|     <div> | ||||
|       <div class="font-bold">Upstreams</div> | ||||
|       <n-code :hljs="hljs" :code="parseData(props.upstreams)" language="json" /> | ||||
|     </div> | ||||
|  | ||||
|     <div> | ||||
|       <div class="font-bold">Processes</div> | ||||
|       <n-code :hljs="hljs" :code="parseData(props.processes)" language="json" /> | ||||
|     </div> | ||||
|  | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NCode } from "naive-ui" | ||||
| import hljs from "highlight.js/lib/core" | ||||
| import json from "highlight.js/lib/languages/json" | ||||
|  | ||||
| hljs.registerLanguage("json", json) | ||||
|  | ||||
| const props = defineProps<{ rules: any[], upstreams: any[], processes: any[] }>() | ||||
|  | ||||
| function parseData(data: any): string { | ||||
|   return JSON.stringify(data, null, 1) | ||||
|     .replace(/  +/g, " ") | ||||
|     .replace(/\n/g, "") | ||||
| } | ||||
| </script> | ||||
| @@ -1,76 +0,0 @@ | ||||
| <template> | ||||
|   <div> | ||||
|     <n-card title="Sites"> | ||||
|       <template #header-extra> | ||||
|         <sites-table-add @reload="readSites()" /> | ||||
|       </template> | ||||
|  | ||||
|       <n-data-table | ||||
|         :columns="columns" | ||||
|         :data="data" | ||||
|         :row-key="(row: any) => row.id" | ||||
|       /> | ||||
|     </n-card> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NCard, NDataTable, NTag } from "naive-ui" | ||||
| import { h, ref } from "vue" | ||||
| import SitesTableExpand from "@/components/data/sites-table-expand.vue" | ||||
| import SitesTableAction from "@/components/data/sites-table-action.vue" | ||||
| import SitesTableAdd from "@/components/data/sites-table-add.vue" | ||||
|  | ||||
| const columns: any[] = [ | ||||
|   { | ||||
|     type: "expand", | ||||
|     renderExpand(row: any) { | ||||
|       return h(SitesTableExpand, { ...row, class: "pl-[38px]" }) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: "ID", | ||||
|     key: "id", | ||||
|     render(row: any) { | ||||
|       return h(NTag, { type: "info", bordered: false, size: "small" }, row?.id) | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: "Rules", | ||||
|     key: "rules", | ||||
|     render(row: any) { | ||||
|       return row?.rules?.length ?? 0 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: "Upstreams", | ||||
|     key: "upstreams", | ||||
|     render(row: any) { | ||||
|       return row?.upstreams?.length ?? 0 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: "Processes", | ||||
|     key: "processes", | ||||
|     render(row: any) { | ||||
|       return row?.processes?.length ?? 0 | ||||
|     } | ||||
|   }, | ||||
|   { | ||||
|     title: "Actions", | ||||
|     key: "actions", | ||||
|     render(row: any) { | ||||
|       return h(SitesTableAction, { ...row, onReload: () => readSites() }) | ||||
|     } | ||||
|   } | ||||
| ] | ||||
|  | ||||
| const data = ref<any[]>([]) | ||||
|  | ||||
| async function readSites() { | ||||
|   const resp = await fetch("/cgi/sites") | ||||
|   data.value = await resp.json() | ||||
| } | ||||
|  | ||||
| readSites() | ||||
| </script> | ||||
							
								
								
									
										3
									
								
								pkg/sideload/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								pkg/sideload/view/src/index.css
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,3 @@ | ||||
| @tailwind base; | ||||
| @tailwind components; | ||||
| @tailwind utilities; | ||||
							
								
								
									
										17
									
								
								pkg/sideload/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								pkg/sideload/view/src/index.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| import "./index.css"; | ||||
|  | ||||
| /* @refresh reload */ | ||||
| import { render } from "solid-js/web"; | ||||
|  | ||||
| import { Route, Router } from "@solidjs/router"; | ||||
|  | ||||
| import RootLayout from "./layouts/RootLayout"; | ||||
| import Dashboard from "./pages/dashboard"; | ||||
|  | ||||
| const root = document.getElementById("root"); | ||||
|  | ||||
| render(() => ( | ||||
|     <Router root={RootLayout}> | ||||
|         <Route path="/" component={Dashboard} /> | ||||
|     </Router> | ||||
| ), root!); | ||||
							
								
								
									
										11
									
								
								pkg/sideload/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								pkg/sideload/view/src/layouts/RootLayout.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| import Navbar from "./shared/Navbar"; | ||||
|  | ||||
| export default function RootLayout(props: any) { | ||||
|   return ( | ||||
|     <div> | ||||
|       <Navbar /> | ||||
|  | ||||
|       <main class="h-[calc(100vh-64px)]">{props.children}</main> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @@ -1,60 +0,0 @@ | ||||
| <template> | ||||
|   <n-layout> | ||||
|     <n-layout-header class="header py-[8px] px-[36px]" bordered> | ||||
|       <div class="flex items-center gap-2"> | ||||
|         <router-link class="link" to="/"> | ||||
|           RoadSign<i>!</i> | ||||
|         </router-link> | ||||
|       </div> | ||||
|  | ||||
|       <div class="nav-menu"> | ||||
|         <div class="h-full flex items-center header-nav"> | ||||
|           <n-menu v-model:value="key" :options="options" mode="horizontal" /> | ||||
|         </div> | ||||
|       </div> | ||||
|     </n-layout-header> | ||||
|     <n-layout-content class="h-[calc(100vh-70px)] container mx-auto" content-style="padding: 24px"> | ||||
|       <router-view /> | ||||
|     </n-layout-content> | ||||
|   </n-layout> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { type MenuOption, NIcon, NLayout, NLayoutContent, NLayoutHeader, NMenu } from "naive-ui" | ||||
| import { type Component, h, ref } from "vue" | ||||
| import { Dashboard } from "@vicons/carbon" | ||||
| import { RouterLink, useRoute, useRouter } from "vue-router" | ||||
|  | ||||
| const route = useRoute() | ||||
| const router = useRouter() | ||||
| const key = ref(route.name?.toString()) | ||||
|  | ||||
| router.afterEach((to) => { | ||||
|   key.value = to.name?.toString() ?? "index" | ||||
| }) | ||||
|  | ||||
| const options: MenuOption[] = [ | ||||
|   { | ||||
|     label: () => h(RouterLink, { to: { name: "dashboard" } }, "Dashboard"), | ||||
|     icon: renderIcon(Dashboard), | ||||
|     key: "dashboard" | ||||
|   } | ||||
| ] | ||||
|  | ||||
| function renderIcon(icon: Component) { | ||||
|   return () => h(NIcon, null, { default: () => h(icon) }) | ||||
| } | ||||
| </script> | ||||
|  | ||||
| <style scoped> | ||||
| .header { | ||||
|   display: grid; | ||||
|   grid-template-columns: 1fr auto 1fr; | ||||
|   gap: 40px; | ||||
| } | ||||
|  | ||||
| .link { | ||||
|   all: unset; | ||||
|   cursor: pointer; | ||||
| } | ||||
| </style> | ||||
							
								
								
									
										62
									
								
								pkg/sideload/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								pkg/sideload/view/src/layouts/shared/Navbar.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,62 @@ | ||||
| import { For } from "solid-js"; | ||||
|  | ||||
| interface MenuItem { | ||||
|   label: string; | ||||
|   href: string; | ||||
| } | ||||
|  | ||||
| export default function Navbar() { | ||||
|   const nav: MenuItem[] = [{ label: "Dashboard", href: "/" }]; | ||||
|  | ||||
|   return ( | ||||
|     <div class="navbar bg-base-100 shadow-md"> | ||||
|       <div class="navbar-start"> | ||||
|         <div class="dropdown"> | ||||
|           <div tabIndex={0} role="button" class="btn btn-ghost lg:hidden"> | ||||
|             <svg | ||||
|               xmlns="http://www.w3.org/2000/svg" | ||||
|               class="h-5 w-5" | ||||
|               fill="none" | ||||
|               viewBox="0 0 24 24" | ||||
|               stroke="currentColor" | ||||
|             > | ||||
|               <path | ||||
|                 stroke-linecap="round" | ||||
|                 stroke-linejoin="round" | ||||
|                 stroke-width="2" | ||||
|                 d="M4 6h16M4 12h8m-8 6h16" | ||||
|               /> | ||||
|             </svg> | ||||
|           </div> | ||||
|           <ul | ||||
|             tabIndex={0} | ||||
|             class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52" | ||||
|           > | ||||
|             <For each={nav}> | ||||
|               {(item) => ( | ||||
|                 <li> | ||||
|                   <a href={item.href}>{item.label}</a> | ||||
|                 </li> | ||||
|               )} | ||||
|             </For> | ||||
|           </ul> | ||||
|         </div> | ||||
|         <a href="/" class="btn btn-ghost text-xl"> | ||||
|           RoadSign | ||||
|         </a> | ||||
|       </div> | ||||
|       <div class="navbar-center hidden lg:flex"> | ||||
|         <ul class="menu menu-horizontal px-1"> | ||||
|           <For each={nav}> | ||||
|             {(item) => ( | ||||
|               <li> | ||||
|                 <a href={item.href}>{item.label}</a> | ||||
|               </li> | ||||
|             )} | ||||
|           </For> | ||||
|         </ul> | ||||
|       </div> | ||||
|       <div class="navbar-end"></div> | ||||
|     </div> | ||||
|   ); | ||||
| } | ||||
| @@ -1,16 +0,0 @@ | ||||
| import "./assets/main.css" | ||||
|  | ||||
| import "virtual:uno.css" | ||||
|  | ||||
| import { createApp } from "vue" | ||||
| import { createPinia } from "pinia" | ||||
|  | ||||
| import root from "./root.vue" | ||||
| import router from "./router" | ||||
|  | ||||
| const app = createApp(root) | ||||
|  | ||||
| app.use(createPinia()) | ||||
| app.use(router) | ||||
|  | ||||
| app.mount("#app") | ||||
							
								
								
									
										10
									
								
								pkg/sideload/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								pkg/sideload/view/src/pages/dashboard.tsx
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| export default function Dashboard() { | ||||
|     return ( | ||||
|         <div class="h-full w-full flex justify-center items-center"> | ||||
|             <div class="max-w-96 text-center"> | ||||
|                 <h1 class="text-2xl font-bold">Hold on</h1> | ||||
|                 <p>Our brand new sideload administration panel is still in progress. For now, you can use sideload api and roadsign cli.</p> | ||||
|             </div> | ||||
|         </div> | ||||
|     ) | ||||
| } | ||||
| @@ -1,9 +0,0 @@ | ||||
| <template> | ||||
|   <n-message-provider> | ||||
|     <router-view /> | ||||
|   </n-message-provider> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NMessageProvider } from "naive-ui" | ||||
| </script> | ||||
| @@ -1,21 +0,0 @@ | ||||
| import { createRouter, createWebHistory } from "vue-router" | ||||
|  | ||||
| const router = createRouter({ | ||||
|   history: createWebHistory(import.meta.env.BASE_URL), | ||||
|   routes: [ | ||||
|     { | ||||
|       path: "/", | ||||
|       name: "layouts.main", | ||||
|       component: () => import("@/layouts/main.vue"), | ||||
|       children: [ | ||||
|         { | ||||
|           path: "/", | ||||
|           name: "dashboard", | ||||
|           component: () => import("@/views/dashboard.vue") | ||||
|         }, | ||||
|       ] | ||||
|     }, | ||||
|   ] | ||||
| }) | ||||
|  | ||||
| export default router | ||||
| @@ -1,35 +0,0 @@ | ||||
| <template> | ||||
|   <div class="flex flex-col gap-2"> | ||||
|     <div class="grid gap-2 grid-cols-2 lg:grid-cols-4"> | ||||
|       <n-card embedded> | ||||
|         <n-statistic label="Status">{{ data?.status ? "Operational" : "Incident" }}</n-statistic> | ||||
|       </n-card> | ||||
|       <n-card embedded> | ||||
|         <n-statistic label="Sites">{{ data?.sites }}</n-statistic> | ||||
|       </n-card> | ||||
|       <n-card embedded> | ||||
|         <n-statistic label="Upstreams">{{ data?.upstreams }}</n-statistic> | ||||
|       </n-card> | ||||
|       <n-card embedded> | ||||
|         <n-statistic label="Processes">{{ data?.processes }}</n-statistic> | ||||
|       </n-card> | ||||
|     </div> | ||||
|  | ||||
|     <sites-table /> | ||||
|   </div> | ||||
| </template> | ||||
|  | ||||
| <script setup lang="ts"> | ||||
| import { NCard, NStatistic } from "naive-ui" | ||||
| import { ref } from "vue" | ||||
| import SitesTable from "@/components/data/sites-table.vue" | ||||
|  | ||||
| const data = ref<any>({}) | ||||
|  | ||||
| async function readStatistics() { | ||||
|   const resp = await fetch("/cgi/statistics") | ||||
|   data.value = await resp.json() | ||||
| } | ||||
|  | ||||
| readStatistics() | ||||
| </script> | ||||
							
								
								
									
										44
									
								
								pkg/sideload/view/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								pkg/sideload/view/tailwind.config.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,44 @@ | ||||
| /** @type {import('tailwindcss').Config} */ | ||||
| export default { | ||||
|   content: ["./src/**/*.{js,jsx,ts,tsx}"], | ||||
|   theme: { | ||||
|     extend: {}, | ||||
|   }, | ||||
|   daisyui: { | ||||
|     themes: [ | ||||
|       { | ||||
|         light: { | ||||
|           ...require("daisyui/src/theming/themes")["light"], | ||||
|           primary: "#4750a3", | ||||
|           secondary: "#93c5fd", | ||||
|           accent: "#0f766e", | ||||
|           info: "#67e8f9", | ||||
|           success: "#15803d", | ||||
|           warning: "#f97316", | ||||
|           error: "#dc2626", | ||||
|           "--rounded-box": "0", | ||||
|           "--rounded-btn": "0", | ||||
|           "--rounded-badge": "0", | ||||
|           "--tab-radius": "0", | ||||
|         }, | ||||
|       }, | ||||
|       { | ||||
|         dark: { | ||||
|           ...require("daisyui/src/theming/themes")["dark"], | ||||
|           primary: "#4750a3", | ||||
|           secondary: "#93c5fd", | ||||
|           accent: "#0f766e", | ||||
|           info: "#67e8f9", | ||||
|           success: "#15803d", | ||||
|           warning: "#f97316", | ||||
|           error: "#dc2626", | ||||
|           "--rounded-box": "0", | ||||
|           "--rounded-btn": "0", | ||||
|           "--rounded-badge": "0", | ||||
|           "--tab-radius": "0", | ||||
|         }, | ||||
|       }, | ||||
|     ], | ||||
|   }, | ||||
|   plugins: [require("daisyui")], | ||||
| }; | ||||
| @@ -1,13 +0,0 @@ | ||||
| { | ||||
|   "extends": "@vue/tsconfig/tsconfig.dom.json", | ||||
|   "include": ["env.d.ts", "src/**/*", "src/**/*.vue"], | ||||
|   "exclude": ["src/**/__tests__/*"], | ||||
|   "compilerOptions": { | ||||
|     "composite": true, | ||||
|     "noEmit": true, | ||||
|     "baseUrl": ".", | ||||
|     "paths": { | ||||
|       "@/*": ["./src/*"] | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -1,11 +1,26 @@ | ||||
| { | ||||
|   "files": [], | ||||
|   "references": [ | ||||
|     { | ||||
|       "path": "./tsconfig.node.json" | ||||
|     }, | ||||
|     { | ||||
|       "path": "./tsconfig.app.json" | ||||
|     } | ||||
|   ] | ||||
|   "compilerOptions": { | ||||
|     "target": "ES2020", | ||||
|     "useDefineForClassFields": true, | ||||
|     "module": "ESNext", | ||||
|     "lib": ["ES2020", "DOM", "DOM.Iterable"], | ||||
|     "skipLibCheck": true, | ||||
|  | ||||
|     /* Bundler mode */ | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowImportingTsExtensions": true, | ||||
|     "resolveJsonModule": true, | ||||
|     "isolatedModules": true, | ||||
|     "noEmit": true, | ||||
|     "jsx": "preserve", | ||||
|     "jsxImportSource": "solid-js", | ||||
|  | ||||
|     /* Linting */ | ||||
|     "strict": true, | ||||
|     "noUnusedLocals": true, | ||||
|     "noUnusedParameters": true, | ||||
|     "noFallthroughCasesInSwitch": true | ||||
|   }, | ||||
|   "include": ["src"], | ||||
|   "references": [{ "path": "./tsconfig.node.json" }] | ||||
| } | ||||
|   | ||||
| @@ -1,17 +1,10 @@ | ||||
| { | ||||
|   "extends": "@tsconfig/node18/tsconfig.json", | ||||
|   "include": [ | ||||
|     "vite.config.*", | ||||
|     "vitest.config.*", | ||||
|     "cypress.config.*", | ||||
|     "nightwatch.conf.*", | ||||
|     "playwright.config.*" | ||||
|   ], | ||||
|   "compilerOptions": { | ||||
|     "composite": true, | ||||
|     "noEmit": true, | ||||
|     "skipLibCheck": true, | ||||
|     "module": "ESNext", | ||||
|     "moduleResolution": "Bundler", | ||||
|     "types": ["node"] | ||||
|   } | ||||
|     "moduleResolution": "bundler", | ||||
|     "allowSyntheticDefaultImports": true | ||||
|   }, | ||||
|   "include": ["vite.config.ts"] | ||||
| } | ||||
|   | ||||
| @@ -1,5 +0,0 @@ | ||||
| import { defineConfig, presetUno } from "unocss" | ||||
|  | ||||
| export default defineConfig({ | ||||
|   presets: [presetUno({ preflight: false })] | ||||
| }) | ||||
| @@ -1,24 +1,6 @@ | ||||
| import { fileURLToPath, URL } from "node:url" | ||||
| import { defineConfig } from 'vite' | ||||
| import solid from 'vite-plugin-solid' | ||||
|  | ||||
| import { defineConfig } from "vite" | ||||
| import vue from "@vitejs/plugin-vue" | ||||
| import unocss from "unocss/vite" | ||||
|  | ||||
| // https://vitejs.dev/config/ | ||||
| export default defineConfig({ | ||||
|   plugins: [ | ||||
|     vue(), | ||||
|     unocss() | ||||
|   ], | ||||
|   resolve: { | ||||
|     alias: { | ||||
|       "@": fileURLToPath(new URL("./src", import.meta.url)) | ||||
|     } | ||||
|   }, | ||||
|   server: { | ||||
|     proxy: { | ||||
|       "/webhooks": "http://127.0.0.1:81", | ||||
|       "/cgi": "http://127.0.0.1:81" | ||||
|     } | ||||
|   } | ||||
|   plugins: [solid()], | ||||
| }) | ||||
|   | ||||
							
								
								
									
										144
									
								
								pkg/sign/pm.go
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								pkg/sign/pm.go
									
									
									
									
									
								
							| @@ -1,144 +0,0 @@ | ||||
| package sign | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"github.com/samber/lo" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| type ProcessStatus = int8 | ||||
|  | ||||
| const ( | ||||
| 	ProcessCreated = ProcessStatus(iota) | ||||
| 	ProcessStarting | ||||
| 	ProcessStarted | ||||
| 	ProcessExited | ||||
| 	ProcessFailure | ||||
| ) | ||||
|  | ||||
| type ProcessInstance struct { | ||||
| 	ID          string     `json:"id" yaml:"id"` | ||||
| 	Workdir     string     `json:"workdir" yaml:"workdir"` | ||||
| 	Command     []string   `json:"command" yaml:"command"` | ||||
| 	Environment []string   `json:"environment" yaml:"environment"` | ||||
| 	Prepares    [][]string `json:"prepares" yaml:"prepares"` | ||||
| 	Preheat     bool       `json:"preheat" yaml:"preheat"` | ||||
|  | ||||
| 	Cmd    *exec.Cmd       `json:"-"` | ||||
| 	Logger strings.Builder `json:"-"` | ||||
|  | ||||
| 	Status ProcessStatus `json:"status"` | ||||
| } | ||||
|  | ||||
| func (v *ProcessInstance) BootProcess() error { | ||||
| 	if v.Cmd != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if err := v.PrepareProcess(); err != nil { | ||||
| 		return err | ||||
| 	} | ||||
| 	if v.Cmd == nil { | ||||
| 		return v.StartProcess() | ||||
| 	} | ||||
| 	if v.Cmd.Process == nil || v.Cmd.ProcessState == nil { | ||||
| 		return v.StartProcess() | ||||
| 	} | ||||
| 	if v.Cmd.ProcessState.Exited() { | ||||
| 		return v.StartProcess() | ||||
| 	} else if v.Cmd.ProcessState.Exited() { | ||||
| 		return fmt.Errorf("process already dead") | ||||
| 	} | ||||
| 	if v.Cmd.ProcessState.Exited() { | ||||
| 		return fmt.Errorf("cannot start process") | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (v *ProcessInstance) PrepareProcess() error { | ||||
| 	for _, script := range v.Prepares { | ||||
| 		if len(script) <= 0 { | ||||
| 			continue | ||||
| 		} | ||||
| 		cmd := exec.Command(script[0], script[1:]...) | ||||
| 		cmd.Dir = filepath.Join(v.Workdir) | ||||
| 		if err := cmd.Run(); err != nil { | ||||
| 			return err | ||||
| 		} | ||||
| 	} | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (v *ProcessInstance) StartProcess() error { | ||||
| 	if len(v.Command) <= 0 { | ||||
| 		return fmt.Errorf("you need set the command for %s to enable process manager", v.ID) | ||||
| 	} | ||||
|  | ||||
| 	v.Cmd = exec.Command(v.Command[0], v.Command[1:]...) | ||||
| 	v.Cmd.Dir = filepath.Join(v.Workdir) | ||||
| 	v.Cmd.Env = append(v.Cmd.Env, v.Environment...) | ||||
| 	v.Cmd.Stdout = &v.Logger | ||||
| 	v.Cmd.Stderr = &v.Logger | ||||
|  | ||||
| 	// Monitor | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			if v.Cmd.Process == nil || v.Cmd.ProcessState == nil { | ||||
| 				v.Status = ProcessStarting | ||||
| 			} else if !v.Cmd.ProcessState.Exited() { | ||||
| 				v.Status = ProcessStarted | ||||
| 			} else { | ||||
| 				v.Status = lo.Ternary(v.Cmd.ProcessState.Success(), ProcessExited, ProcessFailure) | ||||
| 				return | ||||
| 			} | ||||
| 			time.Sleep(100 * time.Millisecond) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return v.Cmd.Start() | ||||
| } | ||||
|  | ||||
| func (v *ProcessInstance) StopProcess() error { | ||||
| 	if v.Cmd != nil && v.Cmd.Process != nil { | ||||
| 		if err := v.Cmd.Process.Signal(os.Interrupt); err != nil { | ||||
| 			v.Cmd.Process.Kill() | ||||
| 			return err | ||||
| 		} else { | ||||
| 			v.Cmd = nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (v *ProcessInstance) GetLogs() string { | ||||
| 	return v.Logger.String() | ||||
| } | ||||
|  | ||||
| func (v *RoadApp) PreheatProcesses(callbacks ...func(total int, success int)) { | ||||
| 	var processes []*ProcessInstance | ||||
| 	for _, site := range v.Sites { | ||||
| 		for _, process := range site.Processes { | ||||
| 			if process.Preheat { | ||||
| 				processes = append(processes, process) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	success := 0 | ||||
| 	for _, process := range processes { | ||||
| 		if process.BootProcess() == nil { | ||||
| 			success++ | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	if len(callbacks) > 0 { | ||||
| 		for _, callback := range callbacks { | ||||
| 			callback(len(processes), success) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| package sign | ||||
|  | ||||
| import ( | ||||
| 	"code.smartsheep.studio/goatworks/roadsign/pkg/sign/transformers" | ||||
| 	"errors" | ||||
| 	"math/rand" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/rs/zerolog/log" | ||||
| ) | ||||
|  | ||||
| type RoadApp struct { | ||||
| 	Sites []*SiteConfig `json:"sites"` | ||||
| } | ||||
|  | ||||
| func (v *RoadApp) Forward(ctx *fiber.Ctx, site *SiteConfig) error { | ||||
| 	if len(site.Upstreams) == 0 { | ||||
| 		return errors.New("invalid configuration") | ||||
| 	} | ||||
|  | ||||
| 	// Boot processes | ||||
| 	for _, process := range site.Processes { | ||||
| 		if err := process.BootProcess(); err != nil { | ||||
| 			log.Warn().Err(err).Msgf("An error occurred when booting process (%s) for %s", process.ID, site.ID) | ||||
| 			return fiber.ErrBadGateway | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	// Do forward | ||||
| 	idx := rand.Intn(len(site.Upstreams)) | ||||
| 	upstream := site.Upstreams[idx] | ||||
|  | ||||
| 	switch upstream.GetType() { | ||||
| 	case UpstreamTypeHypertext: | ||||
| 		return makeHypertextResponse(ctx, upstream) | ||||
| 	case UpstreamTypeFile: | ||||
| 		return makeFileResponse(ctx, upstream) | ||||
| 	default: | ||||
| 		return fiber.ErrBadGateway | ||||
| 	} | ||||
| } | ||||
|  | ||||
| type RequestTransformerConfig = transformers.RequestTransformerConfig | ||||
|  | ||||
| type SiteConfig struct { | ||||
| 	ID           string                      `json:"id"` | ||||
| 	Rules        []*RouterRule               `json:"rules" yaml:"rules"` | ||||
| 	Transformers []*RequestTransformerConfig `json:"transformers" yaml:"transformers"` | ||||
| 	Upstreams    []*UpstreamInstance         `json:"upstreams" yaml:"upstreams"` | ||||
| 	Processes    []*ProcessInstance          `json:"processes" yaml:"processes"` | ||||
| } | ||||
|  | ||||
| type RouterRule struct { | ||||
| 	Host    []string            `json:"host" yaml:"host"` | ||||
| 	Path    []string            `json:"path" yaml:"path"` | ||||
| 	Queries map[string]string   `json:"queries" yaml:"queries"` | ||||
| 	Headers map[string][]string `json:"headers" yaml:"headers"` | ||||
| } | ||||
| @@ -1,58 +0,0 @@ | ||||
| package sign | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/url" | ||||
| 	"strings" | ||||
|  | ||||
| 	"github.com/gofiber/fiber/v2" | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	UpstreamTypeFile      = "file" | ||||
| 	UpstreamTypeHypertext = "hypertext" | ||||
| 	UpstreamTypeUnknown   = "unknown" | ||||
| ) | ||||
|  | ||||
| type UpstreamInstance struct { | ||||
| 	ID  string `json:"id" yaml:"id"` | ||||
| 	URI string `json:"uri" yaml:"uri"` | ||||
| } | ||||
|  | ||||
| func (v *UpstreamInstance) GetType() string { | ||||
| 	protocol := strings.SplitN(v.URI, "://", 2)[0] | ||||
| 	switch protocol { | ||||
| 	case "file", "files": | ||||
| 		return UpstreamTypeFile | ||||
| 	case "http", "https": | ||||
| 		return UpstreamTypeHypertext | ||||
| 	} | ||||
|  | ||||
| 	return UpstreamTypeUnknown | ||||
| } | ||||
|  | ||||
| func (v *UpstreamInstance) GetRawURI() (string, url.Values) { | ||||
| 	uri := strings.SplitN(v.URI, "://", 2)[1] | ||||
| 	data := strings.SplitN(uri, "?", 2) | ||||
| 	data = append(data, " ") // Make data array least have two element | ||||
| 	qs, _ := url.ParseQuery(data[0]) | ||||
|  | ||||
| 	return data[0], qs | ||||
| } | ||||
|  | ||||
| func (v *UpstreamInstance) MakeURI(ctx *fiber.Ctx) string { | ||||
| 	var queries []string | ||||
| 	for k, v := range ctx.Queries() { | ||||
| 		parsed, _ := url.QueryUnescape(v) | ||||
| 		value := url.QueryEscape(parsed) | ||||
| 		queries = append(queries, fmt.Sprintf("%s=%s", k, value)) | ||||
| 	} | ||||
|  | ||||
| 	path := string(ctx.Request().URI().Path()) | ||||
| 	hash := string(ctx.Request().URI().Hash()) | ||||
|  | ||||
| 	return v.URI + path + | ||||
| 		lo.Ternary(len(queries) > 0, "?"+strings.Join(queries, "&"), "") + | ||||
| 		lo.Ternary(len(hash) > 0, "#"+hash, "") | ||||
| } | ||||
							
								
								
									
										120
									
								
								pkg/warden/executor.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										120
									
								
								pkg/warden/executor.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,120 @@ | ||||
| package warden | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"os" | ||||
| 	"os/exec" | ||||
| 	"path/filepath" | ||||
| 	"strings" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/samber/lo" | ||||
| ) | ||||
|  | ||||
| var InstancePool []*AppInstance | ||||
|  | ||||
| func GetFromPool(id string) *AppInstance { | ||||
| 	val, ok := lo.Find(InstancePool, func(item *AppInstance) bool { | ||||
| 		return item.Manifest.ID == id | ||||
| 	}) | ||||
| 	return lo.Ternary(ok, val, nil) | ||||
| } | ||||
|  | ||||
| func StartPool() []error { | ||||
| 	var errors []error | ||||
| 	for _, instance := range InstancePool { | ||||
| 		if err := instance.Wake(); err != nil { | ||||
| 			errors = append(errors, err) | ||||
| 		} | ||||
| 	} | ||||
| 	return errors | ||||
| } | ||||
|  | ||||
| type AppStatus = int8 | ||||
|  | ||||
| const ( | ||||
| 	AppCreated = AppStatus(iota) | ||||
| 	AppStarting | ||||
| 	AppStarted | ||||
| 	AppExited | ||||
| 	AppFailure | ||||
| ) | ||||
|  | ||||
| type AppInstance struct { | ||||
| 	Manifest Application `json:"manifest"` | ||||
|  | ||||
| 	Cmd    *exec.Cmd       `json:"-"` | ||||
| 	Logger strings.Builder `json:"-"` | ||||
|  | ||||
| 	Status AppStatus `json:"status"` | ||||
| } | ||||
|  | ||||
| func (v *AppInstance) Wake() error { | ||||
| 	if v.Cmd != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	if v.Cmd == nil { | ||||
| 		return v.Start() | ||||
| 	} | ||||
| 	if v.Cmd.Process == nil || v.Cmd.ProcessState == nil { | ||||
| 		return v.Start() | ||||
| 	} | ||||
| 	if v.Cmd.ProcessState.Exited() { | ||||
| 		return v.Start() | ||||
| 	} else if v.Cmd.ProcessState.Exited() { | ||||
| 		return fmt.Errorf("process already dead") | ||||
| 	} | ||||
| 	if v.Cmd.ProcessState.Exited() { | ||||
| 		return fmt.Errorf("cannot start process") | ||||
| 	} else { | ||||
| 		return nil | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func (v *AppInstance) Start() error { | ||||
| 	manifest := v.Manifest | ||||
|  | ||||
| 	if len(manifest.Command) <= 0 { | ||||
| 		return fmt.Errorf("you need set the command for %s to enable process manager", manifest.ID) | ||||
| 	} | ||||
|  | ||||
| 	v.Cmd = exec.Command(manifest.Command[0], manifest.Command[1:]...) | ||||
| 	v.Cmd.Dir = filepath.Join(manifest.Workdir) | ||||
| 	v.Cmd.Env = append(v.Cmd.Env, manifest.Environment...) | ||||
| 	v.Cmd.Stdout = &v.Logger | ||||
| 	v.Cmd.Stderr = &v.Logger | ||||
|  | ||||
| 	// Monitor | ||||
| 	go func() { | ||||
| 		for { | ||||
| 			if v.Cmd.Process == nil || v.Cmd.ProcessState == nil { | ||||
| 				v.Status = AppStarting | ||||
| 			} else if !v.Cmd.ProcessState.Exited() { | ||||
| 				v.Status = AppStarted | ||||
| 			} else { | ||||
| 				v.Status = lo.Ternary(v.Cmd.ProcessState.Success(), AppExited, AppFailure) | ||||
| 				return | ||||
| 			} | ||||
| 			time.Sleep(100 * time.Millisecond) | ||||
| 		} | ||||
| 	}() | ||||
|  | ||||
| 	return v.Cmd.Start() | ||||
| } | ||||
|  | ||||
| func (v *AppInstance) Stop() error { | ||||
| 	if v.Cmd != nil && v.Cmd.Process != nil { | ||||
| 		if err := v.Cmd.Process.Signal(os.Interrupt); err != nil { | ||||
| 			v.Cmd.Process.Kill() | ||||
| 			return err | ||||
| 		} else { | ||||
| 			v.Cmd = nil | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (v *AppInstance) Logs() string { | ||||
| 	return v.Logger.String() | ||||
| } | ||||
							
								
								
									
										8
									
								
								pkg/warden/manifest.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								pkg/warden/manifest.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| package warden | ||||
|  | ||||
| type Application struct { | ||||
| 	ID          string   `json:"id" toml:"id"` | ||||
| 	Workdir     string   `json:"workdir" toml:"workdir"` | ||||
| 	Command     []string `json:"command" toml:"command"` | ||||
| 	Environment []string `json:"environment" toml:"environment"` | ||||
| } | ||||
							
								
								
									
										35
									
								
								settings.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								settings.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| [debug] | ||||
| print_routes = false | ||||
|  | ||||
| [hypertext] | ||||
| sideload_ports = [":81"] | ||||
| sideload_secured_ports = [] | ||||
| ports = [":8000"] | ||||
| secured_ports = [] | ||||
|  | ||||
| [hypertext.certificate] | ||||
| redirect = false | ||||
| sideload_key = "./cert.key" | ||||
| sideload_pem = "./cert.pem" | ||||
| key = "./cert.key" | ||||
| pem = "./cert.pem" | ||||
|  | ||||
| [hypertext.limitation] | ||||
| max_body_size = 549_755_813_888 # 512 GiB | ||||
| max_qps = -1 | ||||
|  | ||||
| [paths] | ||||
| configs = "./config" | ||||
|  | ||||
| [telemetry] | ||||
| request_logging = true | ||||
| capture_traces = true | ||||
|  | ||||
| [performance] | ||||
| traces_limit = 256 | ||||
| network_timeout = 3_000 | ||||
| prefork = false | ||||
|  | ||||
| [security] | ||||
| sideload_trusted_proxies = ["localhost"] | ||||
| credential = "e81f43f32d934271af6322e5376f5f59" | ||||
							
								
								
									
										28
									
								
								settings.yml
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								settings.yml
									
									
									
									
									
								
							| @@ -1,28 +0,0 @@ | ||||
| debug: | ||||
|   print_routes: true | ||||
| hypertext: | ||||
|   sideload_ports: | ||||
|     - :81 | ||||
|   sideload_secured_ports: [ ] | ||||
|   certificate: | ||||
|     redirect: false | ||||
|     sideload_key: ./cert.key | ||||
|     sideload_pem: ./cert.pem | ||||
|     key: ./cert.key | ||||
|     pem: ./cert.pem | ||||
|   limitation: | ||||
|     max_body_size: 536870912 | ||||
|     max_qps: -1 | ||||
|   ports: | ||||
|     - :8000 | ||||
|   secured_ports: [ ] | ||||
| paths: | ||||
|   configs: ./config | ||||
| performance: | ||||
|   request_logging: true | ||||
|   network_timeout: 3000 | ||||
|   prefork: false | ||||
| security: | ||||
|   sideload_trusted_proxies: | ||||
|     - localhost | ||||
|   credential: e81f43f32d934271af6322e5376f5f59 | ||||
							
								
								
									
										4
									
								
								test/data/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										4
									
								
								test/data/.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1 @@ | ||||
| /ssr | ||||
| /spa | ||||
| /congress | ||||
| /warden | ||||
							
								
								
									
										27
									
								
								test/data/sse/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								test/data/sse/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,27 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
| <head> | ||||
|   <meta charset="UTF-8"> | ||||
|   <meta http-equiv="X-UA-Compatible" content="IE=edge"> | ||||
|   <meta name="viewport" content="width=device-width, initial-scale=1.0"> | ||||
|   <title>SSE Example</title> | ||||
| </head> | ||||
| <body> | ||||
| <h1>Server-Sent Events Example</h1> | ||||
| <div id="sse-data"></div> | ||||
|  | ||||
| <script> | ||||
|   const eventSource = new EventSource("/sse") | ||||
|   const sseDataElement = document.getElementById("sse-data") | ||||
|  | ||||
|   eventSource.onmessage = (event) => { | ||||
|     sseDataElement.innerText = `Data from server: ${event.data}` | ||||
|   } | ||||
|  | ||||
|   eventSource.onerror = (error) => { | ||||
|     console.error("EventSource failed:", error) | ||||
|     eventSource.close() | ||||
|   } | ||||
| </script> | ||||
| </body> | ||||
| </html> | ||||
							
								
								
									
										28
									
								
								test/data/sse/server.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								test/data/sse/server.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,28 @@ | ||||
| import time | ||||
|  | ||||
| from flask import Flask, render_template, Response | ||||
|  | ||||
| app = Flask(__name__, template_folder=".") | ||||
|  | ||||
|  | ||||
| # Generator function to simulate real-time updates | ||||
| def event_stream(): | ||||
|     count = 0 | ||||
|     while True: | ||||
|         time.sleep(1) | ||||
|         count += 1 | ||||
|         yield f"data: {count}\n\n" | ||||
|  | ||||
|  | ||||
| @app.route('/') | ||||
| def index(): | ||||
|     return render_template('index.html') | ||||
|  | ||||
|  | ||||
| @app.route('/sse') | ||||
| def sse(): | ||||
|     return Response(event_stream(), content_type='text/event-stream') | ||||
|  | ||||
|  | ||||
| if __name__ == '__main__': | ||||
|     app.run(debug=True, threaded=True) | ||||
							
								
								
									
										57
									
								
								test/data/websocket/index.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								test/data/websocket/index.html
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| <!DOCTYPE html> | ||||
| <html lang="en"> | ||||
|   <head> | ||||
|     <meta charset="UTF-8" /> | ||||
|     <meta name="viewport" content="width=device-width, initial-scale=1.0" /> | ||||
|     <title>WebSocket Durability Test</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <h1>WebSocket Client</h1> | ||||
|     <p> | ||||
|       This client will send a message every 500ms, or you can send message | ||||
|       manually. | ||||
|     </p> | ||||
|     <div id="messages"></div> | ||||
|     <input type="text" id="messageInput" placeholder="Type a message..." /> | ||||
|     <button onclick="sendMessage()">Send Message</button> | ||||
|  | ||||
|     <script> | ||||
|       const socket = new WebSocket("ws://localhost:8000/ws"); | ||||
|  | ||||
|       socket.onopen = function (event) { | ||||
|         appendMessage("Connection opened"); | ||||
|  | ||||
|         setInterval(() => autoSendMessage(), 500) | ||||
|       }; | ||||
|  | ||||
|       socket.onmessage = function (event) { | ||||
|         appendMessage("Received: " + event.data); | ||||
|       }; | ||||
|  | ||||
|       socket.onclose = function (event) { | ||||
|         appendMessage("Connection closed"); | ||||
|       }; | ||||
|  | ||||
|       function sendMessage() { | ||||
|         const messageInput = document.getElementById("messageInput"); | ||||
|         const message = messageInput.value; | ||||
|  | ||||
|         if (message.trim() !== "") { | ||||
|           socket.send(message); | ||||
|           appendMessage("Sent: " + message); | ||||
|           messageInput.value = ""; | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       function autoSendMessage() { | ||||
|         const message = `[AutoSend] A new message has been sent at ${new Date().toUTCString()}`; | ||||
|         socket.send(message); | ||||
|       } | ||||
|  | ||||
|       function appendMessage(message) { | ||||
|         const messagesDiv = document.getElementById("messages"); | ||||
|         messagesDiv.innerHTML += `<p>${message}</p>`; | ||||
|       } | ||||
|     </script> | ||||
|   </body> | ||||
| </html> | ||||
							
								
								
									
										29
									
								
								test/data/websocket/server.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								test/data/websocket/server.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| import asyncio | ||||
| import websockets | ||||
|  | ||||
| async def handle_websocket(websocket, path): | ||||
|     # This function will be called whenever a new WebSocket connection is established | ||||
|  | ||||
|     # Send a welcome message to the client | ||||
|     await websocket.send("Welcome to the WebSocket server!") | ||||
|  | ||||
|     try: | ||||
|         # Enter the main loop to handle incoming messages | ||||
|         async for message in websocket: | ||||
|             # Print the received message | ||||
|             print(f"Received message: {message}") | ||||
|  | ||||
|             # Send a response back to the client | ||||
|             response = f"Server received: {message}" | ||||
|             await websocket.send(response) | ||||
|     except websockets.exceptions.ConnectionClosedError: | ||||
|         print("Connection closed by the client.") | ||||
|  | ||||
| # Create the WebSocket server | ||||
| start_server = websockets.serve(handle_websocket, "localhost", 8765) | ||||
|  | ||||
| print("WebSocket server started at ws://localhost:8765") | ||||
|  | ||||
| # Run the server indefinitely | ||||
| asyncio.get_event_loop().run_until_complete(start_server) | ||||
| asyncio.get_event_loop().run_forever() | ||||
							
								
								
									
										9
									
								
								test/roadsign-spa/config/example.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								test/roadsign-spa/config/example.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| id = "example" | ||||
|  | ||||
| [[locations]] | ||||
| id = "example-location" | ||||
| host = ["localhost:8000"] | ||||
| path = ["/"] | ||||
| [[locations.destinations]] | ||||
| id = "example-destination" | ||||
| uri = "files://../data/spa?fallback=index.html" | ||||
| @@ -1,8 +0,0 @@ | ||||
| name: Example Site | ||||
| rules: | ||||
|   - host: ["localhost:8000"] | ||||
|     path: ["/"] | ||||
| upstreams: | ||||
|   - id: example | ||||
|     name: Benchmarking Data | ||||
|     uri: files://../data/spa?fallback=index.html | ||||
							
								
								
									
										31
									
								
								test/roadsign-spa/settings.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								test/roadsign-spa/settings.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| [debug] | ||||
| print_routes = true | ||||
|  | ||||
| [hypertext] | ||||
| sideload_ports = [":81"] | ||||
| sideload_secured_ports = [] | ||||
| ports = [":8000"] | ||||
| secured_ports = [] | ||||
|  | ||||
| [hypertext.certificate] | ||||
| redirect = false | ||||
| sideload_key = "./cert.key" | ||||
| sideload_pem = "./cert.pem" | ||||
| key = "./cert.key" | ||||
| pem = "./cert.pem" | ||||
|  | ||||
| [hypertext.limitation] | ||||
| max_body_size = 549_755_813_888 # 512 GiB | ||||
| max_qps = -1 | ||||
|  | ||||
| [paths] | ||||
| configs = "./config" | ||||
|  | ||||
| [performance] | ||||
| request_logging = true | ||||
| network_timeout = 3_000 | ||||
| prefork = false | ||||
|  | ||||
| [security] | ||||
| sideload_trusted_proxies = ["localhost"] | ||||
| credential = "e81f43f32d934271af6322e5376f5f59" | ||||
| @@ -1,26 +0,0 @@ | ||||
| debug: | ||||
|     print_routes: false | ||||
| hypertext: | ||||
|     sideload_ports: [":81"] | ||||
|     sideload_secured_ports: [] | ||||
|     certificate: | ||||
|         sideload_key: ./cert.key | ||||
|         sideload_pem: ./cert.pem | ||||
|         key: ./cert.key | ||||
|         pem: ./cert.pem | ||||
|     limitation: | ||||
|         max_body_size: 536870912 | ||||
|         max_qps: -1 | ||||
|     ports: | ||||
|         - :8000 | ||||
|     secured_ports: [] | ||||
| paths: | ||||
|     configs: ./config | ||||
| performance: | ||||
|     request_logging: false | ||||
|     network_timeout: 3000 | ||||
|     prefork: false | ||||
| security: | ||||
|     sideload_trusted_proxies: | ||||
|         - localhost | ||||
|     credential: e81f43f32d934271af6322e5376f5f59 | ||||
							
								
								
									
										15
									
								
								test/roadsign-ssr/config/example.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								test/roadsign-ssr/config/example.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| id = "example-region" | ||||
|  | ||||
| [[locations]] | ||||
| id = "example-warden" | ||||
| host = ["localhost:8000"] | ||||
| path = ["/"] | ||||
| [[locations.destinations]] | ||||
| id = "example-warden-destination" | ||||
| uri = "http://localhost:4321" | ||||
|  | ||||
| [[applications]] | ||||
| id = "example-warden-app" | ||||
| workdir = "../data/warden" | ||||
| command = ["node", "dist/server/entry.mjs"] | ||||
| environment = ["PUBLIC_CMS=https://smartsheep.studio"] | ||||
| @@ -1,12 +0,0 @@ | ||||
| name: Example Site | ||||
| rules: | ||||
|   - host: ["localhost:8000"] | ||||
|     path: ["/"] | ||||
| upstreams: | ||||
|   - id: example | ||||
|     name: Benchmarking Data | ||||
|     uri: http://localhost:3000 | ||||
| processes: | ||||
|   - id: nuxt-ssr | ||||
|     workdir: ../data/ssr | ||||
|     command: ["node", ".output/server/index.mjs"] | ||||
							
								
								
									
										31
									
								
								test/roadsign-ssr/settings.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								test/roadsign-ssr/settings.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| [debug] | ||||
| print_routes = true | ||||
|  | ||||
| [hypertext] | ||||
| sideload_ports = [":81"] | ||||
| sideload_secured_ports = [] | ||||
| ports = [":8000"] | ||||
| secured_ports = [] | ||||
|  | ||||
| [hypertext.certificate] | ||||
| redirect = false | ||||
| sideload_key = "./cert.key" | ||||
| sideload_pem = "./cert.pem" | ||||
| key = "./cert.key" | ||||
| pem = "./cert.pem" | ||||
|  | ||||
| [hypertext.limitation] | ||||
| max_body_size = 549_755_813_888 # 512 GiB | ||||
| max_qps = -1 | ||||
|  | ||||
| [paths] | ||||
| configs = "./config" | ||||
|  | ||||
| [performance] | ||||
| request_logging = true | ||||
| network_timeout = 3_000 | ||||
| prefork = false | ||||
|  | ||||
| [security] | ||||
| sideload_trusted_proxies = ["localhost"] | ||||
| credential = "e81f43f32d934271af6322e5376f5f59" | ||||
| @@ -1,26 +0,0 @@ | ||||
| debug: | ||||
|     print_routes: false | ||||
| hypertext: | ||||
|     sideload_ports: [":81"] | ||||
|     sideload_secured_ports: [] | ||||
|     certificate: | ||||
|         sideload_key: ./cert.key | ||||
|         sideload_pem: ./cert.pem | ||||
|         key: ./cert.key | ||||
|         pem: ./cert.pem | ||||
|     limitation: | ||||
|         max_body_size: 536870912 | ||||
|         max_qps: -1 | ||||
|     ports: | ||||
|         - :8000 | ||||
|     secured_ports: [] | ||||
| paths: | ||||
|     configs: ./config | ||||
| performance: | ||||
|     request_logging: false | ||||
|     network_timeout: 3000 | ||||
|     prefork: false | ||||
| security: | ||||
|     sideload_trusted_proxies: | ||||
|         - localhost | ||||
|     credential: e81f43f32d934271af6322e5376f5f59 | ||||
							
								
								
									
										9
									
								
								test/roadsign-with-prefork/config/example.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								test/roadsign-with-prefork/config/example.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,9 @@ | ||||
| id = "example" | ||||
|  | ||||
| [[locations]] | ||||
| id = "example-location" | ||||
| host = ["localhost:8000"] | ||||
| path = ["/"] | ||||
| [[locations.destinations]] | ||||
| id = "example-destination" | ||||
| uri = "files://../data/spa?fallback=index.html" | ||||
| @@ -1,8 +0,0 @@ | ||||
| name: Example Site | ||||
| rules: | ||||
|   - host: ["localhost:8000"] | ||||
|     path: ["/"] | ||||
| upstreams: | ||||
|   - id: example | ||||
|     name: Benchmarking Data | ||||
|     uri: files://../data | ||||
							
								
								
									
										31
									
								
								test/roadsign-with-prefork/settings.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								test/roadsign-with-prefork/settings.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| [debug] | ||||
| print_routes = true | ||||
|  | ||||
| [hypertext] | ||||
| sideload_ports = [":81"] | ||||
| sideload_secured_ports = [] | ||||
| ports = [":8000"] | ||||
| secured_ports = [] | ||||
|  | ||||
| [hypertext.certificate] | ||||
| redirect = false | ||||
| sideload_key = "./cert.key" | ||||
| sideload_pem = "./cert.pem" | ||||
| key = "./cert.key" | ||||
| pem = "./cert.pem" | ||||
|  | ||||
| [hypertext.limitation] | ||||
| max_body_size = 549_755_813_888 # 512 GiB | ||||
| max_qps = -1 | ||||
|  | ||||
| [paths] | ||||
| configs = "./config" | ||||
|  | ||||
| [performance] | ||||
| request_logging = true | ||||
| network_timeout = 3_000 | ||||
| prefork = true | ||||
|  | ||||
| [security] | ||||
| sideload_trusted_proxies = ["localhost"] | ||||
| credential = "e81f43f32d934271af6322e5376f5f59" | ||||
| @@ -1,26 +0,0 @@ | ||||
| debug: | ||||
|     print_routes: false | ||||
| hypertext: | ||||
|     sideload_ports: [":81"] | ||||
|     sideload_secured_ports: [] | ||||
|     certificate: | ||||
|         sideload_key: ./cert.key | ||||
|         sideload_pem: ./cert.pem | ||||
|         key: ./cert.key | ||||
|         pem: ./cert.pem | ||||
|     limitation: | ||||
|         max_body_size: 536870912 | ||||
|         max_qps: -1 | ||||
|     ports: | ||||
|         - :8000 | ||||
|     secured_ports: [] | ||||
| paths: | ||||
|     configs: ./config | ||||
| performance: | ||||
|     request_logging: false | ||||
|     network_timeout: 3000 | ||||
|     prefork: true | ||||
| security: | ||||
|     sideload_trusted_proxies: | ||||
|         - localhost | ||||
|     credential: e81f43f32d934271af6322e5376f5f59 | ||||
							
								
								
									
										31
									
								
								test/roadsign/settings.toml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								test/roadsign/settings.toml
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| [debug] | ||||
| print_routes = true | ||||
|  | ||||
| [hypertext] | ||||
| sideload_ports = [":81"] | ||||
| sideload_secured_ports = [] | ||||
| ports = [":8000"] | ||||
| secured_ports = [] | ||||
|  | ||||
| [hypertext.certificate] | ||||
| redirect = false | ||||
| sideload_key = "./cert.key" | ||||
| sideload_pem = "./cert.pem" | ||||
| key = "./cert.key" | ||||
| pem = "./cert.pem" | ||||
|  | ||||
| [hypertext.limitation] | ||||
| max_body_size = 549_755_813_888 # 512 GiB | ||||
| max_qps = -1 | ||||
|  | ||||
| [paths] | ||||
| configs = "./config" | ||||
|  | ||||
| [performance] | ||||
| request_logging = true | ||||
| network_timeout = 3_000 | ||||
| prefork = false | ||||
|  | ||||
| [security] | ||||
| sideload_trusted_proxies = ["localhost"] | ||||
| credential = "e81f43f32d934271af6322e5376f5f59" | ||||
| @@ -1,26 +0,0 @@ | ||||
| debug: | ||||
|     print_routes: false | ||||
| hypertext: | ||||
|     sideload_ports: [":81"] | ||||
|     sideload_secured_ports: [] | ||||
|     certificate: | ||||
|         sideload_key: ./cert.key | ||||
|         sideload_pem: ./cert.pem | ||||
|         key: ./cert.key | ||||
|         pem: ./cert.pem | ||||
|     limitation: | ||||
|         max_body_size: 536870912 | ||||
|         max_qps: -1 | ||||
|     ports: | ||||
|         - :8000 | ||||
|     secured_ports: [] | ||||
| paths: | ||||
|     configs: ./config | ||||
| performance: | ||||
|     request_logging: false | ||||
|     network_timeout: 3000 | ||||
|     prefork: false | ||||
| security: | ||||
|     sideload_trusted_proxies: | ||||
|         - localhost | ||||
|     credential: e81f43f32d934271af6322e5376f5f59 | ||||
		Reference in New Issue
	
	Block a user