upd: helper utility updated
This commit is contained in:
parent
690fe2f603
commit
9de05ac027
@ -0,0 +1,430 @@
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimCipherAlgorithm = "AES-256"
|
||||
obrimCipherSignature = "OBRIMCIPHER"
|
||||
obrimCipherVersion = "1"
|
||||
obrimCipherSaltSize = 32
|
||||
obrimCipherNonceSize = 12
|
||||
)
|
||||
|
||||
type ObrimCipherConfig struct {
|
||||
Key string `json:"key"`
|
||||
Input string `json:"input"`
|
||||
Output string `json:"output"`
|
||||
}
|
||||
|
||||
type obrimCipherResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
type obrimCipherStandardPayload struct {
|
||||
Operation string `json:"operation"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
InputPath string `json:"inputPath"`
|
||||
OutputPath string `json:"outputPath"`
|
||||
}
|
||||
|
||||
type obrimCipherSaltedPayload struct {
|
||||
Operation string `json:"operation"`
|
||||
Algorithm string `json:"algorithm"`
|
||||
Format string `json:"format"`
|
||||
Version string `json:"version"`
|
||||
InputPath string `json:"inputPath"`
|
||||
OutputPath string `json:"outputPath"`
|
||||
}
|
||||
|
||||
// ObrimCipher executes encryption or decryption workflows.
|
||||
func ObrimCipher(operationType string, config ObrimCipherConfig) ([]byte, error) {
|
||||
if err := obrimCipherValidateType(operationType); err != nil {
|
||||
return obrimCipherBuildResponse(false, "FAILED_INVALID_TYPE", nil)
|
||||
}
|
||||
|
||||
if err := obrimCipherValidateConfig(config); err != nil {
|
||||
return obrimCipherBuildResponse(false, "FAILED_INVALID_CONFIG", nil)
|
||||
}
|
||||
|
||||
if err := obrimCipherValidateKey(config.Key); err != nil {
|
||||
return obrimCipherBuildResponse(false, "FAILED_INVALID_KEY", nil)
|
||||
}
|
||||
|
||||
inputPath, err := obrimCipherResolvePath(config.Input)
|
||||
if err != nil {
|
||||
return obrimCipherBuildResponse(false, "FAILED_INVALID_INPUT_PATH", nil)
|
||||
}
|
||||
|
||||
outputPath, err := obrimCipherResolvePath(config.Output)
|
||||
if err != nil {
|
||||
return obrimCipherBuildResponse(false, "FAILED_INVALID_OUTPUT_PATH", nil)
|
||||
}
|
||||
|
||||
inputData, err := obrimCipherReadInput(inputPath)
|
||||
if err != nil {
|
||||
return obrimCipherBuildResponse(false, "FAILED_INPUT_ACCESS", nil)
|
||||
}
|
||||
|
||||
var outputData []byte
|
||||
|
||||
switch operationType {
|
||||
case "encrypt-standard":
|
||||
outputData, err = obrimCipherEncryptStandard(inputData, config.Key)
|
||||
|
||||
case "encrypt-salted":
|
||||
outputData, err = obrimCipherEncryptSalted(inputData, config.Key)
|
||||
|
||||
case "decrypt-standard":
|
||||
outputData, err = obrimCipherDecryptStandard(inputData, config.Key)
|
||||
|
||||
case "decrypt-salted":
|
||||
outputData, err = obrimCipherDecryptSalted(inputData, config.Key)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
switch operationType {
|
||||
case "encrypt-standard", "encrypt-salted":
|
||||
return obrimCipherBuildResponse(false, "FAILED_ENCRYPTION", nil)
|
||||
case "decrypt-standard", "decrypt-salted":
|
||||
return obrimCipherBuildResponse(false, "FAILED_DECRYPTION", nil)
|
||||
}
|
||||
}
|
||||
|
||||
if err := obrimCipherWriteOutput(outputPath, outputData); err != nil {
|
||||
return obrimCipherBuildResponse(false, "FAILED_OUTPUT_WRITE", nil)
|
||||
}
|
||||
|
||||
switch operationType {
|
||||
case "encrypt-standard":
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
"SUCCESS_ENCRYPT_STANDARD",
|
||||
obrimCipherStandardPayload{
|
||||
Operation: operationType,
|
||||
Algorithm: obrimCipherAlgorithm,
|
||||
InputPath: inputPath,
|
||||
OutputPath: outputPath,
|
||||
},
|
||||
)
|
||||
|
||||
case "decrypt-standard":
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
"SUCCESS_DECRYPT_STANDARD",
|
||||
obrimCipherStandardPayload{
|
||||
Operation: operationType,
|
||||
Algorithm: obrimCipherAlgorithm,
|
||||
InputPath: inputPath,
|
||||
OutputPath: outputPath,
|
||||
},
|
||||
)
|
||||
|
||||
case "encrypt-salted":
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
"SUCCESS_ENCRYPT_SALTED",
|
||||
obrimCipherSaltedPayload{
|
||||
Operation: operationType,
|
||||
Algorithm: obrimCipherAlgorithm,
|
||||
Format: obrimCipherSignature,
|
||||
Version: obrimCipherVersion,
|
||||
InputPath: inputPath,
|
||||
OutputPath: outputPath,
|
||||
},
|
||||
)
|
||||
|
||||
case "decrypt-salted":
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
"SUCCESS_DECRYPT_SALTED",
|
||||
obrimCipherSaltedPayload{
|
||||
Operation: operationType,
|
||||
Algorithm: obrimCipherAlgorithm,
|
||||
Format: obrimCipherSignature,
|
||||
Version: obrimCipherVersion,
|
||||
InputPath: inputPath,
|
||||
OutputPath: outputPath,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
return obrimCipherBuildResponse(false, "FAILED_UNKNOWN_OPERATION", nil)
|
||||
}
|
||||
|
||||
// obrimCipherValidateType validates operation type.
|
||||
func obrimCipherValidateType(operationType string) error {
|
||||
switch operationType {
|
||||
case "encrypt-standard",
|
||||
"encrypt-salted",
|
||||
"decrypt-standard",
|
||||
"decrypt-salted":
|
||||
return nil
|
||||
default:
|
||||
return errors.New("invalid operation type")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCipherValidateConfig validates configuration values.
|
||||
func obrimCipherValidateConfig(config ObrimCipherConfig) error {
|
||||
if config.Key == "" {
|
||||
return errors.New("missing key")
|
||||
}
|
||||
|
||||
if config.Input == "" {
|
||||
return errors.New("missing input")
|
||||
}
|
||||
|
||||
if config.Output == "" {
|
||||
return errors.New("missing output")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obrimCipherValidateKey validates AES-256 key requirements.
|
||||
func obrimCipherValidateKey(key string) error {
|
||||
if len([]byte(key)) != 32 {
|
||||
return errors.New("invalid aes-256 key")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obrimCipherResolvePath resolves paths to absolute paths.
|
||||
func obrimCipherResolvePath(path string) (string, error) {
|
||||
return filepath.Abs(path)
|
||||
}
|
||||
|
||||
// obrimCipherReadInput reads source file content.
|
||||
func obrimCipherReadInput(path string) ([]byte, error) {
|
||||
return os.ReadFile(path)
|
||||
}
|
||||
|
||||
// obrimCipherWriteOutput writes processed output.
|
||||
func obrimCipherWriteOutput(path string, data []byte) error {
|
||||
directory := filepath.Dir(path)
|
||||
|
||||
if err := os.MkdirAll(directory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tempFile, err := os.CreateTemp(directory, ".obrim-cipher-*")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tempPath := tempFile.Name()
|
||||
|
||||
defer func() {
|
||||
_ = tempFile.Close()
|
||||
}()
|
||||
|
||||
if _, err := tempFile.Write(data); err != nil {
|
||||
_ = os.Remove(tempPath)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tempFile.Sync(); err != nil {
|
||||
_ = os.Remove(tempPath)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := tempFile.Close(); err != nil {
|
||||
_ = os.Remove(tempPath)
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Rename(tempPath, path)
|
||||
}
|
||||
|
||||
// obrimCipherBuildResponse builds standardized responses.
|
||||
func obrimCipherBuildResponse(status bool, code string, payload interface{}) ([]byte, error) {
|
||||
response := obrimCipherResponse{
|
||||
Status: status,
|
||||
Code: code,
|
||||
Payload: payload,
|
||||
}
|
||||
|
||||
result, err := json.Marshal(response)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// obrimCipherEncryptStandard encrypts data without salt metadata.
|
||||
func obrimCipherEncryptStandard(data []byte, key string) ([]byte, error) {
|
||||
return obrimCipherEncryptAES(data, []byte(key))
|
||||
}
|
||||
|
||||
// obrimCipherGenerateSalt generates cryptographically secure salt.
|
||||
func obrimCipherGenerateSalt() ([]byte, error) {
|
||||
salt := make([]byte, obrimCipherSaltSize)
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, salt); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return salt, nil
|
||||
}
|
||||
|
||||
// obrimCipherBuildHeader builds encrypted file header.
|
||||
func obrimCipherBuildHeader() []byte {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(obrimCipherSignature)
|
||||
buffer.WriteByte('|')
|
||||
buffer.WriteString(obrimCipherVersion)
|
||||
buffer.WriteByte('|')
|
||||
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
// obrimCipherEncryptSalted encrypts data with embedded salt metadata.
|
||||
func obrimCipherEncryptSalted(data []byte, key string) ([]byte, error) {
|
||||
salt, err := obrimCipherGenerateSalt()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derivedKey := sha256.Sum256(append([]byte(key), salt...))
|
||||
|
||||
encrypted, err := obrimCipherEncryptAES(data, derivedKey[:])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header := obrimCipherBuildHeader()
|
||||
|
||||
result := make([]byte, 0, len(header)+len(salt)+len(encrypted))
|
||||
result = append(result, header...)
|
||||
result = append(result, salt...)
|
||||
result = append(result, encrypted...)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// obrimCipherDecryptStandard decrypts AES-256 encrypted data.
|
||||
func obrimCipherDecryptStandard(data []byte, key string) ([]byte, error) {
|
||||
return obrimCipherDecryptAES(data, []byte(key))
|
||||
}
|
||||
|
||||
// obrimCipherParseHeader parses encrypted metadata.
|
||||
func obrimCipherParseHeader(data []byte) (string, string, int, error) {
|
||||
header := []byte(obrimCipherSignature + "|" + obrimCipherVersion + "|")
|
||||
|
||||
if len(data) < len(header) {
|
||||
return "", "", 0, errors.New("invalid header")
|
||||
}
|
||||
|
||||
if !bytes.Equal(data[:len(header)], header) {
|
||||
return "", "", 0, errors.New("invalid header")
|
||||
}
|
||||
|
||||
return obrimCipherSignature, obrimCipherVersion, len(header), nil
|
||||
}
|
||||
|
||||
// obrimCipherExtractSalt extracts embedded salt.
|
||||
func obrimCipherExtractSalt(data []byte, offset int) ([]byte, error) {
|
||||
if len(data) < offset+obrimCipherSaltSize {
|
||||
return nil, errors.New("salt extraction failed")
|
||||
}
|
||||
|
||||
return data[offset : offset+obrimCipherSaltSize], nil
|
||||
}
|
||||
|
||||
// obrimCipherExtractPayload extracts encrypted payload.
|
||||
func obrimCipherExtractPayload(data []byte, offset int) ([]byte, error) {
|
||||
start := offset + obrimCipherSaltSize
|
||||
|
||||
if len(data) <= start {
|
||||
return nil, errors.New("payload extraction failed")
|
||||
}
|
||||
|
||||
return data[start:], nil
|
||||
}
|
||||
|
||||
// obrimCipherDecryptSalted decrypts AES-256 encrypted data with salt metadata.
|
||||
func obrimCipherDecryptSalted(data []byte, key string) ([]byte, error) {
|
||||
_, _, offset, err := obrimCipherParseHeader(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
salt, err := obrimCipherExtractSalt(data, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload, err := obrimCipherExtractPayload(data, offset)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
derivedKey := sha256.Sum256(append([]byte(key), salt...))
|
||||
|
||||
return obrimCipherDecryptAES(payload, derivedKey[:])
|
||||
}
|
||||
|
||||
// obrimCipherEncryptAES performs AES-256 GCM encryption.
|
||||
func obrimCipherEncryptAES(data []byte, key []byte) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := make([]byte, obrimCipherNonceSize)
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
encrypted := gcm.Seal(nil, nonce, data, nil)
|
||||
|
||||
result := make([]byte, 0, len(nonce)+len(encrypted))
|
||||
result = append(result, nonce...)
|
||||
result = append(result, encrypted...)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// obrimCipherDecryptAES performs AES-256 GCM decryption.
|
||||
func obrimCipherDecryptAES(data []byte, key []byte) ([]byte, error) {
|
||||
if len(data) < obrimCipherNonceSize {
|
||||
return nil, errors.New("invalid encrypted payload")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
nonce := data[:obrimCipherNonceSize]
|
||||
payload := data[obrimCipherNonceSize:]
|
||||
|
||||
return gcm.Open(nil, nonce, payload, nil)
|
||||
}
|
||||
@ -0,0 +1,433 @@
|
||||
package codec
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sqids/sqids-go"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimCodecOperationEncode = "encode"
|
||||
obrimCodecOperationDecode = "decode"
|
||||
|
||||
obrimCodecFormatBase8 = "base8"
|
||||
obrimCodecFormatBase10 = "base10"
|
||||
obrimCodecFormatBase16 = "base16"
|
||||
obrimCodecFormatBase32 = "base32"
|
||||
obrimCodecFormatBase64 = "base64"
|
||||
obrimCodecFormatSqids = "sqids"
|
||||
|
||||
obrimCodecCaseLower = "lower"
|
||||
obrimCodecCaseUpper = "upper"
|
||||
)
|
||||
|
||||
type obrimCodecConfiguration struct {
|
||||
Format string
|
||||
Data interface{}
|
||||
Charset string
|
||||
Salt string
|
||||
Case string
|
||||
Length int
|
||||
}
|
||||
|
||||
type obrimCodecResult struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
type obrimCodecPayload struct {
|
||||
Operation string `json:"operation"`
|
||||
Format string `json:"format"`
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// ObrimCodec validates configuration, dispatches codec operations, and returns structured results.
|
||||
func ObrimCodec(operation string, config map[string]interface{}) map[string]interface{} {
|
||||
validatedConfiguration, validationError := obrimCodecValidation(operation, config)
|
||||
if validationError != nil {
|
||||
return map[string]interface{}{
|
||||
"status": false,
|
||||
"code": "FAILED_VALIDATION",
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
value, dispatchError := obrimCodecDispatch(operation, validatedConfiguration)
|
||||
if dispatchError != nil {
|
||||
return map[string]interface{}{
|
||||
"status": false,
|
||||
"code": "FAILED_OPERATION",
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"status": true,
|
||||
"code": "SUCCESS_OPERATION",
|
||||
"payload": map[string]interface{}{
|
||||
"operation": operation,
|
||||
"format": validatedConfiguration.Format,
|
||||
"value": value,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCodecValidation validates required parameters, supported operations, formats, and parameter types.
|
||||
func obrimCodecValidation(operation string, config map[string]interface{}) (*obrimCodecConfiguration, error) {
|
||||
switch operation {
|
||||
case obrimCodecOperationEncode, obrimCodecOperationDecode:
|
||||
default:
|
||||
return nil, errors.New("unsupported operation")
|
||||
}
|
||||
|
||||
formatValue, exists := config["format"]
|
||||
if !exists {
|
||||
return nil, errors.New("missing format")
|
||||
}
|
||||
|
||||
format, ok := formatValue.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid format type")
|
||||
}
|
||||
|
||||
switch format {
|
||||
case obrimCodecFormatBase8,
|
||||
obrimCodecFormatBase10,
|
||||
obrimCodecFormatBase16,
|
||||
obrimCodecFormatBase32,
|
||||
obrimCodecFormatBase64,
|
||||
obrimCodecFormatSqids:
|
||||
default:
|
||||
return nil, errors.New("unsupported format")
|
||||
}
|
||||
|
||||
data, exists := config["data"]
|
||||
if !exists {
|
||||
return nil, errors.New("missing data")
|
||||
}
|
||||
|
||||
charsetValue, exists := config["charset"]
|
||||
if !exists {
|
||||
return nil, errors.New("missing charset")
|
||||
}
|
||||
|
||||
charset, ok := charsetValue.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid charset type")
|
||||
}
|
||||
|
||||
saltValue, exists := config["salt"]
|
||||
if !exists {
|
||||
return nil, errors.New("missing salt")
|
||||
}
|
||||
|
||||
salt, ok := saltValue.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid salt type")
|
||||
}
|
||||
|
||||
configuration := &obrimCodecConfiguration{
|
||||
Format: format,
|
||||
Data: data,
|
||||
Charset: charset,
|
||||
Salt: salt,
|
||||
}
|
||||
|
||||
if format == obrimCodecFormatSqids {
|
||||
lengthValue, exists := config["length"]
|
||||
if !exists {
|
||||
return nil, errors.New("missing length")
|
||||
}
|
||||
|
||||
switch value := lengthValue.(type) {
|
||||
case int:
|
||||
configuration.Length = value
|
||||
case float64:
|
||||
configuration.Length = int(value)
|
||||
default:
|
||||
return nil, errors.New("invalid length type")
|
||||
}
|
||||
} else {
|
||||
caseValue, exists := config["case"]
|
||||
if !exists {
|
||||
return nil, errors.New("missing case")
|
||||
}
|
||||
|
||||
caseRule, ok := caseValue.(string)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid case type")
|
||||
}
|
||||
|
||||
switch caseRule {
|
||||
case obrimCodecCaseLower, obrimCodecCaseUpper:
|
||||
default:
|
||||
return nil, errors.New("invalid case value")
|
||||
}
|
||||
|
||||
configuration.Case = caseRule
|
||||
}
|
||||
|
||||
switch data.(type) {
|
||||
case string, []byte:
|
||||
default:
|
||||
return nil, errors.New("invalid data type")
|
||||
}
|
||||
|
||||
return configuration, nil
|
||||
}
|
||||
|
||||
// obrimCodecDispatch routes validated requests to the appropriate codec implementation.
|
||||
func obrimCodecDispatch(operation string, configuration *obrimCodecConfiguration) (string, error) {
|
||||
switch operation {
|
||||
case obrimCodecOperationEncode:
|
||||
switch configuration.Format {
|
||||
case obrimCodecFormatBase8:
|
||||
return obrimCodecEncodeBase8(configuration)
|
||||
case obrimCodecFormatBase10:
|
||||
return obrimCodecEncodeBase10(configuration)
|
||||
case obrimCodecFormatBase16:
|
||||
return obrimCodecEncodeBase16(configuration)
|
||||
case obrimCodecFormatBase32:
|
||||
return obrimCodecEncodeBase32(configuration)
|
||||
case obrimCodecFormatBase64:
|
||||
return obrimCodecEncodeBase64(configuration)
|
||||
case obrimCodecFormatSqids:
|
||||
return obrimCodecEncodeSqids(configuration)
|
||||
}
|
||||
case obrimCodecOperationDecode:
|
||||
switch configuration.Format {
|
||||
case obrimCodecFormatBase8:
|
||||
return obrimCodecDecodeBase8(configuration)
|
||||
case obrimCodecFormatBase10:
|
||||
return obrimCodecDecodeBase10(configuration)
|
||||
case obrimCodecFormatBase16:
|
||||
return obrimCodecDecodeBase16(configuration)
|
||||
case obrimCodecFormatBase32:
|
||||
return obrimCodecDecodeBase32(configuration)
|
||||
case obrimCodecFormatBase64:
|
||||
return obrimCodecDecodeBase64(configuration)
|
||||
case obrimCodecFormatSqids:
|
||||
return obrimCodecDecodeSqids(configuration)
|
||||
}
|
||||
}
|
||||
|
||||
return "", errors.New("dispatch failure")
|
||||
}
|
||||
|
||||
// obrimCodecNormalizeInput converts supported input types into string form.
|
||||
func obrimCodecNormalizeInput(data interface{}) 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 {
|
||||
prefix := charset + salt
|
||||
return strings.TrimPrefix(value, prefix)
|
||||
}
|
||||
|
||||
// obrimCodecApplyCase applies output casing.
|
||||
func obrimCodecApplyCase(value string, rule string) string {
|
||||
switch rule {
|
||||
case obrimCodecCaseUpper:
|
||||
return strings.ToUpper(value)
|
||||
default:
|
||||
return strings.ToLower(value)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase8 encodes data using Base8 encoding.
|
||||
func obrimCodecEncodeBase8(configuration *obrimCodecConfiguration) (string, error) {
|
||||
input := obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(configuration.Data),
|
||||
configuration.Charset,
|
||||
configuration.Salt,
|
||||
)
|
||||
|
||||
var builder strings.Builder
|
||||
|
||||
for _, character := range []byte(input) {
|
||||
builder.WriteString(fmt.Sprintf("%03o", character))
|
||||
}
|
||||
|
||||
return obrimCodecApplyCase(builder.String(), configuration.Case), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase8 decodes Base8 encoded data.
|
||||
func obrimCodecDecodeBase8(configuration *obrimCodecConfiguration) (string, error) {
|
||||
input := strings.ToLower(obrimCodecNormalizeInput(configuration.Data))
|
||||
|
||||
if len(input)%3 != 0 {
|
||||
return "", errors.New("invalid base8 data")
|
||||
}
|
||||
|
||||
decoded := make([]byte, 0)
|
||||
|
||||
for index := 0; index < len(input); index += 3 {
|
||||
value, errorValue := strconv.ParseUint(input[index:index+3], 8, 8)
|
||||
if errorValue != nil {
|
||||
return "", errorValue
|
||||
}
|
||||
|
||||
decoded = append(decoded, byte(value))
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase10 encodes data using Base10 encoding.
|
||||
func obrimCodecEncodeBase10(configuration *obrimCodecConfiguration) (string, error) {
|
||||
input := obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(configuration.Data),
|
||||
configuration.Charset,
|
||||
configuration.Salt,
|
||||
)
|
||||
|
||||
value := new(big.Int).SetBytes([]byte(input))
|
||||
return obrimCodecApplyCase(value.Text(10), configuration.Case), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase10 decodes Base10 encoded data.
|
||||
func obrimCodecDecodeBase10(configuration *obrimCodecConfiguration) (string, error) {
|
||||
value := new(big.Int)
|
||||
|
||||
if _, success := value.SetString(obrimCodecNormalizeInput(configuration.Data), 10); !success {
|
||||
return "", errors.New("invalid base10 data")
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(string(value.Bytes()), configuration.Charset, configuration.Salt), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase16 encodes data using Base16 encoding.
|
||||
func obrimCodecEncodeBase16(configuration *obrimCodecConfiguration) (string, error) {
|
||||
input := obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(configuration.Data),
|
||||
configuration.Charset,
|
||||
configuration.Salt,
|
||||
)
|
||||
|
||||
output := hex.EncodeToString([]byte(input))
|
||||
return obrimCodecApplyCase(output, configuration.Case), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase16 decodes Base16 encoded data.
|
||||
func obrimCodecDecodeBase16(configuration *obrimCodecConfiguration) (string, error) {
|
||||
decoded, err := hex.DecodeString(obrimCodecNormalizeInput(configuration.Data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase32 encodes data using Base32 encoding.
|
||||
func obrimCodecEncodeBase32(configuration *obrimCodecConfiguration) (string, error) {
|
||||
input := obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(configuration.Data),
|
||||
configuration.Charset,
|
||||
configuration.Salt,
|
||||
)
|
||||
|
||||
output := base32.StdEncoding.EncodeToString([]byte(input))
|
||||
return obrimCodecApplyCase(output, configuration.Case), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase32 decodes Base32 encoded data.
|
||||
func obrimCodecDecodeBase32(configuration *obrimCodecConfiguration) (string, error) {
|
||||
decoded, err := base32.StdEncoding.DecodeString(strings.ToUpper(obrimCodecNormalizeInput(configuration.Data)))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase64 encodes data using Base64 encoding.
|
||||
func obrimCodecEncodeBase64(configuration *obrimCodecConfiguration) (string, error) {
|
||||
input := obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(configuration.Data),
|
||||
configuration.Charset,
|
||||
configuration.Salt,
|
||||
)
|
||||
|
||||
output := base64.StdEncoding.EncodeToString([]byte(input))
|
||||
return obrimCodecApplyCase(output, configuration.Case), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase64 decodes Base64 encoded data.
|
||||
func obrimCodecDecodeBase64(configuration *obrimCodecConfiguration) (string, error) {
|
||||
decoded, err := base64.StdEncoding.DecodeString(obrimCodecNormalizeInput(configuration.Data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeSqids encodes data using Sqids encoding with minimum length enforcement.
|
||||
func obrimCodecEncodeSqids(configuration *obrimCodecConfiguration) (string, error) {
|
||||
input := obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(configuration.Data),
|
||||
configuration.Charset,
|
||||
configuration.Salt,
|
||||
)
|
||||
|
||||
numbers := make([]uint64, 0)
|
||||
|
||||
for _, value := range []byte(input) {
|
||||
numbers = append(numbers, uint64(value))
|
||||
}
|
||||
|
||||
sqidsInstance, err := sqids.New(sqids.Options{
|
||||
Alphabet: configuration.Charset,
|
||||
MinLength: uint8(configuration.Length),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return sqidsInstance.Encode(numbers)
|
||||
}
|
||||
|
||||
// obrimCodecDecodeSqids decodes Sqids encoded data using deterministic length validation.
|
||||
func obrimCodecDecodeSqids(configuration *obrimCodecConfiguration) (string, error) {
|
||||
sqidsInstance, err := sqids.New(sqids.Options{
|
||||
Alphabet: configuration.Charset,
|
||||
MinLength: uint8(configuration.Length),
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
numbers, err := sqidsInstance.Decode(obrimCodecNormalizeInput(configuration.Data))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
output := make([]byte, 0)
|
||||
|
||||
for _, value := range numbers {
|
||||
output = append(output, byte(value))
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(string(output), configuration.Charset, configuration.Salt), nil
|
||||
}
|
||||
@ -0,0 +1,218 @@
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimDatetimeTypeLocal = "local"
|
||||
obrimDatetimeTypeTrusted = "trusted"
|
||||
|
||||
obrimDatetimeCodeSuccess = "SUCCESS_DATETIME_RETRIEVED"
|
||||
obrimDatetimeCodeInvalidType = "FAILED_INVALID_TYPE"
|
||||
obrimDatetimeCodeInvalidFormat = "FAILED_INVALID_FORMAT"
|
||||
obrimDatetimeCodeTrustedTime = "FAILED_TRUSTED_TIME_RETRIEVAL"
|
||||
obrimDatetimeCodeInternal = "FAILED_INTERNAL"
|
||||
obrimDatetimeTrustedNTPPoolServer = "pool.ntp.org:123"
|
||||
)
|
||||
|
||||
// ObrimDatetimePayload represents datetime payload data.
|
||||
type ObrimDatetimePayload struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// ObrimDatetimeResponse represents the standard utility response.
|
||||
type ObrimDatetimeResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload *ObrimDatetimePayload `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimDatetimePatternMap stores 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 datetime from a selected source.
|
||||
func ObrimDatetime(datetimeType string, config map[string]string) ObrimDatetimeResponse {
|
||||
log.Println("[datetime] initialization started")
|
||||
|
||||
if code := obrimDatetimeValidate(datetimeType, config); code != "" {
|
||||
log.Printf("[datetime] validation failed: %s", code)
|
||||
return obrimDatetimeResponse(false, code, "")
|
||||
}
|
||||
|
||||
format := config["format"]
|
||||
|
||||
var (
|
||||
currentTime time.Time
|
||||
err error
|
||||
)
|
||||
|
||||
switch datetimeType {
|
||||
case obrimDatetimeTypeLocal:
|
||||
currentTime = obrimDatetimeLocal()
|
||||
|
||||
case obrimDatetimeTypeTrusted:
|
||||
currentTime, err = obrimDatetimeTrusted()
|
||||
if err != nil {
|
||||
log.Printf("[datetime] trusted retrieval failed: %v", err)
|
||||
return obrimDatetimeResponse(false, obrimDatetimeCodeTrustedTime, "")
|
||||
}
|
||||
|
||||
default:
|
||||
return obrimDatetimeResponse(false, obrimDatetimeCodeInvalidType, "")
|
||||
}
|
||||
|
||||
value := obrimDatetimeFormatApply(currentTime, format)
|
||||
|
||||
log.Println("[datetime] retrieval completed successfully")
|
||||
|
||||
return obrimDatetimeResponse(true, obrimDatetimeCodeSuccess, value)
|
||||
}
|
||||
|
||||
// obrimDatetimeValidate validates utility inputs.
|
||||
func obrimDatetimeValidate(datetimeType string, config map[string]string) string {
|
||||
switch datetimeType {
|
||||
case obrimDatetimeTypeLocal, obrimDatetimeTypeTrusted:
|
||||
default:
|
||||
return obrimDatetimeCodeInvalidType
|
||||
}
|
||||
|
||||
format, exists := config["format"]
|
||||
if !exists || format == "" {
|
||||
return obrimDatetimeCodeInvalidFormat
|
||||
}
|
||||
|
||||
if !obrimDatetimePatternValidate(format) {
|
||||
return obrimDatetimeCodeInvalidFormat
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimDatetimePatternValidate validates supported format patterns.
|
||||
func obrimDatetimePatternValidate(pattern string) bool {
|
||||
_, exists := obrimDatetimePatternMap[pattern]
|
||||
|
||||
if exists {
|
||||
return true
|
||||
}
|
||||
|
||||
switch pattern {
|
||||
case "timestampsec", "timestampmil":
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// obrimDatetimePatternResolve resolves patterns into layouts.
|
||||
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, exists := obrimDatetimePatternResolve(pattern)
|
||||
if !exists {
|
||||
return ""
|
||||
}
|
||||
|
||||
return datetimeValue.Format(layout)
|
||||
}
|
||||
|
||||
// obrimDatetimeResponse builds standardized responses.
|
||||
func obrimDatetimeResponse(status bool, code string, value string) ObrimDatetimeResponse {
|
||||
if !status {
|
||||
return ObrimDatetimeResponse{
|
||||
Status: false,
|
||||
Code: code,
|
||||
Payload: nil,
|
||||
}
|
||||
}
|
||||
|
||||
return ObrimDatetimeResponse{
|
||||
Status: true,
|
||||
Code: code,
|
||||
Payload: &ObrimDatetimePayload{
|
||||
Value: value,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimDatetimeLocal retrieves local system time.
|
||||
func obrimDatetimeLocal() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// obrimDatetimeTrusted retrieves trusted NTP time.
|
||||
func obrimDatetimeTrusted() (time.Time, error) {
|
||||
return obrimDatetimeTrustedSync()
|
||||
}
|
||||
|
||||
// obrimDatetimeTrustedSync synchronizes with framework-managed NTP source.
|
||||
func obrimDatetimeTrustedSync() (time.Time, error) {
|
||||
connection, err := net.DialTimeout("udp", obrimDatetimeTrustedNTPPoolServer, 5*time.Second)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
defer connection.Close()
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
response := make([]byte, 48)
|
||||
|
||||
if _, err = connection.Read(response); err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
seconds := binary.BigEndian.Uint32(response[40:44])
|
||||
fraction := binary.BigEndian.Uint32(response[44:48])
|
||||
|
||||
const ntpEpochOffset = 2208988800
|
||||
|
||||
unixSeconds := int64(seconds) - ntpEpochOffset
|
||||
nanoseconds := (int64(fraction) * 1e9) >> 32
|
||||
|
||||
if unixSeconds < 0 {
|
||||
return time.Time{}, fmt.Errorf("invalid ntp timestamp")
|
||||
}
|
||||
|
||||
return time.Unix(unixSeconds, nanoseconds).UTC(), nil
|
||||
}
|
||||
@ -0,0 +1,531 @@
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ObrimFilesystemResult represents the standardized utility response.
|
||||
type ObrimFilesystemResult struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload any `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimFilesystemEntry represents a filesystem listing entry.
|
||||
type obrimFilesystemEntry struct {
|
||||
Name string `json:"name"`
|
||||
Path string `json:"path"`
|
||||
Entity string `json:"entity"`
|
||||
Size int64 `json:"size"`
|
||||
ModifiedTime time.Time `json:"modified_time"`
|
||||
}
|
||||
|
||||
// ObrimFilesystem executes a filesystem operation and returns a structured result.
|
||||
func ObrimFilesystem(operationType string, config map[string]any) map[string]any {
|
||||
if config == nil {
|
||||
config = map[string]any{}
|
||||
}
|
||||
|
||||
if err := obrimFilesystemValidate(operationType, config); err != nil {
|
||||
return ObrimStatus(false, "FAILED_VALIDATION", nil)
|
||||
}
|
||||
|
||||
switch operationType {
|
||||
case "list":
|
||||
return obrimFilesystemList(config)
|
||||
|
||||
case "check":
|
||||
return obrimFilesystemCheck(config)
|
||||
|
||||
case "create":
|
||||
return obrimFilesystemCreate(config)
|
||||
|
||||
case "delete":
|
||||
return obrimFilesystemDelete(config)
|
||||
|
||||
case "permission":
|
||||
return obrimFilesystemPermission(config)
|
||||
|
||||
default:
|
||||
return ObrimStatus(false, "FAILED_UNSUPPORTED_TYPE", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemValidate validates operation type and configuration.
|
||||
func obrimFilesystemValidate(operationType string, config map[string]any) error {
|
||||
allowedTypes := map[string]bool{
|
||||
"list": true,
|
||||
"check": true,
|
||||
"create": true,
|
||||
"delete": true,
|
||||
"permission": true,
|
||||
}
|
||||
|
||||
if !allowedTypes[operationType] {
|
||||
return errors.New("unsupported type")
|
||||
}
|
||||
|
||||
switch operationType {
|
||||
case "create", "delete":
|
||||
entity := strings.TrimSpace(fmt.Sprintf("%v", config["entity"]))
|
||||
if entity != "file" && entity != "directory" {
|
||||
return errors.New("invalid entity")
|
||||
}
|
||||
}
|
||||
|
||||
if strategy, ok := config["strategy"]; ok {
|
||||
value := fmt.Sprintf("%v", strategy)
|
||||
|
||||
if value != "user" && value != "custom" {
|
||||
return errors.New("invalid strategy")
|
||||
}
|
||||
|
||||
if value == "custom" {
|
||||
if strings.TrimSpace(fmt.Sprintf("%v", config["base"])) == "" {
|
||||
return errors.New("missing base")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obrimFilesystemResolve resolves and normalizes filesystem paths.
|
||||
func obrimFilesystemResolve(config map[string]any) (string, error) {
|
||||
base := "."
|
||||
|
||||
if strategy, ok := config["strategy"]; ok {
|
||||
switch fmt.Sprintf("%v", strategy) {
|
||||
case "user":
|
||||
currentUser, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
base = currentUser.HomeDir
|
||||
|
||||
case "custom":
|
||||
base = fmt.Sprintf("%v", config["base"])
|
||||
}
|
||||
}
|
||||
|
||||
name := strings.TrimSpace(fmt.Sprintf("%v", config["name"]))
|
||||
|
||||
resolved := filepath.Join(base, name)
|
||||
|
||||
absolute, err := filepath.Abs(filepath.Clean(resolved))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return absolute, nil
|
||||
}
|
||||
|
||||
// obrimFilesystemPayload builds standardized payload structures.
|
||||
func obrimFilesystemPayload(payload map[string]any) map[string]any {
|
||||
return payload
|
||||
}
|
||||
|
||||
// obrimFilesystemEntityType determines filesystem entity type.
|
||||
func obrimFilesystemEntityType(path string) string {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return "directory"
|
||||
}
|
||||
|
||||
return "file"
|
||||
}
|
||||
|
||||
// obrimFilesystemPermissionValidate validates permission specifications.
|
||||
func obrimFilesystemPermissionValidate(permission string) error {
|
||||
if permission == "" {
|
||||
return errors.New("permission required")
|
||||
}
|
||||
|
||||
_, err := fs.FileMode(0).String(), error(nil)
|
||||
return err
|
||||
}
|
||||
|
||||
// obrimFilesystemList executes filesystem listing operations.
|
||||
func obrimFilesystemList(config map[string]any) map[string]any {
|
||||
basePath, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil)
|
||||
}
|
||||
|
||||
entries, err := obrimFilesystemListTraverse(basePath, config)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_EXECUTION", nil)
|
||||
}
|
||||
|
||||
entries = obrimFilesystemListFilter(entries, config)
|
||||
entries = obrimFilesystemListSort(entries, config)
|
||||
|
||||
payload := obrimFilesystemPayload(map[string]any{
|
||||
"entries": entries,
|
||||
"total": len(entries),
|
||||
"recursive": getBool(config, "recursive"),
|
||||
"base_path": basePath,
|
||||
})
|
||||
|
||||
return ObrimStatus(true, "SUCCESS_LIST", payload)
|
||||
}
|
||||
|
||||
// obrimFilesystemListTraverse traverses filesystem entities according to listing configuration.
|
||||
func obrimFilesystemListTraverse(basePath string, config map[string]any) ([]obrimFilesystemEntry, error) {
|
||||
var entries []obrimFilesystemEntry
|
||||
|
||||
recursive := getBool(config, "recursive")
|
||||
|
||||
if recursive {
|
||||
err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if path == basePath {
|
||||
return nil
|
||||
}
|
||||
|
||||
entries = append(entries, obrimFilesystemEntry{
|
||||
Name: info.Name(),
|
||||
Path: path,
|
||||
Entity: obrimFilesystemEntityType(path),
|
||||
Size: info.Size(),
|
||||
ModifiedTime: info.ModTime(),
|
||||
})
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return entries, err
|
||||
}
|
||||
|
||||
list, err := os.ReadDir(basePath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, item := range list {
|
||||
info, _ := item.Info()
|
||||
|
||||
entries = append(entries, obrimFilesystemEntry{
|
||||
Name: item.Name(),
|
||||
Path: filepath.Join(basePath, item.Name()),
|
||||
Entity: map[bool]string{true: "directory", false: "file"}[item.IsDir()],
|
||||
Size: info.Size(),
|
||||
ModifiedTime: info.ModTime(),
|
||||
})
|
||||
}
|
||||
|
||||
return entries, nil
|
||||
}
|
||||
|
||||
// obrimFilesystemListFilter applies entity and pattern filtering.
|
||||
func obrimFilesystemListFilter(entries []obrimFilesystemEntry, config map[string]any) []obrimFilesystemEntry {
|
||||
var filtered []obrimFilesystemEntry
|
||||
|
||||
entity := strings.TrimSpace(fmt.Sprintf("%v", config["entity"]))
|
||||
pattern := strings.TrimSpace(fmt.Sprintf("%v", config["filter_pattern"]))
|
||||
includeHidden := getBool(config, "include_hidden")
|
||||
|
||||
for _, entry := range entries {
|
||||
if !includeHidden && strings.HasPrefix(entry.Name, ".") {
|
||||
continue
|
||||
}
|
||||
|
||||
if entity != "" && entry.Entity != entity {
|
||||
continue
|
||||
}
|
||||
|
||||
if pattern != "" {
|
||||
match, _ := filepath.Match(pattern, entry.Name)
|
||||
if !match {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
filtered = append(filtered, entry)
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// obrimFilesystemListSort applies result sorting behavior.
|
||||
func obrimFilesystemListSort(entries []obrimFilesystemEntry, config map[string]any) []obrimFilesystemEntry {
|
||||
sortBy := strings.TrimSpace(fmt.Sprintf("%v", config["sort_by"]))
|
||||
order := strings.TrimSpace(fmt.Sprintf("%v", config["sort_order"]))
|
||||
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
var result bool
|
||||
|
||||
switch sortBy {
|
||||
case "size":
|
||||
result = entries[i].Size < entries[j].Size
|
||||
|
||||
case "modified":
|
||||
result = entries[i].ModifiedTime.Before(entries[j].ModifiedTime)
|
||||
|
||||
default:
|
||||
result = entries[i].Name < entries[j].Name
|
||||
}
|
||||
|
||||
if order == "desc" {
|
||||
return !result
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
|
||||
return entries
|
||||
}
|
||||
|
||||
// obrimFilesystemCheck executes filesystem validation operations.
|
||||
func obrimFilesystemCheck(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil)
|
||||
}
|
||||
|
||||
_, err = os.Stat(path)
|
||||
|
||||
exists := err == nil
|
||||
|
||||
readable, writable := obrimFilesystemCheckAccess(path)
|
||||
|
||||
payload := obrimFilesystemPayload(map[string]any{
|
||||
"path": path,
|
||||
"exists": exists,
|
||||
"entity": obrimFilesystemEntityType(path),
|
||||
"readable": readable,
|
||||
"writable": writable,
|
||||
})
|
||||
|
||||
return ObrimStatus(true, "SUCCESS_CHECK", payload)
|
||||
}
|
||||
|
||||
// obrimFilesystemCheckAccess verifies filesystem accessibility requirements.
|
||||
func obrimFilesystemCheckAccess(path string) (bool, bool) {
|
||||
readable := true
|
||||
writable := true
|
||||
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
readable = false
|
||||
} else {
|
||||
_ = file.Close()
|
||||
}
|
||||
|
||||
file, err = os.OpenFile(path, os.O_WRONLY, 0)
|
||||
if err != nil {
|
||||
writable = false
|
||||
} else {
|
||||
_ = file.Close()
|
||||
}
|
||||
|
||||
return readable, writable
|
||||
}
|
||||
|
||||
// obrimFilesystemCreate executes filesystem entity creation operations.
|
||||
func obrimFilesystemCreate(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil)
|
||||
}
|
||||
|
||||
entity := fmt.Sprintf("%v", config["entity"])
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return ObrimStatus(false, "FAILED_EXECUTION", nil)
|
||||
}
|
||||
|
||||
switch entity {
|
||||
case "file":
|
||||
err = obrimFilesystemCreateFile(path)
|
||||
|
||||
case "directory":
|
||||
err = obrimFilesystemCreateDirectory(path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_EXECUTION", nil)
|
||||
}
|
||||
|
||||
hidden := getBool(config, "hidden")
|
||||
|
||||
payload := obrimFilesystemPayload(map[string]any{
|
||||
"path": path,
|
||||
"entity": entity,
|
||||
"hidden": hidden,
|
||||
"created": true,
|
||||
})
|
||||
|
||||
return ObrimStatus(true, "SUCCESS_CREATE", payload)
|
||||
}
|
||||
|
||||
// obrimFilesystemCreateFile creates a filesystem file.
|
||||
func obrimFilesystemCreateFile(path string) error {
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return file.Close()
|
||||
}
|
||||
|
||||
// obrimFilesystemCreateDirectory creates a filesystem directory.
|
||||
func obrimFilesystemCreateDirectory(path string) error {
|
||||
return os.MkdirAll(path, 0755)
|
||||
}
|
||||
|
||||
// obrimFilesystemDelete executes filesystem entity deletion operations.
|
||||
func obrimFilesystemDelete(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil)
|
||||
}
|
||||
|
||||
entity := fmt.Sprintf("%v", config["entity"])
|
||||
|
||||
switch entity {
|
||||
case "file":
|
||||
err = obrimFilesystemDeleteFile(path)
|
||||
|
||||
case "directory":
|
||||
err = obrimFilesystemDeleteDirectory(path, getBool(config, "recursive"))
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_EXECUTION", nil)
|
||||
}
|
||||
|
||||
payload := obrimFilesystemPayload(map[string]any{
|
||||
"path": path,
|
||||
"entity": entity,
|
||||
"deleted": true,
|
||||
})
|
||||
|
||||
return ObrimStatus(true, "SUCCESS_DELETE", payload)
|
||||
}
|
||||
|
||||
// obrimFilesystemDeleteFile deletes a filesystem file.
|
||||
func obrimFilesystemDeleteFile(path string) error {
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
// obrimFilesystemDeleteDirectory deletes a filesystem directory.
|
||||
func obrimFilesystemDeleteDirectory(path string, recursive bool) error {
|
||||
if recursive {
|
||||
return os.RemoveAll(path)
|
||||
}
|
||||
|
||||
return os.Remove(path)
|
||||
}
|
||||
|
||||
// obrimFilesystemPermission executes filesystem permission operations.
|
||||
func obrimFilesystemPermission(config map[string]any) map[string]any {
|
||||
mode := strings.TrimSpace(fmt.Sprintf("%v", config["mode"]))
|
||||
|
||||
switch mode {
|
||||
case "write":
|
||||
return obrimFilesystemPermissionWrite(config)
|
||||
|
||||
default:
|
||||
return obrimFilesystemPermissionRead(config)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemPermissionRead retrieves filesystem permissions.
|
||||
func obrimFilesystemPermissionRead(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil)
|
||||
}
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_EXECUTION", nil)
|
||||
}
|
||||
|
||||
payload := obrimFilesystemPayload(map[string]any{
|
||||
"path": path,
|
||||
"permission": info.Mode().Perm().String(),
|
||||
"mode": "read",
|
||||
"updated": false,
|
||||
})
|
||||
|
||||
return ObrimStatus(true, "SUCCESS_PERMISSION", payload)
|
||||
}
|
||||
|
||||
// obrimFilesystemPermissionWrite updates filesystem permissions.
|
||||
func obrimFilesystemPermissionWrite(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil)
|
||||
}
|
||||
|
||||
permission := strings.TrimSpace(fmt.Sprintf("%v", config["permission"]))
|
||||
|
||||
if err := obrimFilesystemPermissionValidate(permission); err != nil {
|
||||
return ObrimStatus(false, "FAILED_VALIDATION", nil)
|
||||
}
|
||||
|
||||
var mode os.FileMode
|
||||
|
||||
_, err = fmt.Sscanf(permission, "%o", &mode)
|
||||
if err != nil {
|
||||
return ObrimStatus(false, "FAILED_VALIDATION", nil)
|
||||
}
|
||||
|
||||
if runtime.GOOS != "windows" {
|
||||
if err := os.Chmod(path, mode); err != nil {
|
||||
return ObrimStatus(false, "FAILED_EXECUTION", nil)
|
||||
}
|
||||
}
|
||||
|
||||
payload := obrimFilesystemPayload(map[string]any{
|
||||
"path": path,
|
||||
"permission": permission,
|
||||
"mode": "write",
|
||||
"updated": true,
|
||||
})
|
||||
|
||||
return ObrimStatus(true, "SUCCESS_PERMISSION", payload)
|
||||
}
|
||||
|
||||
// ObrimStatus returns standardized utility status responses.
|
||||
func ObrimStatus(status bool, code string, payload any) map[string]any {
|
||||
return map[string]any{
|
||||
"status": status,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// getBool safely reads boolean config values.
|
||||
func getBool(config map[string]any, key string) bool {
|
||||
value, ok := config[key]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
boolean, ok := value.(bool)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return boolean
|
||||
}
|
||||
@ -0,0 +1,412 @@
|
||||
package hash
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// ObrimHashResponse represents the standard utility response structure.
|
||||
type ObrimHashResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
// ObrimHashCalculateStandardPayload represents a standard hash generation payload.
|
||||
type ObrimHashCalculateStandardPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
// ObrimHashCalculateSaltedPayload represents a salted hash generation payload.
|
||||
type ObrimHashCalculateSaltedPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hash string `json:"hash"`
|
||||
Salt string `json:"salt"`
|
||||
}
|
||||
|
||||
// ObrimHashCompareStandardPayload represents a standard hash comparison payload.
|
||||
type ObrimHashCompareStandardPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Matched bool `json:"matched"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
// ObrimHashCompareSaltedPayload represents a salted hash comparison payload.
|
||||
type ObrimHashCompareSaltedPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Matched bool `json:"matched"`
|
||||
Hash string `json:"hash"`
|
||||
Salt string `json:"salt"`
|
||||
}
|
||||
|
||||
// ObrimHashConfig represents utility configuration.
|
||||
type ObrimHashConfig struct {
|
||||
Mode string
|
||||
Algorithm string
|
||||
Value string
|
||||
Hash string
|
||||
Salt string
|
||||
}
|
||||
|
||||
// obrimHashAllowedConfigKeys defines 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]string) ObrimHashResponse {
|
||||
if response := obrimHashValidateType(hashType); response != nil {
|
||||
return *response
|
||||
}
|
||||
|
||||
if response := obrimHashValidateConfig(hashType, config); response != nil {
|
||||
return *response
|
||||
}
|
||||
|
||||
parsedConfig := ObrimHashConfig{
|
||||
Mode: config["mode"],
|
||||
Algorithm: config["algorithm"],
|
||||
Value: config["value"],
|
||||
Hash: config["hash"],
|
||||
Salt: config["salt"],
|
||||
}
|
||||
|
||||
if response := obrimHashValidateMode(parsedConfig.Mode); response != nil {
|
||||
return *response
|
||||
}
|
||||
|
||||
if response := obrimHashValidateAlgorithm(parsedConfig.Mode, parsedConfig.Algorithm); response != nil {
|
||||
return *response
|
||||
}
|
||||
|
||||
switch hashType {
|
||||
case "calculate":
|
||||
return obrimHashCalculate(parsedConfig)
|
||||
case "compare":
|
||||
return obrimHashCompare(parsedConfig)
|
||||
default:
|
||||
return obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_TYPE")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashValidateType validates operation type.
|
||||
func obrimHashValidateType(hashType string) *ObrimHashResponse {
|
||||
switch hashType {
|
||||
case "calculate", "compare":
|
||||
return nil
|
||||
default:
|
||||
response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_TYPE")
|
||||
return &response
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashValidateMode validates hashing mode.
|
||||
func obrimHashValidateMode(mode string) *ObrimHashResponse {
|
||||
switch mode {
|
||||
case "standard", "salted":
|
||||
return nil
|
||||
default:
|
||||
response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_MODE")
|
||||
return &response
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashValidateAlgorithm validates hashing algorithm selection.
|
||||
func obrimHashValidateAlgorithm(mode string, algorithm string) *ObrimHashResponse {
|
||||
switch mode {
|
||||
case "standard":
|
||||
switch algorithm {
|
||||
case "sha256", "sha512":
|
||||
return nil
|
||||
}
|
||||
case "salted":
|
||||
switch algorithm {
|
||||
case "salted_sha256", "salted_sha512":
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_ALGORITHM")
|
||||
return &response
|
||||
}
|
||||
|
||||
// obrimHashValidateConfig validates configuration keys and values.
|
||||
func obrimHashValidateConfig(hashType string, config map[string]string) *ObrimHashResponse {
|
||||
if config == nil {
|
||||
response := obrimHashBuildErrorPayload("FAILED_INVALID_CONFIG")
|
||||
return &response
|
||||
}
|
||||
|
||||
for key := range config {
|
||||
if _, exists := obrimHashAllowedConfigKeys[key]; !exists {
|
||||
response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_CONFIG_KEY")
|
||||
return &response
|
||||
}
|
||||
}
|
||||
|
||||
if config["mode"] == "" {
|
||||
response := obrimHashBuildErrorPayload("FAILED_MISSING_MODE")
|
||||
return &response
|
||||
}
|
||||
|
||||
if config["algorithm"] == "" {
|
||||
response := obrimHashBuildErrorPayload("FAILED_MISSING_ALGORITHM")
|
||||
return &response
|
||||
}
|
||||
|
||||
if config["value"] == "" {
|
||||
response := obrimHashBuildErrorPayload("FAILED_EMPTY_VALUE")
|
||||
return &response
|
||||
}
|
||||
|
||||
switch hashType {
|
||||
case "calculate":
|
||||
if config["mode"] == "standard" && config["salt"] != "" {
|
||||
response := obrimHashBuildErrorPayload("FAILED_SALT_NOT_ALLOWED")
|
||||
return &response
|
||||
}
|
||||
|
||||
case "compare":
|
||||
if config["hash"] == "" {
|
||||
response := obrimHashBuildErrorPayload("FAILED_EMPTY_HASH")
|
||||
return &response
|
||||
}
|
||||
|
||||
if config["mode"] == "standard" && config["salt"] != "" {
|
||||
response := obrimHashBuildErrorPayload("FAILED_SALT_NOT_ALLOWED")
|
||||
return &response
|
||||
}
|
||||
|
||||
if config["mode"] == "salted" && config["salt"] == "" {
|
||||
response := obrimHashBuildErrorPayload("FAILED_MISSING_SALT")
|
||||
return &response
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// obrimHashGenerateHash executes the selected hashing algorithm.
|
||||
func obrimHashGenerateHash(algorithm string, value string, salt string) (string, bool) {
|
||||
var input []byte
|
||||
|
||||
switch algorithm {
|
||||
case "sha256":
|
||||
input = []byte(value)
|
||||
|
||||
// Generate SHA-256 hash.
|
||||
sum := sha256.Sum256(input)
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
|
||||
case "sha512":
|
||||
input = []byte(value)
|
||||
|
||||
// Generate SHA-512 hash.
|
||||
sum := sha512.Sum512(input)
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
|
||||
case "salted_sha256":
|
||||
input = []byte(value + salt)
|
||||
|
||||
// Generate salted SHA-256 hash.
|
||||
sum := sha256.Sum256(input)
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
|
||||
case "salted_sha512":
|
||||
input = []byte(value + salt)
|
||||
|
||||
// Generate salted SHA-512 hash.
|
||||
sum := sha512.Sum512(input)
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
}
|
||||
|
||||
return "", false
|
||||
}
|
||||
|
||||
// obrimHashCalculate processes hash generation requests.
|
||||
func obrimHashCalculate(config ObrimHashConfig) ObrimHashResponse {
|
||||
switch config.Mode {
|
||||
case "standard":
|
||||
return obrimHashCalculateStandard(config)
|
||||
|
||||
case "salted":
|
||||
return obrimHashCalculateSalted(config)
|
||||
|
||||
default:
|
||||
return obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_MODE")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashCalculateStandard generates a deterministic hash.
|
||||
func obrimHashCalculateStandard(config ObrimHashConfig) ObrimHashResponse {
|
||||
hashValue, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
"",
|
||||
)
|
||||
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION")
|
||||
}
|
||||
|
||||
payload := ObrimHashCalculateStandardPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Hash: hashValue,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
"SUCCESS_HASH_GENERATED",
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHashCalculateSalted generates a salted hash and salt pair.
|
||||
func obrimHashCalculateSalted(config ObrimHashConfig) ObrimHashResponse {
|
||||
// Store active salt value.
|
||||
activeSalt := config.Salt
|
||||
|
||||
if activeSalt == "" {
|
||||
generatedSalt, success := obrimHashGenerateSalt()
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload("FAILED_SALT_GENERATION")
|
||||
}
|
||||
|
||||
activeSalt = generatedSalt
|
||||
}
|
||||
|
||||
hashValue, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
activeSalt,
|
||||
)
|
||||
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION")
|
||||
}
|
||||
|
||||
payload := ObrimHashCalculateSaltedPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Hash: hashValue,
|
||||
Salt: activeSalt,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
"SUCCESS_HASH_GENERATED",
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHashGenerateSalt generates a cryptographically secure salt.
|
||||
func obrimHashGenerateSalt() (string, bool) {
|
||||
// Allocate random salt bytes.
|
||||
saltBytes := make([]byte, 32)
|
||||
|
||||
if _, err := rand.Read(saltBytes); err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return hex.EncodeToString(saltBytes), true
|
||||
}
|
||||
|
||||
// obrimHashCompare processes hash verification requests.
|
||||
func obrimHashCompare(config ObrimHashConfig) ObrimHashResponse {
|
||||
switch config.Mode {
|
||||
case "standard":
|
||||
return obrimHashCompareStandard(config)
|
||||
|
||||
case "salted":
|
||||
return obrimHashCompareSalted(config)
|
||||
|
||||
default:
|
||||
return obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_MODE")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashCompareStandard regenerates a standard hash for verification.
|
||||
func obrimHashCompareStandard(config ObrimHashConfig) ObrimHashResponse {
|
||||
regeneratedHash, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
"",
|
||||
)
|
||||
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION")
|
||||
}
|
||||
|
||||
matched := obrimHashVerify(
|
||||
regeneratedHash,
|
||||
config.Hash,
|
||||
)
|
||||
|
||||
payload := ObrimHashCompareStandardPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Matched: matched,
|
||||
Hash: config.Hash,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
"SUCCESS_HASH_COMPARED",
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHashCompareSalted regenerates a salted hash for verification.
|
||||
func obrimHashCompareSalted(config ObrimHashConfig) ObrimHashResponse {
|
||||
regeneratedHash, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
config.Salt,
|
||||
)
|
||||
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION")
|
||||
}
|
||||
|
||||
matched := obrimHashVerify(
|
||||
regeneratedHash,
|
||||
config.Hash,
|
||||
)
|
||||
|
||||
payload := ObrimHashCompareSaltedPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Matched: matched,
|
||||
Hash: config.Hash,
|
||||
Salt: config.Salt,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
"SUCCESS_HASH_COMPARED",
|
||||
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 interface{}) ObrimHashResponse {
|
||||
return ObrimHashResponse{
|
||||
Status: true,
|
||||
Code: code,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashBuildErrorPayload constructs error response payloads.
|
||||
func obrimHashBuildErrorPayload(code string) ObrimHashResponse {
|
||||
return ObrimHashResponse{
|
||||
Status: false,
|
||||
Code: code,
|
||||
Payload: nil,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,276 @@
|
||||
package key
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ObrimKeyResponse represents the standardized utility response.
|
||||
type ObrimKeyResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimKeyAllowedSymmetricFields defines allowed symmetric configuration fields.
|
||||
var obrimKeyAllowedSymmetricFields = map[string]struct{}{
|
||||
"algorithm": {},
|
||||
"key_size": {},
|
||||
}
|
||||
|
||||
// obrimKeyAllowedAsymmetricFields defines 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 {
|
||||
if response := obrimKeyValidate(keyType, config); response != nil {
|
||||
return response
|
||||
}
|
||||
|
||||
return obrimKeyDispatch(keyType, config)
|
||||
}
|
||||
|
||||
// obrimKeyValidate validates common request structure and schema integrity.
|
||||
func obrimKeyValidate(keyType string, config map[string]any) map[string]any {
|
||||
if strings.TrimSpace(keyType) == "" {
|
||||
return obrimKeyBuildError("FAILED_MISSING_TYPE")
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
return obrimKeyBuildError("FAILED_MISSING_CONFIG")
|
||||
}
|
||||
|
||||
normalizedType := strings.ToLower(strings.TrimSpace(keyType))
|
||||
|
||||
switch normalizedType {
|
||||
case "symmetric":
|
||||
return obrimKeyValidateSymmetricConfig(config)
|
||||
|
||||
case "asymmetric":
|
||||
return obrimKeyValidateAsymmetricConfig(config)
|
||||
|
||||
default:
|
||||
return obrimKeyBuildError("FAILED_UNSUPPORTED_TYPE")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyDispatch routes execution to the appropriate cryptographic workflow.
|
||||
func obrimKeyDispatch(keyType string, config map[string]any) map[string]any {
|
||||
switch strings.ToLower(strings.TrimSpace(keyType)) {
|
||||
case "symmetric":
|
||||
return obrimKeyGenerateSymmetric(config)
|
||||
|
||||
case "asymmetric":
|
||||
return obrimKeyGenerateAsymmetric(config)
|
||||
|
||||
default:
|
||||
return obrimKeyBuildError("FAILED_UNSUPPORTED_TYPE")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyValidateSymmetricConfig validates symmetric configuration values.
|
||||
func obrimKeyValidateSymmetricConfig(config map[string]any) map[string]any {
|
||||
for field := range config {
|
||||
if _, exists := obrimKeyAllowedSymmetricFields[field]; !exists {
|
||||
return obrimKeyBuildError("FAILED_INVALID_CONFIG_FIELD")
|
||||
}
|
||||
}
|
||||
|
||||
algorithmValue, exists := config["algorithm"]
|
||||
if !exists {
|
||||
return obrimKeyBuildError("FAILED_MISSING_ALGORITHM")
|
||||
}
|
||||
|
||||
algorithm, ok := algorithmValue.(string)
|
||||
if !ok {
|
||||
return obrimKeyBuildError("FAILED_INVALID_ALGORITHM")
|
||||
}
|
||||
|
||||
if strings.ToLower(strings.TrimSpace(algorithm)) != "aes" {
|
||||
return obrimKeyBuildError("FAILED_UNSUPPORTED_ALGORITHM")
|
||||
}
|
||||
|
||||
keySizeValue, exists := config["key_size"]
|
||||
if !exists {
|
||||
return obrimKeyBuildError("FAILED_MISSING_KEY_SIZE")
|
||||
}
|
||||
|
||||
keySize, ok := obrimKeyConvertInt(keySizeValue)
|
||||
if !ok {
|
||||
return obrimKeyBuildError("FAILED_INVALID_KEY_SIZE")
|
||||
}
|
||||
|
||||
switch keySize {
|
||||
case 128, 192, 256:
|
||||
return nil
|
||||
default:
|
||||
return obrimKeyBuildError("FAILED_UNSUPPORTED_KEY_SIZE")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyValidateAsymmetricConfig validates asymmetric configuration values.
|
||||
func obrimKeyValidateAsymmetricConfig(config map[string]any) map[string]any {
|
||||
for field := range config {
|
||||
if _, exists := obrimKeyAllowedAsymmetricFields[field]; !exists {
|
||||
return obrimKeyBuildError("FAILED_INVALID_CONFIG_FIELD")
|
||||
}
|
||||
}
|
||||
|
||||
algorithmValue, exists := config["algorithm"]
|
||||
if !exists {
|
||||
return obrimKeyBuildError("FAILED_MISSING_ALGORITHM")
|
||||
}
|
||||
|
||||
algorithm, ok := algorithmValue.(string)
|
||||
if !ok {
|
||||
return obrimKeyBuildError("FAILED_INVALID_ALGORITHM")
|
||||
}
|
||||
|
||||
if strings.ToLower(strings.TrimSpace(algorithm)) != "ecc" {
|
||||
return obrimKeyBuildError("FAILED_UNSUPPORTED_ALGORITHM")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(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.
|
||||
keyMaterial := make([]byte, keySize/8)
|
||||
|
||||
if _, err := rand.Read(keyMaterial); err != nil {
|
||||
return nil, "FAILED_KEY_GENERATION"
|
||||
}
|
||||
|
||||
if _, err := aes.NewCipher(keyMaterial); err != nil {
|
||||
return nil, "FAILED_KEY_GENERATION"
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"type": "symmetric",
|
||||
"algorithm": "aes",
|
||||
"key_size": keySize,
|
||||
"key_material": base64.StdEncoding.EncodeToString(keyMaterial),
|
||||
"generated_at": time.Now().UTC().Format(time.RFC3339),
|
||||
}, ""
|
||||
}
|
||||
|
||||
// obrimKeyNormalizeSymmetricConfig normalizes symmetric configuration values.
|
||||
func obrimKeyNormalizeSymmetricConfig(config map[string]any) map[string]any {
|
||||
keySize, _ := obrimKeyConvertInt(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(payload)
|
||||
}
|
||||
|
||||
// obrimKeyGenerateECC generates ECC key pair material.
|
||||
func obrimKeyGenerateECC(config map[string]any) (map[string]any, string) {
|
||||
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, "FAILED_KEY_GENERATION"
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"type": "asymmetric",
|
||||
"algorithm": "ecc",
|
||||
"curve": "eddsa",
|
||||
"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(payload map[string]any) map[string]any {
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": "SUCCESS_KEY_GENERATED",
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyBuildError constructs standardized error responses.
|
||||
func obrimKeyBuildError(code string) map[string]any {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": code,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyConvertInt converts supported numeric values to int.
|
||||
func obrimKeyConvertInt(value any) (int, bool) {
|
||||
switch converted := value.(type) {
|
||||
case int:
|
||||
return converted, true
|
||||
case int8:
|
||||
return int(converted), true
|
||||
case int16:
|
||||
return int(converted), true
|
||||
case int32:
|
||||
return int(converted), true
|
||||
case int64:
|
||||
return int(converted), true
|
||||
case uint:
|
||||
return int(converted), true
|
||||
case uint8:
|
||||
return int(converted), true
|
||||
case uint16:
|
||||
return int(converted), true
|
||||
case uint32:
|
||||
return int(converted), true
|
||||
case uint64:
|
||||
return int(converted), true
|
||||
case float32:
|
||||
if float32(int(converted)) == converted {
|
||||
return int(converted), true
|
||||
}
|
||||
case float64:
|
||||
if float64(int(converted)) == converted {
|
||||
return int(converted), true
|
||||
}
|
||||
}
|
||||
|
||||
return 0, false
|
||||
}
|
||||
@ -0,0 +1,183 @@
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"go/module/essential/visible/service/helper/retriever"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimLogMetadataResource = "software/metadata"
|
||||
obrimLogMetadataPath = "software.lower"
|
||||
obrimLogFileExtension = ".log"
|
||||
)
|
||||
|
||||
// ObrimLog writes a structured log entry.
|
||||
func ObrimLog(label string, message string) {
|
||||
softwareName := obrimLogSoftwareNameResolve()
|
||||
if softwareName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
timestamp := obrimLogTimestampGenerate()
|
||||
entry := obrimLogEntryBuild(timestamp, label, message)
|
||||
|
||||
directoryPath := obrimLogDirectoryResolve(softwareName)
|
||||
if directoryPath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
filePath := obrimLogFileResolve(directoryPath, softwareName)
|
||||
|
||||
if err := obrimLogFileEnsure(directoryPath, filePath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_ = obrimLogWrite(filePath, entry)
|
||||
}
|
||||
|
||||
// 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", 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(directoryPath string, softwareName string) string {
|
||||
return filepath.Join(directoryPath, softwareName+obrimLogFileExtension)
|
||||
}
|
||||
|
||||
// obrimLogFileEnsure creates and verifies required log directories and files.
|
||||
func obrimLogFileEnsure(directoryPath string, filePath string) error {
|
||||
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
|
||||
}
|
||||
|
||||
// obrimLogSoftwareNameResolve retrieves the software name from metadata resources.
|
||||
func obrimLogSoftwareNameResolve() string {
|
||||
value := retriever.ObrimRetriever(
|
||||
"json",
|
||||
map[string]any{
|
||||
"resource": obrimLogMetadataResource,
|
||||
"path": obrimLogMetadataPath,
|
||||
},
|
||||
)
|
||||
|
||||
switch softwareName := value.(type) {
|
||||
case string:
|
||||
return strings.TrimSpace(softwareName)
|
||||
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// obrimLogDirectoryResolveLinux resolves Linux log directories.
|
||||
func obrimLogDirectoryResolveLinux(softwareName string) string {
|
||||
// Mutable XDG state directory environment value.
|
||||
xdgStateHome := strings.TrimSpace(os.Getenv("XDG_STATE_HOME"))
|
||||
|
||||
if xdgStateHome != "" {
|
||||
return filepath.Join(
|
||||
xdgStateHome,
|
||||
"."+softwareName,
|
||||
"logs",
|
||||
)
|
||||
}
|
||||
|
||||
homeDirectory, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(
|
||||
homeDirectory,
|
||||
".local",
|
||||
"state",
|
||||
"."+softwareName,
|
||||
"logs",
|
||||
)
|
||||
}
|
||||
|
||||
// obrimLogDirectoryResolveWindows resolves Windows log directories.
|
||||
func obrimLogDirectoryResolveWindows(softwareName string) string {
|
||||
// Mutable local application data directory environment value.
|
||||
localAppData := strings.TrimSpace(os.Getenv("LOCALAPPDATA"))
|
||||
|
||||
if localAppData == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(
|
||||
localAppData,
|
||||
softwareName,
|
||||
"Logs",
|
||||
)
|
||||
}
|
||||
|
||||
// obrimLogDirectoryResolveMacOS resolves macOS log directories.
|
||||
func obrimLogDirectoryResolveMacOS(softwareName string) string {
|
||||
homeDirectory, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(
|
||||
homeDirectory,
|
||||
"Library",
|
||||
"Logs",
|
||||
"."+softwareName,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,365 @@
|
||||
package marker
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Purpose: Supported marker generation types.
|
||||
const (
|
||||
obrimMarkerTypeTime = "time"
|
||||
obrimMarkerTypeRandom = "random"
|
||||
obrimMarkerTypeEncoded = "encoded"
|
||||
)
|
||||
|
||||
// Purpose: Default random marker length.
|
||||
const obrimMarkerDefaultRandomLength = 16
|
||||
|
||||
// Purpose: Default encoded marker length.
|
||||
const obrimMarkerDefaultEncodedLength = 16
|
||||
|
||||
// Purpose: Default character set used for marker generation.
|
||||
const obrimMarkerDefaultCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
|
||||
// Purpose: Sequence counter used to improve uniqueness for time markers.
|
||||
var obrimMarkerSequence uint64
|
||||
|
||||
// Purpose: Standardized utility response structure.
|
||||
type obrimMarkerResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload any `json:"payload"`
|
||||
}
|
||||
|
||||
// Purpose: Time marker payload structure.
|
||||
type obrimMarkerTimePayload struct {
|
||||
Marker string `json:"marker"`
|
||||
Epoch int64 `json:"epoch"`
|
||||
Instance string `json:"instance"`
|
||||
}
|
||||
|
||||
// Purpose: Random marker payload structure.
|
||||
type obrimMarkerRandomPayload struct {
|
||||
Marker string `json:"marker"`
|
||||
Length int `json:"length"`
|
||||
Charset string `json:"charset"`
|
||||
}
|
||||
|
||||
// Purpose: Encoded marker payload structure.
|
||||
type obrimMarkerEncodedPayload struct {
|
||||
Marker string `json:"marker"`
|
||||
Source string `json:"source"`
|
||||
Length int `json:"length"`
|
||||
}
|
||||
|
||||
// Purpose: Generate markers using the selected marker strategy.
|
||||
func ObrimMarker(markerType string, config map[string]any) map[string]any {
|
||||
obrimMarkerLog("initialization", markerType)
|
||||
|
||||
if config == nil {
|
||||
config = map[string]any{}
|
||||
}
|
||||
|
||||
if err := obrimMarkerValidateType(markerType); err != nil {
|
||||
obrimMarkerLog("validation_failure", err.Error())
|
||||
return obrimMarkerBuildResponse(false, "FAILED_INVALID_TYPE", nil)
|
||||
}
|
||||
|
||||
response := obrimMarkerRoute(markerType, config)
|
||||
|
||||
if status, ok := response["status"].(bool); ok && status {
|
||||
obrimMarkerLog("success", markerType)
|
||||
} else {
|
||||
obrimMarkerLog("failure", markerType)
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// Purpose: Validate supported marker generation types.
|
||||
func obrimMarkerValidateType(markerType string) error {
|
||||
switch strings.ToLower(strings.TrimSpace(markerType)) {
|
||||
case obrimMarkerTypeTime,
|
||||
obrimMarkerTypeRandom,
|
||||
obrimMarkerTypeEncoded:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unsupported marker type")
|
||||
}
|
||||
}
|
||||
|
||||
// Purpose: Route execution to the selected marker generation workflow.
|
||||
func obrimMarkerRoute(markerType string, config map[string]any) map[string]any {
|
||||
switch strings.ToLower(strings.TrimSpace(markerType)) {
|
||||
case obrimMarkerTypeTime:
|
||||
return obrimMarkerTime(config)
|
||||
|
||||
case obrimMarkerTypeRandom:
|
||||
return obrimMarkerRandom(config)
|
||||
|
||||
case obrimMarkerTypeEncoded:
|
||||
return obrimMarkerEncoded(config)
|
||||
|
||||
default:
|
||||
return obrimMarkerBuildResponse(false, "FAILED_INVALID_TYPE", nil)
|
||||
}
|
||||
}
|
||||
|
||||
// Purpose: Build standardized success and error responses.
|
||||
func obrimMarkerBuildResponse(status bool, code string, payload any) map[string]any {
|
||||
return map[string]any{
|
||||
"status": status,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// Purpose: Generate time-based unique markers.
|
||||
func obrimMarkerTime(config map[string]any) map[string]any {
|
||||
epoch, instance, err := obrimMarkerValidateTime(config)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(false, "FAILED_TIME_VALIDATION", nil)
|
||||
}
|
||||
|
||||
marker, err := obrimMarkerTimeGenerate(epoch, instance)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(false, "FAILED_TIME_GENERATION", nil)
|
||||
}
|
||||
|
||||
payload := obrimMarkerTimePayload{
|
||||
Marker: marker,
|
||||
Epoch: epoch,
|
||||
Instance: instance,
|
||||
}
|
||||
|
||||
return obrimMarkerBuildResponse(true, "SUCCESS_TIME_GENERATED", payload)
|
||||
}
|
||||
|
||||
// Purpose: Validate time marker configuration parameters.
|
||||
func obrimMarkerValidateTime(config map[string]any) (int64, string, error) {
|
||||
epoch := int64(0)
|
||||
instance := ""
|
||||
|
||||
if value, exists := config["epoch"]; exists {
|
||||
switch v := value.(type) {
|
||||
case int64:
|
||||
epoch = v
|
||||
case int:
|
||||
epoch = int64(v)
|
||||
case float64:
|
||||
epoch = int64(v)
|
||||
default:
|
||||
return 0, "", fmt.Errorf("invalid epoch")
|
||||
}
|
||||
|
||||
if epoch < 0 {
|
||||
return 0, "", fmt.Errorf("invalid epoch")
|
||||
}
|
||||
}
|
||||
|
||||
if value, exists := config["instance"]; exists {
|
||||
instance, _ = value.(string)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(instance) == "" {
|
||||
instance = "default"
|
||||
}
|
||||
|
||||
return epoch, instance, nil
|
||||
}
|
||||
|
||||
// Purpose: Produce the final time-based marker value.
|
||||
func obrimMarkerTimeGenerate(epoch int64, instance string) (string, error) {
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
if epoch > now {
|
||||
return "", fmt.Errorf("epoch exceeds current time")
|
||||
}
|
||||
|
||||
timestamp := now - epoch
|
||||
|
||||
sequence := atomic.AddUint64(&obrimMarkerSequence, 1)
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%X-%s-%X",
|
||||
timestamp,
|
||||
strings.ToUpper(instance),
|
||||
sequence,
|
||||
), nil
|
||||
}
|
||||
|
||||
// Purpose: Generate random collision-resistant markers.
|
||||
func obrimMarkerRandom(config map[string]any) map[string]any {
|
||||
length, charset, err := obrimMarkerValidateRandom(config)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(false, "FAILED_RANDOM_VALIDATION", nil)
|
||||
}
|
||||
|
||||
marker, err := obrimMarkerRandomGenerate(length, charset)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(false, "FAILED_RANDOM_GENERATION", nil)
|
||||
}
|
||||
|
||||
payload := obrimMarkerRandomPayload{
|
||||
Marker: marker,
|
||||
Length: length,
|
||||
Charset: charset,
|
||||
}
|
||||
|
||||
return obrimMarkerBuildResponse(true, "SUCCESS_RANDOM_GENERATED", payload)
|
||||
}
|
||||
|
||||
// Purpose: Validate random marker configuration parameters.
|
||||
func obrimMarkerValidateRandom(config map[string]any) (int, string, error) {
|
||||
length := obrimMarkerDefaultRandomLength
|
||||
charset := obrimMarkerDefaultCharset
|
||||
|
||||
if value, exists := config["length"]; exists {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
length = v
|
||||
case int64:
|
||||
length = int(v)
|
||||
case float64:
|
||||
length = int(v)
|
||||
default:
|
||||
return 0, "", fmt.Errorf("invalid length")
|
||||
}
|
||||
}
|
||||
|
||||
if value, exists := config["charset"]; exists {
|
||||
if text, ok := value.(string); ok {
|
||||
charset = text
|
||||
}
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return 0, "", fmt.Errorf("invalid length")
|
||||
}
|
||||
|
||||
if len(charset) == 0 {
|
||||
return 0, "", fmt.Errorf("invalid charset")
|
||||
}
|
||||
|
||||
return length, charset, nil
|
||||
}
|
||||
|
||||
// Purpose: Produce the final random marker value.
|
||||
func obrimMarkerRandomGenerate(length int, charset string) (string, error) {
|
||||
buffer := make([]byte, length)
|
||||
|
||||
for index := 0; index < length; index++ {
|
||||
randomValue := make([]byte, 8)
|
||||
|
||||
if _, err := rand.Read(randomValue); err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
position := binary.BigEndian.Uint64(randomValue) % uint64(len(charset))
|
||||
buffer[index] = charset[position]
|
||||
}
|
||||
|
||||
return string(buffer), nil
|
||||
}
|
||||
|
||||
// Purpose: Generate deterministic encoded markers.
|
||||
func obrimMarkerEncoded(config map[string]any) map[string]any {
|
||||
data, length, charset, salt, err := obrimMarkerValidateEncoded(config)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(false, "FAILED_ENCODED_VALIDATION", nil)
|
||||
}
|
||||
|
||||
marker, err := obrimMarkerEncodedGenerate(data, length, charset, salt)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(false, "FAILED_ENCODED_GENERATION", nil)
|
||||
}
|
||||
|
||||
payload := obrimMarkerEncodedPayload{
|
||||
Marker: marker,
|
||||
Source: data,
|
||||
Length: length,
|
||||
}
|
||||
|
||||
return obrimMarkerBuildResponse(true, "SUCCESS_ENCODED_GENERATED", payload)
|
||||
}
|
||||
|
||||
// Purpose: Validate encoded marker configuration parameters.
|
||||
func obrimMarkerValidateEncoded(config map[string]any) (string, int, string, string, error) {
|
||||
data := ""
|
||||
length := obrimMarkerDefaultEncodedLength
|
||||
charset := obrimMarkerDefaultCharset
|
||||
salt := ""
|
||||
|
||||
if value, exists := config["data"]; exists {
|
||||
data, _ = value.(string)
|
||||
}
|
||||
|
||||
if strings.TrimSpace(data) == "" {
|
||||
return "", 0, "", "", fmt.Errorf("missing data")
|
||||
}
|
||||
|
||||
if value, exists := config["length"]; exists {
|
||||
switch v := value.(type) {
|
||||
case int:
|
||||
length = v
|
||||
case int64:
|
||||
length = int(v)
|
||||
case float64:
|
||||
length = int(v)
|
||||
default:
|
||||
return "", 0, "", "", fmt.Errorf("invalid length")
|
||||
}
|
||||
}
|
||||
|
||||
if value, exists := config["charset"]; exists {
|
||||
if text, ok := value.(string); ok {
|
||||
charset = text
|
||||
}
|
||||
}
|
||||
|
||||
if value, exists := config["salt"]; exists {
|
||||
salt, _ = value.(string)
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return "", 0, "", "", fmt.Errorf("invalid length")
|
||||
}
|
||||
|
||||
if len(charset) == 0 {
|
||||
return "", 0, "", "", fmt.Errorf("invalid charset")
|
||||
}
|
||||
|
||||
return data, length, charset, salt, nil
|
||||
}
|
||||
|
||||
// Purpose: Produce the final encoded marker value.
|
||||
func obrimMarkerEncodedGenerate(
|
||||
data string,
|
||||
length int,
|
||||
charset string,
|
||||
salt string,
|
||||
) (string, error) {
|
||||
hash := sha256.Sum256([]byte(data + salt))
|
||||
|
||||
output := make([]byte, length)
|
||||
|
||||
for index := 0; index < length; index++ {
|
||||
hashIndex := index % len(hash)
|
||||
position := int(math.Abs(float64(hash[hashIndex]))) % len(charset)
|
||||
output[index] = charset[position]
|
||||
}
|
||||
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// Purpose: Log utility lifecycle events.
|
||||
func obrimMarkerLog(event string, message string) {
|
||||
_ = event
|
||||
_ = message
|
||||
}
|
||||
@ -0,0 +1,335 @@
|
||||
package progress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimProgressTypeCountable = "countable"
|
||||
obrimProgressTypeUncountable = "uncountable"
|
||||
|
||||
obrimProgressStateStarted = "started"
|
||||
obrimProgressStateRunning = "running"
|
||||
obrimProgressStateCompleted = "completed"
|
||||
obrimProgressStateCanceled = "canceled"
|
||||
|
||||
obrimProgressSuccessStarted = "SUCCESS_PROGRESS_STARTED"
|
||||
obrimProgressSuccessRunning = "SUCCESS_PROGRESS_RUNNING"
|
||||
obrimProgressSuccessCompleted = "SUCCESS_PROGRESS_COMPLETED"
|
||||
obrimProgressSuccessCanceled = "SUCCESS_PROGRESS_CANCELED"
|
||||
|
||||
obrimProgressFailedMisconfigured = "FAILED_PROGRESS_MISCONFIGURED"
|
||||
|
||||
obrimProgressVisualWidth = 20
|
||||
)
|
||||
|
||||
var (
|
||||
// obrimProgressMutex protects progress state transitions from corruption.
|
||||
obrimProgressMutex sync.Mutex
|
||||
)
|
||||
|
||||
// ObrimProgressConfig defines progress configuration.
|
||||
type ObrimProgressConfig struct {
|
||||
State string `json:"state"`
|
||||
Current int `json:"current,omitempty"`
|
||||
Target int `json:"target,omitempty"`
|
||||
Placeholder string `json:"placeholder,omitempty"`
|
||||
}
|
||||
|
||||
// ObrimProgressResponse defines standardized utility responses.
|
||||
type ObrimProgressResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload interface{} `json:"payload"`
|
||||
}
|
||||
|
||||
// ObrimProgressCountablePayload defines countable progress payload.
|
||||
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.
|
||||
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 ObrimProgressConfig) ObrimProgressResponse {
|
||||
obrimProgressMutex.Lock()
|
||||
defer obrimProgressMutex.Unlock()
|
||||
|
||||
if err := obrimProgressValidate(progressType, config); err != nil {
|
||||
return obrimProgressBuildResponse(false, obrimProgressFailedMisconfigured, nil)
|
||||
}
|
||||
|
||||
stateCode := obrimProgressHandleState(config.State)
|
||||
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
return obrimProgressCountable(config, stateCode)
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
return obrimProgressUncountable(config, stateCode)
|
||||
|
||||
default:
|
||||
return obrimProgressBuildResponse(false, obrimProgressFailedMisconfigured, nil)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimProgressValidate validates input parameters and configuration consistency.
|
||||
func obrimProgressValidate(progressType string, config ObrimProgressConfig) error {
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
if config.Target <= 0 {
|
||||
return fmt.Errorf("invalid target")
|
||||
}
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
if strings.TrimSpace(config.Placeholder) == "" {
|
||||
return fmt.Errorf("missing placeholder")
|
||||
}
|
||||
|
||||
default:
|
||||
return fmt.Errorf("invalid type")
|
||||
}
|
||||
|
||||
switch config.State {
|
||||
case obrimProgressStateStarted,
|
||||
obrimProgressStateRunning,
|
||||
obrimProgressStateCompleted,
|
||||
obrimProgressStateCanceled:
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("invalid state")
|
||||
}
|
||||
}
|
||||
|
||||
// obrimProgressHandleState processes lifecycle state transitions.
|
||||
func obrimProgressHandleState(state string) string {
|
||||
switch state {
|
||||
case obrimProgressStateStarted:
|
||||
log.Printf(`{"feature":"progress","event":"started"}`)
|
||||
return obrimProgressSuccessStarted
|
||||
|
||||
case obrimProgressStateRunning:
|
||||
log.Printf(`{"feature":"progress","event":"running"}`)
|
||||
return obrimProgressSuccessRunning
|
||||
|
||||
case obrimProgressStateCompleted:
|
||||
log.Printf(`{"feature":"progress","event":"completed"}`)
|
||||
return obrimProgressSuccessCompleted
|
||||
|
||||
case obrimProgressStateCanceled:
|
||||
log.Printf(`{"feature":"progress","event":"canceled"}`)
|
||||
return obrimProgressSuccessCanceled
|
||||
|
||||
default:
|
||||
return obrimProgressFailedMisconfigured
|
||||
}
|
||||
}
|
||||
|
||||
// obrimProgressCountable processes countable progress tracking workflows.
|
||||
func obrimProgressCountable(
|
||||
config ObrimProgressConfig,
|
||||
code string,
|
||||
) ObrimProgressResponse {
|
||||
percentage := obrimProgressCalculatePercentage(
|
||||
config.Current,
|
||||
config.Target,
|
||||
)
|
||||
|
||||
message := obrimProgressGenerateMessage(
|
||||
obrimProgressTypeCountable,
|
||||
config.State,
|
||||
percentage,
|
||||
"",
|
||||
)
|
||||
|
||||
visual := obrimProgressGenerateVisual(
|
||||
obrimProgressTypeCountable,
|
||||
percentage,
|
||||
"",
|
||||
)
|
||||
|
||||
payload := ObrimProgressCountablePayload{
|
||||
State: config.State,
|
||||
Current: config.Current,
|
||||
Target: config.Target,
|
||||
Percentage: percentage,
|
||||
Message: message,
|
||||
Visual: visual,
|
||||
}
|
||||
|
||||
return obrimProgressBuildResponse(true, code, payload)
|
||||
}
|
||||
|
||||
// obrimProgressCalculatePercentage calculates and normalizes completion percentages.
|
||||
func obrimProgressCalculatePercentage(current int, target int) int {
|
||||
if current < 0 {
|
||||
return 0
|
||||
}
|
||||
|
||||
if target <= 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 ObrimProgressConfig,
|
||||
code string,
|
||||
) ObrimProgressResponse {
|
||||
placeholder := obrimProgressResolvePlaceholder(
|
||||
config.Placeholder,
|
||||
)
|
||||
|
||||
message := obrimProgressGenerateMessage(
|
||||
obrimProgressTypeUncountable,
|
||||
config.State,
|
||||
0,
|
||||
placeholder,
|
||||
)
|
||||
|
||||
visual := obrimProgressGenerateVisual(
|
||||
obrimProgressTypeUncountable,
|
||||
0,
|
||||
placeholder,
|
||||
)
|
||||
|
||||
payload := ObrimProgressUncountablePayload{
|
||||
State: config.State,
|
||||
Placeholder: placeholder,
|
||||
Message: message,
|
||||
Visual: visual,
|
||||
}
|
||||
|
||||
return obrimProgressBuildResponse(true, code, payload)
|
||||
}
|
||||
|
||||
// obrimProgressResolvePlaceholder validates and prepares placeholder activity messages.
|
||||
func obrimProgressResolvePlaceholder(placeholder string) string {
|
||||
return strings.TrimSpace(placeholder)
|
||||
}
|
||||
|
||||
// obrimProgressGenerateMessage generates human-readable progress messages.
|
||||
func obrimProgressGenerateMessage(
|
||||
progressType string,
|
||||
state string,
|
||||
percentage int,
|
||||
placeholder string,
|
||||
) string {
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
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 "Progress completed successfully."
|
||||
|
||||
case obrimProgressStateCanceled:
|
||||
return "Progress canceled."
|
||||
}
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
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,
|
||||
percentage int,
|
||||
placeholder string,
|
||||
) string {
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
filled := int(
|
||||
math.Round(
|
||||
(float64(percentage) / 100.0) * obrimProgressVisualWidth,
|
||||
),
|
||||
)
|
||||
|
||||
if filled > obrimProgressVisualWidth {
|
||||
filled = obrimProgressVisualWidth
|
||||
}
|
||||
|
||||
if filled < 0 {
|
||||
filled = 0
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"[%s%s] %d%%",
|
||||
strings.Repeat("=", filled),
|
||||
strings.Repeat(" ", obrimProgressVisualWidth-filled),
|
||||
percentage,
|
||||
)
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
return fmt.Sprintf("[...] %s", placeholder)
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimProgressBuildResponse constructs standardized utility response payloads.
|
||||
func obrimProgressBuildResponse(
|
||||
status bool,
|
||||
code string,
|
||||
payload interface{},
|
||||
) ObrimProgressResponse {
|
||||
if !status {
|
||||
payload = nil
|
||||
}
|
||||
|
||||
return ObrimProgressResponse{
|
||||
Status: status,
|
||||
Code: code,
|
||||
Payload: payload,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,396 @@
|
||||
package retriever
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"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"
|
||||
ObrimRetrieverCodeFailedProviderUnavailable = "FAILED_PROVIDER_UNAVAILABLE"
|
||||
)
|
||||
|
||||
// ObrimRetrieverJsonProvider defines a JSON retrieval provider.
|
||||
type ObrimRetrieverJsonProvider interface {
|
||||
Retrieve() (any, error)
|
||||
}
|
||||
|
||||
// ObrimRetrieverResponse defines the standardized utility response.
|
||||
type ObrimRetrieverResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonRegistry stores registered JSON providers.
|
||||
var obrimRetrieverJsonRegistry = map[string]ObrimRetrieverJsonProvider{}
|
||||
|
||||
// obrimRetrieverDbRegistry stores registered DB providers.
|
||||
var obrimRetrieverDbRegistry = map[string]any{}
|
||||
|
||||
// ObrimRetriever retrieves data using the configured retrieval type.
|
||||
func ObrimRetriever(retrievalType string, config map[string]any) map[string]any {
|
||||
if !obrimRetrieverValidateType(retrievalType) {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
ObrimRetrieverCodeFailedUnsupportedType,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if errCode := obrimRetrieverValidateConfig(retrievalType, config); errCode != "" {
|
||||
return obrimRetrieverBuildResponse(false, errCode, 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) {
|
||||
jsonProvider, ok := provider.(ObrimRetrieverJsonProvider)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
obrimRetrieverJsonRegistry[resource] = jsonProvider
|
||||
}
|
||||
|
||||
// ObrimRetrieverDbRegister registers a DB provider.
|
||||
func ObrimRetrieverDbRegister(resource string, provider any) {
|
||||
obrimRetrieverDbRegistry[resource] = provider
|
||||
}
|
||||
|
||||
// obrimRetrieverValidateType validates the retrieval type.
|
||||
func obrimRetrieverValidateType(retrievalType string) bool {
|
||||
switch retrievalType {
|
||||
case obrimRetrieverTypeJson:
|
||||
return true
|
||||
|
||||
case obrimRetrieverTypeDb:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimRetrieverValidateConfig validates 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.
|
||||
func obrimRetrieverResolveProvider(
|
||||
retrievalType string,
|
||||
resource string,
|
||||
) (any, bool) {
|
||||
switch retrievalType {
|
||||
case obrimRetrieverTypeJson:
|
||||
provider, ok := obrimRetrieverJsonRegistry[resource]
|
||||
return provider, ok
|
||||
|
||||
case obrimRetrieverTypeDb:
|
||||
provider, ok := obrimRetrieverDbRegistry[resource]
|
||||
return provider, ok
|
||||
}
|
||||
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// obrimRetrieverBuildResponse builds a standard 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)
|
||||
|
||||
providerAny, ok := obrimRetrieverResolveProvider(
|
||||
obrimRetrieverTypeJson,
|
||||
resource,
|
||||
)
|
||||
if !ok {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
ObrimRetrieverCodeFailedResourceNotFound,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
provider, ok := providerAny.(ObrimRetrieverJsonProvider)
|
||||
if !ok {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
ObrimRetrieverCodeFailedProviderUnavailable,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if fieldsValue, exists := config["fields"]; exists {
|
||||
return obrimRetrieverJsonFields(provider, fieldsValue)
|
||||
}
|
||||
|
||||
if pathValue, exists := config["path"]; exists {
|
||||
return obrimRetrieverJsonPath(provider, pathValue.(string))
|
||||
}
|
||||
|
||||
return obrimRetrieverJsonResource(provider)
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonResource retrieves an entire resource.
|
||||
func obrimRetrieverJsonResource(
|
||||
provider ObrimRetrieverJsonProvider,
|
||||
) map[string]any {
|
||||
data, err := provider.Retrieve()
|
||||
if err != nil {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
ObrimRetrieverCodeFailedResourceNotFound,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimRetrieverBuildResponse(
|
||||
true,
|
||||
ObrimRetrieverCodeSuccessResource,
|
||||
map[string]any{
|
||||
"data": data,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonPath retrieves a path value.
|
||||
func obrimRetrieverJsonPath(
|
||||
provider ObrimRetrieverJsonProvider,
|
||||
path string,
|
||||
) map[string]any {
|
||||
data, err := provider.Retrieve()
|
||||
if err != nil {
|
||||
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 field values.
|
||||
func obrimRetrieverJsonFields(
|
||||
provider ObrimRetrieverJsonProvider,
|
||||
fieldsValue any,
|
||||
) map[string]any {
|
||||
data, err := provider.Retrieve()
|
||||
if err != nil {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
ObrimRetrieverCodeFailedResourceNotFound,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
fields, ok := obrimRetrieverNormalizeFields(fieldsValue)
|
||||
if !ok {
|
||||
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 configuration.
|
||||
func obrimRetrieverJsonValidate(config map[string]any) string {
|
||||
resourceValue, exists := config["resource"]
|
||||
if !exists {
|
||||
return ObrimRetrieverCodeFailedMissingConfig
|
||||
}
|
||||
|
||||
resource, ok := resourceValue.(string)
|
||||
if !ok || strings.TrimSpace(resource) == "" {
|
||||
return ObrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
_, hasPath := config["path"]
|
||||
_, hasFields := config["fields"]
|
||||
|
||||
if hasPath && hasFields {
|
||||
return ObrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
if hasPath {
|
||||
path, ok := config["path"].(string)
|
||||
if !ok || strings.TrimSpace(path) == "" {
|
||||
return ObrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
}
|
||||
|
||||
if hasFields {
|
||||
fields, ok := obrimRetrieverNormalizeFields(config["fields"])
|
||||
if !ok || len(fields) == 0 {
|
||||
return ObrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimRetrieverDb processes DB retrieval requests.
|
||||
func obrimRetrieverDb(config map[string]any) map[string]any {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
ObrimRetrieverCodeFailedNotImplemented,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimRetrieverDbValidate validates DB configuration.
|
||||
func obrimRetrieverDbValidate(config map[string]any) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonResolvePath resolves a dot path.
|
||||
func obrimRetrieverJsonResolvePath(
|
||||
data any,
|
||||
path string,
|
||||
) (any, bool) {
|
||||
current := data
|
||||
|
||||
for _, segment := range strings.Split(path, ".") {
|
||||
object, ok := current.(map[string]any)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
value, exists := object[segment]
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
current = value
|
||||
}
|
||||
|
||||
return current, true
|
||||
}
|
||||
|
||||
// obrimRetrieverNormalizeFields normalizes field inputs.
|
||||
func obrimRetrieverNormalizeFields(
|
||||
value any,
|
||||
) ([]string, bool) {
|
||||
switch fields := value.(type) {
|
||||
case []string:
|
||||
return fields, true
|
||||
|
||||
case []any:
|
||||
result := make([]string, 0, len(fields))
|
||||
|
||||
for _, field := range fields {
|
||||
text, ok := field.(string)
|
||||
if !ok {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
result = append(result, text)
|
||||
}
|
||||
|
||||
return result, true
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimRetrieverError formats internal errors.
|
||||
func obrimRetrieverError(message string) error {
|
||||
return fmt.Errorf("%s", message)
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
// obrimStatusLabelInfo represents the INFO status label.
|
||||
obrimStatusLabelInfo = "INFO"
|
||||
|
||||
// obrimStatusLabelWarning represents the WARNING status label.
|
||||
obrimStatusLabelWarning = "WARNING"
|
||||
|
||||
// obrimStatusLabelSuccess represents the SUCCESS status label.
|
||||
obrimStatusLabelSuccess = "SUCCESS"
|
||||
|
||||
// obrimStatusLabelError represents the ERROR status label.
|
||||
obrimStatusLabelError = "ERROR"
|
||||
)
|
||||
|
||||
// ObrimStatus emits a standardized CLI status message.
|
||||
func ObrimStatus(label string, message string) {
|
||||
normalizedLabel := obrimStatusNormalizeLabel(label)
|
||||
formattedLabel := obrimStatusFormatLabel(normalizedLabel)
|
||||
indicator := obrimStatusFormatIndicator(normalizedLabel)
|
||||
finalMessage := obrimStatusBuildMessage(formattedLabel, indicator, message)
|
||||
|
||||
fmt.Println(finalMessage)
|
||||
}
|
||||
|
||||
// obrimStatusNormalizeLabel normalizes and validates status 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 status labels.
|
||||
func obrimStatusFormatLabel(label string) string {
|
||||
return fmt.Sprintf("[%s]", label)
|
||||
}
|
||||
|
||||
// obrimStatusFormatIndicator generates visual status indicators.
|
||||
func obrimStatusFormatIndicator(label string) string {
|
||||
switch label {
|
||||
case obrimStatusLabelInfo:
|
||||
return "ℹ"
|
||||
case obrimStatusLabelWarning:
|
||||
return "⚠"
|
||||
case obrimStatusLabelSuccess:
|
||||
return "✓"
|
||||
case obrimStatusLabelError:
|
||||
return "✖"
|
||||
default:
|
||||
return "ℹ"
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
go 1.26.4
|
||||
|
||||
require github.com/sqids/sqids-go v0.4.1
|
||||
|
||||
Loading…
Reference in New Issue
Block a user