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
|
module go/module
|
||||||
|
|
||||||
go 1.26.4
|
go 1.26.4
|
||||||
|
|
||||||
|
require github.com/sqids/sqids-go v0.4.1
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user