Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8532eb49c7 | |||
| 0200408344 | |||
| 425b7bbb3f | |||
| c33c1305ff | |||
| 7a695c239e |
74
Makefile
74
Makefile
@ -46,7 +46,7 @@ TARGET_FILE_TYPE=\
|
|||||||
-o -name "*.yml"
|
-o -name "*.yml"
|
||||||
|
|
||||||
# Go Module Placeholder
|
# Go Module Placeholder
|
||||||
# Replacement rules applied by upd-go-mod. Valid sed replacement expressions.
|
# Replacement rules applied by upd-gm-ph. Valid sed replacement expressions.
|
||||||
GO_MODULE_REPLACEMENT=\
|
GO_MODULE_REPLACEMENT=\
|
||||||
-e 's|gomodule|$(GO_MODULE)|g' \
|
-e 's|gomodule|$(GO_MODULE)|g' \
|
||||||
-e 's|go/module|$(GO_MODULE)|g' \
|
-e 's|go/module|$(GO_MODULE)|g' \
|
||||||
@ -60,10 +60,10 @@ GO_MODULE_REPLACEMENT=\
|
|||||||
# PHONY Target
|
# PHONY Target
|
||||||
.PHONY: \
|
.PHONY: \
|
||||||
help \
|
help \
|
||||||
|
upd-gm-ph \
|
||||||
|
upd-fw-md \
|
||||||
|
upd-sw-md \
|
||||||
go-ini \
|
go-ini \
|
||||||
upd-go-mod \
|
|
||||||
upd-fw-imd \
|
|
||||||
upd-sw-imd \
|
|
||||||
go-tid \
|
go-tid \
|
||||||
go-stp \
|
go-stp \
|
||||||
pre-rel \
|
pre-rel \
|
||||||
@ -87,48 +87,36 @@ GO_MODULE_REPLACEMENT=\
|
|||||||
|
|
||||||
help:
|
help:
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo "Available Command:"
|
@echo "Available Commands:"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " make go-stp Initialize Go module and tidy dependencies"
|
@printf " %-18s %s\n" "upd-gm-ph" "Update Go module placeholder"
|
||||||
|
@printf " %-18s %s\n" "upd-fw-md" "Update framework metadata"
|
||||||
|
@printf " %-18s %s\n" "upd-sw-md" "Update software metadata"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " make upd-go-mod Update Go module placeholders"
|
@printf " %-18s %s\n" "go-ini" "Initialize Go module"
|
||||||
@echo " make upd-fw-imd Update framework metadata"
|
@printf " %-18s %s\n" "go-tid" "Tidy Go module dependencies"
|
||||||
@echo " make upd-sw-imd Update software metadata"
|
@printf " %-18s %s\n" "go-stp" "Initialize Go module and tidy dependencies"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " make go-bld Build all platform binaries"
|
@printf " %-18s %s\n" "go-bld" "Build all platform binaries"
|
||||||
@echo " make go-bld-lin Build all Linux binaries"
|
@printf " %-18s %s\n" "go-bld-lin" "Build all Linux binaries"
|
||||||
@echo " make go-bld-win Build all Windows binaries"
|
@printf " %-18s %s\n" "go-bld-win" "Build all Windows binaries"
|
||||||
@echo " make go-bld-mac Build all macOS binaries"
|
@printf " %-18s %s\n" "go-bld-mac" "Build all macOS binaries"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " make bld-lin-amd Build Linux AMD64 binary"
|
@printf " %-18s %s\n" "bld-lin-amd" "Build Linux AMD64 binary"
|
||||||
@echo " make bld-lin-arm Build Linux ARM64 binary"
|
@printf " %-18s %s\n" "bld-lin-arm" "Build Linux ARM64 binary"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " make bld-win-amd Build Windows AMD64 binary"
|
@printf " %-18s %s\n" "bld-win-amd" "Build Windows AMD64 binary"
|
||||||
@echo " make bld-win-arm Build Windows ARM64 binary"
|
@printf " %-18s %s\n" "bld-win-arm" "Build Windows ARM64 binary"
|
||||||
@echo ""
|
@echo ""
|
||||||
@echo " make bld-mac-amd Build macOS AMD64 binary"
|
@printf " %-18s %s\n" "bld-mac-amd" "Build macOS AMD64 binary"
|
||||||
@echo " make bld-mac-arm Build macOS ARM64 binary"
|
@printf " %-18s %s\n" "bld-mac-arm" "Build macOS ARM64 binary"
|
||||||
@echo ""
|
@echo ""
|
||||||
|
|
||||||
# =========================================================
|
|
||||||
# Initialize
|
|
||||||
# =========================================================
|
|
||||||
|
|
||||||
go-ini:
|
|
||||||
@test -f go.mod || go mod init $(GO_MODULE)
|
|
||||||
|
|
||||||
go-tid:
|
|
||||||
go mod tidy
|
|
||||||
|
|
||||||
go-stp: \
|
|
||||||
go-ini \
|
|
||||||
go-tid
|
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Update
|
# Update
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|
||||||
upd-go-mod:
|
upd-gm-ph:
|
||||||
|
|
||||||
@find . -type f \
|
@find . -type f \
|
||||||
\( $(TARGET_FILE_TYPE) \) \
|
\( $(TARGET_FILE_TYPE) \) \
|
||||||
@ -146,7 +134,7 @@ upd-go-mod:
|
|||||||
|
|
||||||
@echo "Go Module Updated!"
|
@echo "Go Module Updated!"
|
||||||
|
|
||||||
upd-fw-imd:
|
upd-fw-md:
|
||||||
|
|
||||||
@jq \
|
@jq \
|
||||||
--arg framework_regular "$(FRAMEWORK_REGULAR)" \
|
--arg framework_regular "$(FRAMEWORK_REGULAR)" \
|
||||||
@ -161,7 +149,7 @@ upd-fw-imd:
|
|||||||
|
|
||||||
@echo "Framework Metadata Updated!"
|
@echo "Framework Metadata Updated!"
|
||||||
|
|
||||||
upd-sw-imd:
|
upd-sw-md:
|
||||||
|
|
||||||
@jq \
|
@jq \
|
||||||
--arg software_regular "$(SOFTWARE_REGULAR)" \
|
--arg software_regular "$(SOFTWARE_REGULAR)" \
|
||||||
@ -185,6 +173,20 @@ pre-rel:
|
|||||||
@mkdir -p "$(RELEASE_DIR)"
|
@mkdir -p "$(RELEASE_DIR)"
|
||||||
@find "$(RELEASE_DIR)" -mindepth 1 -delete
|
@find "$(RELEASE_DIR)" -mindepth 1 -delete
|
||||||
|
|
||||||
|
# =========================================================
|
||||||
|
# Initialize
|
||||||
|
# =========================================================
|
||||||
|
|
||||||
|
go-ini:
|
||||||
|
@test -f go.mod || go mod init $(GO_MODULE)
|
||||||
|
|
||||||
|
go-tid:
|
||||||
|
go mod tidy
|
||||||
|
|
||||||
|
go-stp: \
|
||||||
|
go-ini \
|
||||||
|
go-tid
|
||||||
|
|
||||||
# =========================================================
|
# =========================================================
|
||||||
# Build Group
|
# Build Group
|
||||||
# =========================================================
|
# =========================================================
|
||||||
|
|||||||
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Handler
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide a JSON resource provider for the Retriever utility.
|
||||||
|
| - Embed, parse, and expose a complete JSON resource payload.
|
||||||
|
| - Abstract resource storage details from consumers.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||||
|
| - Associate the handler with a unique resource identifier.
|
||||||
|
| - Return the complete parsed resource payload only.
|
||||||
|
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||||
|
| - Do not implement business logic within the handler.
|
||||||
|
| - Do not perform querying, transformation, or resource discovery.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - retriever.ObrimRetrieverJsonRegister(
|
||||||
|
| "software/metadata",
|
||||||
|
| obrimHandlerProvider{},
|
||||||
|
| )
|
||||||
|
| - response := retriever.ObrimRetriever(
|
||||||
|
| "json",
|
||||||
|
| map[string]any{
|
||||||
|
| "resource": "software/metadata",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"go/module/essential/visible/service/helper/retriever"
|
||||||
|
)
|
||||||
|
|
||||||
|
// constant
|
||||||
|
const (
|
||||||
|
// resource
|
||||||
|
obrimHandlerResourceIdentifier = "framework/glossary" // change identifier if needed
|
||||||
|
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||||
|
obrimHandlerResourceFileExt = ".json" // do not change
|
||||||
|
|
||||||
|
// success
|
||||||
|
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||||
|
|
||||||
|
// fail
|
||||||
|
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||||
|
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||||
|
)
|
||||||
|
|
||||||
|
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||||
|
//
|
||||||
|
//go:embed resource.json
|
||||||
|
var obrimHandlerEmbeddedResource embed.FS
|
||||||
|
|
||||||
|
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||||
|
type obrimHandlerProvider struct{}
|
||||||
|
|
||||||
|
// Retrieve loads and returns the complete parsed resource payload.
|
||||||
|
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||||
|
return obrimHandlerFetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// init registers the handler during package initialization.
|
||||||
|
func init() {
|
||||||
|
obrimHandlerRegister()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerRegister registers the handler with Retriever.
|
||||||
|
func obrimHandlerRegister() {
|
||||||
|
retriever.ObrimRetrieverJsonRegister(
|
||||||
|
obrimHandlerResourceIdentifier,
|
||||||
|
obrimHandlerProvider{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||||
|
func obrimHandlerFetch() (any, error) {
|
||||||
|
resource, err := obrimHandlerLoadResource()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := obrimHandlerParseResource(resource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerLoadResource loads the embedded resource.
|
||||||
|
func obrimHandlerLoadResource() ([]byte, error) {
|
||||||
|
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||||
|
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||||
|
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||||
|
var payload map[string]any
|
||||||
|
|
||||||
|
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Handler
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide a JSON resource provider for the Retriever utility.
|
||||||
|
| - Embed, parse, and expose a complete JSON resource payload.
|
||||||
|
| - Abstract resource storage details from consumers.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||||
|
| - Associate the handler with a unique resource identifier.
|
||||||
|
| - Return the complete parsed resource payload only.
|
||||||
|
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||||
|
| - Do not implement business logic within the handler.
|
||||||
|
| - Do not perform querying, transformation, or resource discovery.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - retriever.ObrimRetrieverJsonRegister(
|
||||||
|
| "software/metadata",
|
||||||
|
| obrimHandlerProvider{},
|
||||||
|
| )
|
||||||
|
| - response := retriever.ObrimRetriever(
|
||||||
|
| "json",
|
||||||
|
| map[string]any{
|
||||||
|
| "resource": "software/metadata",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"go/module/essential/visible/service/helper/retriever"
|
||||||
|
)
|
||||||
|
|
||||||
|
// constant
|
||||||
|
const (
|
||||||
|
// resource
|
||||||
|
obrimHandlerResourceIdentifier = "framework/metadata" // change identifier if needed
|
||||||
|
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||||
|
obrimHandlerResourceFileExt = ".json" // do not change
|
||||||
|
|
||||||
|
// success
|
||||||
|
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||||
|
|
||||||
|
// fail
|
||||||
|
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||||
|
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||||
|
)
|
||||||
|
|
||||||
|
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||||
|
//
|
||||||
|
//go:embed resource.json
|
||||||
|
var obrimHandlerEmbeddedResource embed.FS
|
||||||
|
|
||||||
|
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||||
|
type obrimHandlerProvider struct{}
|
||||||
|
|
||||||
|
// Retrieve loads and returns the complete parsed resource payload.
|
||||||
|
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||||
|
return obrimHandlerFetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// init registers the handler during package initialization.
|
||||||
|
func init() {
|
||||||
|
obrimHandlerRegister()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerRegister registers the handler with Retriever.
|
||||||
|
func obrimHandlerRegister() {
|
||||||
|
retriever.ObrimRetrieverJsonRegister(
|
||||||
|
obrimHandlerResourceIdentifier,
|
||||||
|
obrimHandlerProvider{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||||
|
func obrimHandlerFetch() (any, error) {
|
||||||
|
resource, err := obrimHandlerLoadResource()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := obrimHandlerParseResource(resource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerLoadResource loads the embedded resource.
|
||||||
|
func obrimHandlerLoadResource() ([]byte, error) {
|
||||||
|
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||||
|
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||||
|
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||||
|
var payload map[string]any
|
||||||
|
|
||||||
|
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Handler
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide a JSON resource provider for the Retriever utility.
|
||||||
|
| - Embed, parse, and expose a complete JSON resource payload.
|
||||||
|
| - Abstract resource storage details from consumers.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||||
|
| - Associate the handler with a unique resource identifier.
|
||||||
|
| - Return the complete parsed resource payload only.
|
||||||
|
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||||
|
| - Do not implement business logic within the handler.
|
||||||
|
| - Do not perform querying, transformation, or resource discovery.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - retriever.ObrimRetrieverJsonRegister(
|
||||||
|
| "software/metadata",
|
||||||
|
| obrimHandlerProvider{},
|
||||||
|
| )
|
||||||
|
| - response := retriever.ObrimRetriever(
|
||||||
|
| "json",
|
||||||
|
| map[string]any{
|
||||||
|
| "resource": "software/metadata",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"go/module/essential/visible/service/helper/retriever"
|
||||||
|
)
|
||||||
|
|
||||||
|
// constant
|
||||||
|
const (
|
||||||
|
// resource
|
||||||
|
obrimHandlerResourceIdentifier = "software/glossary" // change identifier if needed
|
||||||
|
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||||
|
obrimHandlerResourceFileExt = ".json" // do not change
|
||||||
|
|
||||||
|
// success
|
||||||
|
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||||
|
|
||||||
|
// fail
|
||||||
|
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||||
|
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||||
|
)
|
||||||
|
|
||||||
|
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||||
|
//
|
||||||
|
//go:embed resource.json
|
||||||
|
var obrimHandlerEmbeddedResource embed.FS
|
||||||
|
|
||||||
|
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||||
|
type obrimHandlerProvider struct{}
|
||||||
|
|
||||||
|
// Retrieve loads and returns the complete parsed resource payload.
|
||||||
|
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||||
|
return obrimHandlerFetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// init registers the handler during package initialization.
|
||||||
|
func init() {
|
||||||
|
obrimHandlerRegister()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerRegister registers the handler with Retriever.
|
||||||
|
func obrimHandlerRegister() {
|
||||||
|
retriever.ObrimRetrieverJsonRegister(
|
||||||
|
obrimHandlerResourceIdentifier,
|
||||||
|
obrimHandlerProvider{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||||
|
func obrimHandlerFetch() (any, error) {
|
||||||
|
resource, err := obrimHandlerLoadResource()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := obrimHandlerParseResource(resource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerLoadResource loads the embedded resource.
|
||||||
|
func obrimHandlerLoadResource() ([]byte, error) {
|
||||||
|
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||||
|
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||||
|
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||||
|
var payload map[string]any
|
||||||
|
|
||||||
|
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Handler
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide a JSON resource provider for the Retriever utility.
|
||||||
|
| - Embed, parse, and expose a complete JSON resource payload.
|
||||||
|
| - Abstract resource storage details from consumers.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||||
|
| - Associate the handler with a unique resource identifier.
|
||||||
|
| - Return the complete parsed resource payload only.
|
||||||
|
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||||
|
| - Do not implement business logic within the handler.
|
||||||
|
| - Do not perform querying, transformation, or resource discovery.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - retriever.ObrimRetrieverJsonRegister(
|
||||||
|
| "software/metadata",
|
||||||
|
| obrimHandlerProvider{},
|
||||||
|
| )
|
||||||
|
| - response := retriever.ObrimRetriever(
|
||||||
|
| "json",
|
||||||
|
| map[string]any{
|
||||||
|
| "resource": "software/metadata",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package handler
|
||||||
|
|
||||||
|
import (
|
||||||
|
"embed"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
"go/module/essential/visible/service/helper/retriever"
|
||||||
|
)
|
||||||
|
|
||||||
|
// constant
|
||||||
|
const (
|
||||||
|
// resource
|
||||||
|
obrimHandlerResourceIdentifier = "software/metadata" // change identifier if needed
|
||||||
|
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||||
|
obrimHandlerResourceFileExt = ".json" // do not change
|
||||||
|
|
||||||
|
// success
|
||||||
|
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||||
|
|
||||||
|
// fail
|
||||||
|
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||||
|
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||||
|
)
|
||||||
|
|
||||||
|
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||||
|
//
|
||||||
|
//go:embed resource.json
|
||||||
|
var obrimHandlerEmbeddedResource embed.FS
|
||||||
|
|
||||||
|
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||||
|
type obrimHandlerProvider struct{}
|
||||||
|
|
||||||
|
// Retrieve loads and returns the complete parsed resource payload.
|
||||||
|
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||||
|
return obrimHandlerFetch()
|
||||||
|
}
|
||||||
|
|
||||||
|
// init registers the handler during package initialization.
|
||||||
|
func init() {
|
||||||
|
obrimHandlerRegister()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerRegister registers the handler with Retriever.
|
||||||
|
func obrimHandlerRegister() {
|
||||||
|
retriever.ObrimRetrieverJsonRegister(
|
||||||
|
obrimHandlerResourceIdentifier,
|
||||||
|
obrimHandlerProvider{},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||||
|
func obrimHandlerFetch() (any, error) {
|
||||||
|
resource, err := obrimHandlerLoadResource()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, err := obrimHandlerParseResource(resource)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerLoadResource loads the embedded resource.
|
||||||
|
func obrimHandlerLoadResource() ([]byte, error) {
|
||||||
|
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||||
|
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||||
|
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||||
|
var payload map[string]any
|
||||||
|
|
||||||
|
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||||
|
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||||
|
}
|
||||||
|
|
||||||
|
return payload, nil
|
||||||
|
}
|
||||||
@ -0,0 +1,808 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Cipher
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide reusable AES-256 encryption and decryption capabilities
|
||||||
|
| for file-based data processing.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Use encrypt-standard for AES-256 encryption without salt metadata.
|
||||||
|
| - Use encrypt-salted for AES-256 encryption with embedded salt,
|
||||||
|
| signature, and version metadata.
|
||||||
|
| - Use decrypt-standard only for files produced by
|
||||||
|
| encrypt-standard.
|
||||||
|
| - Use decrypt-salted only for files produced by
|
||||||
|
| encrypt-salted.
|
||||||
|
| - Always provide a valid AES-256 compatible key.
|
||||||
|
| - Always provide valid input and output file paths.
|
||||||
|
| - Do not manually modify salted file headers, version
|
||||||
|
| information, or embedded salt metadata.
|
||||||
|
| - Treat generated salt as non-secret metadata required for
|
||||||
|
| successful decryption.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimCipher(
|
||||||
|
| "encrypt-standard",
|
||||||
|
| map[string]any{
|
||||||
|
| "key": "<32-byte-key>",
|
||||||
|
| "input": "./plain.txt",
|
||||||
|
| "output": "./encrypted.bin",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimCipher(
|
||||||
|
| "encrypt-salted",
|
||||||
|
| map[string]any{
|
||||||
|
| "key": "<32-byte-key>",
|
||||||
|
| "input": "./plain.txt",
|
||||||
|
| "output": "./encrypted.obc",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimCipher(
|
||||||
|
| "decrypt-standard",
|
||||||
|
| map[string]any{
|
||||||
|
| "key": "<32-byte-key>",
|
||||||
|
| "input": "./encrypted.bin",
|
||||||
|
| "output": "./plain.txt",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimCipher(
|
||||||
|
| "decrypt-salted",
|
||||||
|
| map[string]any{
|
||||||
|
| "key": "<32-byte-key>",
|
||||||
|
| "input": "./encrypted.obc",
|
||||||
|
| "output": "./plain.txt",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package cipher
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/aes"
|
||||||
|
cryptocipher "crypto/cipher"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimCipherTypeEncryptStandard = "encrypt-standard"
|
||||||
|
obrimCipherTypeEncryptSalted = "encrypt-salted"
|
||||||
|
obrimCipherTypeDecryptStandard = "decrypt-standard"
|
||||||
|
obrimCipherTypeDecryptSalted = "decrypt-salted"
|
||||||
|
|
||||||
|
obrimCipherAlgorithm = "AES-256"
|
||||||
|
|
||||||
|
obrimCipherFormatSignature = "OBRIMCIPHER"
|
||||||
|
obrimCipherFormatVersion = "1"
|
||||||
|
|
||||||
|
obrimCipherSaltLength = 32
|
||||||
|
obrimCipherNonceLength = 12
|
||||||
|
|
||||||
|
ObrimCipherCodeSuccessEncryptStandard = "SUCCESS_ENCRYPT_STANDARD"
|
||||||
|
ObrimCipherCodeSuccessEncryptSalted = "SUCCESS_ENCRYPT_SALTED"
|
||||||
|
ObrimCipherCodeSuccessDecryptStandard = "SUCCESS_DECRYPT_STANDARD"
|
||||||
|
ObrimCipherCodeSuccessDecryptSalted = "SUCCESS_DECRYPT_SALTED"
|
||||||
|
|
||||||
|
ObrimCipherCodeFailedInvalidType = "FAILED_INVALID_TYPE"
|
||||||
|
ObrimCipherCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||||
|
ObrimCipherCodeFailedInvalidKey = "FAILED_INVALID_KEY"
|
||||||
|
ObrimCipherCodeFailedInvalidInputPath = "FAILED_INVALID_INPUT_PATH"
|
||||||
|
ObrimCipherCodeFailedInvalidOutputPath = "FAILED_INVALID_OUTPUT_PATH"
|
||||||
|
ObrimCipherCodeFailedInputAccess = "FAILED_INPUT_ACCESS"
|
||||||
|
ObrimCipherCodeFailedOutputWrite = "FAILED_OUTPUT_WRITE"
|
||||||
|
ObrimCipherCodeFailedEncryption = "FAILED_ENCRYPTION"
|
||||||
|
ObrimCipherCodeFailedDecryption = "FAILED_DECRYPTION"
|
||||||
|
ObrimCipherCodeFailedSaltGeneration = "FAILED_SALT_GENERATION"
|
||||||
|
ObrimCipherCodeFailedSignatureValidation = "FAILED_SIGNATURE_VALIDATION"
|
||||||
|
ObrimCipherCodeFailedVersionValidation = "FAILED_VERSION_VALIDATION"
|
||||||
|
ObrimCipherCodeFailedSaltExtraction = "FAILED_SALT_EXTRACTION"
|
||||||
|
ObrimCipherCodeFailedPayloadExtraction = "FAILED_PAYLOAD_EXTRACTION"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObrimCipherConfig defines utility configuration.
|
||||||
|
type ObrimCipherConfig struct {
|
||||||
|
Key string `json:"key"`
|
||||||
|
Input string `json:"input"`
|
||||||
|
Output string `json:"output"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimCipherResponse defines standardized utility response.
|
||||||
|
type ObrimCipherResponse struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Payload map[string]any `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherHeader defines encrypted file metadata.
|
||||||
|
type obrimCipherHeader struct {
|
||||||
|
Signature string
|
||||||
|
Version string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimCipher executes encryption and decryption workflows.
|
||||||
|
func ObrimCipher(
|
||||||
|
cipherType string,
|
||||||
|
config map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
if !obrimCipherValidateType(cipherType) {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedInvalidType,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !obrimCipherValidateConfig(config) {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedInvalidConfig,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
key := config["key"].(string)
|
||||||
|
|
||||||
|
if !obrimCipherValidateKey(key) {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedInvalidKey,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputPath, inputOk := obrimCipherResolvePath(
|
||||||
|
config["input"].(string),
|
||||||
|
)
|
||||||
|
|
||||||
|
if !inputOk {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedInvalidInputPath,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
outputPath, outputOk := obrimCipherResolvePath(
|
||||||
|
config["output"].(string),
|
||||||
|
)
|
||||||
|
|
||||||
|
if !outputOk {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedInvalidOutputPath,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
inputData, readErr := obrimCipherReadInput(inputPath)
|
||||||
|
if readErr != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedInputAccess,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch cipherType {
|
||||||
|
case obrimCipherTypeEncryptStandard:
|
||||||
|
return obrimCipherEncryptStandard(
|
||||||
|
key,
|
||||||
|
inputPath,
|
||||||
|
outputPath,
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimCipherTypeEncryptSalted:
|
||||||
|
return obrimCipherEncryptSalted(
|
||||||
|
key,
|
||||||
|
inputPath,
|
||||||
|
outputPath,
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimCipherTypeDecryptStandard:
|
||||||
|
return obrimCipherDecryptStandard(
|
||||||
|
key,
|
||||||
|
inputPath,
|
||||||
|
outputPath,
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimCipherTypeDecryptSalted:
|
||||||
|
return obrimCipherDecryptSalted(
|
||||||
|
key,
|
||||||
|
inputPath,
|
||||||
|
outputPath,
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedInvalidType,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherValidateType validates operation type.
|
||||||
|
func obrimCipherValidateType(cipherType string) bool {
|
||||||
|
switch cipherType {
|
||||||
|
case obrimCipherTypeEncryptStandard:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimCipherTypeEncryptSalted:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimCipherTypeDecryptStandard:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimCipherTypeDecryptSalted:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherValidateConfig validates configuration.
|
||||||
|
func obrimCipherValidateConfig(config map[string]any) bool {
|
||||||
|
if config == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
keyValue, keyExists := config["key"]
|
||||||
|
if !keyExists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
inputValue, inputExists := config["input"]
|
||||||
|
if !inputExists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
outputValue, outputExists := config["output"]
|
||||||
|
if !outputExists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
key, keyOk := keyValue.(string)
|
||||||
|
if !keyOk || key == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
input, inputOk := inputValue.(string)
|
||||||
|
if !inputOk || input == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
output, outputOk := outputValue.(string)
|
||||||
|
if !outputOk || output == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherValidateKey validates AES-256 key requirements.
|
||||||
|
func obrimCipherValidateKey(key string) bool {
|
||||||
|
return len([]byte(key)) == 32
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherResolvePath resolves a path to absolute form.
|
||||||
|
func obrimCipherResolvePath(path string) (string, bool) {
|
||||||
|
resolvedPath, err := filepath.Abs(path)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return resolvedPath, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherReadInput reads source file content.
|
||||||
|
func obrimCipherReadInput(path string) ([]byte, error) {
|
||||||
|
return os.ReadFile(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherWriteOutput writes processed output safely.
|
||||||
|
func obrimCipherWriteOutput(
|
||||||
|
path string,
|
||||||
|
data []byte,
|
||||||
|
) error {
|
||||||
|
var directory = filepath.Dir(path)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(directory, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var temporaryFile, createErr = os.CreateTemp(
|
||||||
|
directory,
|
||||||
|
".obrim-cipher-*",
|
||||||
|
)
|
||||||
|
|
||||||
|
if createErr != nil {
|
||||||
|
return createErr
|
||||||
|
}
|
||||||
|
|
||||||
|
var temporaryPath = temporaryFile.Name()
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
_ = temporaryFile.Close()
|
||||||
|
}()
|
||||||
|
|
||||||
|
if _, err := temporaryFile.Write(data); err != nil {
|
||||||
|
_ = os.Remove(temporaryPath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := temporaryFile.Sync(); err != nil {
|
||||||
|
_ = os.Remove(temporaryPath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := temporaryFile.Close(); err != nil {
|
||||||
|
_ = os.Remove(temporaryPath)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Rename(temporaryPath, path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherBuildResponse builds standardized responses.
|
||||||
|
func obrimCipherBuildResponse(
|
||||||
|
status bool,
|
||||||
|
code string,
|
||||||
|
payload map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": status,
|
||||||
|
"code": code,
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherEncryptStandard encrypts data without salt metadata.
|
||||||
|
func obrimCipherEncryptStandard(
|
||||||
|
key string,
|
||||||
|
inputPath string,
|
||||||
|
outputPath string,
|
||||||
|
inputData []byte,
|
||||||
|
) map[string]any {
|
||||||
|
encryptedData, err := obrimCipherEncryptAES(
|
||||||
|
[]byte(key),
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedEncryption,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obrimCipherWriteOutput(
|
||||||
|
outputPath,
|
||||||
|
encryptedData,
|
||||||
|
); err != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedOutputWrite,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
true,
|
||||||
|
ObrimCipherCodeSuccessEncryptStandard,
|
||||||
|
map[string]any{
|
||||||
|
"operation": obrimCipherTypeEncryptStandard,
|
||||||
|
"algorithm": obrimCipherAlgorithm,
|
||||||
|
"inputPath": inputPath,
|
||||||
|
"outputPath": outputPath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherGenerateSalt generates a cryptographic salt.
|
||||||
|
func obrimCipherGenerateSalt() ([]byte, error) {
|
||||||
|
var salt = make([]byte, obrimCipherSaltLength)
|
||||||
|
|
||||||
|
_, err := io.ReadFull(rand.Reader, salt)
|
||||||
|
|
||||||
|
return salt, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherBuildHeader builds encrypted file header.
|
||||||
|
func obrimCipherBuildHeader() []byte {
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
|
||||||
|
buffer.WriteString(obrimCipherFormatSignature)
|
||||||
|
buffer.WriteByte(0)
|
||||||
|
|
||||||
|
buffer.WriteString(obrimCipherFormatVersion)
|
||||||
|
buffer.WriteByte(0)
|
||||||
|
|
||||||
|
return buffer.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherEncryptSalted encrypts data with embedded salt metadata.
|
||||||
|
func obrimCipherEncryptSalted(
|
||||||
|
key string,
|
||||||
|
inputPath string,
|
||||||
|
outputPath string,
|
||||||
|
inputData []byte,
|
||||||
|
) map[string]any {
|
||||||
|
salt, saltErr := obrimCipherGenerateSalt()
|
||||||
|
|
||||||
|
if saltErr != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedSaltGeneration,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var derivedKey = sha256.Sum256(
|
||||||
|
append([]byte(key), salt...),
|
||||||
|
)
|
||||||
|
|
||||||
|
encryptedData, err := obrimCipherEncryptAES(
|
||||||
|
derivedKey[:],
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedEncryption,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var header = obrimCipherBuildHeader()
|
||||||
|
|
||||||
|
var outputData []byte
|
||||||
|
outputData = append(outputData, header...)
|
||||||
|
outputData = append(outputData, salt...)
|
||||||
|
outputData = append(outputData, encryptedData...)
|
||||||
|
|
||||||
|
if err := obrimCipherWriteOutput(
|
||||||
|
outputPath,
|
||||||
|
outputData,
|
||||||
|
); err != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedOutputWrite,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
true,
|
||||||
|
ObrimCipherCodeSuccessEncryptSalted,
|
||||||
|
map[string]any{
|
||||||
|
"operation": obrimCipherTypeEncryptSalted,
|
||||||
|
"algorithm": obrimCipherAlgorithm,
|
||||||
|
"format": obrimCipherFormatSignature,
|
||||||
|
"version": obrimCipherFormatVersion,
|
||||||
|
"inputPath": inputPath,
|
||||||
|
"outputPath": outputPath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherDecryptStandard decrypts AES-256 encrypted data.
|
||||||
|
func obrimCipherDecryptStandard(
|
||||||
|
key string,
|
||||||
|
inputPath string,
|
||||||
|
outputPath string,
|
||||||
|
inputData []byte,
|
||||||
|
) map[string]any {
|
||||||
|
decryptedData, err := obrimCipherDecryptAES(
|
||||||
|
[]byte(key),
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedDecryption,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obrimCipherWriteOutput(
|
||||||
|
outputPath,
|
||||||
|
decryptedData,
|
||||||
|
); err != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedOutputWrite,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
true,
|
||||||
|
ObrimCipherCodeSuccessDecryptStandard,
|
||||||
|
map[string]any{
|
||||||
|
"operation": obrimCipherTypeDecryptStandard,
|
||||||
|
"algorithm": obrimCipherAlgorithm,
|
||||||
|
"inputPath": inputPath,
|
||||||
|
"outputPath": outputPath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherParseHeader parses encrypted metadata.
|
||||||
|
func obrimCipherParseHeader(
|
||||||
|
data []byte,
|
||||||
|
) (obrimCipherHeader, int, error) {
|
||||||
|
var signatureBytes = []byte(obrimCipherFormatSignature)
|
||||||
|
|
||||||
|
if len(data) < len(signatureBytes)+1 {
|
||||||
|
return obrimCipherHeader{}, 0, errors.New("signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(
|
||||||
|
data[:len(signatureBytes)],
|
||||||
|
signatureBytes,
|
||||||
|
) {
|
||||||
|
return obrimCipherHeader{}, 0, errors.New("signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset = len(signatureBytes)
|
||||||
|
|
||||||
|
if data[offset] != 0 {
|
||||||
|
return obrimCipherHeader{}, 0, errors.New("signature")
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++
|
||||||
|
|
||||||
|
var versionBytes = []byte(obrimCipherFormatVersion)
|
||||||
|
|
||||||
|
if len(data) < offset+len(versionBytes)+1 {
|
||||||
|
return obrimCipherHeader{}, 0, errors.New("version")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !bytes.Equal(
|
||||||
|
data[offset:offset+len(versionBytes)],
|
||||||
|
versionBytes,
|
||||||
|
) {
|
||||||
|
return obrimCipherHeader{}, 0, errors.New("version")
|
||||||
|
}
|
||||||
|
|
||||||
|
offset += len(versionBytes)
|
||||||
|
|
||||||
|
if data[offset] != 0 {
|
||||||
|
return obrimCipherHeader{}, 0, errors.New("version")
|
||||||
|
}
|
||||||
|
|
||||||
|
offset++
|
||||||
|
|
||||||
|
return obrimCipherHeader{
|
||||||
|
Signature: obrimCipherFormatSignature,
|
||||||
|
Version: obrimCipherFormatVersion,
|
||||||
|
}, offset, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherExtractSalt extracts embedded salt.
|
||||||
|
func obrimCipherExtractSalt(
|
||||||
|
data []byte,
|
||||||
|
offset int,
|
||||||
|
) ([]byte, error) {
|
||||||
|
if len(data) < offset+obrimCipherSaltLength {
|
||||||
|
return nil, errors.New("salt")
|
||||||
|
}
|
||||||
|
|
||||||
|
return data[offset : offset+obrimCipherSaltLength], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherExtractPayload extracts encrypted payload.
|
||||||
|
func obrimCipherExtractPayload(
|
||||||
|
data []byte,
|
||||||
|
offset int,
|
||||||
|
) ([]byte, error) {
|
||||||
|
var payloadOffset = offset + obrimCipherSaltLength
|
||||||
|
|
||||||
|
if len(data) <= payloadOffset {
|
||||||
|
return nil, errors.New("payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
return data[payloadOffset:], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherDecryptSalted decrypts AES-256 encrypted data using extracted salt.
|
||||||
|
func obrimCipherDecryptSalted(
|
||||||
|
key string,
|
||||||
|
inputPath string,
|
||||||
|
outputPath string,
|
||||||
|
inputData []byte,
|
||||||
|
) map[string]any {
|
||||||
|
header, offset, headerErr := obrimCipherParseHeader(
|
||||||
|
inputData,
|
||||||
|
)
|
||||||
|
|
||||||
|
if headerErr != nil {
|
||||||
|
if headerErr.Error() == "signature" {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedSignatureValidation,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedVersionValidation,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
salt, saltErr := obrimCipherExtractSalt(
|
||||||
|
inputData,
|
||||||
|
offset,
|
||||||
|
)
|
||||||
|
|
||||||
|
if saltErr != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedSaltExtraction,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload, payloadErr := obrimCipherExtractPayload(
|
||||||
|
inputData,
|
||||||
|
offset,
|
||||||
|
)
|
||||||
|
|
||||||
|
if payloadErr != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedPayloadExtraction,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var derivedKey = sha256.Sum256(
|
||||||
|
append([]byte(key), salt...),
|
||||||
|
)
|
||||||
|
|
||||||
|
decryptedData, decryptErr := obrimCipherDecryptAES(
|
||||||
|
derivedKey[:],
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
|
||||||
|
if decryptErr != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedDecryption,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obrimCipherWriteOutput(
|
||||||
|
outputPath,
|
||||||
|
decryptedData,
|
||||||
|
); err != nil {
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCipherCodeFailedOutputWrite,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCipherBuildResponse(
|
||||||
|
true,
|
||||||
|
ObrimCipherCodeSuccessDecryptSalted,
|
||||||
|
map[string]any{
|
||||||
|
"operation": obrimCipherTypeDecryptSalted,
|
||||||
|
"algorithm": obrimCipherAlgorithm,
|
||||||
|
"format": header.Signature,
|
||||||
|
"version": header.Version,
|
||||||
|
"inputPath": inputPath,
|
||||||
|
"outputPath": outputPath,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherEncryptAES performs AES-256 encryption.
|
||||||
|
func obrimCipherEncryptAES(
|
||||||
|
key []byte,
|
||||||
|
data []byte,
|
||||||
|
) ([]byte, error) {
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cryptocipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var nonce = make([]byte, obrimCipherNonceLength)
|
||||||
|
|
||||||
|
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var encryptedData = gcm.Seal(
|
||||||
|
nil,
|
||||||
|
nonce,
|
||||||
|
data,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
|
||||||
|
var result []byte
|
||||||
|
result = append(result, nonce...)
|
||||||
|
result = append(result, encryptedData...)
|
||||||
|
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherDecryptAES performs AES-256 decryption.
|
||||||
|
func obrimCipherDecryptAES(
|
||||||
|
key []byte,
|
||||||
|
data []byte,
|
||||||
|
) ([]byte, error) {
|
||||||
|
if len(data) < obrimCipherNonceLength {
|
||||||
|
return nil, errors.New("invalid payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
block, err := aes.NewCipher(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
gcm, err := cryptocipher.NewGCM(block)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var nonce = data[:obrimCipherNonceLength]
|
||||||
|
var payload = data[obrimCipherNonceLength:]
|
||||||
|
|
||||||
|
return gcm.Open(
|
||||||
|
nil,
|
||||||
|
nonce,
|
||||||
|
payload,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCipherMarshalResponse marshals response data.
|
||||||
|
func obrimCipherMarshalResponse(
|
||||||
|
response ObrimCipherResponse,
|
||||||
|
) ([]byte, error) {
|
||||||
|
return json.Marshal(response)
|
||||||
|
}
|
||||||
@ -0,0 +1,686 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Codec
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide reusable encoding and decoding capabilities for framework
|
||||||
|
| and software services.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Use ObrimCodec() as the public entry point for all codec operations.
|
||||||
|
| - Always provide a valid operation type and configuration map.
|
||||||
|
| - Use supported formats only: base8, base10, base16, base32,
|
||||||
|
| base64, and sqids.
|
||||||
|
| - Validate all inputs before processing encoding or decoding requests.
|
||||||
|
| - Use charset and salt values consistently between encode and decode
|
||||||
|
| operations.
|
||||||
|
| - Use case configuration for non-sqids formats.
|
||||||
|
| - Use length configuration for sqids format.
|
||||||
|
| - Treat empty sqids inputs and outputs as operation failures.
|
||||||
|
| - Consume responses through the standardized status, code, and
|
||||||
|
| payload structure.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimCodec("encode", config)
|
||||||
|
| - ObrimCodec("decode", config)
|
||||||
|
| - ObrimCodec("encode", map[string]any{
|
||||||
|
| "format": "base64",
|
||||||
|
| "data": "example",
|
||||||
|
| "charset": "",
|
||||||
|
| "salt": "",
|
||||||
|
| "case": "lower",
|
||||||
|
| })
|
||||||
|
| - ObrimCodec("decode", map[string]any{
|
||||||
|
| "format": "sqids",
|
||||||
|
| "data": "abc123",
|
||||||
|
| "charset": "abcdefghijklmnopqrstuvwxyz",
|
||||||
|
| "salt": "",
|
||||||
|
| "length": 8,
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package codec
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/base32"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/sqids/sqids-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimCodecTypeEncode = "encode"
|
||||||
|
obrimCodecTypeDecode = "decode"
|
||||||
|
|
||||||
|
obrimCodecFormatBase8 = "base8"
|
||||||
|
obrimCodecFormatBase10 = "base10"
|
||||||
|
obrimCodecFormatBase16 = "base16"
|
||||||
|
obrimCodecFormatBase32 = "base32"
|
||||||
|
obrimCodecFormatBase64 = "base64"
|
||||||
|
obrimCodecFormatSqids = "sqids"
|
||||||
|
|
||||||
|
obrimCodecCaseLower = "lower"
|
||||||
|
obrimCodecCaseUpper = "upper"
|
||||||
|
|
||||||
|
ObrimCodecCodeSuccessEncode = "SUCCESS_ENCODE"
|
||||||
|
ObrimCodecCodeSuccessDecode = "SUCCESS_DECODE"
|
||||||
|
|
||||||
|
ObrimCodecCodeFailedValidation = "FAILED_VALIDATION"
|
||||||
|
ObrimCodecCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||||
|
ObrimCodecCodeFailedUnsupportedFormat = "FAILED_UNSUPPORTED_FORMAT"
|
||||||
|
ObrimCodecCodeFailedMissingConfiguration = "FAILED_MISSING_CONFIGURATION"
|
||||||
|
ObrimCodecCodeFailedInvalidConfiguration = "FAILED_INVALID_CONFIGURATION"
|
||||||
|
ObrimCodecCodeFailedOperation = "FAILED_OPERATION"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObrimCodecResponse defines the standardized utility response.
|
||||||
|
type ObrimCodecResponse struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Payload map[string]any `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecConfiguration stores validated codec configuration.
|
||||||
|
type obrimCodecConfiguration struct {
|
||||||
|
Format string
|
||||||
|
Data any
|
||||||
|
Charset string
|
||||||
|
Salt string
|
||||||
|
Case string
|
||||||
|
Length int
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimCodec validates configuration, dispatches codec operations, and returns structured results.
|
||||||
|
func ObrimCodec(codecType string, config map[string]any) map[string]any {
|
||||||
|
validatedConfig, code := obrimCodecValidation(codecType, config)
|
||||||
|
if code != "" {
|
||||||
|
return obrimCodecBuildResponse(false, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := obrimCodecDispatch(codecType, validatedConfig)
|
||||||
|
if err != nil {
|
||||||
|
return obrimCodecBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimCodecCodeFailedOperation,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var successCode string
|
||||||
|
|
||||||
|
switch codecType {
|
||||||
|
case obrimCodecTypeEncode:
|
||||||
|
successCode = ObrimCodecCodeSuccessEncode
|
||||||
|
|
||||||
|
case obrimCodecTypeDecode:
|
||||||
|
successCode = ObrimCodecCodeSuccessDecode
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecBuildResponse(
|
||||||
|
true,
|
||||||
|
successCode,
|
||||||
|
map[string]any{
|
||||||
|
"operation": codecType,
|
||||||
|
"format": validatedConfig.Format,
|
||||||
|
"value": value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecValidation validates required parameters, supported operations, formats, and parameter types.
|
||||||
|
func obrimCodecValidation(
|
||||||
|
codecType string,
|
||||||
|
config map[string]any,
|
||||||
|
) (*obrimCodecConfiguration, string) {
|
||||||
|
if config == nil {
|
||||||
|
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
switch codecType {
|
||||||
|
case obrimCodecTypeEncode:
|
||||||
|
case obrimCodecTypeDecode:
|
||||||
|
default:
|
||||||
|
return nil, ObrimCodecCodeFailedUnsupportedType
|
||||||
|
}
|
||||||
|
|
||||||
|
formatValue, exists := config["format"]
|
||||||
|
if !exists {
|
||||||
|
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
format, ok := formatValue.(string)
|
||||||
|
if !ok || strings.TrimSpace(format) == "" {
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
switch format {
|
||||||
|
case obrimCodecFormatBase8:
|
||||||
|
case obrimCodecFormatBase10:
|
||||||
|
case obrimCodecFormatBase16:
|
||||||
|
case obrimCodecFormatBase32:
|
||||||
|
case obrimCodecFormatBase64:
|
||||||
|
case obrimCodecFormatSqids:
|
||||||
|
default:
|
||||||
|
return nil, ObrimCodecCodeFailedUnsupportedFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
dataValue, exists := config["data"]
|
||||||
|
if !exists {
|
||||||
|
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
switch dataValue.(type) {
|
||||||
|
case string:
|
||||||
|
case []byte:
|
||||||
|
default:
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
charsetValue, exists := config["charset"]
|
||||||
|
if !exists {
|
||||||
|
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
charset, ok := charsetValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
saltValue, exists := config["salt"]
|
||||||
|
if !exists {
|
||||||
|
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
salt, ok := saltValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
var validatedConfig = &obrimCodecConfiguration{
|
||||||
|
Format: format,
|
||||||
|
Data: dataValue,
|
||||||
|
Charset: charset,
|
||||||
|
Salt: salt,
|
||||||
|
}
|
||||||
|
|
||||||
|
if format == obrimCodecFormatSqids {
|
||||||
|
lengthValue, exists := config["length"]
|
||||||
|
if !exists {
|
||||||
|
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
switch value := lengthValue.(type) {
|
||||||
|
case int:
|
||||||
|
validatedConfig.Length = value
|
||||||
|
|
||||||
|
case int8:
|
||||||
|
validatedConfig.Length = int(value)
|
||||||
|
|
||||||
|
case int16:
|
||||||
|
validatedConfig.Length = int(value)
|
||||||
|
|
||||||
|
case int32:
|
||||||
|
validatedConfig.Length = int(value)
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
validatedConfig.Length = int(value)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
validatedConfig.Length = int(value)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
if validatedConfig.Length < 0 {
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
caseValue, exists := config["case"]
|
||||||
|
if !exists {
|
||||||
|
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
caseRule, ok := caseValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
switch caseRule {
|
||||||
|
case obrimCodecCaseLower:
|
||||||
|
case obrimCodecCaseUpper:
|
||||||
|
default:
|
||||||
|
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||||
|
}
|
||||||
|
|
||||||
|
validatedConfig.Case = caseRule
|
||||||
|
}
|
||||||
|
|
||||||
|
return validatedConfig, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecDispatch routes validated requests to the appropriate codec implementation.
|
||||||
|
func obrimCodecDispatch(
|
||||||
|
codecType string,
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
switch codecType {
|
||||||
|
case obrimCodecTypeEncode:
|
||||||
|
switch config.Format {
|
||||||
|
case obrimCodecFormatBase8:
|
||||||
|
return obrimCodecEncodeBase8(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase10:
|
||||||
|
return obrimCodecEncodeBase10(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase16:
|
||||||
|
return obrimCodecEncodeBase16(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase32:
|
||||||
|
return obrimCodecEncodeBase32(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase64:
|
||||||
|
return obrimCodecEncodeBase64(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatSqids:
|
||||||
|
return obrimCodecEncodeSqids(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
case obrimCodecTypeDecode:
|
||||||
|
switch config.Format {
|
||||||
|
case obrimCodecFormatBase8:
|
||||||
|
return obrimCodecDecodeBase8(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase10:
|
||||||
|
return obrimCodecDecodeBase10(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase16:
|
||||||
|
return obrimCodecDecodeBase16(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase32:
|
||||||
|
return obrimCodecDecodeBase32(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatBase64:
|
||||||
|
return obrimCodecDecodeBase64(config)
|
||||||
|
|
||||||
|
case obrimCodecFormatSqids:
|
||||||
|
return obrimCodecDecodeSqids(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", obrimCodecError("unsupported dispatch")
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecBuildResponse builds a standard response.
|
||||||
|
func obrimCodecBuildResponse(
|
||||||
|
status bool,
|
||||||
|
code string,
|
||||||
|
payload map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": status,
|
||||||
|
"code": code,
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecNormalizeInput converts supported input types into string form.
|
||||||
|
func obrimCodecNormalizeInput(data any) string {
|
||||||
|
switch value := data.(type) {
|
||||||
|
case string:
|
||||||
|
return value
|
||||||
|
|
||||||
|
case []byte:
|
||||||
|
return string(value)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecApplyTransform applies charset and salt transformations.
|
||||||
|
func obrimCodecApplyTransform(
|
||||||
|
value string,
|
||||||
|
charset string,
|
||||||
|
salt string,
|
||||||
|
) string {
|
||||||
|
return charset + salt + value
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecReverseTransform reverses charset and salt transformations.
|
||||||
|
func obrimCodecReverseTransform(
|
||||||
|
value string,
|
||||||
|
charset string,
|
||||||
|
salt string,
|
||||||
|
) string {
|
||||||
|
var prefix = charset + salt
|
||||||
|
|
||||||
|
return strings.TrimPrefix(value, prefix)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecApplyCase applies output casing.
|
||||||
|
func obrimCodecApplyCase(
|
||||||
|
value string,
|
||||||
|
caseRule string,
|
||||||
|
) string {
|
||||||
|
switch caseRule {
|
||||||
|
case obrimCodecCaseUpper:
|
||||||
|
return strings.ToUpper(value)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return strings.ToLower(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecEncodeBase8 encodes data using Base8 encoding.
|
||||||
|
func obrimCodecEncodeBase8(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var input = obrimCodecApplyTransform(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
)
|
||||||
|
|
||||||
|
var builder strings.Builder
|
||||||
|
|
||||||
|
for _, value := range []byte(input) {
|
||||||
|
builder.WriteString(fmt.Sprintf("%03o", value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecApplyCase(
|
||||||
|
builder.String(),
|
||||||
|
config.Case,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecDecodeBase8 decodes Base8 encoded data.
|
||||||
|
func obrimCodecDecodeBase8(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var input = strings.ToLower(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(input)%3 != 0 {
|
||||||
|
return "", obrimCodecError("invalid base8 data")
|
||||||
|
}
|
||||||
|
|
||||||
|
var output []byte
|
||||||
|
|
||||||
|
for index := 0; index < len(input); index += 3 {
|
||||||
|
value, err := strconv.ParseUint(
|
||||||
|
input[index:index+3],
|
||||||
|
8,
|
||||||
|
8,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
output = append(output, byte(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecReverseTransform(
|
||||||
|
string(output),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecEncodeBase10 encodes data using Base10 encoding.
|
||||||
|
func obrimCodecEncodeBase10(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var input = obrimCodecApplyTransform(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
)
|
||||||
|
|
||||||
|
var value = new(big.Int).SetBytes([]byte(input))
|
||||||
|
|
||||||
|
return obrimCodecApplyCase(
|
||||||
|
value.Text(10),
|
||||||
|
config.Case,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecDecodeBase10 decodes Base10 encoded data.
|
||||||
|
func obrimCodecDecodeBase10(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var value = new(big.Int)
|
||||||
|
|
||||||
|
if _, ok := value.SetString(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
10,
|
||||||
|
); !ok {
|
||||||
|
return "", obrimCodecError("invalid base10 data")
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecReverseTransform(
|
||||||
|
string(value.Bytes()),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecEncodeBase16 encodes data using Base16 encoding.
|
||||||
|
func obrimCodecEncodeBase16(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var input = obrimCodecApplyTransform(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
)
|
||||||
|
|
||||||
|
var output = hex.EncodeToString([]byte(input))
|
||||||
|
|
||||||
|
return obrimCodecApplyCase(
|
||||||
|
output,
|
||||||
|
config.Case,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecDecodeBase16 decodes Base16 encoded data.
|
||||||
|
func obrimCodecDecodeBase16(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var output, err = hex.DecodeString(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecReverseTransform(
|
||||||
|
string(output),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecEncodeBase32 encodes data using Base32 encoding.
|
||||||
|
func obrimCodecEncodeBase32(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var input = obrimCodecApplyTransform(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
)
|
||||||
|
|
||||||
|
var output = base32.StdEncoding.EncodeToString(
|
||||||
|
[]byte(input),
|
||||||
|
)
|
||||||
|
|
||||||
|
return obrimCodecApplyCase(
|
||||||
|
output,
|
||||||
|
config.Case,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecDecodeBase32 decodes Base32 encoded data.
|
||||||
|
func obrimCodecDecodeBase32(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var output, err = base32.StdEncoding.DecodeString(
|
||||||
|
strings.ToUpper(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecReverseTransform(
|
||||||
|
string(output),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecEncodeBase64 encodes data using Base64 encoding.
|
||||||
|
func obrimCodecEncodeBase64(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var input = obrimCodecApplyTransform(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
)
|
||||||
|
|
||||||
|
var output = base64.StdEncoding.EncodeToString(
|
||||||
|
[]byte(input),
|
||||||
|
)
|
||||||
|
|
||||||
|
return obrimCodecApplyCase(
|
||||||
|
output,
|
||||||
|
config.Case,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecDecodeBase64 decodes Base64 encoded data.
|
||||||
|
func obrimCodecDecodeBase64(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var output, err = base64.StdEncoding.DecodeString(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecReverseTransform(
|
||||||
|
string(output),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecEncodeSqids encodes data using Sqids encoding with minimum length enforcement.
|
||||||
|
func obrimCodecEncodeSqids(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var input = obrimCodecApplyTransform(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
)
|
||||||
|
|
||||||
|
var numbers = make([]uint64, 0, len(input))
|
||||||
|
|
||||||
|
for _, value := range []byte(input) {
|
||||||
|
numbers = append(numbers, uint64(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(numbers) == 0 {
|
||||||
|
return "", obrimCodecError("empty sqids input")
|
||||||
|
}
|
||||||
|
|
||||||
|
var sqidsInstance, err = sqids.New(
|
||||||
|
sqids.Options{
|
||||||
|
Alphabet: config.Charset,
|
||||||
|
MinLength: uint8(config.Length),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return sqidsInstance.Encode(numbers)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecDecodeSqids decodes Sqids encoded data using deterministic length validation.
|
||||||
|
func obrimCodecDecodeSqids(
|
||||||
|
config *obrimCodecConfiguration,
|
||||||
|
) (string, error) {
|
||||||
|
var sqidsInstance, err = sqids.New(
|
||||||
|
sqids.Options{
|
||||||
|
Alphabet: config.Charset,
|
||||||
|
MinLength: uint8(config.Length),
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var numbers = sqidsInstance.Decode(
|
||||||
|
obrimCodecNormalizeInput(config.Data),
|
||||||
|
)
|
||||||
|
|
||||||
|
if len(numbers) == 0 {
|
||||||
|
return "", obrimCodecError(
|
||||||
|
"invalid or empty sqids data",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var output = make([]byte, 0, len(numbers))
|
||||||
|
|
||||||
|
for _, value := range numbers {
|
||||||
|
if value > 255 {
|
||||||
|
return "", obrimCodecError(
|
||||||
|
"decoded sqids value exceeds byte range",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
output = append(output, byte(value))
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimCodecReverseTransform(
|
||||||
|
string(output),
|
||||||
|
config.Charset,
|
||||||
|
config.Salt,
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimCodecError formats internal errors.
|
||||||
|
func obrimCodecError(message string) error {
|
||||||
|
return errors.New(message)
|
||||||
|
}
|
||||||
@ -0,0 +1,329 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Datetime Utility
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Retrieve the current date and time from either the host system
|
||||||
|
| clock or a trusted framework-managed NTP source and return the
|
||||||
|
| result using a framework-supported date/time format pattern.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Use ObrimDatetime() as the primary entry point.
|
||||||
|
| - Always provide a valid datetime type.
|
||||||
|
| - Supported types are "local" and "trusted".
|
||||||
|
| - Always provide the "format" configuration key.
|
||||||
|
| - Use only framework-supported format patterns.
|
||||||
|
| - Expect all successful results to be returned as strings.
|
||||||
|
| - Use "local" when host system time is sufficient.
|
||||||
|
| - Use "trusted" when synchronized network time is required.
|
||||||
|
| - Handle both success and failure response codes.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimDatetime(
|
||||||
|
| "local",
|
||||||
|
| map[string]any{
|
||||||
|
| "format": "yyyy-MM-dd",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimDatetime(
|
||||||
|
| "trusted",
|
||||||
|
| map[string]any{
|
||||||
|
| "format": "yyyy-MM-dd HH:mm:ss z",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package datetime
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/binary"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimDatetimeTypeLocal = "local"
|
||||||
|
obrimDatetimeTypeTrusted = "trusted"
|
||||||
|
|
||||||
|
ObrimDatetimeCodeSuccessRetrieved = "SUCCESS_DATETIME_RETRIEVED"
|
||||||
|
|
||||||
|
ObrimDatetimeCodeFailedInvalidType = "FAILED_INVALID_TYPE"
|
||||||
|
ObrimDatetimeCodeFailedInvalidFormat = "FAILED_INVALID_FORMAT"
|
||||||
|
ObrimDatetimeCodeFailedTrustedTimeRetrieval = "FAILED_TRUSTED_TIME_RETRIEVAL"
|
||||||
|
)
|
||||||
|
|
||||||
|
const obrimDatetimeTrustedNtpPool = "pool.ntp.org:123"
|
||||||
|
|
||||||
|
// ObrimDatetimePayload defines the success payload structure.
|
||||||
|
type ObrimDatetimePayload struct {
|
||||||
|
Value string `json:"value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimDatetimeResponse defines the utility response structure.
|
||||||
|
type ObrimDatetimeResponse struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Payload *ObrimDatetimePayload `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimePatternMap stores framework-supported format mappings.
|
||||||
|
var obrimDatetimePatternMap = map[string]string{
|
||||||
|
"yyyy-MM-dd": "2006-01-02",
|
||||||
|
"MMM dd, yyyy": "Jan 02, 2006",
|
||||||
|
"MMMM dd, yyyy": "January 02, 2006",
|
||||||
|
"EEE, MMM dd, yyyy": "Mon, Jan 02, 2006",
|
||||||
|
"EEE, MMMM dd, yyyy": "Mon, January 02, 2006",
|
||||||
|
"EEEE, MMM dd, yyyy": "Monday, Jan 02, 2006",
|
||||||
|
"EEEE, MMMM dd, yyyy": "Monday, January 02, 2006",
|
||||||
|
"HH:mm:ss": "15:04:05",
|
||||||
|
"hh:mm:ss a": "03:04:05 PM",
|
||||||
|
"yyyy-MM-dd HH:mm:ss": "2006-01-02 15:04:05",
|
||||||
|
"yyyy-MM-dd HH:mm:ss z": "2006-01-02 15:04:05 MST",
|
||||||
|
"yyyy-MM-dd'T'HH:mm:ssXXX": time.RFC3339,
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimDatetime retrieves and formats current date and time.
|
||||||
|
func ObrimDatetime(
|
||||||
|
datetimeType string,
|
||||||
|
config map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
log.Println("[datetime] initialization started")
|
||||||
|
|
||||||
|
format, code := obrimDatetimeValidate(datetimeType, config)
|
||||||
|
if code != "" {
|
||||||
|
log.Printf("[datetime] validation failed: %s", code)
|
||||||
|
|
||||||
|
return obrimDatetimeResponse(
|
||||||
|
false,
|
||||||
|
code,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
var datetimeValue time.Time
|
||||||
|
var err error
|
||||||
|
|
||||||
|
switch datetimeType {
|
||||||
|
case obrimDatetimeTypeLocal:
|
||||||
|
datetimeValue = obrimDatetimeLocal()
|
||||||
|
|
||||||
|
case obrimDatetimeTypeTrusted:
|
||||||
|
datetimeValue, err = obrimDatetimeTrusted()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf(
|
||||||
|
"[datetime] trusted time retrieval failed: %v",
|
||||||
|
err,
|
||||||
|
)
|
||||||
|
|
||||||
|
return obrimDatetimeResponse(
|
||||||
|
false,
|
||||||
|
ObrimDatetimeCodeFailedTrustedTimeRetrieval,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
formattedValue := obrimDatetimeFormatApply(
|
||||||
|
datetimeValue,
|
||||||
|
format,
|
||||||
|
)
|
||||||
|
|
||||||
|
log.Println("[datetime] datetime retrieved successfully")
|
||||||
|
|
||||||
|
return obrimDatetimeResponse(
|
||||||
|
true,
|
||||||
|
ObrimDatetimeCodeSuccessRetrieved,
|
||||||
|
formattedValue,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimeValidate validates utility inputs.
|
||||||
|
func obrimDatetimeValidate(
|
||||||
|
datetimeType string,
|
||||||
|
config map[string]any,
|
||||||
|
) (string, string) {
|
||||||
|
switch datetimeType {
|
||||||
|
case obrimDatetimeTypeLocal:
|
||||||
|
case obrimDatetimeTypeTrusted:
|
||||||
|
default:
|
||||||
|
return "", ObrimDatetimeCodeFailedInvalidType
|
||||||
|
}
|
||||||
|
|
||||||
|
formatValue, exists := config["format"]
|
||||||
|
if !exists {
|
||||||
|
return "", ObrimDatetimeCodeFailedInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
format, ok := formatValue.(string)
|
||||||
|
if !ok || format == "" {
|
||||||
|
return "", ObrimDatetimeCodeFailedInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
if !obrimDatetimePatternValidate(format) {
|
||||||
|
return "", ObrimDatetimeCodeFailedInvalidFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
return format, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimePatternValidate validates supported patterns.
|
||||||
|
func obrimDatetimePatternValidate(pattern string) bool {
|
||||||
|
switch pattern {
|
||||||
|
case "timestampsec":
|
||||||
|
return true
|
||||||
|
|
||||||
|
case "timestampmil":
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
_, exists := obrimDatetimePatternMap[pattern]
|
||||||
|
|
||||||
|
return exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimePatternResolve resolves pattern mappings.
|
||||||
|
func obrimDatetimePatternResolve(
|
||||||
|
pattern string,
|
||||||
|
) (string, bool) {
|
||||||
|
layout, exists := obrimDatetimePatternMap[pattern]
|
||||||
|
|
||||||
|
return layout, exists
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimeFormatApply formats datetime values.
|
||||||
|
func obrimDatetimeFormatApply(
|
||||||
|
datetimeValue time.Time,
|
||||||
|
pattern string,
|
||||||
|
) string {
|
||||||
|
switch pattern {
|
||||||
|
case "timestampsec":
|
||||||
|
return strconv.FormatInt(
|
||||||
|
datetimeValue.Unix(),
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
|
||||||
|
case "timestampmil":
|
||||||
|
return strconv.FormatInt(
|
||||||
|
datetimeValue.UnixMilli(),
|
||||||
|
10,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
layout, _ := obrimDatetimePatternResolve(pattern)
|
||||||
|
|
||||||
|
return datetimeValue.Format(layout)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimeResponse builds utility responses.
|
||||||
|
func obrimDatetimeResponse(
|
||||||
|
status bool,
|
||||||
|
code string,
|
||||||
|
value string,
|
||||||
|
) map[string]any {
|
||||||
|
if !status {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": code,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": code,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"value": value,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimeLocal retrieves host system time.
|
||||||
|
func obrimDatetimeLocal() time.Time {
|
||||||
|
return time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimeTrusted retrieves trusted NTP time.
|
||||||
|
func obrimDatetimeTrusted() (time.Time, error) {
|
||||||
|
return obrimDatetimeTrustedSync()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimDatetimeTrustedSync synchronizes trusted time from NTP.
|
||||||
|
func obrimDatetimeTrustedSync() (time.Time, error) {
|
||||||
|
connection, err := net.DialTimeout(
|
||||||
|
"udp",
|
||||||
|
obrimDatetimeTrustedNtpPool,
|
||||||
|
5*time.Second,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer connection.Close()
|
||||||
|
|
||||||
|
var request = make([]byte, 48)
|
||||||
|
|
||||||
|
request[0] = 0x1B
|
||||||
|
|
||||||
|
if _, err = connection.Write(request); err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = connection.SetDeadline(
|
||||||
|
time.Now().Add(5 * time.Second),
|
||||||
|
); err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var response = make([]byte, 48)
|
||||||
|
|
||||||
|
if _, err = connection.Read(response); err != nil {
|
||||||
|
return time.Time{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var ntpSeconds = binary.BigEndian.Uint32(
|
||||||
|
response[40:44],
|
||||||
|
)
|
||||||
|
|
||||||
|
var ntpFraction = binary.BigEndian.Uint32(
|
||||||
|
response[44:48],
|
||||||
|
)
|
||||||
|
|
||||||
|
const ntpEpochOffset = 2208988800
|
||||||
|
|
||||||
|
var unixSeconds = int64(ntpSeconds) - ntpEpochOffset
|
||||||
|
|
||||||
|
var nanoseconds = (int64(ntpFraction) * 1e9) >> 32
|
||||||
|
|
||||||
|
if unixSeconds < 0 {
|
||||||
|
return time.Time{}, fmt.Errorf(
|
||||||
|
"invalid ntp timestamp",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return time.Unix(
|
||||||
|
unixSeconds,
|
||||||
|
nanoseconds,
|
||||||
|
).UTC(), nil
|
||||||
|
}
|
||||||
@ -0,0 +1,748 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Filesystem
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide reusable, deterministic, and OS-agnostic filesystem
|
||||||
|
| operations for files and directories through a unified execution
|
||||||
|
| interface.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Use ObrimFilesystem() as the single public entry point.
|
||||||
|
| - Execute only one filesystem operation per invocation.
|
||||||
|
| - Validate operation type and configuration before execution.
|
||||||
|
| - Resolve filesystem paths through supported strategies.
|
||||||
|
| - Return structured and deterministic results for all executions.
|
||||||
|
| - Remain independent of runtime, CLI, and third-party frameworks.
|
||||||
|
| - Use Go standard library filesystem capabilities whenever possible.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimFilesystem("list", map[string]any{
|
||||||
|
| "strategy": "user",
|
||||||
|
| "recursive": true,
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
| - ObrimFilesystem("check", map[string]any{
|
||||||
|
| "strategy": "custom",
|
||||||
|
| "base": "/tmp",
|
||||||
|
| "name": "example.txt",
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
| - ObrimFilesystem("create", map[string]any{
|
||||||
|
| "entity": "directory",
|
||||||
|
| "strategy": "custom",
|
||||||
|
| "base": "/tmp",
|
||||||
|
| "name": "workspace",
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package filesystem
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/user"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimFilesystemTypeList = "list"
|
||||||
|
obrimFilesystemTypeCheck = "check"
|
||||||
|
obrimFilesystemTypeCreate = "create"
|
||||||
|
obrimFilesystemTypeDelete = "delete"
|
||||||
|
obrimFilesystemTypePermission = "permission"
|
||||||
|
|
||||||
|
obrimFilesystemEntityFile = "file"
|
||||||
|
obrimFilesystemEntityDirectory = "directory"
|
||||||
|
|
||||||
|
obrimFilesystemStrategyUser = "user"
|
||||||
|
obrimFilesystemStrategyCustom = "custom"
|
||||||
|
|
||||||
|
obrimFilesystemModeRead = "read"
|
||||||
|
obrimFilesystemModeWrite = "write"
|
||||||
|
|
||||||
|
obrimFilesystemCodeSuccessList = "SUCCESS_LIST"
|
||||||
|
obrimFilesystemCodeSuccessCheck = "SUCCESS_CHECK"
|
||||||
|
obrimFilesystemCodeSuccessCreate = "SUCCESS_CREATE"
|
||||||
|
obrimFilesystemCodeSuccessDelete = "SUCCESS_DELETE"
|
||||||
|
obrimFilesystemCodeSuccessPermission = "SUCCESS_PERMISSION"
|
||||||
|
|
||||||
|
obrimFilesystemCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||||
|
obrimFilesystemCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
|
||||||
|
obrimFilesystemCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||||
|
obrimFilesystemCodeFailedUnsupportedConfig = "FAILED_UNSUPPORTED_CONFIGURATION"
|
||||||
|
obrimFilesystemCodeFailedPathResolution = "FAILED_PATH_RESOLUTION"
|
||||||
|
obrimFilesystemCodeFailedExecution = "FAILED_EXECUTION"
|
||||||
|
obrimFilesystemCodeFailedPermission = "FAILED_PERMISSION"
|
||||||
|
obrimFilesystemCodeFailedEntityNotFound = "FAILED_ENTITY_NOT_FOUND"
|
||||||
|
obrimFilesystemCodeFailedUnsupportedEntity = "FAILED_UNSUPPORTED_ENTITY"
|
||||||
|
obrimFilesystemCodeFailedInvalidPermission = "FAILED_INVALID_PERMISSION"
|
||||||
|
)
|
||||||
|
|
||||||
|
var obrimFilesystemSupportedKeys = map[string]map[string]bool{
|
||||||
|
obrimFilesystemTypeList: {
|
||||||
|
"entity": true,
|
||||||
|
"strategy": true,
|
||||||
|
"base": true,
|
||||||
|
"name": true,
|
||||||
|
"recursive": true,
|
||||||
|
"include_hidden": true,
|
||||||
|
"sort_by": true,
|
||||||
|
"sort_order": true,
|
||||||
|
"filter_pattern": true,
|
||||||
|
"follow_symlink": true,
|
||||||
|
"absolute_path": true,
|
||||||
|
},
|
||||||
|
obrimFilesystemTypeCheck: {
|
||||||
|
"entity": true,
|
||||||
|
"strategy": true,
|
||||||
|
"base": true,
|
||||||
|
"name": true,
|
||||||
|
"readable": true,
|
||||||
|
"writable": true,
|
||||||
|
},
|
||||||
|
obrimFilesystemTypeCreate: {
|
||||||
|
"entity": true,
|
||||||
|
"strategy": true,
|
||||||
|
"base": true,
|
||||||
|
"name": true,
|
||||||
|
"hidden": true,
|
||||||
|
},
|
||||||
|
obrimFilesystemTypeDelete: {
|
||||||
|
"entity": true,
|
||||||
|
"strategy": true,
|
||||||
|
"base": true,
|
||||||
|
"name": true,
|
||||||
|
"recursive": true,
|
||||||
|
},
|
||||||
|
obrimFilesystemTypePermission: {
|
||||||
|
"strategy": true,
|
||||||
|
"base": true,
|
||||||
|
"name": true,
|
||||||
|
"mode": true,
|
||||||
|
"permission": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimFilesystem executes filesystem operations.
|
||||||
|
func ObrimFilesystem(operationType string, config map[string]any) map[string]any {
|
||||||
|
if code := obrimFilesystemValidate(operationType, config); code != "" {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": code,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch operationType {
|
||||||
|
case obrimFilesystemTypeList:
|
||||||
|
return obrimFilesystemList(config)
|
||||||
|
|
||||||
|
case obrimFilesystemTypeCheck:
|
||||||
|
return obrimFilesystemCheck(config)
|
||||||
|
|
||||||
|
case obrimFilesystemTypeCreate:
|
||||||
|
return obrimFilesystemCreate(config)
|
||||||
|
|
||||||
|
case obrimFilesystemTypeDelete:
|
||||||
|
return obrimFilesystemDelete(config)
|
||||||
|
|
||||||
|
case obrimFilesystemTypePermission:
|
||||||
|
return obrimFilesystemPermission(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedUnsupportedType,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemValidate validates operation inputs.
|
||||||
|
func obrimFilesystemValidate(operationType string, config map[string]any) string {
|
||||||
|
if config == nil {
|
||||||
|
return obrimFilesystemCodeFailedMissingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, exists := obrimFilesystemSupportedKeys[operationType]; !exists {
|
||||||
|
return obrimFilesystemCodeFailedUnsupportedType
|
||||||
|
}
|
||||||
|
|
||||||
|
for key := range config {
|
||||||
|
if !obrimFilesystemSupportedKeys[operationType][key] {
|
||||||
|
return obrimFilesystemCodeFailedUnsupportedConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch operationType {
|
||||||
|
case obrimFilesystemTypeCreate,
|
||||||
|
obrimFilesystemTypeDelete:
|
||||||
|
entity, _ := config["entity"].(string)
|
||||||
|
|
||||||
|
if entity != obrimFilesystemEntityFile &&
|
||||||
|
entity != obrimFilesystemEntityDirectory {
|
||||||
|
return obrimFilesystemCodeFailedUnsupportedEntity
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemResolve resolves and normalizes paths.
|
||||||
|
func obrimFilesystemResolve(config map[string]any) (string, error) {
|
||||||
|
var basePath string
|
||||||
|
|
||||||
|
strategy, _ := config["strategy"].(string)
|
||||||
|
|
||||||
|
switch strategy {
|
||||||
|
case "", obrimFilesystemStrategyUser:
|
||||||
|
homePath, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
currentUser, userErr := user.Current()
|
||||||
|
if userErr != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
homePath = currentUser.HomeDir
|
||||||
|
}
|
||||||
|
|
||||||
|
basePath = homePath
|
||||||
|
|
||||||
|
case obrimFilesystemStrategyCustom:
|
||||||
|
basePath, _ = config["base"].(string)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", os.ErrInvalid
|
||||||
|
}
|
||||||
|
|
||||||
|
name, _ := config["name"].(string)
|
||||||
|
|
||||||
|
resolvedPath := filepath.Clean(filepath.Join(basePath, name))
|
||||||
|
|
||||||
|
return filepath.Abs(resolvedPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemPayload builds standardized payloads.
|
||||||
|
func obrimFilesystemPayload(payload map[string]any) map[string]any {
|
||||||
|
return payload
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemEntityType determines entity type.
|
||||||
|
func obrimFilesystemEntityType(path string) string {
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.IsDir() {
|
||||||
|
return obrimFilesystemEntityDirectory
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimFilesystemEntityFile
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemPermissionValidate validates permission values.
|
||||||
|
func obrimFilesystemPermissionValidate(permission string) bool {
|
||||||
|
if permission == "" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err := strconv.ParseUint(permission, 8, 32)
|
||||||
|
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemList executes listing operations.
|
||||||
|
func obrimFilesystemList(config map[string]any) map[string]any {
|
||||||
|
basePath, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := obrimFilesystemListTraverse(basePath, config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedExecution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = obrimFilesystemListFilter(entries, config)
|
||||||
|
obrimFilesystemListSort(entries, config)
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessList,
|
||||||
|
"payload": obrimFilesystemPayload(map[string]any{
|
||||||
|
"entries": entries,
|
||||||
|
"total": len(entries),
|
||||||
|
"recursive": config["recursive"] == true,
|
||||||
|
"base_path": basePath,
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemListTraverse traverses filesystem entities.
|
||||||
|
func obrimFilesystemListTraverse(basePath string, config map[string]any) ([]map[string]any, error) {
|
||||||
|
var entries []map[string]any
|
||||||
|
|
||||||
|
recursive, _ := config["recursive"].(bool)
|
||||||
|
includeHidden, _ := config["include_hidden"].(bool)
|
||||||
|
absolutePath, _ := config["absolute_path"].(bool)
|
||||||
|
|
||||||
|
walkFunction := func(path string, info os.FileInfo, walkErr error) error {
|
||||||
|
if walkErr != nil {
|
||||||
|
return walkErr
|
||||||
|
}
|
||||||
|
|
||||||
|
if path == basePath {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !includeHidden && strings.HasPrefix(info.Name(), ".") {
|
||||||
|
if info.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
entryPath := path
|
||||||
|
|
||||||
|
if !absolutePath {
|
||||||
|
entryPath = info.Name()
|
||||||
|
}
|
||||||
|
|
||||||
|
entries = append(entries, map[string]any{
|
||||||
|
"name": info.Name(),
|
||||||
|
"path": entryPath,
|
||||||
|
"entity": obrimFilesystemEntityType(path),
|
||||||
|
"size": info.Size(),
|
||||||
|
"modified": info.ModTime().UTC().Format(time.RFC3339),
|
||||||
|
})
|
||||||
|
|
||||||
|
if !recursive && info.IsDir() {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err := filepath.Walk(basePath, walkFunction)
|
||||||
|
|
||||||
|
return entries, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemListFilter filters listing results.
|
||||||
|
func obrimFilesystemListFilter(entries []map[string]any, config map[string]any) []map[string]any {
|
||||||
|
var filtered []map[string]any
|
||||||
|
|
||||||
|
entity, _ := config["entity"].(string)
|
||||||
|
pattern, _ := config["filter_pattern"].(string)
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entity != "" && entry["entity"] != entity {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if pattern != "" {
|
||||||
|
matched, _ := filepath.Match(pattern, entry["name"].(string))
|
||||||
|
|
||||||
|
if !matched {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered = append(filtered, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
return filtered
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemListSort sorts listing results.
|
||||||
|
func obrimFilesystemListSort(entries []map[string]any, config map[string]any) {
|
||||||
|
sortBy, _ := config["sort_by"].(string)
|
||||||
|
sortOrder, _ := config["sort_order"].(string)
|
||||||
|
|
||||||
|
sort.Slice(entries, func(i, j int) bool {
|
||||||
|
var result bool
|
||||||
|
|
||||||
|
switch sortBy {
|
||||||
|
case "size":
|
||||||
|
result = entries[i]["size"].(int64) < entries[j]["size"].(int64)
|
||||||
|
|
||||||
|
case "modified":
|
||||||
|
result = entries[i]["modified"].(string) < entries[j]["modified"].(string)
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = entries[i]["name"].(string) < entries[j]["name"].(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sortOrder == "desc" {
|
||||||
|
return !result
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemCheck executes validation operations.
|
||||||
|
func obrimFilesystemCheck(config map[string]any) map[string]any {
|
||||||
|
path, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_, statErr := os.Stat(path)
|
||||||
|
|
||||||
|
exists := statErr == nil
|
||||||
|
entity := ""
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
entity = obrimFilesystemEntityType(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
readable := false
|
||||||
|
writable := false
|
||||||
|
|
||||||
|
if exists {
|
||||||
|
readable, writable = obrimFilesystemCheckAccess(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessCheck,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"path": path,
|
||||||
|
"exists": exists,
|
||||||
|
"entity": entity,
|
||||||
|
"readable": readable,
|
||||||
|
"writable": writable,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemCheckAccess verifies accessibility.
|
||||||
|
func obrimFilesystemCheckAccess(path string) (bool, bool) {
|
||||||
|
readable := true
|
||||||
|
writable := true
|
||||||
|
|
||||||
|
readFile, readErr := os.Open(path)
|
||||||
|
if readErr != nil {
|
||||||
|
readable = false
|
||||||
|
} else {
|
||||||
|
_ = readFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
writeFile, writeErr := os.OpenFile(path, os.O_WRONLY, 0)
|
||||||
|
if writeErr != nil {
|
||||||
|
writable = false
|
||||||
|
} else {
|
||||||
|
_ = writeFile.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return readable, writable
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemCreate executes creation operations.
|
||||||
|
func obrimFilesystemCreate(config map[string]any) map[string]any {
|
||||||
|
entity := config["entity"].(string)
|
||||||
|
|
||||||
|
switch entity {
|
||||||
|
case obrimFilesystemEntityFile:
|
||||||
|
return obrimFilesystemCreateFile(config)
|
||||||
|
|
||||||
|
case obrimFilesystemEntityDirectory:
|
||||||
|
return obrimFilesystemCreateDirectory(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedUnsupportedEntity,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemCreateFile creates a file.
|
||||||
|
func obrimFilesystemCreateFile(config map[string]any) map[string]any {
|
||||||
|
path, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedExecution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Create(path)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedExecution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = file.Close()
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessCreate,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"path": path,
|
||||||
|
"entity": obrimFilesystemEntityFile,
|
||||||
|
"hidden": config["hidden"] == true,
|
||||||
|
"created": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemCreateDirectory creates a directory.
|
||||||
|
func obrimFilesystemCreateDirectory(config map[string]any) map[string]any {
|
||||||
|
path, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.MkdirAll(path, 0755); err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedExecution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessCreate,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"path": path,
|
||||||
|
"entity": obrimFilesystemEntityDirectory,
|
||||||
|
"hidden": config["hidden"] == true,
|
||||||
|
"created": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemDelete executes deletion operations.
|
||||||
|
func obrimFilesystemDelete(config map[string]any) map[string]any {
|
||||||
|
entity := config["entity"].(string)
|
||||||
|
|
||||||
|
switch entity {
|
||||||
|
case obrimFilesystemEntityFile:
|
||||||
|
return obrimFilesystemDeleteFile(config)
|
||||||
|
|
||||||
|
case obrimFilesystemEntityDirectory:
|
||||||
|
return obrimFilesystemDeleteDirectory(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedUnsupportedEntity,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemDeleteFile deletes a file.
|
||||||
|
func obrimFilesystemDeleteFile(config map[string]any) map[string]any {
|
||||||
|
path, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := os.Remove(path); err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedExecution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessDelete,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"path": path,
|
||||||
|
"entity": obrimFilesystemEntityFile,
|
||||||
|
"deleted": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemDeleteDirectory deletes a directory.
|
||||||
|
func obrimFilesystemDeleteDirectory(config map[string]any) map[string]any {
|
||||||
|
path, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
recursive, _ := config["recursive"].(bool)
|
||||||
|
|
||||||
|
if recursive {
|
||||||
|
err = os.RemoveAll(path)
|
||||||
|
} else {
|
||||||
|
err = os.Remove(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedExecution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessDelete,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"path": path,
|
||||||
|
"entity": obrimFilesystemEntityDirectory,
|
||||||
|
"deleted": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemPermission executes permission operations.
|
||||||
|
func obrimFilesystemPermission(config map[string]any) map[string]any {
|
||||||
|
mode, _ := config["mode"].(string)
|
||||||
|
|
||||||
|
switch mode {
|
||||||
|
case "", obrimFilesystemModeRead:
|
||||||
|
return obrimFilesystemPermissionRead(config)
|
||||||
|
|
||||||
|
case obrimFilesystemModeWrite:
|
||||||
|
return obrimFilesystemPermissionWrite(config)
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedInvalidConfig,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemPermissionRead retrieves permissions.
|
||||||
|
func obrimFilesystemPermissionRead(config map[string]any) map[string]any {
|
||||||
|
path, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedEntityNotFound,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessPermission,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"path": path,
|
||||||
|
"permission": info.Mode().Perm().String(),
|
||||||
|
"mode": obrimFilesystemModeRead,
|
||||||
|
"updated": false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimFilesystemPermissionWrite updates permissions.
|
||||||
|
func obrimFilesystemPermissionWrite(config map[string]any) map[string]any {
|
||||||
|
path, err := obrimFilesystemResolve(config)
|
||||||
|
if err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPathResolution,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
permission, _ := config["permission"].(string)
|
||||||
|
|
||||||
|
if !obrimFilesystemPermissionValidate(permission) {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedInvalidPermission,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
value, _ := strconv.ParseUint(permission, 8, 32)
|
||||||
|
|
||||||
|
if err := os.Chmod(path, os.FileMode(value)); err != nil {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": obrimFilesystemCodeFailedPermission,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": obrimFilesystemCodeSuccessPermission,
|
||||||
|
"payload": map[string]any{
|
||||||
|
"path": path,
|
||||||
|
"permission": permission,
|
||||||
|
"mode": obrimFilesystemModeWrite,
|
||||||
|
"updated": true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,594 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Hash
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Generate and verify cryptographic hash values using standard
|
||||||
|
| or salted hashing methods for integrity verification,
|
||||||
|
| canonical fingerprinting, resource identification,
|
||||||
|
| comparison workflows, and security-oriented hashing
|
||||||
|
| requirements.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Call ObrimHash() as the single public entry point.
|
||||||
|
| - Provide exactly two primary parameters: type and config.
|
||||||
|
| - Use type "calculate" to generate hash values.
|
||||||
|
| - Use type "compare" to verify hash values.
|
||||||
|
| - Use mode "standard" for deterministic hashing.
|
||||||
|
| - Use mode "salted" for salt-assisted hashing.
|
||||||
|
| - Use only supported algorithms for the selected mode.
|
||||||
|
| - Preserve generated salt values when future verification
|
||||||
|
| is required.
|
||||||
|
| - Always evaluate the returned status and code values before
|
||||||
|
| consuming payload data.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimHash(
|
||||||
|
| "calculate",
|
||||||
|
| map[string]any{
|
||||||
|
| "mode": "standard",
|
||||||
|
| "algorithm": "sha256",
|
||||||
|
| "value": "example",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimHash(
|
||||||
|
| "calculate",
|
||||||
|
| map[string]any{
|
||||||
|
| "mode": "salted",
|
||||||
|
| "algorithm": "salted_sha512",
|
||||||
|
| "value": "example",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimHash(
|
||||||
|
| "compare",
|
||||||
|
| map[string]any{
|
||||||
|
| "mode": "standard",
|
||||||
|
| "algorithm": "sha256",
|
||||||
|
| "value": "example",
|
||||||
|
| "hash": "<hash>",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimHash(
|
||||||
|
| "compare",
|
||||||
|
| map[string]any{
|
||||||
|
| "mode": "salted",
|
||||||
|
| "algorithm": "salted_sha512",
|
||||||
|
| "value": "example",
|
||||||
|
| "hash": "<hash>",
|
||||||
|
| "salt": "<salt>",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package hash
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/hex"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimHashTypeCalculate = "calculate"
|
||||||
|
obrimHashTypeCompare = "compare"
|
||||||
|
|
||||||
|
obrimHashModeStandard = "standard"
|
||||||
|
obrimHashModeSalted = "salted"
|
||||||
|
|
||||||
|
obrimHashAlgorithmSHA256 = "sha256"
|
||||||
|
obrimHashAlgorithmSHA512 = "sha512"
|
||||||
|
obrimHashAlgorithmSaltedSHA256 = "salted_sha256"
|
||||||
|
obrimHashAlgorithmSaltedSHA512 = "salted_sha512"
|
||||||
|
|
||||||
|
ObrimHashCodeSuccessHashGenerated = "SUCCESS_HASH_GENERATED"
|
||||||
|
ObrimHashCodeSuccessHashCompared = "SUCCESS_HASH_COMPARED"
|
||||||
|
|
||||||
|
ObrimHashCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||||
|
ObrimHashCodeFailedUnsupportedMode = "FAILED_UNSUPPORTED_MODE"
|
||||||
|
ObrimHashCodeFailedUnsupportedAlgorithm = "FAILED_UNSUPPORTED_ALGORITHM"
|
||||||
|
ObrimHashCodeFailedUnsupportedConfigKey = "FAILED_UNSUPPORTED_CONFIG_KEY"
|
||||||
|
ObrimHashCodeFailedMissingMode = "FAILED_MISSING_MODE"
|
||||||
|
ObrimHashCodeFailedMissingAlgorithm = "FAILED_MISSING_ALGORITHM"
|
||||||
|
ObrimHashCodeFailedMissingValue = "FAILED_MISSING_VALUE"
|
||||||
|
ObrimHashCodeFailedMissingHash = "FAILED_MISSING_HASH"
|
||||||
|
ObrimHashCodeFailedMissingSalt = "FAILED_MISSING_SALT"
|
||||||
|
ObrimHashCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||||
|
ObrimHashCodeFailedSaltNotAllowed = "FAILED_SALT_NOT_ALLOWED"
|
||||||
|
ObrimHashCodeFailedHashGeneration = "FAILED_HASH_GENERATION"
|
||||||
|
ObrimHashCodeFailedSaltGeneration = "FAILED_SALT_GENERATION"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObrimHashResponse defines the standard utility response.
|
||||||
|
type ObrimHashResponse struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Payload any `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimHashConfig defines utility configuration.
|
||||||
|
type ObrimHashConfig struct {
|
||||||
|
Mode string
|
||||||
|
Algorithm string
|
||||||
|
Value string
|
||||||
|
Hash string
|
||||||
|
Salt string
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimHashCalculateStandardPayload defines calculate standard payload.
|
||||||
|
type ObrimHashCalculateStandardPayload struct {
|
||||||
|
Algorithm string `json:"algorithm"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimHashCalculateSaltedPayload defines calculate salted payload.
|
||||||
|
type ObrimHashCalculateSaltedPayload struct {
|
||||||
|
Algorithm string `json:"algorithm"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Salt string `json:"salt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimHashCompareStandardPayload defines compare standard payload.
|
||||||
|
type ObrimHashCompareStandardPayload struct {
|
||||||
|
Algorithm string `json:"algorithm"`
|
||||||
|
Matched bool `json:"matched"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimHashCompareSaltedPayload defines compare salted payload.
|
||||||
|
type ObrimHashCompareSaltedPayload struct {
|
||||||
|
Algorithm string `json:"algorithm"`
|
||||||
|
Matched bool `json:"matched"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
Salt string `json:"salt"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashAllowedConfigKeys stores supported configuration keys.
|
||||||
|
var obrimHashAllowedConfigKeys = map[string]struct{}{
|
||||||
|
"mode": {},
|
||||||
|
"algorithm": {},
|
||||||
|
"value": {},
|
||||||
|
"hash": {},
|
||||||
|
"salt": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimHash processes hash generation and verification requests.
|
||||||
|
func ObrimHash(
|
||||||
|
hashType string,
|
||||||
|
config map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
if !obrimHashValidateType(hashType) {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedUnsupportedType,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
hashConfig, errorCode := obrimHashValidateConfig(
|
||||||
|
hashType,
|
||||||
|
config,
|
||||||
|
)
|
||||||
|
if errorCode != "" {
|
||||||
|
return obrimHashBuildErrorPayload(errorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !obrimHashValidateMode(hashConfig.Mode) {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedUnsupportedMode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !obrimHashValidateAlgorithm(
|
||||||
|
hashConfig.Mode,
|
||||||
|
hashConfig.Algorithm,
|
||||||
|
) {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedUnsupportedAlgorithm,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch hashType {
|
||||||
|
case obrimHashTypeCalculate:
|
||||||
|
return obrimHashCalculate(hashConfig)
|
||||||
|
|
||||||
|
case obrimHashTypeCompare:
|
||||||
|
return obrimHashCompare(hashConfig)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedUnsupportedType,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashValidateType validates operation type.
|
||||||
|
func obrimHashValidateType(hashType string) bool {
|
||||||
|
switch hashType {
|
||||||
|
case obrimHashTypeCalculate:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimHashTypeCompare:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashValidateMode validates hashing mode.
|
||||||
|
func obrimHashValidateMode(mode string) bool {
|
||||||
|
switch mode {
|
||||||
|
case obrimHashModeStandard:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimHashModeSalted:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashValidateAlgorithm validates hashing algorithm selection.
|
||||||
|
func obrimHashValidateAlgorithm(
|
||||||
|
mode string,
|
||||||
|
algorithm string,
|
||||||
|
) bool {
|
||||||
|
switch mode {
|
||||||
|
case obrimHashModeStandard:
|
||||||
|
switch algorithm {
|
||||||
|
case obrimHashAlgorithmSHA256:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimHashAlgorithmSHA512:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
case obrimHashModeSalted:
|
||||||
|
switch algorithm {
|
||||||
|
case obrimHashAlgorithmSaltedSHA256:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimHashAlgorithmSaltedSHA512:
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashValidateConfig validates configuration keys and values.
|
||||||
|
func obrimHashValidateConfig(
|
||||||
|
hashType string,
|
||||||
|
config map[string]any,
|
||||||
|
) (ObrimHashConfig, string) {
|
||||||
|
var hashConfig ObrimHashConfig
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
for key := range config {
|
||||||
|
if _, exists := obrimHashAllowedConfigKeys[key]; !exists {
|
||||||
|
return hashConfig, ObrimHashCodeFailedUnsupportedConfigKey
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
modeValue, ok := config["mode"].(string)
|
||||||
|
if !ok || modeValue == "" {
|
||||||
|
return hashConfig, ObrimHashCodeFailedMissingMode
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithmValue, ok := config["algorithm"].(string)
|
||||||
|
if !ok || algorithmValue == "" {
|
||||||
|
return hashConfig, ObrimHashCodeFailedMissingAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
valueValue, ok := config["value"].(string)
|
||||||
|
if !ok || valueValue == "" {
|
||||||
|
return hashConfig, ObrimHashCodeFailedMissingValue
|
||||||
|
}
|
||||||
|
|
||||||
|
hashConfig.Mode = modeValue
|
||||||
|
hashConfig.Algorithm = algorithmValue
|
||||||
|
hashConfig.Value = valueValue
|
||||||
|
|
||||||
|
if hashValue, exists := config["hash"]; exists {
|
||||||
|
hashText, ok := hashValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
hashConfig.Hash = hashText
|
||||||
|
}
|
||||||
|
|
||||||
|
if saltValue, exists := config["salt"]; exists {
|
||||||
|
saltText, ok := saltValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
hashConfig.Salt = saltText
|
||||||
|
}
|
||||||
|
|
||||||
|
switch hashType {
|
||||||
|
case obrimHashTypeCalculate:
|
||||||
|
if hashConfig.Mode == obrimHashModeStandard &&
|
||||||
|
hashConfig.Salt != "" {
|
||||||
|
return hashConfig, ObrimHashCodeFailedSaltNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
case obrimHashTypeCompare:
|
||||||
|
if hashConfig.Hash == "" {
|
||||||
|
return hashConfig, ObrimHashCodeFailedMissingHash
|
||||||
|
}
|
||||||
|
|
||||||
|
if hashConfig.Mode == obrimHashModeStandard &&
|
||||||
|
hashConfig.Salt != "" {
|
||||||
|
return hashConfig, ObrimHashCodeFailedSaltNotAllowed
|
||||||
|
}
|
||||||
|
|
||||||
|
if hashConfig.Mode == obrimHashModeSalted &&
|
||||||
|
hashConfig.Salt == "" {
|
||||||
|
return hashConfig, ObrimHashCodeFailedMissingSalt
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashConfig, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashGenerateHash executes the selected hashing algorithm.
|
||||||
|
func obrimHashGenerateHash(
|
||||||
|
algorithm string,
|
||||||
|
value string,
|
||||||
|
salt string,
|
||||||
|
) (string, bool) {
|
||||||
|
switch algorithm {
|
||||||
|
case obrimHashAlgorithmSHA256:
|
||||||
|
sum := sha256.Sum256([]byte(value))
|
||||||
|
return hex.EncodeToString(sum[:]), true
|
||||||
|
|
||||||
|
case obrimHashAlgorithmSHA512:
|
||||||
|
sum := sha512.Sum512([]byte(value))
|
||||||
|
return hex.EncodeToString(sum[:]), true
|
||||||
|
|
||||||
|
case obrimHashAlgorithmSaltedSHA256:
|
||||||
|
sum := sha256.Sum256([]byte(value + salt))
|
||||||
|
return hex.EncodeToString(sum[:]), true
|
||||||
|
|
||||||
|
case obrimHashAlgorithmSaltedSHA512:
|
||||||
|
sum := sha512.Sum512([]byte(value + salt))
|
||||||
|
return hex.EncodeToString(sum[:]), true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashCalculate processes hash generation requests.
|
||||||
|
func obrimHashCalculate(
|
||||||
|
config ObrimHashConfig,
|
||||||
|
) map[string]any {
|
||||||
|
switch config.Mode {
|
||||||
|
case obrimHashModeStandard:
|
||||||
|
return obrimHashCalculateStandard(config)
|
||||||
|
|
||||||
|
case obrimHashModeSalted:
|
||||||
|
return obrimHashCalculateSalted(config)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedUnsupportedMode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashCalculateStandard generates a deterministic hash.
|
||||||
|
func obrimHashCalculateStandard(
|
||||||
|
config ObrimHashConfig,
|
||||||
|
) map[string]any {
|
||||||
|
hashValue, success := obrimHashGenerateHash(
|
||||||
|
config.Algorithm,
|
||||||
|
config.Value,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
if !success {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedHashGeneration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := ObrimHashCalculateStandardPayload{
|
||||||
|
Algorithm: config.Algorithm,
|
||||||
|
Hash: hashValue,
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimHashBuildSuccessPayload(
|
||||||
|
ObrimHashCodeSuccessHashGenerated,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashCalculateSalted generates a salted hash and salt pair.
|
||||||
|
func obrimHashCalculateSalted(
|
||||||
|
config ObrimHashConfig,
|
||||||
|
) map[string]any {
|
||||||
|
var activeSalt string
|
||||||
|
|
||||||
|
activeSalt = config.Salt
|
||||||
|
|
||||||
|
if activeSalt == "" {
|
||||||
|
generatedSalt, success := obrimHashGenerateSalt()
|
||||||
|
if !success {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedSaltGeneration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
activeSalt = generatedSalt
|
||||||
|
}
|
||||||
|
|
||||||
|
hashValue, success := obrimHashGenerateHash(
|
||||||
|
config.Algorithm,
|
||||||
|
config.Value,
|
||||||
|
activeSalt,
|
||||||
|
)
|
||||||
|
if !success {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedHashGeneration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := ObrimHashCalculateSaltedPayload{
|
||||||
|
Algorithm: config.Algorithm,
|
||||||
|
Hash: hashValue,
|
||||||
|
Salt: activeSalt,
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimHashBuildSuccessPayload(
|
||||||
|
ObrimHashCodeSuccessHashGenerated,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashGenerateSalt generates a cryptographically secure salt.
|
||||||
|
func obrimHashGenerateSalt() (string, bool) {
|
||||||
|
var saltBytes = make([]byte, 32)
|
||||||
|
|
||||||
|
_, err := rand.Read(saltBytes)
|
||||||
|
if err != nil {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
|
||||||
|
return hex.EncodeToString(saltBytes), true
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashCompare processes hash verification requests.
|
||||||
|
func obrimHashCompare(
|
||||||
|
config ObrimHashConfig,
|
||||||
|
) map[string]any {
|
||||||
|
switch config.Mode {
|
||||||
|
case obrimHashModeStandard:
|
||||||
|
return obrimHashCompareStandard(config)
|
||||||
|
|
||||||
|
case obrimHashModeSalted:
|
||||||
|
return obrimHashCompareSalted(config)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedUnsupportedMode,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashCompareStandard regenerates a standard hash for verification.
|
||||||
|
func obrimHashCompareStandard(
|
||||||
|
config ObrimHashConfig,
|
||||||
|
) map[string]any {
|
||||||
|
regeneratedHash, success := obrimHashGenerateHash(
|
||||||
|
config.Algorithm,
|
||||||
|
config.Value,
|
||||||
|
"",
|
||||||
|
)
|
||||||
|
if !success {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedHashGeneration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := ObrimHashCompareStandardPayload{
|
||||||
|
Algorithm: config.Algorithm,
|
||||||
|
Matched: obrimHashVerify(
|
||||||
|
regeneratedHash,
|
||||||
|
config.Hash,
|
||||||
|
),
|
||||||
|
Hash: config.Hash,
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimHashBuildSuccessPayload(
|
||||||
|
ObrimHashCodeSuccessHashCompared,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashCompareSalted regenerates a salted hash for verification.
|
||||||
|
func obrimHashCompareSalted(
|
||||||
|
config ObrimHashConfig,
|
||||||
|
) map[string]any {
|
||||||
|
regeneratedHash, success := obrimHashGenerateHash(
|
||||||
|
config.Algorithm,
|
||||||
|
config.Value,
|
||||||
|
config.Salt,
|
||||||
|
)
|
||||||
|
if !success {
|
||||||
|
return obrimHashBuildErrorPayload(
|
||||||
|
ObrimHashCodeFailedHashGeneration,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := ObrimHashCompareSaltedPayload{
|
||||||
|
Algorithm: config.Algorithm,
|
||||||
|
Matched: obrimHashVerify(
|
||||||
|
regeneratedHash,
|
||||||
|
config.Hash,
|
||||||
|
),
|
||||||
|
Hash: config.Hash,
|
||||||
|
Salt: config.Salt,
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimHashBuildSuccessPayload(
|
||||||
|
ObrimHashCodeSuccessHashCompared,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashVerify compares regenerated and supplied hash values.
|
||||||
|
func obrimHashVerify(
|
||||||
|
regeneratedHash string,
|
||||||
|
suppliedHash string,
|
||||||
|
) bool {
|
||||||
|
return regeneratedHash == suppliedHash
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashBuildSuccessPayload constructs success response payloads.
|
||||||
|
func obrimHashBuildSuccessPayload(
|
||||||
|
code string,
|
||||||
|
payload any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": code,
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimHashBuildErrorPayload constructs error response payloads.
|
||||||
|
func obrimHashBuildErrorPayload(
|
||||||
|
code string,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": code,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,399 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Key
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Generate cryptographic key material through a type-driven
|
||||||
|
| orchestration workflow with strict validation, schema isolation,
|
||||||
|
| and algorithm-specific generation routines.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Call ObrimKey() as the single public entry point.
|
||||||
|
| - Provide an explicit generation type and configuration.
|
||||||
|
| - Use "symmetric" type for AES key generation.
|
||||||
|
| - Use "asymmetric" type for ECC EdDSA key pair generation.
|
||||||
|
| - Provide only supported configuration fields.
|
||||||
|
| - Complete configuration validation before generation.
|
||||||
|
| - Consume generated key material only from successful responses.
|
||||||
|
| - Check response status and code before accessing payload data.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimKey(
|
||||||
|
| "symmetric",
|
||||||
|
| map[string]any{
|
||||||
|
| "algorithm": "aes",
|
||||||
|
| "key_size": 256,
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimKey(
|
||||||
|
| "asymmetric",
|
||||||
|
| map[string]any{
|
||||||
|
| "algorithm": "ecc",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package key
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/aes"
|
||||||
|
"crypto/ed25519"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/base64"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimKeyTypeSymmetric = "symmetric"
|
||||||
|
obrimKeyTypeAsymmetric = "asymmetric"
|
||||||
|
|
||||||
|
obrimKeyAlgorithmAES = "aes"
|
||||||
|
obrimKeyAlgorithmECC = "ecc"
|
||||||
|
|
||||||
|
obrimKeyCurveEdDSA = "eddsa"
|
||||||
|
|
||||||
|
ObrimKeyCodeSuccessSymmetricGenerated = "SUCCESS_SYMMETRIC_KEY_GENERATED"
|
||||||
|
ObrimKeyCodeSuccessAsymmetricGenerated = "SUCCESS_ASYMMETRIC_KEY_GENERATED"
|
||||||
|
|
||||||
|
ObrimKeyCodeFailedMissingType = "FAILED_MISSING_TYPE"
|
||||||
|
ObrimKeyCodeFailedInvalidType = "FAILED_INVALID_TYPE"
|
||||||
|
ObrimKeyCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||||
|
ObrimKeyCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
|
||||||
|
ObrimKeyCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||||
|
ObrimKeyCodeFailedUnsupportedField = "FAILED_UNSUPPORTED_CONFIGURATION_FIELD"
|
||||||
|
ObrimKeyCodeFailedMissingAlgorithm = "FAILED_MISSING_ALGORITHM"
|
||||||
|
ObrimKeyCodeFailedInvalidAlgorithm = "FAILED_INVALID_ALGORITHM"
|
||||||
|
ObrimKeyCodeFailedUnsupportedAlgorithm = "FAILED_UNSUPPORTED_ALGORITHM"
|
||||||
|
ObrimKeyCodeFailedMissingKeySize = "FAILED_MISSING_KEY_SIZE"
|
||||||
|
ObrimKeyCodeFailedInvalidKeySize = "FAILED_INVALID_KEY_SIZE"
|
||||||
|
ObrimKeyCodeFailedUnsupportedKeySize = "FAILED_UNSUPPORTED_KEY_SIZE"
|
||||||
|
ObrimKeyCodeFailedKeyGeneration = "FAILED_KEY_GENERATION"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ObrimKeyResponse struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Payload map[string]any `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyAllowedSymmetricFields stores allowed symmetric configuration fields.
|
||||||
|
var obrimKeyAllowedSymmetricFields = map[string]struct{}{
|
||||||
|
"algorithm": {},
|
||||||
|
"key_size": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyAllowedAsymmetricFields stores allowed asymmetric configuration fields.
|
||||||
|
var obrimKeyAllowedAsymmetricFields = map[string]struct{}{
|
||||||
|
"algorithm": {},
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimKey dispatches key generation requests and orchestrates validation, generation, and response construction.
|
||||||
|
func ObrimKey(keyType string, config map[string]any) map[string]any {
|
||||||
|
normalizedType, errorResponse := obrimKeyValidate(keyType, config)
|
||||||
|
if errorResponse != nil {
|
||||||
|
return errorResponse
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimKeyDispatch(normalizedType, config)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyValidate validates common request structure and schema integrity.
|
||||||
|
func obrimKeyValidate(
|
||||||
|
keyType string,
|
||||||
|
config map[string]any,
|
||||||
|
) (string, map[string]any) {
|
||||||
|
if strings.TrimSpace(keyType) == "" {
|
||||||
|
return "", obrimKeyBuildError(ObrimKeyCodeFailedMissingType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
return "", obrimKeyBuildError(ObrimKeyCodeFailedMissingConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizedType := strings.ToLower(strings.TrimSpace(keyType))
|
||||||
|
|
||||||
|
switch normalizedType {
|
||||||
|
case obrimKeyTypeSymmetric:
|
||||||
|
if errorCode := obrimKeyValidateSymmetricConfig(config); errorCode != "" {
|
||||||
|
return "", obrimKeyBuildError(errorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
case obrimKeyTypeAsymmetric:
|
||||||
|
if errorCode := obrimKeyValidateAsymmetricConfig(config); errorCode != "" {
|
||||||
|
return "", obrimKeyBuildError(errorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", obrimKeyBuildError(ObrimKeyCodeFailedUnsupportedType)
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalizedType, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyDispatch routes execution to the appropriate cryptographic workflow.
|
||||||
|
func obrimKeyDispatch(
|
||||||
|
keyType string,
|
||||||
|
config map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
switch keyType {
|
||||||
|
case obrimKeyTypeSymmetric:
|
||||||
|
return obrimKeyGenerateSymmetric(config)
|
||||||
|
|
||||||
|
case obrimKeyTypeAsymmetric:
|
||||||
|
return obrimKeyGenerateAsymmetric(config)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimKeyBuildError(ObrimKeyCodeFailedUnsupportedType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyValidateSymmetricConfig validates symmetric configuration values.
|
||||||
|
func obrimKeyValidateSymmetricConfig(config map[string]any) string {
|
||||||
|
for field := range config {
|
||||||
|
if _, exists := obrimKeyAllowedSymmetricFields[field]; !exists {
|
||||||
|
return ObrimKeyCodeFailedUnsupportedField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithmValue, exists := config["algorithm"]
|
||||||
|
if !exists {
|
||||||
|
return ObrimKeyCodeFailedMissingAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithm, ok := algorithmValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return ObrimKeyCodeFailedInvalidAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithm = strings.ToLower(strings.TrimSpace(algorithm))
|
||||||
|
|
||||||
|
if algorithm != obrimKeyAlgorithmAES {
|
||||||
|
return ObrimKeyCodeFailedUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
keySizeValue, exists := config["key_size"]
|
||||||
|
if !exists {
|
||||||
|
return ObrimKeyCodeFailedMissingKeySize
|
||||||
|
}
|
||||||
|
|
||||||
|
keySize, ok := obrimKeyNormalizeInteger(keySizeValue)
|
||||||
|
if !ok {
|
||||||
|
return ObrimKeyCodeFailedInvalidKeySize
|
||||||
|
}
|
||||||
|
|
||||||
|
switch keySize {
|
||||||
|
case 128, 192, 256:
|
||||||
|
return ""
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ObrimKeyCodeFailedUnsupportedKeySize
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyValidateAsymmetricConfig validates asymmetric configuration values.
|
||||||
|
func obrimKeyValidateAsymmetricConfig(config map[string]any) string {
|
||||||
|
for field := range config {
|
||||||
|
if _, exists := obrimKeyAllowedAsymmetricFields[field]; !exists {
|
||||||
|
return ObrimKeyCodeFailedUnsupportedField
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithmValue, exists := config["algorithm"]
|
||||||
|
if !exists {
|
||||||
|
return ObrimKeyCodeFailedMissingAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithm, ok := algorithmValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return ObrimKeyCodeFailedInvalidAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
algorithm = strings.ToLower(strings.TrimSpace(algorithm))
|
||||||
|
|
||||||
|
if algorithm != obrimKeyAlgorithmECC {
|
||||||
|
return ObrimKeyCodeFailedUnsupportedAlgorithm
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyGenerateSymmetric generates symmetric key material.
|
||||||
|
func obrimKeyGenerateSymmetric(config map[string]any) map[string]any {
|
||||||
|
normalizedConfig := obrimKeyNormalizeSymmetricConfig(config)
|
||||||
|
|
||||||
|
payload, errorCode := obrimKeyGenerateAES(normalizedConfig)
|
||||||
|
if errorCode != "" {
|
||||||
|
return obrimKeyBuildError(errorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimKeyBuildPayload(
|
||||||
|
ObrimKeyCodeSuccessSymmetricGenerated,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyGenerateAES generates AES key material.
|
||||||
|
func obrimKeyGenerateAES(
|
||||||
|
config map[string]any,
|
||||||
|
) (map[string]any, string) {
|
||||||
|
keySize := config["key_size"].(int)
|
||||||
|
|
||||||
|
// obrimKeyMaterial stores generated AES key bytes.
|
||||||
|
var obrimKeyMaterial = make([]byte, keySize/8)
|
||||||
|
|
||||||
|
if _, err := rand.Read(obrimKeyMaterial); err != nil {
|
||||||
|
return nil, ObrimKeyCodeFailedKeyGeneration
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := aes.NewCipher(obrimKeyMaterial); err != nil {
|
||||||
|
return nil, ObrimKeyCodeFailedKeyGeneration
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"type": obrimKeyTypeSymmetric,
|
||||||
|
"algorithm": obrimKeyAlgorithmAES,
|
||||||
|
"key_size": keySize,
|
||||||
|
"key_material": base64.StdEncoding.EncodeToString(obrimKeyMaterial),
|
||||||
|
"generated_at": time.Now().UTC().Format(time.RFC3339),
|
||||||
|
}, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyNormalizeSymmetricConfig normalizes symmetric configuration values.
|
||||||
|
func obrimKeyNormalizeSymmetricConfig(
|
||||||
|
config map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
keySize, _ := obrimKeyNormalizeInteger(config["key_size"])
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"algorithm": strings.ToLower(
|
||||||
|
strings.TrimSpace(config["algorithm"].(string)),
|
||||||
|
),
|
||||||
|
"key_size": keySize,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyGenerateAsymmetric generates asymmetric key material.
|
||||||
|
func obrimKeyGenerateAsymmetric(config map[string]any) map[string]any {
|
||||||
|
normalizedConfig := obrimKeyNormalizeAsymmetricConfig(config)
|
||||||
|
|
||||||
|
payload, errorCode := obrimKeyGenerateECC(normalizedConfig)
|
||||||
|
if errorCode != "" {
|
||||||
|
return obrimKeyBuildError(errorCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimKeyBuildPayload(
|
||||||
|
ObrimKeyCodeSuccessAsymmetricGenerated,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyGenerateECC generates ECC key pair material.
|
||||||
|
func obrimKeyGenerateECC(
|
||||||
|
config map[string]any,
|
||||||
|
) (map[string]any, string) {
|
||||||
|
_ = config
|
||||||
|
|
||||||
|
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
return nil, ObrimKeyCodeFailedKeyGeneration
|
||||||
|
}
|
||||||
|
|
||||||
|
return map[string]any{
|
||||||
|
"type": obrimKeyTypeAsymmetric,
|
||||||
|
"algorithm": obrimKeyAlgorithmECC,
|
||||||
|
"curve": obrimKeyCurveEdDSA,
|
||||||
|
"public_key": base64.StdEncoding.EncodeToString(publicKey),
|
||||||
|
"private_key": base64.StdEncoding.EncodeToString(privateKey),
|
||||||
|
"generated_at": time.Now().UTC().Format(time.RFC3339),
|
||||||
|
}, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyNormalizeAsymmetricConfig normalizes asymmetric configuration values.
|
||||||
|
func obrimKeyNormalizeAsymmetricConfig(
|
||||||
|
config map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"algorithm": strings.ToLower(
|
||||||
|
strings.TrimSpace(config["algorithm"].(string)),
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyBuildPayload constructs standardized success payloads.
|
||||||
|
func obrimKeyBuildPayload(
|
||||||
|
code string,
|
||||||
|
payload map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": true,
|
||||||
|
"code": code,
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyBuildError constructs standardized error responses.
|
||||||
|
func obrimKeyBuildError(code string) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": false,
|
||||||
|
"code": code,
|
||||||
|
"payload": nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimKeyNormalizeInteger normalizes integer values.
|
||||||
|
func obrimKeyNormalizeInteger(value any) (int, bool) {
|
||||||
|
switch normalizedValue := value.(type) {
|
||||||
|
case int:
|
||||||
|
return normalizedValue, true
|
||||||
|
|
||||||
|
case int8:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case int16:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case int32:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case uint:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case uint8:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case uint16:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case uint32:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
case uint64:
|
||||||
|
return int(normalizedValue), true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,259 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Log
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide framework-level structured logging capabilities.
|
||||||
|
| - Persist plaintext log entries using a deterministic and platform-aware storage strategy.
|
||||||
|
| - Enable reusable logging functionality for framework and software components.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Use ObrimLog() to write structured log entries.
|
||||||
|
| - Use labels in the format SERVICENAME/UTILITYNAME or SERVICENAME/FEATURENAME.
|
||||||
|
| - Use human-readable messages suitable for operational diagnostics and tracing.
|
||||||
|
| - Allow the utility to manage log file creation and storage path resolution.
|
||||||
|
| - Use append-only logging to preserve historical log records.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimLog("SYSTEM/LOG", "Log utility initialized.")
|
||||||
|
| - ObrimLog("PROJECT/CREATE", "Project created successfully.")
|
||||||
|
| - ObrimLog("USER/AUTHENTICATION", "User authentication completed.")
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package log
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimLogSoftwareName = "softwarelower"
|
||||||
|
obrimLogFileExtension = ".log"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObrimLog writes a structured log entry.
|
||||||
|
func ObrimLog(label string, message string) {
|
||||||
|
softwareName := obrimLogSoftwareNameResolve()
|
||||||
|
if softwareName == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := obrimLogEntryBuild(
|
||||||
|
obrimLogTimestampGenerate(),
|
||||||
|
label,
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
|
||||||
|
filePath := obrimLogFileResolve(softwareName)
|
||||||
|
if filePath == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := obrimLogFileEnsure(filePath); err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = obrimLogWrite(filePath, entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogSoftwareNameResolve resolves the software name from a local variable.
|
||||||
|
func obrimLogSoftwareNameResolve() string {
|
||||||
|
return strings.TrimSpace(obrimLogSoftwareName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogTimestampGenerate generates deterministic timestamps.
|
||||||
|
func obrimLogTimestampGenerate() string {
|
||||||
|
return time.Now().UTC().Format(time.RFC3339)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogEntryBuild constructs formatted log entries.
|
||||||
|
func obrimLogEntryBuild(
|
||||||
|
timestamp string,
|
||||||
|
label string,
|
||||||
|
message string,
|
||||||
|
) string {
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"[%s] [%s] %s\n",
|
||||||
|
strings.TrimSpace(timestamp),
|
||||||
|
strings.TrimSpace(label),
|
||||||
|
strings.TrimSpace(message),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogDirectoryResolve resolves platform-specific log directory paths.
|
||||||
|
func obrimLogDirectoryResolve(
|
||||||
|
softwareName string,
|
||||||
|
) string {
|
||||||
|
switch runtime.GOOS {
|
||||||
|
case "linux":
|
||||||
|
return obrimLogDirectoryResolveLinux(softwareName)
|
||||||
|
|
||||||
|
case "windows":
|
||||||
|
return obrimLogDirectoryResolveWindows(softwareName)
|
||||||
|
|
||||||
|
case "darwin":
|
||||||
|
return obrimLogDirectoryResolveMacOS(softwareName)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogFileResolve resolves platform-specific log file paths.
|
||||||
|
func obrimLogFileResolve(
|
||||||
|
softwareName string,
|
||||||
|
) string {
|
||||||
|
directoryPath := obrimLogDirectoryResolve(softwareName)
|
||||||
|
if directoryPath == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(
|
||||||
|
directoryPath,
|
||||||
|
softwareName+obrimLogFileExtension,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogFileEnsure creates and verifies required log directories and files.
|
||||||
|
func obrimLogFileEnsure(
|
||||||
|
filePath string,
|
||||||
|
) error {
|
||||||
|
directoryPath := filepath.Dir(filePath)
|
||||||
|
|
||||||
|
if err := os.MkdirAll(directoryPath, 0o755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(
|
||||||
|
filePath,
|
||||||
|
os.O_CREATE,
|
||||||
|
0o644,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return file.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogWrite persists log entries to the filesystem.
|
||||||
|
func obrimLogWrite(
|
||||||
|
filePath string,
|
||||||
|
entry string,
|
||||||
|
) error {
|
||||||
|
file, err := os.OpenFile(
|
||||||
|
filePath,
|
||||||
|
os.O_APPEND|os.O_CREATE|os.O_WRONLY,
|
||||||
|
0o644,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = file.WriteString(entry)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogDirectoryResolveLinux resolves Linux log directory paths.
|
||||||
|
func obrimLogDirectoryResolveLinux(
|
||||||
|
softwareName string,
|
||||||
|
) string {
|
||||||
|
// Mutable XDG state home environment variable.
|
||||||
|
var xdgStateHome = strings.TrimSpace(
|
||||||
|
os.Getenv("XDG_STATE_HOME"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if xdgStateHome != "" {
|
||||||
|
return filepath.Join(
|
||||||
|
xdgStateHome,
|
||||||
|
"."+softwareName,
|
||||||
|
"logs",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Mutable user home directory.
|
||||||
|
var homeDirectory string
|
||||||
|
|
||||||
|
userHomeDirectory, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
homeDirectory = userHomeDirectory
|
||||||
|
|
||||||
|
return filepath.Join(
|
||||||
|
homeDirectory,
|
||||||
|
".local",
|
||||||
|
"state",
|
||||||
|
"."+softwareName,
|
||||||
|
"logs",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogDirectoryResolveWindows resolves Windows log directory paths.
|
||||||
|
func obrimLogDirectoryResolveWindows(
|
||||||
|
softwareName string,
|
||||||
|
) string {
|
||||||
|
// Mutable LOCALAPPDATA environment variable.
|
||||||
|
var localAppData = strings.TrimSpace(
|
||||||
|
os.Getenv("LOCALAPPDATA"),
|
||||||
|
)
|
||||||
|
|
||||||
|
if localAppData == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return filepath.Join(
|
||||||
|
localAppData,
|
||||||
|
softwareName,
|
||||||
|
"Logs",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimLogDirectoryResolveMacOS resolves macOS log directory paths.
|
||||||
|
func obrimLogDirectoryResolveMacOS(
|
||||||
|
softwareName string,
|
||||||
|
) string {
|
||||||
|
// Mutable user home directory.
|
||||||
|
var homeDirectory string
|
||||||
|
|
||||||
|
userHomeDirectory, err := os.UserHomeDir()
|
||||||
|
if err != nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
homeDirectory = userHomeDirectory
|
||||||
|
|
||||||
|
return filepath.Join(
|
||||||
|
homeDirectory,
|
||||||
|
"Library",
|
||||||
|
"Logs",
|
||||||
|
"."+softwareName,
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,479 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Marker
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide a unified utility for generating unique markers through
|
||||||
|
| time-based, random, and encoded generation strategies using a
|
||||||
|
| single standardized entry point.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Use ObrimMarker() as the primary entry point for all marker
|
||||||
|
| generation operations.
|
||||||
|
| - Provide a supported marker type and the required configuration
|
||||||
|
| values for the selected generation strategy.
|
||||||
|
| - Consume responses through the standardized status, code, and
|
||||||
|
| payload structure.
|
||||||
|
| - Validate response status before accessing payload data.
|
||||||
|
| - Use time markers for distributed uniqueness requirements.
|
||||||
|
| - Use random markers for collision-resistant identifier generation.
|
||||||
|
| - Use encoded markers for deterministic identifier generation from
|
||||||
|
| source data.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimMarker("time", map[string]any{
|
||||||
|
| "epoch": 1704067200000,
|
||||||
|
| "instance": "NODE01",
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
| - ObrimMarker("random", map[string]any{
|
||||||
|
| "length": 32,
|
||||||
|
| "charset": "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
| - ObrimMarker("encoded", map[string]any{
|
||||||
|
| "data": "customer-1001",
|
||||||
|
| "length": 24,
|
||||||
|
| "salt": "obrim",
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package marker
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimMarkerTypeTime = "time"
|
||||||
|
obrimMarkerTypeRandom = "random"
|
||||||
|
obrimMarkerTypeEncoded = "encoded"
|
||||||
|
|
||||||
|
ObrimMarkerCodeSuccessTimeGenerated = "SUCCESS_TIME_GENERATED"
|
||||||
|
ObrimMarkerCodeSuccessRandomGenerated = "SUCCESS_RANDOM_GENERATED"
|
||||||
|
ObrimMarkerCodeSuccessEncodedGenerated = "SUCCESS_ENCODED_GENERATED"
|
||||||
|
|
||||||
|
ObrimMarkerCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||||
|
ObrimMarkerCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
|
||||||
|
ObrimMarkerCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||||
|
ObrimMarkerCodeFailedGeneration = "FAILED_GENERATION"
|
||||||
|
ObrimMarkerCodeFailedValidation = "FAILED_VALIDATION"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimMarkerDefaultRandomLength = 16
|
||||||
|
obrimMarkerDefaultEncodedLength = 16
|
||||||
|
obrimMarkerDefaultCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObrimMarkerResponse defines the standardized utility response.
|
||||||
|
type ObrimMarkerResponse struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Payload map[string]any `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimMarker routes marker generation requests and returns a standardized marker response.
|
||||||
|
func ObrimMarker(markerType string, config map[string]any) map[string]any {
|
||||||
|
obrimMarkerLog("initialization")
|
||||||
|
|
||||||
|
if !obrimMarkerValidateType(markerType) {
|
||||||
|
obrimMarkerLog("failure")
|
||||||
|
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimMarkerCodeFailedUnsupportedType,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if config == nil {
|
||||||
|
config = map[string]any{}
|
||||||
|
}
|
||||||
|
|
||||||
|
response := obrimMarkerRoute(markerType, config)
|
||||||
|
|
||||||
|
if status, ok := response["status"].(bool); ok && status {
|
||||||
|
obrimMarkerLog("success")
|
||||||
|
} else {
|
||||||
|
obrimMarkerLog("failure")
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerValidateType validates supported marker generation types.
|
||||||
|
func obrimMarkerValidateType(markerType string) bool {
|
||||||
|
switch strings.TrimSpace(markerType) {
|
||||||
|
case obrimMarkerTypeTime:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimMarkerTypeRandom:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimMarkerTypeEncoded:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerRoute routes execution to the selected marker generation workflow.
|
||||||
|
func obrimMarkerRoute(
|
||||||
|
markerType string,
|
||||||
|
config map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
switch markerType {
|
||||||
|
case obrimMarkerTypeTime:
|
||||||
|
return obrimMarkerTime(config)
|
||||||
|
|
||||||
|
case obrimMarkerTypeRandom:
|
||||||
|
return obrimMarkerRandom(config)
|
||||||
|
|
||||||
|
case obrimMarkerTypeEncoded:
|
||||||
|
return obrimMarkerEncoded(config)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimMarkerCodeFailedUnsupportedType,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerBuildResponse builds standardized success and error responses.
|
||||||
|
func obrimMarkerBuildResponse(
|
||||||
|
status bool,
|
||||||
|
code string,
|
||||||
|
payload map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": status,
|
||||||
|
"code": code,
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerTime generates time-based unique markers.
|
||||||
|
func obrimMarkerTime(config map[string]any) map[string]any {
|
||||||
|
epoch, instance, code := obrimMarkerValidateTime(config)
|
||||||
|
if code != "" {
|
||||||
|
return obrimMarkerBuildResponse(false, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
marker, err := obrimMarkerTimeGenerate(epoch, instance)
|
||||||
|
if err != nil {
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimMarkerCodeFailedGeneration,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
true,
|
||||||
|
ObrimMarkerCodeSuccessTimeGenerated,
|
||||||
|
map[string]any{
|
||||||
|
"marker": marker,
|
||||||
|
"epoch": epoch,
|
||||||
|
"instance": instance,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerValidateTime validates time marker configuration parameters.
|
||||||
|
func obrimMarkerValidateTime(
|
||||||
|
config map[string]any,
|
||||||
|
) (int64, string, string) {
|
||||||
|
epochValue, epochExists := config["epoch"]
|
||||||
|
instanceValue, instanceExists := config["instance"]
|
||||||
|
|
||||||
|
if !epochExists {
|
||||||
|
return 0, "", ObrimMarkerCodeFailedMissingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if !instanceExists {
|
||||||
|
return 0, "", ObrimMarkerCodeFailedMissingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var epoch int64
|
||||||
|
|
||||||
|
switch value := epochValue.(type) {
|
||||||
|
case int64:
|
||||||
|
epoch = value
|
||||||
|
|
||||||
|
case int:
|
||||||
|
epoch = int64(value)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
epoch = int64(value)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
instance, ok := instanceValue.(string)
|
||||||
|
if !ok || strings.TrimSpace(instance) == "" {
|
||||||
|
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if epoch < 0 {
|
||||||
|
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
return epoch, instance, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerTimeGenerate produces the final time-based marker value.
|
||||||
|
func obrimMarkerTimeGenerate(
|
||||||
|
epoch int64,
|
||||||
|
instance string,
|
||||||
|
) (string, error) {
|
||||||
|
now := time.Now().UnixMilli()
|
||||||
|
|
||||||
|
if epoch > now {
|
||||||
|
return "", obrimMarkerError("invalid epoch")
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"%X%s",
|
||||||
|
now-epoch,
|
||||||
|
strings.ToUpper(instance),
|
||||||
|
), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerRandom generates random collision-resistant markers.
|
||||||
|
func obrimMarkerRandom(config map[string]any) map[string]any {
|
||||||
|
length, charset, code := obrimMarkerValidateRandom(config)
|
||||||
|
if code != "" {
|
||||||
|
return obrimMarkerBuildResponse(false, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
marker, err := obrimMarkerRandomGenerate(length, charset)
|
||||||
|
if err != nil {
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimMarkerCodeFailedGeneration,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
true,
|
||||||
|
ObrimMarkerCodeSuccessRandomGenerated,
|
||||||
|
map[string]any{
|
||||||
|
"marker": marker,
|
||||||
|
"length": length,
|
||||||
|
"charset": charset,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerValidateRandom validates random marker configuration parameters.
|
||||||
|
func obrimMarkerValidateRandom(
|
||||||
|
config map[string]any,
|
||||||
|
) (int, string, string) {
|
||||||
|
var length = obrimMarkerDefaultRandomLength
|
||||||
|
var charset = obrimMarkerDefaultCharset
|
||||||
|
|
||||||
|
if value, exists := config["length"]; exists {
|
||||||
|
switch typedValue := value.(type) {
|
||||||
|
case int:
|
||||||
|
length = typedValue
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
length = int(typedValue)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
length = int(typedValue)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value, exists := config["charset"]; exists {
|
||||||
|
typedValue, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
charset = typedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if length <= 0 {
|
||||||
|
return 0, "", ObrimMarkerCodeFailedValidation
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(charset) == "" {
|
||||||
|
return 0, "", ObrimMarkerCodeFailedValidation
|
||||||
|
}
|
||||||
|
|
||||||
|
return length, charset, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerRandomGenerate produces the final random marker value.
|
||||||
|
func obrimMarkerRandomGenerate(
|
||||||
|
length int,
|
||||||
|
charset string,
|
||||||
|
) (string, error) {
|
||||||
|
var buffer = make([]byte, length)
|
||||||
|
var randomBuffer = make([]byte, length)
|
||||||
|
|
||||||
|
_, err := rand.Read(randomBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
for index := range buffer {
|
||||||
|
buffer[index] = charset[int(randomBuffer[index])%len(charset)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(buffer), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerEncoded generates deterministic encoded markers.
|
||||||
|
func obrimMarkerEncoded(config map[string]any) map[string]any {
|
||||||
|
data, length, charset, salt, code := obrimMarkerValidateEncoded(config)
|
||||||
|
if code != "" {
|
||||||
|
return obrimMarkerBuildResponse(false, code, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
marker, err := obrimMarkerEncodedGenerate(
|
||||||
|
data,
|
||||||
|
length,
|
||||||
|
charset,
|
||||||
|
salt,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimMarkerCodeFailedGeneration,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimMarkerBuildResponse(
|
||||||
|
true,
|
||||||
|
ObrimMarkerCodeSuccessEncodedGenerated,
|
||||||
|
map[string]any{
|
||||||
|
"marker": marker,
|
||||||
|
"source": data,
|
||||||
|
"length": length,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerValidateEncoded validates encoded marker configuration parameters.
|
||||||
|
func obrimMarkerValidateEncoded(
|
||||||
|
config map[string]any,
|
||||||
|
) (string, int, string, string, string) {
|
||||||
|
dataValue, exists := config["data"]
|
||||||
|
if !exists {
|
||||||
|
return "", 0, "", "", ObrimMarkerCodeFailedMissingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
data, ok := dataValue.(string)
|
||||||
|
if !ok || strings.TrimSpace(data) == "" {
|
||||||
|
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var length = obrimMarkerDefaultEncodedLength
|
||||||
|
var charset = obrimMarkerDefaultCharset
|
||||||
|
var salt string
|
||||||
|
|
||||||
|
if value, exists := config["length"]; exists {
|
||||||
|
switch typedValue := value.(type) {
|
||||||
|
case int:
|
||||||
|
length = typedValue
|
||||||
|
|
||||||
|
case int64:
|
||||||
|
length = int(typedValue)
|
||||||
|
|
||||||
|
case float64:
|
||||||
|
length = int(typedValue)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if value, exists := config["charset"]; exists {
|
||||||
|
typedValue, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
charset = typedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if value, exists := config["salt"]; exists {
|
||||||
|
typedValue, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
salt = typedValue
|
||||||
|
}
|
||||||
|
|
||||||
|
if length <= 0 {
|
||||||
|
return "", 0, "", "", ObrimMarkerCodeFailedValidation
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(charset) == "" {
|
||||||
|
return "", 0, "", "", ObrimMarkerCodeFailedValidation
|
||||||
|
}
|
||||||
|
|
||||||
|
return data, length, charset, salt, ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerEncodedGenerate produces the final encoded marker value.
|
||||||
|
func obrimMarkerEncodedGenerate(
|
||||||
|
data string,
|
||||||
|
length int,
|
||||||
|
charset string,
|
||||||
|
salt string,
|
||||||
|
) (string, error) {
|
||||||
|
var hash = sha256.Sum256([]byte(data + salt))
|
||||||
|
var output = make([]byte, length)
|
||||||
|
|
||||||
|
for index := 0; index < length; index++ {
|
||||||
|
output[index] = charset[int(hash[index%len(hash)])%len(charset)]
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerLog logs utility lifecycle events.
|
||||||
|
func obrimMarkerLog(event string) {
|
||||||
|
_ = event
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimMarkerError formats internal errors.
|
||||||
|
func obrimMarkerError(message string) error {
|
||||||
|
return fmt.Errorf("%s", message)
|
||||||
|
}
|
||||||
@ -0,0 +1,460 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Progress
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Manage, monitor, and report task progress through standardized
|
||||||
|
| lifecycle-aware progress tracking.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Use "countable" type when progress can be measured using current
|
||||||
|
| and target numeric values.
|
||||||
|
| - Use "uncountable" type when progress is activity-based and cannot
|
||||||
|
| be represented by a completion percentage.
|
||||||
|
| - Always provide a valid lifecycle state before executing the utility.
|
||||||
|
| - Provide "current" and "target" values for countable progress
|
||||||
|
| tracking operations.
|
||||||
|
| - Provide a non-empty "placeholder" value for uncountable progress
|
||||||
|
| tracking operations.
|
||||||
|
| - Use returned payload data for CLI rendering, status reporting,
|
||||||
|
| and workflow monitoring.
|
||||||
|
| - Check the returned status and code before consuming payload data.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimProgress(
|
||||||
|
| "countable",
|
||||||
|
| map[string]any{
|
||||||
|
| "state": "running",
|
||||||
|
| "current": 45,
|
||||||
|
| "target": 100,
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - ObrimProgress(
|
||||||
|
| "uncountable",
|
||||||
|
| map[string]any{
|
||||||
|
| "state": "running",
|
||||||
|
| "placeholder": "Processing records...",
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| - result := ObrimProgress(
|
||||||
|
| "countable",
|
||||||
|
| map[string]any{
|
||||||
|
| "state": "completed",
|
||||||
|
| "current": 100,
|
||||||
|
| "target": 100,
|
||||||
|
| },
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package progress
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimProgressTypeCountable = "countable"
|
||||||
|
obrimProgressTypeUncountable = "uncountable"
|
||||||
|
|
||||||
|
obrimProgressStateStarted = "started"
|
||||||
|
obrimProgressStateRunning = "running"
|
||||||
|
obrimProgressStateCompleted = "completed"
|
||||||
|
obrimProgressStateCanceled = "canceled"
|
||||||
|
|
||||||
|
ObrimProgressCodeSuccessStarted = "SUCCESS_PROGRESS_STARTED"
|
||||||
|
ObrimProgressCodeSuccessRunning = "SUCCESS_PROGRESS_RUNNING"
|
||||||
|
ObrimProgressCodeSuccessCompleted = "SUCCESS_PROGRESS_COMPLETED"
|
||||||
|
ObrimProgressCodeSuccessCanceled = "SUCCESS_PROGRESS_CANCELED"
|
||||||
|
|
||||||
|
ObrimProgressCodeFailedMisconfigured = "FAILED_PROGRESS_MISCONFIGURED"
|
||||||
|
|
||||||
|
obrimProgressVisualLength = 20
|
||||||
|
)
|
||||||
|
|
||||||
|
// obrimProgressStateMutex prevents state corruption during rapid updates.
|
||||||
|
var obrimProgressStateMutex sync.Mutex
|
||||||
|
|
||||||
|
// ObrimProgressCountablePayload defines countable progress payload data.
|
||||||
|
type ObrimProgressCountablePayload struct {
|
||||||
|
State string `json:"state"`
|
||||||
|
Current int `json:"current"`
|
||||||
|
Target int `json:"target"`
|
||||||
|
Percentage int `json:"percentage"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Visual string `json:"visual"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimProgressUncountablePayload defines uncountable progress payload data.
|
||||||
|
type ObrimProgressUncountablePayload struct {
|
||||||
|
State string `json:"state"`
|
||||||
|
Placeholder string `json:"placeholder"`
|
||||||
|
Message string `json:"message"`
|
||||||
|
Visual string `json:"visual"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimProgress manages lifecycle-aware countable and uncountable progress tracking operations.
|
||||||
|
func ObrimProgress(progressType string, config map[string]any) map[string]any {
|
||||||
|
obrimProgressStateMutex.Lock()
|
||||||
|
defer obrimProgressStateMutex.Unlock()
|
||||||
|
|
||||||
|
if !obrimProgressValidate(progressType, config) {
|
||||||
|
return obrimProgressBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimProgressCodeFailedMisconfigured,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
code := obrimProgressHandleState(config["state"].(string))
|
||||||
|
|
||||||
|
switch progressType {
|
||||||
|
case obrimProgressTypeCountable:
|
||||||
|
return obrimProgressCountable(config, code)
|
||||||
|
|
||||||
|
case obrimProgressTypeUncountable:
|
||||||
|
return obrimProgressUncountable(config, code)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimProgressBuildResponse(
|
||||||
|
false,
|
||||||
|
ObrimProgressCodeFailedMisconfigured,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressValidate validates input parameters and configuration consistency.
|
||||||
|
func obrimProgressValidate(
|
||||||
|
progressType string,
|
||||||
|
config map[string]any,
|
||||||
|
) bool {
|
||||||
|
if config == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
stateValue, exists := config["state"]
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
state, ok := stateValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case obrimProgressStateStarted,
|
||||||
|
obrimProgressStateRunning,
|
||||||
|
obrimProgressStateCompleted,
|
||||||
|
obrimProgressStateCanceled:
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
switch progressType {
|
||||||
|
case obrimProgressTypeCountable:
|
||||||
|
currentValue, currentExists := config["current"]
|
||||||
|
targetValue, targetExists := config["target"]
|
||||||
|
|
||||||
|
if !currentExists || !targetExists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
current, currentOk := currentValue.(int)
|
||||||
|
target, targetOk := targetValue.(int)
|
||||||
|
|
||||||
|
if !currentOk || !targetOk {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if target <= 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = current
|
||||||
|
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimProgressTypeUncountable:
|
||||||
|
placeholderValue, exists := config["placeholder"]
|
||||||
|
if !exists {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholder, ok := placeholderValue.(string)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(placeholder) != ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressHandleState processes lifecycle state transitions.
|
||||||
|
func obrimProgressHandleState(state string) string {
|
||||||
|
switch state {
|
||||||
|
case obrimProgressStateStarted:
|
||||||
|
return ObrimProgressCodeSuccessStarted
|
||||||
|
|
||||||
|
case obrimProgressStateRunning:
|
||||||
|
return ObrimProgressCodeSuccessRunning
|
||||||
|
|
||||||
|
case obrimProgressStateCompleted:
|
||||||
|
return ObrimProgressCodeSuccessCompleted
|
||||||
|
|
||||||
|
case obrimProgressStateCanceled:
|
||||||
|
return ObrimProgressCodeSuccessCanceled
|
||||||
|
}
|
||||||
|
|
||||||
|
return ObrimProgressCodeFailedMisconfigured
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressGenerateMessage generates human-readable progress messages.
|
||||||
|
func obrimProgressGenerateMessage(
|
||||||
|
progressType string,
|
||||||
|
state string,
|
||||||
|
payload map[string]any,
|
||||||
|
) string {
|
||||||
|
switch progressType {
|
||||||
|
case obrimProgressTypeCountable:
|
||||||
|
percentage := payload["percentage"].(int)
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case obrimProgressStateStarted:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Progress started (%d%% complete).",
|
||||||
|
percentage,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimProgressStateRunning:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Progress running (%d%% complete).",
|
||||||
|
percentage,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimProgressStateCompleted:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Progress completed (%d%% complete).",
|
||||||
|
percentage,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimProgressStateCanceled:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Progress canceled (%d%% complete).",
|
||||||
|
percentage,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
case obrimProgressTypeUncountable:
|
||||||
|
placeholder := payload["placeholder"].(string)
|
||||||
|
|
||||||
|
switch state {
|
||||||
|
case obrimProgressStateStarted:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Activity started: %s",
|
||||||
|
placeholder,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimProgressStateRunning:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Activity running: %s",
|
||||||
|
placeholder,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimProgressStateCompleted:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Activity completed: %s",
|
||||||
|
placeholder,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimProgressStateCanceled:
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"Activity canceled: %s",
|
||||||
|
placeholder,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressGenerateVisual generates CLI-friendly visual progress representations.
|
||||||
|
func obrimProgressGenerateVisual(
|
||||||
|
progressType string,
|
||||||
|
payload map[string]any,
|
||||||
|
) string {
|
||||||
|
switch progressType {
|
||||||
|
case obrimProgressTypeCountable:
|
||||||
|
percentage := payload["percentage"].(int)
|
||||||
|
|
||||||
|
filled := int(math.Round(
|
||||||
|
(float64(percentage) / 100.0) * float64(obrimProgressVisualLength),
|
||||||
|
))
|
||||||
|
|
||||||
|
if filled > obrimProgressVisualLength {
|
||||||
|
filled = obrimProgressVisualLength
|
||||||
|
}
|
||||||
|
|
||||||
|
if filled < 0 {
|
||||||
|
filled = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf(
|
||||||
|
"[%s%s] %d%%",
|
||||||
|
strings.Repeat("=", filled),
|
||||||
|
strings.Repeat(" ", obrimProgressVisualLength-filled),
|
||||||
|
percentage,
|
||||||
|
)
|
||||||
|
|
||||||
|
case obrimProgressTypeUncountable:
|
||||||
|
return "[....................]"
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressBuildResponse constructs standardized utility response payloads.
|
||||||
|
func obrimProgressBuildResponse(
|
||||||
|
status bool,
|
||||||
|
code string,
|
||||||
|
payload any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": status,
|
||||||
|
"code": code,
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressCountable processes countable progress tracking workflows.
|
||||||
|
func obrimProgressCountable(
|
||||||
|
config map[string]any,
|
||||||
|
code string,
|
||||||
|
) map[string]any {
|
||||||
|
current := config["current"].(int)
|
||||||
|
target := config["target"].(int)
|
||||||
|
state := config["state"].(string)
|
||||||
|
|
||||||
|
percentage := obrimProgressCalculatePercentage(
|
||||||
|
current,
|
||||||
|
target,
|
||||||
|
)
|
||||||
|
|
||||||
|
payloadData := map[string]any{
|
||||||
|
"percentage": percentage,
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := ObrimProgressCountablePayload{
|
||||||
|
State: state,
|
||||||
|
Current: current,
|
||||||
|
Target: target,
|
||||||
|
Percentage: percentage,
|
||||||
|
Message: obrimProgressGenerateMessage(
|
||||||
|
obrimProgressTypeCountable,
|
||||||
|
state,
|
||||||
|
payloadData,
|
||||||
|
),
|
||||||
|
Visual: obrimProgressGenerateVisual(
|
||||||
|
obrimProgressTypeCountable,
|
||||||
|
payloadData,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimProgressBuildResponse(
|
||||||
|
true,
|
||||||
|
code,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressCalculatePercentage calculates and normalizes completion percentages.
|
||||||
|
func obrimProgressCalculatePercentage(
|
||||||
|
current int,
|
||||||
|
target int,
|
||||||
|
) int {
|
||||||
|
if current < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
percentage := int(math.Round(
|
||||||
|
(float64(current) / float64(target)) * 100,
|
||||||
|
))
|
||||||
|
|
||||||
|
if percentage > 100 {
|
||||||
|
return 100
|
||||||
|
}
|
||||||
|
|
||||||
|
if percentage < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return percentage
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressUncountable processes uncountable progress tracking workflows.
|
||||||
|
func obrimProgressUncountable(
|
||||||
|
config map[string]any,
|
||||||
|
code string,
|
||||||
|
) map[string]any {
|
||||||
|
state := config["state"].(string)
|
||||||
|
|
||||||
|
placeholder := obrimProgressResolvePlaceholder(
|
||||||
|
config["placeholder"].(string),
|
||||||
|
)
|
||||||
|
|
||||||
|
payloadData := map[string]any{
|
||||||
|
"placeholder": placeholder,
|
||||||
|
}
|
||||||
|
|
||||||
|
payload := ObrimProgressUncountablePayload{
|
||||||
|
State: state,
|
||||||
|
Placeholder: placeholder,
|
||||||
|
Message: obrimProgressGenerateMessage(
|
||||||
|
obrimProgressTypeUncountable,
|
||||||
|
state,
|
||||||
|
payloadData,
|
||||||
|
),
|
||||||
|
Visual: obrimProgressGenerateVisual(
|
||||||
|
obrimProgressTypeUncountable,
|
||||||
|
payloadData,
|
||||||
|
),
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimProgressBuildResponse(
|
||||||
|
true,
|
||||||
|
code,
|
||||||
|
payload,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimProgressResolvePlaceholder validates and prepares placeholder activity messages.
|
||||||
|
func obrimProgressResolvePlaceholder(
|
||||||
|
placeholder string,
|
||||||
|
) string {
|
||||||
|
return strings.TrimSpace(placeholder)
|
||||||
|
}
|
||||||
@ -0,0 +1,491 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Retriever
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Retrieve data from supported resource providers through a unified
|
||||||
|
| retrieval interface while abstracting provider-specific
|
||||||
|
| implementation details from consumers.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
|
|
||||||
|
| ObrimRetriever()
|
||||||
|
| - Use for unified resource retrieval operations.
|
||||||
|
| - Select a supported retrieval type before execution.
|
||||||
|
| - Provide configuration according to the selected retrieval type.
|
||||||
|
| - Use resource retrieval for complete resource payloads.
|
||||||
|
| - Use path retrieval for individual nested values.
|
||||||
|
| - Use field retrieval for multiple selected values.
|
||||||
|
| - Do not provide both path and fields in the same request.
|
||||||
|
|
|
||||||
|
| ObrimRetrieverJsonRegister()
|
||||||
|
| - Register JSON providers before retrieval operations.
|
||||||
|
| - Use unique resource identifiers for provider registration.
|
||||||
|
| - Register one provider per resource identifier.
|
||||||
|
|
|
||||||
|
| ObrimRetrieverDbRegister()
|
||||||
|
| - Register database providers using unique resource identifiers.
|
||||||
|
| - Reserve registration for future database retrieval support.
|
||||||
|
| - Maintain resource-to-provider mappings through registration.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
|
|
||||||
|
| ObrimRetriever()
|
||||||
|
| - ObrimRetriever("json", map[string]any{
|
||||||
|
| "resource": "metadata",
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
| - ObrimRetriever("json", map[string]any{
|
||||||
|
| "resource": "metadata",
|
||||||
|
| "path": "software.name",
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
| - ObrimRetriever("json", map[string]any{
|
||||||
|
| "resource": "metadata",
|
||||||
|
| "fields": []string{
|
||||||
|
| "software.name",
|
||||||
|
| "software.version",
|
||||||
|
| },
|
||||||
|
| })
|
||||||
|
|
|
||||||
|
| ObrimRetrieverJsonRegister()
|
||||||
|
| - ObrimRetrieverJsonRegister(
|
||||||
|
| "metadata",
|
||||||
|
| metadataProvider,
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
| ObrimRetrieverDbRegister()
|
||||||
|
| - ObrimRetrieverDbRegister(
|
||||||
|
| "database",
|
||||||
|
| databaseProvider,
|
||||||
|
| )
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package retriever
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimRetrieverTypeJson = "json"
|
||||||
|
obrimRetrieverTypeDb = "db"
|
||||||
|
|
||||||
|
obrimRetrieverCodeSuccessResource = "SUCCESS_RESOURCE_RETRIEVED"
|
||||||
|
obrimRetrieverCodeSuccessPath = "SUCCESS_PATH_RETRIEVED"
|
||||||
|
obrimRetrieverCodeSuccessFields = "SUCCESS_FIELDS_RETRIEVED"
|
||||||
|
|
||||||
|
obrimRetrieverCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||||
|
obrimRetrieverCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
|
||||||
|
obrimRetrieverCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||||
|
obrimRetrieverCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||||
|
obrimRetrieverCodeFailedPathNotFound = "FAILED_PATH_NOT_FOUND"
|
||||||
|
obrimRetrieverCodeFailedFieldNotFound = "FAILED_FIELD_NOT_FOUND"
|
||||||
|
obrimRetrieverCodeFailedNotImplemented = "FAILED_NOT_IMPLEMENTED"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ObrimRetrieverJsonProviderRegistry stores registered JSON providers.
|
||||||
|
var ObrimRetrieverJsonProviderRegistry = map[string]any{}
|
||||||
|
|
||||||
|
// ObrimRetrieverDbProviderRegistry stores registered database providers.
|
||||||
|
var ObrimRetrieverDbProviderRegistry = map[string]any{}
|
||||||
|
|
||||||
|
// ObrimRetrieverResponse defines the standardized utility response structure.
|
||||||
|
type ObrimRetrieverResponse struct {
|
||||||
|
Status bool `json:"status"`
|
||||||
|
Code string `json:"code"`
|
||||||
|
Payload map[string]any `json:"payload"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimRetriever retrieves data using a unified retrieval interface.
|
||||||
|
func ObrimRetriever(retrievalType string, config map[string]any) map[string]any {
|
||||||
|
if !obrimRetrieverValidateType(retrievalType) {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedUnsupportedType,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
validationCode := obrimRetrieverValidateConfig(retrievalType, config)
|
||||||
|
|
||||||
|
if validationCode != "" {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
validationCode,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
switch retrievalType {
|
||||||
|
case obrimRetrieverTypeJson:
|
||||||
|
return obrimRetrieverJson(config)
|
||||||
|
|
||||||
|
case obrimRetrieverTypeDb:
|
||||||
|
return obrimRetrieverDb(config)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedUnsupportedType,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimRetrieverJsonRegister registers a JSON provider.
|
||||||
|
func ObrimRetrieverJsonRegister(resource string, provider any) {
|
||||||
|
if strings.TrimSpace(resource) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ObrimRetrieverJsonProviderRegistry[resource] = provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimRetrieverDbRegister registers a database provider.
|
||||||
|
func ObrimRetrieverDbRegister(resource string, provider any) {
|
||||||
|
if strings.TrimSpace(resource) == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
ObrimRetrieverDbProviderRegistry[resource] = provider
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverValidateType validates a retrieval type.
|
||||||
|
func obrimRetrieverValidateType(retrievalType string) bool {
|
||||||
|
switch retrievalType {
|
||||||
|
case obrimRetrieverTypeJson:
|
||||||
|
return true
|
||||||
|
|
||||||
|
case obrimRetrieverTypeDb:
|
||||||
|
return true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverValidateConfig validates retrieval configuration.
|
||||||
|
func obrimRetrieverValidateConfig(
|
||||||
|
retrievalType string,
|
||||||
|
config map[string]any,
|
||||||
|
) string {
|
||||||
|
if config == nil {
|
||||||
|
return obrimRetrieverCodeFailedMissingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
switch retrievalType {
|
||||||
|
case obrimRetrieverTypeJson:
|
||||||
|
return obrimRetrieverJsonValidate(config)
|
||||||
|
|
||||||
|
case obrimRetrieverTypeDb:
|
||||||
|
return obrimRetrieverDbValidate(config)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimRetrieverCodeFailedUnsupportedType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverResolveProvider resolves a provider by resource identifier.
|
||||||
|
func obrimRetrieverResolveProvider(
|
||||||
|
retrievalType string,
|
||||||
|
resource string,
|
||||||
|
) (any, bool) {
|
||||||
|
switch retrievalType {
|
||||||
|
case obrimRetrieverTypeJson:
|
||||||
|
provider, exists := ObrimRetrieverJsonProviderRegistry[resource]
|
||||||
|
return provider, exists
|
||||||
|
|
||||||
|
case obrimRetrieverTypeDb:
|
||||||
|
provider, exists := ObrimRetrieverDbProviderRegistry[resource]
|
||||||
|
return provider, exists
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverBuildResponse builds a standardized utility response.
|
||||||
|
func obrimRetrieverBuildResponse(
|
||||||
|
status bool,
|
||||||
|
code string,
|
||||||
|
payload map[string]any,
|
||||||
|
) map[string]any {
|
||||||
|
return map[string]any{
|
||||||
|
"status": status,
|
||||||
|
"code": code,
|
||||||
|
"payload": payload,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJson processes JSON retrieval requests.
|
||||||
|
func obrimRetrieverJson(config map[string]any) map[string]any {
|
||||||
|
resource := config["resource"].(string)
|
||||||
|
|
||||||
|
provider, exists := obrimRetrieverResolveProvider(
|
||||||
|
obrimRetrieverTypeJson,
|
||||||
|
resource,
|
||||||
|
)
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedResourceNotFound,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fields, exists := config["fields"]; exists {
|
||||||
|
return obrimRetrieverJsonFields(provider, fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
if path, exists := config["path"]; exists {
|
||||||
|
return obrimRetrieverJsonPath(provider, path.(string))
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimRetrieverJsonResource(provider)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJsonResource retrieves an entire resource.
|
||||||
|
func obrimRetrieverJsonResource(provider any) map[string]any {
|
||||||
|
data, success := obrimRetrieverJsonRetrieve(provider)
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedResourceNotFound,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
true,
|
||||||
|
obrimRetrieverCodeSuccessResource,
|
||||||
|
map[string]any{
|
||||||
|
"data": data,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJsonPath retrieves a value using a dot-notation path.
|
||||||
|
func obrimRetrieverJsonPath(provider any, path string) map[string]any {
|
||||||
|
data, success := obrimRetrieverJsonRetrieve(provider)
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedResourceNotFound,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
value, found := obrimRetrieverJsonResolvePath(data, path)
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedPathNotFound,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
true,
|
||||||
|
obrimRetrieverCodeSuccessPath,
|
||||||
|
map[string]any{
|
||||||
|
"data": value,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJsonFields retrieves multiple values using field selection.
|
||||||
|
func obrimRetrieverJsonFields(provider any, fieldsValue any) map[string]any {
|
||||||
|
data, success := obrimRetrieverJsonRetrieve(provider)
|
||||||
|
|
||||||
|
if !success {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedResourceNotFound,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fields, valid := obrimRetrieverJsonNormalizeFields(fieldsValue)
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedInvalidConfig,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
result := map[string]any{}
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
value, found := obrimRetrieverJsonResolvePath(data, field)
|
||||||
|
|
||||||
|
if !found {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedFieldNotFound,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
result[field] = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
true,
|
||||||
|
obrimRetrieverCodeSuccessFields,
|
||||||
|
map[string]any{
|
||||||
|
"data": result,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJsonValidate validates JSON retrieval configuration.
|
||||||
|
func obrimRetrieverJsonValidate(config map[string]any) string {
|
||||||
|
resourceValue, exists := config["resource"]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return obrimRetrieverCodeFailedMissingConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
resource, valid := resourceValue.(string)
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
return obrimRetrieverCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.TrimSpace(resource) == "" {
|
||||||
|
return obrimRetrieverCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
_, pathExists := config["path"]
|
||||||
|
_, fieldsExists := config["fields"]
|
||||||
|
|
||||||
|
if pathExists && fieldsExists {
|
||||||
|
return obrimRetrieverCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
if pathExists {
|
||||||
|
path, valid := config["path"].(string)
|
||||||
|
|
||||||
|
if !valid || strings.TrimSpace(path) == "" {
|
||||||
|
return obrimRetrieverCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fieldsExists {
|
||||||
|
fields, valid := obrimRetrieverJsonNormalizeFields(config["fields"])
|
||||||
|
|
||||||
|
if !valid || len(fields) == 0 {
|
||||||
|
return obrimRetrieverCodeFailedInvalidConfig
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverDb processes database retrieval requests.
|
||||||
|
func obrimRetrieverDb(config map[string]any) map[string]any {
|
||||||
|
return obrimRetrieverBuildResponse(
|
||||||
|
false,
|
||||||
|
obrimRetrieverCodeFailedNotImplemented,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverDbValidate validates database retrieval configuration.
|
||||||
|
func obrimRetrieverDbValidate(config map[string]any) string {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJsonRetrieve retrieves resource data from a provider.
|
||||||
|
func obrimRetrieverJsonRetrieve(provider any) (any, bool) {
|
||||||
|
switch retrievalProvider := provider.(type) {
|
||||||
|
case func() any:
|
||||||
|
return retrievalProvider(), true
|
||||||
|
|
||||||
|
case func() map[string]any:
|
||||||
|
return retrievalProvider(), true
|
||||||
|
|
||||||
|
case func() (any, bool):
|
||||||
|
return retrievalProvider()
|
||||||
|
|
||||||
|
case map[string]any:
|
||||||
|
return retrievalProvider, true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJsonResolvePath resolves a dot-notation path.
|
||||||
|
func obrimRetrieverJsonResolvePath(data any, path string) (any, bool) {
|
||||||
|
current := data
|
||||||
|
|
||||||
|
for _, segment := range strings.Split(path, ".") {
|
||||||
|
object, valid := current.(map[string]any)
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
value, exists := object[segment]
|
||||||
|
|
||||||
|
if !exists {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
current = value
|
||||||
|
}
|
||||||
|
|
||||||
|
return current, true
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimRetrieverJsonNormalizeFields normalizes field selections.
|
||||||
|
func obrimRetrieverJsonNormalizeFields(fieldsValue any) ([]string, bool) {
|
||||||
|
switch fields := fieldsValue.(type) {
|
||||||
|
case []string:
|
||||||
|
return fields, true
|
||||||
|
|
||||||
|
case []any:
|
||||||
|
result := make([]string, 0, len(fields))
|
||||||
|
|
||||||
|
for _, field := range fields {
|
||||||
|
fieldString, valid := field.(string)
|
||||||
|
|
||||||
|
if !valid {
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
|
||||||
|
result = append(result, fieldString)
|
||||||
|
}
|
||||||
|
|
||||||
|
return result, true
|
||||||
|
|
||||||
|
default:
|
||||||
|
return nil, false
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,124 @@
|
|||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Metadata
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Name:
|
||||||
|
| - Status
|
||||||
|
|
|
||||||
|
| Purpose:
|
||||||
|
| - Provide a framework-level CLI status utility that standardizes
|
||||||
|
| terminal message presentation using semantic labels and visual
|
||||||
|
| indicators to ensure consistent output across all softwares.
|
||||||
|
|
|
||||||
|
| Guideline:
|
||||||
|
| - Call ObrimStatus() whenever a standardized terminal status message
|
||||||
|
| must be emitted.
|
||||||
|
| - Use only supported semantic labels: INFO, WARNING, SUCCESS, ERROR.
|
||||||
|
| - Invalid, unsupported, or empty labels are automatically normalized
|
||||||
|
| to INFO.
|
||||||
|
| - Status operates through terminal output side-effects and does not
|
||||||
|
| return any value.
|
||||||
|
|
|
||||||
|
| Example:
|
||||||
|
| - ObrimStatus("INFO", "Application started.")
|
||||||
|
| - ObrimStatus("SUCCESS", "Configuration loaded.")
|
||||||
|
| - ObrimStatus("WARNING", "Configuration file not found.")
|
||||||
|
| - ObrimStatus("ERROR", "Unable to establish connection.")
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
| Credit
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
| Contributor:
|
||||||
|
| - Rajon Ahmed
|
||||||
|
| - Blockonite
|
||||||
|
|
|
||||||
|
|--------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
package status
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
obrimStatusLabelInfo = "INFO"
|
||||||
|
obrimStatusLabelWarning = "WARNING"
|
||||||
|
obrimStatusLabelSuccess = "SUCCESS"
|
||||||
|
obrimStatusLabelError = "ERROR"
|
||||||
|
)
|
||||||
|
|
||||||
|
// obrimStatusIndicatorRegistry stores status indicators.
|
||||||
|
var obrimStatusIndicatorRegistry = map[string]string{
|
||||||
|
obrimStatusLabelInfo: "ℹ",
|
||||||
|
obrimStatusLabelWarning: "⚠",
|
||||||
|
obrimStatusLabelSuccess: "✓",
|
||||||
|
obrimStatusLabelError: "✖",
|
||||||
|
}
|
||||||
|
|
||||||
|
// ObrimStatus emits a standardized CLI status message.
|
||||||
|
func ObrimStatus(label string, message string) {
|
||||||
|
normalizedLabel := obrimStatusNormalizeLabel(label)
|
||||||
|
formattedLabel := obrimStatusFormatLabel(normalizedLabel)
|
||||||
|
formattedIndicator := obrimStatusFormatIndicator(normalizedLabel)
|
||||||
|
formattedMessage := obrimStatusBuildMessage(
|
||||||
|
formattedLabel,
|
||||||
|
formattedIndicator,
|
||||||
|
message,
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println(formattedMessage)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimStatusNormalizeLabel normalizes and validates labels.
|
||||||
|
func obrimStatusNormalizeLabel(label string) string {
|
||||||
|
normalizedLabel := strings.ToUpper(strings.TrimSpace(label))
|
||||||
|
|
||||||
|
switch normalizedLabel {
|
||||||
|
case obrimStatusLabelInfo:
|
||||||
|
return obrimStatusLabelInfo
|
||||||
|
|
||||||
|
case obrimStatusLabelWarning:
|
||||||
|
return obrimStatusLabelWarning
|
||||||
|
|
||||||
|
case obrimStatusLabelSuccess:
|
||||||
|
return obrimStatusLabelSuccess
|
||||||
|
|
||||||
|
case obrimStatusLabelError:
|
||||||
|
return obrimStatusLabelError
|
||||||
|
|
||||||
|
default:
|
||||||
|
return obrimStatusLabelInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimStatusFormatLabel formats semantic labels.
|
||||||
|
func obrimStatusFormatLabel(label string) string {
|
||||||
|
return fmt.Sprintf("[%s]", label)
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimStatusFormatIndicator generates visual status indicators.
|
||||||
|
func obrimStatusFormatIndicator(label string) string {
|
||||||
|
indicator, exists := obrimStatusIndicatorRegistry[label]
|
||||||
|
if !exists {
|
||||||
|
return obrimStatusIndicatorRegistry[obrimStatusLabelInfo]
|
||||||
|
}
|
||||||
|
|
||||||
|
return indicator
|
||||||
|
}
|
||||||
|
|
||||||
|
// obrimStatusBuildMessage constructs the final terminal message.
|
||||||
|
func obrimStatusBuildMessage(
|
||||||
|
label string,
|
||||||
|
indicator string,
|
||||||
|
message string,
|
||||||
|
) string {
|
||||||
|
return fmt.Sprintf("%s %s %s", indicator, label, message)
|
||||||
|
}
|
||||||
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
|||||||
module go/module
|
module go/module
|
||||||
|
|
||||||
go 1.26.4
|
go 1.26.4
|
||||||
|
|
||||||
|
require github.com/sqids/sqids-go v0.4.1
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user