Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion go.mod

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 6 additions & 6 deletions tavern/internal/c2/api_claim_tasks.go
Original file line number Diff line number Diff line change
Expand Up @@ -379,21 +379,21 @@ func (srv *Server) ClaimTasks(ctx context.Context, req *c2pb.ClaimTasksRequest)
return nil, rollback(tx, fmt.Errorf("failed to parse task parameters (id=%d,questID=%d): %w", taskID, claimedQuest.ID, err))
}
}
claimedFiles, err := claimedTome.QueryFiles().All(ctx)
claimedAssets, err := claimedTome.QueryAssets().All(ctx)
if err != nil {
return nil, rollback(tx, fmt.Errorf("failed to load tome files (id=%d,tomeID=%d)", taskID, claimedTome.ID))
return nil, rollback(tx, fmt.Errorf("failed to load tome assets (id=%d,tomeID=%d)", taskID, claimedTome.ID))
}
claimedFileNames := make([]string, 0, len(claimedFiles))
for _, f := range claimedFiles {
claimedFileNames = append(claimedFileNames, f.Name)
claimedAssetNames := make([]string, 0, len(claimedAssets))
for _, a := range claimedAssets {
claimedAssetNames = append(claimedAssetNames, a.Name)
}
resp.Tasks = append(resp.Tasks, &c2pb.Task{
Id: int64(claimedTask.ID),
QuestName: claimedQuest.Name,
Tome: &epb.Tome{
Eldritch: claimedTome.Eldritch,
Parameters: params,
FileNames: claimedFileNames,
FileNames: claimedAssetNames,
},
})
}
Expand Down
16 changes: 8 additions & 8 deletions tavern/internal/c2/api_fetch_asset.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,32 +9,32 @@ import (
"google.golang.org/grpc/status"
"realm.pub/tavern/internal/c2/c2pb"
"realm.pub/tavern/internal/ent"
"realm.pub/tavern/internal/ent/file"
"realm.pub/tavern/internal/ent/asset"
)

func (srv *Server) FetchAsset(req *c2pb.FetchAssetRequest, stream c2pb.C2_FetchAssetServer) error {
ctx := stream.Context()

// Load File
// Load Asset
name := req.GetName()
f, err := srv.graph.File.Query().
Where(file.Name(name)).
a, err := srv.graph.Asset.Query().
Where(asset.Name(name)).
Only(ctx)
if ent.IsNotFound(err) {
return status.Errorf(codes.NotFound, "%v", err)
}
if err != nil {
return status.Errorf(codes.Internal, "failed to query file (%q): %v", name, err)
return status.Errorf(codes.Internal, "failed to query asset (%q): %v", name, err)
}

// Set Header Metadata
stream.SetHeader(metadata.Pairs(
"sha3-256-checksum", f.Hash,
"file-size", fmt.Sprintf("%d", f.Size),
"sha3-256-checksum", a.Hash,
"file-size", fmt.Sprintf("%d", a.Size),
))

// Send Asset Chunks
buf := bytes.NewBuffer(f.Content)
buf := bytes.NewBuffer(a.Content)
for {
// Check Empty Buffer
if buf.Len() < 1 {
Expand Down
10 changes: 5 additions & 5 deletions tavern/internal/c2/api_fetch_asset_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ func TestFetchAsset(t *testing.T) {
_, err := rand.Read(data)
require.NoError(t, err)

// Create File
f := graph.File.Create().
// Create Asset
a := graph.Asset.Create().
SetName(tc.fileName).
SetContent(data).
SaveX(ctx)
Expand Down Expand Up @@ -93,15 +93,15 @@ func TestFetchAsset(t *testing.T) {
}

// Assert Content
assert.Equal(t, f.Content, buf.Bytes())
assert.Equal(t, a.Content, buf.Bytes())

// Assert Headers
metadata, err := fileClient.Header()
require.NoError(t, err)
require.Len(t, metadata.Get("sha3-256-checksum"), 1)
assert.Equal(t, f.Hash, metadata.Get("sha3-256-checksum")[0])
assert.Equal(t, a.Hash, metadata.Get("sha3-256-checksum")[0])
require.Len(t, metadata.Get("file-size"), 1)
assert.Equal(t, fmt.Sprintf("%d", f.Size), metadata.Get("file-size")[0])
assert.Equal(t, fmt.Sprintf("%d", a.Size), metadata.Get("file-size")[0])
}

// Run Tests
Expand Down
26 changes: 13 additions & 13 deletions tavern/internal/c2/c2test/ent.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ import (
"realm.pub/tavern/internal/c2/c2pb"
"realm.pub/tavern/internal/c2/epb"
"realm.pub/tavern/internal/ent"
"realm.pub/tavern/internal/ent/asset"
"realm.pub/tavern/internal/ent/beacon"
"realm.pub/tavern/internal/ent/file"
"realm.pub/tavern/internal/namegen"
)

Expand Down Expand Up @@ -50,15 +50,15 @@ func ConvertTaskToC2PB(t *testing.T, ctx context.Context, task *ent.Task) *c2pb.
),
)

var fileNames []string
files := task.
var assetNames []string
assets := task.
QueryQuest().
QueryTome().
QueryFiles().
Order(file.ByID()).
QueryAssets().
Order(asset.ByID()).
AllX(ctx)
for _, f := range files {
fileNames = append(fileNames, f.Name)
for _, a := range assets {
assetNames = append(assetNames, a.Name)
}

return &c2pb.Task{
Expand All @@ -70,7 +70,7 @@ func ConvertTaskToC2PB(t *testing.T, ctx context.Context, task *ent.Task) *c2pb.
OnlyX(ctx).
Eldritch,
Parameters: params,
FileNames: fileNames,
FileNames: assetNames,
},
QuestName: task.
QueryQuest().
Expand All @@ -85,16 +85,16 @@ func NewRandomAssignedTask(ctx context.Context, graph *ent.Client, beaconIdentif
Where(
beacon.Identifier(beaconIdentifier),
).OnlyX(ctx)
bundle := graph.File.Create().
bundle := graph.Asset.Create().
SetName(namegen.NewComplex()).
SetContent(newRandomBytes(1024)).
SaveX(ctx)
files := []*ent.File{
graph.File.Create().
assets := []*ent.Asset{
graph.Asset.Create().
SetName(namegen.NewComplex()).
SetContent(newRandomBytes(1024)).
SaveX(ctx),
graph.File.Create().
graph.Asset.Create().
SetName(namegen.NewComplex()).
SetContent(newRandomBytes(1024)).
SaveX(ctx),
Expand All @@ -105,7 +105,7 @@ func NewRandomAssignedTask(ctx context.Context, graph *ent.Client, beaconIdentif
SetDescription(string(newRandomBytes(120))).
SetAuthor("kcarretto").
SetParamDefs(`[{"name":"test-param","label":"Test","type":"string","placeholder":"Enter text..."}]`).
AddFiles(files...).
AddAssets(assets...).
SaveX(ctx)
quest := graph.Quest.Create().
SetName(namegen.NewComplex()).
Expand Down
32 changes: 16 additions & 16 deletions tavern/internal/cdn/download.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,52 +6,52 @@ import (
"strings"

"realm.pub/tavern/internal/ent"
"realm.pub/tavern/internal/ent/file"
"realm.pub/tavern/internal/ent/asset"
"realm.pub/tavern/internal/errors"
)

// HeaderIfNoneMatch is the name of the header the client should set if they wish to only download the file if it has been modified since the provided hash.
// HeaderEtag is set by the server to the hash of the downloaded file.
// HeaderIfNoneMatch is the name of the header the client should set if they wish to only download the asset if it has been modified since the provided hash.
// HeaderEtag is set by the server to the hash of the downloaded asset.
const (
HeaderIfNoneMatch = "If-None-Match"
HeaderEtag = "Etag"
)

// NewDownloadHandler returns an HTTP handler responsible for downloading a file from the CDN.
// NewDownloadHandler returns an HTTP handler responsible for downloading a asset from the CDN.
func NewDownloadHandler(graph *ent.Client, prefix string) http.Handler {
return errors.WrapHandler(func(w http.ResponseWriter, req *http.Request) error {
ctx := req.Context()

// Get the File name from the request URI
fileName := strings.TrimPrefix(req.URL.Path, prefix)
if fileName == "" || fileName == "." || fileName == "/" {
// Get the Asset name from the request URI
assetName := strings.TrimPrefix(req.URL.Path, prefix)
if assetName == "" || assetName == "." || assetName == "/" {
return ErrInvalidFileName
}

fileQuery := graph.File.Query().Where(file.Name(fileName))
assetQuery := graph.Asset.Query().Where(asset.Name(assetName))

// If hash was provided, check to see if the file has been updated. Note that
// If hash was provided, check to see if the asset has been updated. Note that
// http.ServeContent should handle this, but we want to avoid the expensive DB
// query where possible.
if hash := req.Header.Get(HeaderIfNoneMatch); hash != "" {
if exists := fileQuery.Clone().Where(file.Hash(hash)).ExistX(ctx); exists {
if exists := assetQuery.Clone().Where(asset.Hash(hash)).ExistX(ctx); exists {
return ErrFileNotModified
}
}

// Ensure the file exists
if exists := fileQuery.Clone().ExistX(ctx); !exists {
// Ensure the asset exists
if exists := assetQuery.Clone().ExistX(ctx); !exists {
return ErrFileNotFound
}

f := fileQuery.OnlyX(ctx)
a := assetQuery.OnlyX(ctx)

// Set Etag to hash of file
w.Header().Set(HeaderEtag, f.Hash)
// Set Etag to hash of asset
w.Header().Set(HeaderEtag, a.Hash)

// Set Content-Type and serve content
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeContent(w, req, f.Name, f.LastModifiedAt, bytes.NewReader(f.Content))
http.ServeContent(w, req, a.Name, a.LastModifiedAt, bytes.NewReader(a.Content))

return nil
})
Expand Down
16 changes: 8 additions & 8 deletions tavern/internal/cdn/download_link.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ func NewLinkDownloadHandler(graph *ent.Client, prefix string) http.Handler {
return ErrFileNotFound
}

// Query for the link by path, including the associated file
// Query for the link by path, including the associated asset
linkQuery := graph.Link.Query().
Where(link.Path(linkPath)).
WithFile()
WithAsset()

// Ensure the link exists
if exists, err := linkQuery.Clone().Exist(ctx); !exists || err != nil {
Expand Down Expand Up @@ -63,18 +63,18 @@ func NewLinkDownloadHandler(graph *ent.Client, prefix string) http.Handler {
}
}

// Get the associated file
f := l.Edges.File
if f == nil {
// Get the associated asset
a := l.Edges.Asset
if a == nil {
return ErrFileNotFound
}

// Set Etag to hash of file
w.Header().Set(HeaderEtag, f.Hash)
// Set Etag to hash of asset
w.Header().Set(HeaderEtag, a.Hash)

// Set Content-Type and serve content
w.Header().Set("Content-Type", "application/octet-stream")
http.ServeContent(w, req, f.Name, f.LastModifiedAt, bytes.NewReader(f.Content))
http.ServeContent(w, req, a.Name, a.LastModifiedAt, bytes.NewReader(a.Content))

return nil
})
Expand Down
10 changes: 5 additions & 5 deletions tavern/internal/cdn/download_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,19 @@ func TestDownload(t *testing.T) {
defer graph.Close()

expectedContent := []byte("file_content")
existingFile := newFile(graph, "ExistingTestFile", expectedContent)
existingAsset := newAsset(graph, "ExistingTestAsset", expectedContent)

t.Run("File", newDownloadTest(
t.Run("Asset", newDownloadTest(
graph,
newDownloadRequest(existingFile.Name),
newDownloadRequest(existingAsset.Name),
func(t *testing.T, fileContent []byte, err *errors.HTTP) {
assert.Nil(t, err)
assert.Equal(t, string(expectedContent), string(fileContent))
},
))
t.Run("CachedFile", newDownloadTest(
t.Run("CachedAsset", newDownloadTest(
graph,
newDownloadRequest(existingFile.Name, withIfNoneMatchHeader(existingFile.Hash)),
newDownloadRequest(existingAsset.Name, withIfNoneMatchHeader(existingAsset.Hash)),
func(t *testing.T, fileContent []byte, err *errors.HTTP) {
require.NotNil(t, err)
assert.Equal(t, http.StatusNotModified, err.StatusCode)
Expand Down
40 changes: 20 additions & 20 deletions tavern/internal/cdn/upload.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,59 +6,59 @@ import (
"net/http"

"realm.pub/tavern/internal/ent"
"realm.pub/tavern/internal/ent/file"
"realm.pub/tavern/internal/ent/asset"
"realm.pub/tavern/internal/errors"
)

// DefaultMaxUploadSize defines the maximum number of bytes an uploaded file can be.
// DefaultMaxUploadSize defines the maximum number of bytes an uploaded asset can be.
const DefaultMaxUploadSize = 10 << 20

// NewUploadHandler returns an HTTP handler responsible for uploading a file to the CDN.
// NewUploadHandler returns an HTTP handler responsible for uploading a asset to the CDN.
func NewUploadHandler(graph *ent.Client) http.Handler {
return errors.WrapHandler(func(w http.ResponseWriter, req *http.Request) error {
ctx := req.Context()

// Get the File name
// Get the Asset name
if err := req.ParseMultipartForm(DefaultMaxUploadSize); err != nil {
return err
}
fileName := req.PostFormValue("fileName")
if fileName == "" {
assetName := req.PostFormValue("fileName")
if assetName == "" {
return ErrInvalidFileName
}

// Get the File content
// Get the Asset content
f, _, err := req.FormFile("fileContent")
if err != nil {
return fmt.Errorf("%w: %v", ErrInvalidFileContent, err)
}
defer f.Close()
fileContent, err := ioutil.ReadAll(f)
assetContent, err := ioutil.ReadAll(f)
if err != nil {
return fmt.Errorf("%w: %v", ErrInvalidFileContent, err)
}

// Check if it has already been uploaded
fileQuery := graph.File.Query().Where(file.Name(fileName))
exists := fileQuery.Clone().ExistX(ctx)
assetQuery := graph.Asset.Query().Where(asset.Name(assetName))
exists := assetQuery.Clone().ExistX(ctx)

// Create or Update the file
var fileID int
// Create or Update the asset
var assetID int

if exists {
fileID = fileQuery.OnlyIDX(ctx)
graph.File.UpdateOneID(fileID).
SetContent(fileContent).
assetID = assetQuery.OnlyIDX(ctx)
graph.Asset.UpdateOneID(assetID).
SetContent(assetContent).
SaveX(ctx)
} else {
fileID = graph.File.Create().
SetName(fileName).
SetContent(fileContent).
assetID = graph.Asset.Create().
SetName(assetName).
SetContent(assetContent).
SaveX(ctx).ID
}

// Respond with JSON of the file ID
fmt.Fprintf(w, `{"data":{"file":{"id":%d}}}`, fileID)
// Respond with JSON of the asset ID
fmt.Fprintf(w, `{"data":{"asset":{"id":%d}}}`, assetID)
return nil
})
}
Loading
Loading