Basic HTTP servers (#2) and various improvements over docs.
Some checks failed
Linting and tests / Linting (push) Has been cancelled
Linting and tests / Tests (push) Has been cancelled

This commit is contained in:
Stanislav Nikitin 2024-10-12 23:07:49 +05:00
parent 0b5d3a2c37
commit 2ecfe7f8ac
Signed by: pztrn
GPG Key ID: 1E944A0F0568B550
17 changed files with 417 additions and 5 deletions

View File

@ -31,4 +31,3 @@ body:
options: options:
- label: I have read the "Submitting issues" section in [CONTRIBUTING.md](https://code.pztrn.name/apps/featurer/CONTRIBUTING.md) - label: I have read the "Submitting issues" section in [CONTRIBUTING.md](https://code.pztrn.name/apps/featurer/CONTRIBUTING.md)
required: true required: true
visible: [form]

View File

@ -24,4 +24,3 @@ body:
options: options:
- label: I have read the "Submitting issues" section in [CONTRIBUTING.md](https://code.pztrn.name/apps/featurer/CONTRIBUTING.md) - label: I have read the "Submitting issues" section in [CONTRIBUTING.md](https://code.pztrn.name/apps/featurer/CONTRIBUTING.md)
required: true required: true
visible: [form]

View File

@ -25,4 +25,11 @@ In the very end of this file you should place links to diffs between versions, l
## [Unreleased] ## [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 > [Unreleased]: https://code.pztrn.name/apps/featurer/compare/v0.0.1...main

View File

@ -33,6 +33,10 @@ This badge could place only people that present in [maintainers list](MAINTAINER
## Code ## Code
### Before you start contributing code
Please fill bug report or feature request first. Pull requests without filled issue won't be accepted.
### Linting ### Linting
Please use [golangci-lint](https://golangci-lint.run/) with configuration from this repository. 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. 1. `main` branch is a subject of constant changes.
2. Fork repository, make changes, create pull request. 2. Fork repository, make changes, create pull request.
3. Releases are cut from release branches named `release/MAJOR.MINOR`. 3. Releases are cut from release branches named `release/MAJOR.MINOR`.
These agreement will be enforced after first contributor appears! :).

1
DCO
View File

@ -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 Everyone is permitted to copy and distribute verbatim copies of this
license document, but changing it is not allowed. license document, but changing it is not allowed.
Developer's Certificate of Origin 1.1 Developer's Certificate of Origin 1.1
By making a contribution to this project, I certify that: By making a contribution to this project, I certify that:

View File

@ -4,9 +4,9 @@ Features toggling server.
## Documentation ## 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 ## Licensing

31
go.mod
View File

@ -1,3 +1,34 @@
module go.dev.pztrn.name/featurer module go.dev.pztrn.name/featurer
go 1.23 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
View 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=

View File

@ -5,6 +5,7 @@ import (
"go.dev.pztrn.name/featurer/server/internal/application" "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/datastore"
"go.dev.pztrn.name/featurer/server/internal/services/core/http"
) )
func main() { func main() {
@ -23,6 +24,7 @@ func main() {
// Initializing core services first. // Initializing core services first.
checkError(datastore.Initialize(app)) checkError(datastore.Initialize(app))
checkError(http.Initialize(app))
// Then - features services. // Then - features services.

View 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{})
}

View 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
}

View 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
}

View 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(),
)
}
}

View 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
}

View 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)
}

View 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)
}
}

View File

@ -5,9 +5,15 @@ services:
build: build:
context: ../../../ context: ../../../
dockerfile: server/Dockerfile.featurer dockerfile: server/Dockerfile.featurer
ports:
- "15000:5000"
- "15001:5001"
networks: networks:
featurer: featurer:
ipv4_address: 248.248.0.2 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: cap_add:
- SYS_PTRACE - SYS_PTRACE