mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-15 15:40:38 +02:00
Add files via upload
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
package workflowpackage
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Export builds a deterministic, human-readable single-workflow package.
|
||||
func Export(source Document) ([]byte, ExportMetadata, error) {
|
||||
source.ID = strings.TrimSpace(source.ID)
|
||||
source.Name = strings.TrimSpace(source.Name)
|
||||
if source.ID == "" || source.Name == "" || source.Version <= 0 || !safePackageWorkflowID(source.ID) {
|
||||
return nil, ExportMetadata{}, fmt.Errorf("workflow id, name and version are required")
|
||||
}
|
||||
if strings.TrimSpace(source.GraphJSON) == "" {
|
||||
return nil, ExportMetadata{}, fmt.Errorf("workflow graph_json is required")
|
||||
}
|
||||
contentHash, graphHash, payload, err := DocumentHashes(source)
|
||||
if err != nil {
|
||||
return nil, ExportMetadata{}, err
|
||||
}
|
||||
workflowPath := path.Join("workflows", source.ID+".json")
|
||||
createdAt := source.UpdatedAt.UTC()
|
||||
if createdAt.IsZero() {
|
||||
createdAt = time.Unix(0, 0).UTC()
|
||||
}
|
||||
manifest := Manifest{
|
||||
PackageFormat: PackageFormat,
|
||||
FormatVersion: FormatVersion,
|
||||
PackageID: "pkg_" + strings.TrimPrefix(contentHash, "sha256:")[:16],
|
||||
CreatedAt: createdAt.Format(time.RFC3339),
|
||||
Items: []ManifestItem{{
|
||||
Type: "workflow",
|
||||
Path: workflowPath,
|
||||
SourceID: source.ID,
|
||||
SourceRevision: source.Version,
|
||||
ContentHash: contentHash,
|
||||
GraphHash: graphHash,
|
||||
}},
|
||||
}
|
||||
manifestBytes, err := json.Marshal(manifest)
|
||||
if err != nil {
|
||||
return nil, ExportMetadata{}, fmt.Errorf("marshal manifest: %w", err)
|
||||
}
|
||||
checksums := fmt.Sprintf("%s manifest.json\n%s %s\n", strings.TrimPrefix(sha256Prefixed(manifestBytes), "sha256:"), strings.TrimPrefix(contentHash, "sha256:"), workflowPath)
|
||||
|
||||
var out bytes.Buffer
|
||||
zw := zip.NewWriter(&out)
|
||||
for _, entry := range []struct {
|
||||
name string
|
||||
data []byte
|
||||
}{
|
||||
{name: "checksums.sha256", data: []byte(checksums)},
|
||||
{name: "manifest.json", data: manifestBytes},
|
||||
{name: workflowPath, data: payload},
|
||||
} {
|
||||
header := &zip.FileHeader{Name: entry.name, Method: zip.Store}
|
||||
header.SetModTime(time.Unix(0, 0).UTC())
|
||||
writer, err := zw.CreateHeader(header)
|
||||
if err != nil {
|
||||
return nil, ExportMetadata{}, fmt.Errorf("write %s: %w", entry.name, err)
|
||||
}
|
||||
if _, err := writer.Write(entry.data); err != nil {
|
||||
return nil, ExportMetadata{}, fmt.Errorf("write %s: %w", entry.name, err)
|
||||
}
|
||||
}
|
||||
if err := zw.Close(); err != nil {
|
||||
return nil, ExportMetadata{}, fmt.Errorf("close package: %w", err)
|
||||
}
|
||||
pkg := out.Bytes()
|
||||
return pkg, ExportMetadata{
|
||||
PackageHash: sha256Prefixed(pkg),
|
||||
ContentHash: contentHash,
|
||||
GraphHash: graphHash,
|
||||
SourceRevision: source.Version,
|
||||
FileName: source.ID + ".csapkg.zip",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DocumentHashes returns the canonical package item and graph hashes together
|
||||
// with the canonical item bytes used by export and inspection persistence.
|
||||
func DocumentHashes(source Document) (string, string, []byte, error) {
|
||||
graph, err := canonicalJSON([]byte(source.GraphJSON))
|
||||
if err != nil {
|
||||
return "", "", nil, fmt.Errorf("canonicalize graph_json: %w", err)
|
||||
}
|
||||
source.GraphJSON = string(graph)
|
||||
payload, err := json.Marshal(source)
|
||||
if err != nil {
|
||||
return "", "", nil, fmt.Errorf("marshal workflow payload: %w", err)
|
||||
}
|
||||
return sha256Prefixed(payload), sha256Prefixed(graph), payload, nil
|
||||
}
|
||||
Reference in New Issue
Block a user