~edwargix/git.sr.ht

ffb36c00d0574fc15c050a7cbbd29c4ddf3eb47e — Adnan Maolood 2 years ago 9d12b36
api/graph: Delete artifacts upon repository deletion

Upon deleting a repository, gather a list of all the artifacts
associated with it and schedule their deletion in a background task.

Fixes: https://todo.sr.ht/~sircmpwn/git.sr.ht/356
2 files changed, 66 insertions(+), 2 deletions(-)

M api/graph/schema.resolvers.go
M api/repos/middleware.go
M api/graph/schema.resolvers.go => api/graph/schema.resolvers.go +23 -2
@@ 447,6 447,23 @@ func (r *mutationResolver) DeleteRepository(ctx context.Context, id int) (*model
	var repo model.Repository

	if err := database.WithTx(ctx, nil, func(tx *sql.Tx) error {
		rows, err := tx.QueryContext(ctx,
			`DELETE FROM artifacts WHERE repo_id = $1 RETURNING filename;`, id)
		if err != nil {
			return err
		}
		var artifacts []string
		for rows.Next() {
			var filename string
			if err := rows.Scan(&filename); err != nil {
				return err
			}
			artifacts = append(artifacts, filename)
		}
		if err := rows.Err(); err != nil {
			return err
		}

		row := tx.QueryRowContext(ctx, `
			DELETE FROM repository
			WHERE id = $1 AND owner_id = $2


@@ 467,10 484,14 @@ func (r *mutationResolver) DeleteRepository(ctx context.Context, id int) (*model
		webhooks.DeliverRepoEvent(ctx, model.WebhookEventRepoDeleted, &repo)
		webhooks.DeliverLegacyRepoDeleted(ctx, &repo)

		err := os.RemoveAll(repo.Path)
		if err != nil {
		if err := os.RemoveAll(repo.Path); err != nil {
			return err
		}

		if len(artifacts) > 0 {
			username := auth.ForContext(ctx).Username
			repos.DeleteArtifacts(ctx, username, repo.Name, artifacts)
		}
		return nil
	}); err != nil {
		return nil, err

M api/repos/middleware.go => api/repos/middleware.go +43 -0
@@ 3,13 3,18 @@ package repos
import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net/http"
	"path"
	"time"

	"git.sr.ht/~sircmpwn/core-go/config"
	"git.sr.ht/~sircmpwn/core-go/database"
	work "git.sr.ht/~sircmpwn/dowork"
	"github.com/go-git/go-git/v5"
	"github.com/minio/minio-go/v7"
	"github.com/minio/minio-go/v7/pkg/credentials"
)

type contextKey struct {


@@ 69,3 74,41 @@ func Clone(ctx context.Context, repoID int, repo *git.Repository, cloneURL strin
	queue.Enqueue(task)
	log.Printf("Enqueued clone of %s", cloneURL)
}

// Schedules deletion of artifacts.
func DeleteArtifacts(ctx context.Context, username, repoName string, filenames []string) {
	queue, ok := ctx.Value(ctxKey).(*work.Queue)
	if !ok {
		panic("No repos worker for this context")
	}
	task := work.NewTask(func(ctx context.Context) error {
		conf := config.ForContext(ctx)
		upstream, _ := conf.Get("objects", "s3-upstream")
		accessKey, _ := conf.Get("objects", "s3-access-key")
		secretKey, _ := conf.Get("objects", "s3-secret-key")
		bucket, _ := conf.Get("git.sr.ht", "s3-bucket")
		prefix, _ := conf.Get("git.sr.ht", "s3-prefix")

		if upstream == "" || accessKey == "" || secretKey == "" || bucket == "" {
			return fmt.Errorf("Object storage is not enabled for this server")
		}

		mc, err := minio.New(upstream, &minio.Options{
			Creds:  credentials.NewStaticV4(accessKey, secretKey, ""),
			Secure: true,
		})
		if err != nil {
			panic(err)
		}

		for _, filename := range filenames {
			s3path := path.Join(prefix, "artifacts", "~"+username, repoName, filename)
			if err := mc.RemoveObject(ctx, bucket, s3path, minio.RemoveObjectOptions{}); err != nil {
				return err
			}
		}
		return nil
	})
	queue.Enqueue(task)
	log.Printf("Enqueued deletion of %d artifacts", len(filenames))
}