-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #8 from spotlibs/feat/pkg-middlewares-and-ctx
feat: new middlewares and pkg `ctx`
- Loading branch information
Showing
4 changed files
with
200 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
package ctx | ||
|
||
import "github.com/goravel/framework/contracts/http" | ||
|
||
// metadataKey key to identify that a value in context is set and get from this | ||
// package. | ||
const metadataKey = "spotlibs-metadata-key" | ||
|
||
// Metadata holds any request-scoped shared data within brispot microservice. | ||
type Metadata struct { | ||
Authorization string | ||
UserAgent string | ||
CacheControl string | ||
ForwardedFor string | ||
RequestFrom string | ||
DeviceId string | ||
App string | ||
VersionApp string | ||
ReqId string | ||
ReqTags string | ||
ReqUser string | ||
ReqNama string | ||
ReqKodeJabatan string | ||
ReqNamaJabatan string | ||
ReqKodeUker string | ||
ReqNamaUker string | ||
ReqJenisUker string | ||
ReqKodeMainUker string | ||
ReqKodeRegion string | ||
PathGateway string | ||
ApiKey string | ||
} | ||
|
||
// ParseRequest return Metadata from given http context but return empty data | ||
// instead if no data were found. | ||
func ParseRequest(c http.Context) Metadata { | ||
mt, ok := c.Value(metadataKey).(Metadata) | ||
if !ok { | ||
mt = Metadata{} | ||
} | ||
return mt | ||
} | ||
|
||
// SetFromRequestHeader set any available metadata from given http context in | ||
// the request header. | ||
func SetFromRequestHeader(c http.Context) { | ||
mt := Metadata{ | ||
Authorization: c.Request().Header("Authorization"), | ||
UserAgent: c.Request().Header("User-Agent"), | ||
CacheControl: c.Request().Header("Cache-Control"), | ||
ApiKey: c.Request().Header("X-Api-Key"), | ||
ForwardedFor: c.Request().Header("X-Forwarded-For"), | ||
RequestFrom: c.Request().Header("X-Request-From"), | ||
DeviceId: c.Request().Header("X-Device-Id"), | ||
App: c.Request().Header("X-App"), | ||
VersionApp: c.Request().Header("X-Version-App"), | ||
ReqId: c.Request().Header("X-Request-Id"), | ||
ReqTags: c.Request().Header("X-Request-Tags"), | ||
ReqUser: c.Request().Header("X-Request-User"), | ||
ReqNama: c.Request().Header("X-Request-Nama"), | ||
ReqKodeJabatan: c.Request().Header("X-Request-Kode-Jabatan"), | ||
ReqNamaJabatan: c.Request().Header("X-Request-Nama-Jabatan"), | ||
ReqKodeUker: c.Request().Header("X-Request-Kode-Uker"), | ||
ReqNamaUker: c.Request().Header("X-Request-Nama-Uker"), | ||
ReqKodeMainUker: c.Request().Header("X-Request-Kode-MainUker"), | ||
ReqKodeRegion: c.Request().Header("X-Request-Kode-Region"), | ||
ReqJenisUker: c.Request().Header("X-Request-Jenis-Uker"), | ||
PathGateway: c.Request().Header("X-Path-Gateway"), | ||
} | ||
c.WithValue(metadataKey, mt) | ||
} | ||
|
||
// GetReqId shortcut of ParseRequest with ReqId to get request id from given | ||
// http context. | ||
func GetReqId(c http.Context) string { | ||
return ParseRequest(c).ReqId | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
package middleware | ||
|
||
import ( | ||
"slices" | ||
"strings" | ||
"time" | ||
|
||
"github.com/bytedance/sonic" | ||
"github.com/goravel/framework/contracts/http" | ||
"github.com/goravel/framework/facades" | ||
"github.com/spotlibs/go-lib/ctx" | ||
) | ||
|
||
// ActivityMonitor capture and log all request/response. | ||
func ActivityMonitor(c http.Context) { | ||
now := time.Now() | ||
c.Request().Next() | ||
apiActivityRecorder(c, now) | ||
} | ||
|
||
func apiActivityRecorder(c http.Context, start time.Time) { | ||
ct := c.Request().Header("Content-Type") | ||
|
||
// check the content type and capture the request body according to it | ||
var req any | ||
switch { | ||
case hasPrefix(ct, "application/json", "application/x-www-form-urlencoded"): | ||
req = captureRequestMap(c) | ||
case hasPrefix(ct, "multipart/form-data"): | ||
req = captureRequestMultipart(c) | ||
default: | ||
req = captureRequestMap(c) // treat any unhandled content-type as map | ||
} | ||
|
||
// transform back response to an object before capturing | ||
var res map[string]any | ||
_ = sonic.ConfigFastest.Unmarshal(c.Response().Origin().Body().Bytes(), &res) | ||
|
||
// get metadata from context | ||
mt := ctx.ParseRequest(c) | ||
|
||
activityData := map[string]any{ | ||
"app_name": facades.Config().GetString("app.name", "Microservice"), | ||
"host": c.Request().Host(), | ||
"path": c.Request().Path(), | ||
"client_ip": c.Request().Header("X-Forwarded-For", c.Request().Ip()), | ||
"client_app": mt.App, | ||
"path_alias": mt.PathGateway, | ||
"requestID": mt.ReqId, | ||
"requestFrom": mt.RequestFrom, | ||
"requestUser": mt.ReqUser, | ||
"deviceID": mt.DeviceId, | ||
"requestTags": mt.ReqTags, | ||
"requestBody": req, | ||
"responseBody": res, | ||
"responseTime": time.Since(start).Milliseconds(), | ||
"httpCode": c.Response().Origin().Status(), | ||
"requestAt": start.Format(time.RFC3339Nano), | ||
//"memoryUsage": // coming soon | ||
} | ||
st, _ := sonic.ConfigFastest.MarshalToString(activityData) | ||
// currently only log to stdout | ||
println(st) | ||
} | ||
|
||
// captureRequestMap capture request as map and transform it to json string. | ||
func captureRequestMap(c http.Context) any { | ||
return c.Request().All() | ||
} | ||
|
||
// captureRequestMultipart capture request multipart data and only get | ||
// the information of that file such as the filename, size and extension. | ||
func captureRequestMultipart(c http.Context) any { | ||
reqOrg := c.Request().Origin() | ||
_ = reqOrg.ParseMultipartForm(2 << 9) // 1024 | ||
|
||
var bagOfForm []map[string]any | ||
// grab any available form-value | ||
for k, v := range reqOrg.MultipartForm.Value { | ||
bagOfForm = append(bagOfForm, map[string]any{ | ||
"field": k, | ||
"value": v, | ||
}) | ||
} | ||
// grab any available files | ||
for field, header := range reqOrg.MultipartForm.File { | ||
for _, headerFile := range header { | ||
if headerFile != nil { | ||
bagOfForm = append(bagOfForm, map[string]any{ | ||
"field": field, | ||
"filename": headerFile.Filename, | ||
"size": int(headerFile.Size), | ||
}) | ||
} | ||
} | ||
} | ||
|
||
return bagOfForm | ||
} | ||
|
||
// hasPrefix return true if the given s has at least one of the given prefixes. | ||
func hasPrefix(s string, prefix ...string) bool { | ||
return slices.ContainsFunc(prefix, func(pre string) bool { | ||
return strings.HasPrefix(s, pre) | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package middleware | ||
|
||
import ( | ||
"github.com/goravel/framework/contracts/http" | ||
"github.com/spotlibs/go-lib/ctx" | ||
) | ||
|
||
// MetadataHeader set metadata information come from request header to current | ||
// context. | ||
func MetadataHeader(c http.Context) { | ||
ctx.SetFromRequestHeader(c) | ||
c.Request().Next() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters