Basic HTTP servers (#2) and various improvements over docs.
This commit is contained in:
		| @@ -31,4 +31,3 @@ body: | ||||
|       options: | ||||
|         - label: I have read the "Submitting issues" section in [CONTRIBUTING.md](https://code.pztrn.name/apps/featurer/CONTRIBUTING.md) | ||||
|           required: true | ||||
|           visible: [form] | ||||
|   | ||||
| @@ -24,4 +24,3 @@ body: | ||||
|       options: | ||||
|         - label: I have read the "Submitting issues" section in [CONTRIBUTING.md](https://code.pztrn.name/apps/featurer/CONTRIBUTING.md) | ||||
|           required: true | ||||
|           visible: [form] | ||||
|   | ||||
| @@ -25,4 +25,11 @@ In the very end of this file you should place links to diffs between versions, l | ||||
|  | ||||
| ## [Unreleased] | ||||
|  | ||||
| The very first release. | ||||
|  | ||||
| ### Added | ||||
|  | ||||
| - HTTP servers (API and CMS for future use). | ||||
| - Data storage. | ||||
|  | ||||
| > [Unreleased]: https://code.pztrn.name/apps/featurer/compare/v0.0.1...main | ||||
|   | ||||
| @@ -33,6 +33,10 @@ This badge could place only people that present in [maintainers list](MAINTAINER | ||||
|  | ||||
| ## Code | ||||
|  | ||||
| ### Before you start contributing code | ||||
|  | ||||
| Please fill bug report or feature request first. Pull requests without filled issue won't be accepted. | ||||
|  | ||||
| ### Linting | ||||
|  | ||||
| Please use [golangci-lint](https://golangci-lint.run/) with configuration from this repository. | ||||
| @@ -46,3 +50,5 @@ Featurer uses next branching agreement: | ||||
| 1. `main` branch is a subject of constant changes. | ||||
| 2. Fork repository, make changes, create pull request. | ||||
| 3. Releases are cut from release branches named `release/MAJOR.MINOR`. | ||||
|  | ||||
| These agreement will be enforced after first contributor appears! :). | ||||
|   | ||||
							
								
								
									
										1
									
								
								DCO
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								DCO
									
									
									
									
									
								
							| @@ -6,7 +6,6 @@ Copyright (C) 2004, 2006 The Linux Foundation and its contributors. | ||||
| Everyone is permitted to copy and distribute verbatim copies of this | ||||
| license document, but changing it is not allowed. | ||||
|  | ||||
|  | ||||
| Developer's Certificate of Origin 1.1 | ||||
|  | ||||
| By making a contribution to this project, I certify that: | ||||
|   | ||||
| @@ -4,9 +4,9 @@ Features toggling server. | ||||
|  | ||||
| ## Documentation | ||||
|  | ||||
| Complete documentation is stored in [docs](/docs/INDEX.md) directory. | ||||
| Complete documentation is stored in [docs](docs/INDEX.md) directory. | ||||
|  | ||||
| If you want to contribute to project - please take a look at [CONTRIBUTING](/CONTRIBUTING.md) file. | ||||
| If you want to contribute to project - please take a look at [CONTRIBUTING](CONTRIBUTING.md) file. | ||||
|  | ||||
| ## Licensing | ||||
|  | ||||
|   | ||||
							
								
								
									
										31
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								go.mod
									
									
									
									
									
								
							| @@ -1,3 +1,34 @@ | ||||
| module go.dev.pztrn.name/featurer | ||||
|  | ||||
| go 1.23 | ||||
|  | ||||
| require github.com/gin-gonic/gin v1.10.0 | ||||
|  | ||||
| require ( | ||||
| 	github.com/bytedance/sonic v1.11.6 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.1.1 // indirect | ||||
| 	github.com/cloudwego/base64x v0.1.4 // indirect | ||||
| 	github.com/cloudwego/iasm v0.2.0 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.3 // indirect | ||||
| 	github.com/gin-contrib/sse v0.1.0 // indirect | ||||
| 	github.com/go-playground/locales v0.14.1 // indirect | ||||
| 	github.com/go-playground/universal-translator v0.18.1 // indirect | ||||
| 	github.com/go-playground/validator/v10 v10.20.0 // indirect | ||||
| 	github.com/goccy/go-json v0.10.2 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/klauspost/cpuid/v2 v2.2.7 // indirect | ||||
| 	github.com/leodido/go-urn v1.4.0 // indirect | ||||
| 	github.com/mattn/go-isatty v0.0.20 // indirect | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.2 // indirect | ||||
| 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||
| 	github.com/ugorji/go/codec v1.2.12 // indirect | ||||
| 	golang.org/x/arch v0.8.0 // indirect | ||||
| 	golang.org/x/crypto v0.23.0 // indirect | ||||
| 	golang.org/x/net v0.25.0 // indirect | ||||
| 	golang.org/x/sys v0.20.0 // indirect | ||||
| 	golang.org/x/text v0.15.0 // indirect | ||||
| 	google.golang.org/protobuf v1.34.1 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
| ) | ||||
|   | ||||
							
								
								
									
										89
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								go.sum
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,89 @@ | ||||
| github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= | ||||
| github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= | ||||
| github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= | ||||
| github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||||
| github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= | ||||
| github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= | ||||
| github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= | ||||
| github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= | ||||
| github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= | ||||
| github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= | ||||
| github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0= | ||||
| github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk= | ||||
| github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= | ||||
| github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= | ||||
| github.com/gin-gonic/gin v1.10.0 h1:nTuyha1TYqgedzytsKYqna+DfLos46nTv2ygFy86HFU= | ||||
| github.com/gin-gonic/gin v1.10.0/go.mod h1:4PMNQiOhvDRa013RKVbsiNwoyezlm2rm0uX/T7kzp5Y= | ||||
| github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= | ||||
| github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= | ||||
| github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= | ||||
| github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= | ||||
| github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= | ||||
| github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= | ||||
| github.com/go-playground/validator/v10 v10.20.0 h1:K9ISHbSaI0lyB2eWMPJo+kOS/FBExVwjEviJTixqxL8= | ||||
| github.com/go-playground/validator/v10 v10.20.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||||
| github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= | ||||
| github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= | ||||
| github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= | ||||
| github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= | ||||
| github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= | ||||
| 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/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||||
| github.com/klauspost/cpuid/v2 v2.2.7 h1:ZWSB3igEs+d0qvnxR/ZBzXVmxkgt8DdzP6m9pfuVLDM= | ||||
| github.com/klauspost/cpuid/v2 v2.2.7/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | ||||
| github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= | ||||
| github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= | ||||
| github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | ||||
| github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= | ||||
| github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= | ||||
| github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= | ||||
| github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= | ||||
| 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.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM= | ||||
| github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= | ||||
| 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/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= | ||||
| github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= | ||||
| 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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= | ||||
| github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= | ||||
| github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= | ||||
| github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= | ||||
| github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= | ||||
| github.com/ugorji/go/codec v1.2.12 h1:9LC83zGrHhuUA9l16C9AHXAqEV/2wBQ4nkvumAE65EE= | ||||
| github.com/ugorji/go/codec v1.2.12/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg= | ||||
| golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= | ||||
| golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= | ||||
| golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= | ||||
| golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= | ||||
| golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= | ||||
| golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= | ||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||
| golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= | ||||
| golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= | ||||
| golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= | ||||
| golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||
| google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= | ||||
| google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= | ||||
| gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= | ||||
| 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= | ||||
| nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= | ||||
| rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= | ||||
| @@ -5,6 +5,7 @@ import ( | ||||
|  | ||||
| 	"go.dev.pztrn.name/featurer/server/internal/application" | ||||
| 	"go.dev.pztrn.name/featurer/server/internal/services/core/datastore" | ||||
| 	"go.dev.pztrn.name/featurer/server/internal/services/core/http" | ||||
| ) | ||||
|  | ||||
| func main() { | ||||
| @@ -23,6 +24,7 @@ func main() { | ||||
|  | ||||
| 	// Initializing core services first. | ||||
| 	checkError(datastore.Initialize(app)) | ||||
| 	checkError(http.Initialize(app)) | ||||
|  | ||||
| 	// Then - features services. | ||||
|  | ||||
|   | ||||
							
								
								
									
										37
									
								
								server/internal/services/core/http.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								server/internal/services/core/http.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,37 @@ | ||||
| package core | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	// ServerNameAPI is a HTTP server name for API. | ||||
| 	ServerNameAPI = "api" | ||||
| 	// ServerNameCMS is a HTTP server name for CMS. | ||||
| 	ServerNameCMS = "cms" | ||||
| 	// ServiceNameHTTP is a name for service responsible for HTTP servers controlloing. | ||||
| 	ServiceNameHTTP = "http servers" | ||||
| ) | ||||
|  | ||||
| var ( | ||||
| 	// ErrHTTP указывает на ошибку в сервисе управления HTTP серверами. | ||||
| 	ErrHTTP = errors.New("HTTP server") | ||||
| 	// ErrHTTPAssetsBundleAlreadyRegistered говорит о попытке зарегистрировать бандл с ассетами с уже использованным | ||||
| 	// именем. | ||||
| 	ErrHTTPAssetsBundleAlreadyRegistered = errors.New("assets bundle already registered") | ||||
| 	// ErrHTTPCMSAddressInvalid говорит о неправильно заданном адресе для прослушки, который был получен из | ||||
| 	// переменных окружения. | ||||
| 	ErrHTTPCMSAddressInvalid = errors.New("CMS server address invalid") | ||||
| 	// ErrHTTPServerNotFound возникает при попытке получить неизвестный HTTP сервер. | ||||
| 	ErrHTTPServerNotFound = errors.New("HTTP server not found") | ||||
| 	// ErrHTTPServiceIsInvalid говорит о неправильной имплементации сервиса управления HTTP серверами. | ||||
| 	ErrHTTPServiceIsInvalid = errors.New("service implementation is invalid") | ||||
| ) | ||||
|  | ||||
| // HTTP это интерфейс для сервиса управления HTTP серверами. | ||||
| type HTTP interface { | ||||
| 	RegisterHandler(serverName, method, path string, handler func(*gin.Context)) error | ||||
| 	ReplyJSON(ctx *gin.Context, statusCode int, data interface{}) | ||||
| } | ||||
							
								
								
									
										29
									
								
								server/internal/services/core/http/generic_handlers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								server/internal/services/core/http/generic_handlers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
|  | ||||
| 	"go.dev.pztrn.name/featurer/server/internal/services/core" | ||||
| ) | ||||
|  | ||||
| func (h *http) RegisterHandler(serverName, method, path string, handler func(*gin.Context)) error { | ||||
| 	h.serversMutex.RLock() | ||||
| 	defer h.serversMutex.RUnlock() | ||||
|  | ||||
| 	srv, found := h.servers[serverName] | ||||
| 	if !found { | ||||
| 		return fmt.Errorf( | ||||
| 			"%w: registering handler '%s %s': %w", | ||||
| 			core.ErrHTTP, | ||||
| 			method, | ||||
| 			path, | ||||
| 			core.ErrHTTPServerNotFound, | ||||
| 		) | ||||
| 	} | ||||
|  | ||||
| 	_ = srv.Handle(method, path, handler) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										90
									
								
								server/internal/services/core/http/http.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								server/internal/services/core/http/http.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"log/slog" | ||||
| 	stdhttp "net/http" | ||||
| 	"os" | ||||
| 	"strings" | ||||
| 	"sync" | ||||
|  | ||||
| 	"go.dev.pztrn.name/featurer/server/internal/application" | ||||
| 	"go.dev.pztrn.name/featurer/server/internal/services/core" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| const ( | ||||
| 	subsystem = "HTTP servers" | ||||
| ) | ||||
|  | ||||
| var _ = core.HTTP(&http{}) | ||||
|  | ||||
| type http struct { | ||||
| 	app          *application.Application | ||||
| 	servers      map[string]*gin.Engine | ||||
| 	httpServers  map[string]*stdhttp.Server | ||||
| 	serversMutex sync.RWMutex | ||||
| } | ||||
|  | ||||
| // Initialize initializes service. | ||||
| func Initialize(app *application.Application) error { | ||||
| 	httpSrv := &http{ | ||||
| 		app: app, | ||||
| 	} | ||||
|  | ||||
| 	if err := app.RegisterService(httpSrv); err != nil { | ||||
| 		return fmt.Errorf("%w: register service: %w", core.ErrHTTP, err) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (h *http) ConnectDependencies() error { | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (h *http) GetName() string { | ||||
| 	return core.ServiceNameHTTP | ||||
| } | ||||
|  | ||||
| func (h *http) Initialize() error { | ||||
| 	slog.Info("Initializing service...", "service", subsystem) | ||||
|  | ||||
| 	h.servers = make(map[string]*gin.Engine) | ||||
| 	h.httpServers = make(map[string]*stdhttp.Server) | ||||
|  | ||||
| 	serversNames := []string{core.ServerNameAPI, core.ServerNameCMS} | ||||
|  | ||||
| 	for _, name := range serversNames { | ||||
| 		addr, found := os.LookupEnv("FEATURER_" + strings.ToUpper(name) + "_SERVER_ADDRESS") | ||||
| 		if !found { | ||||
| 			return fmt.Errorf("%w: getting address for server '%s' from env: not found", core.ErrHTTP, name) | ||||
| 		} | ||||
|  | ||||
| 		h.createServer(name, addr) | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (h *http) LaunchStartupTasks() error { | ||||
| 	h.startServers() | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
|  | ||||
| func (h *http) Shutdown() error { | ||||
| 	h.serversMutex.Lock() | ||||
| 	defer h.serversMutex.Unlock() | ||||
|  | ||||
| 	for name, server := range h.httpServers { | ||||
| 		slog.Info("Stopping HTTP server...", "service", subsystem, "server", name) | ||||
|  | ||||
| 		if err := server.Shutdown(h.app.GetContext()); err != nil { | ||||
| 			slog.Error("Failed to stop HTTP server!", "service", subsystem, "server", name, "error", err.Error()) | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										29
									
								
								server/internal/services/core/http/logger.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								server/internal/services/core/http/logger.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,29 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"log/slog" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func (h *http) requestLogger(serverName string) gin.HandlerFunc { | ||||
| 	return func(ctx *gin.Context) { | ||||
| 		startTime := time.Now() | ||||
|  | ||||
| 		ctx.Next() | ||||
|  | ||||
| 		slog.Info( | ||||
| 			"HTTP request processed", | ||||
| 			"service", subsystem, | ||||
| 			"server", serverName, | ||||
| 			"client-ip", ctx.ClientIP(), | ||||
| 			"user-agent", ctx.Request.UserAgent(), | ||||
| 			"path", ctx.Request.Method+" "+ctx.Request.URL.String(), | ||||
| 			"request-size", ctx.Request.ContentLength, | ||||
| 			"response-code", ctx.Writer.Status(), | ||||
| 			"response-length", ctx.Writer.Size(), | ||||
| 			"response-time", time.Since(startTime).String(), | ||||
| 		) | ||||
| 	} | ||||
| } | ||||
							
								
								
									
										23
									
								
								server/internal/services/core/http/middlewares.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								server/internal/services/core/http/middlewares.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
|  | ||||
| 	"go.dev.pztrn.name/featurer/server/internal/services/core" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func (h *http) RegisterMiddleware(serverName string, middleware gin.HandlerFunc) error { | ||||
| 	h.serversMutex.RLock() | ||||
| 	defer h.serversMutex.RUnlock() | ||||
|  | ||||
| 	router, found := h.servers[serverName] | ||||
| 	if !found { | ||||
| 		return fmt.Errorf("%w: registering middleware: %w", core.ErrHTTP, core.ErrHTTPServerNotFound) | ||||
| 	} | ||||
|  | ||||
| 	_ = router.Use(middleware) | ||||
|  | ||||
| 	return nil | ||||
| } | ||||
							
								
								
									
										10
									
								
								server/internal/services/core/http/replies.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								server/internal/services/core/http/replies.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func (h *http) ReplyJSON(ctx *gin.Context, statusCode int, data interface{}) { | ||||
| 	ctx.Writer.Header().Add("Content-Type", "application/json") | ||||
| 	ctx.JSON(statusCode, data) | ||||
| } | ||||
							
								
								
									
										56
									
								
								server/internal/services/core/http/servers.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								server/internal/services/core/http/servers.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,56 @@ | ||||
| package http | ||||
|  | ||||
| import ( | ||||
| 	"log/slog" | ||||
| 	stdhttp "net/http" | ||||
| 	"time" | ||||
|  | ||||
| 	"github.com/gin-gonic/gin" | ||||
| ) | ||||
|  | ||||
| func (h *http) createServer(name, address string) { | ||||
| 	engine := gin.New() | ||||
| 	//nolint:gomnd,mnd | ||||
| 	httpServer := &stdhttp.Server{ | ||||
| 		Addr:    address, | ||||
| 		Handler: engine.Handler(), | ||||
| 		// ToDo: move into configuration. | ||||
| 		ReadTimeout:  time.Second * 5, | ||||
| 		WriteTimeout: time.Second * 10, | ||||
| 	} | ||||
|  | ||||
| 	h.serversMutex.Lock() | ||||
| 	h.servers[name] = engine | ||||
| 	h.httpServers[name] = httpServer | ||||
| 	h.serversMutex.Unlock() | ||||
|  | ||||
| 	// ToDo: trusted proxies. | ||||
|  | ||||
| 	engine.Use(h.requestLogger(name)) | ||||
| } | ||||
|  | ||||
| func (h *http) startServers() { | ||||
| 	h.serversMutex.RLock() | ||||
| 	defer h.serversMutex.RUnlock() | ||||
|  | ||||
| 	for srvName, httpServer := range h.httpServers { | ||||
| 		go func(name string, srv *stdhttp.Server) { | ||||
| 			slog.Info( | ||||
| 				"Starting HTTP server...", | ||||
| 				"service", subsystem, | ||||
| 				"server-name", name, | ||||
| 				"server-address", srv.Addr, | ||||
| 			) | ||||
|  | ||||
| 			if err := srv.ListenAndServe(); err != nil { | ||||
| 				slog.Error( | ||||
| 					"Failed to start HTTP server!", | ||||
| 					"service", subsystem, | ||||
| 					"server-name", name, | ||||
| 					"server-address", srv.Addr, | ||||
| 					"error", err.Error(), | ||||
| 				) | ||||
| 			} | ||||
| 		}(srvName, httpServer) | ||||
| 	} | ||||
| } | ||||
| @@ -5,9 +5,15 @@ services: | ||||
|     build: | ||||
|       context: ../../../ | ||||
|       dockerfile: server/Dockerfile.featurer | ||||
|     ports: | ||||
|       - "15000:5000" | ||||
|       - "15001:5001" | ||||
|     networks: | ||||
|       featurer: | ||||
|         ipv4_address: 248.248.0.2 | ||||
|     environment: | ||||
|       FEATURER_API_SERVER_ADDRESS: "0.0.0.0:5000" | ||||
|       FEATURER_CMS_SERVER_ADDRESS: "0.0.0.0:5001" | ||||
|     cap_add: | ||||
|       - SYS_PTRACE | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user