feat(uploads): preview storage paths, detect templates, attach covers and withdraw batches
ci / module boundaries / boundaries (push) Successful in 7s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 55s
ci / ts / ts (packages + web) (push) Failing after 4m4s
ci / api / api (push) Successful in 55s
ci / db / db (push) Failing after 1m14s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m11s
ci / module boundaries / boundaries (push) Successful in 7s
ci / contract / contract (сгенерированный код совпадает с openapi.yaml) (push) Successful in 55s
ci / ts / ts (packages + web) (push) Failing after 4m4s
ci / api / api (push) Successful in 55s
ci / db / db (push) Failing after 1m14s
ci / mobile / mobile (Metro-бандл Android) (push) Successful in 1m11s
This commit is contained in:
1 parent
5e2d1e902b
commit
64dd8ed05e
29 files changed
+1219
-471
No files matched your search
@@ -178,7 +178,7 @@ type CreateFile struct {
|
||||
}
|
||||
|
||||
// Create создаёт пачку и резервирует место; не поместившиеся и дубли отклоняются с кодом.
|
||||
func (s *Service) Create(ctx context.Context, comment *string, files []CreateFile) (uuid.UUID, []Reserved, error) {
|
||||
func (s *Service) Create(ctx context.Context, comment *string, files []CreateFile, cover []byte) (uuid.UUID, []Reserved, error) {
|
||||
p, err := principal(ctx, auth.PermSuggestTracks)
|
||||
if err != nil {
|
||||
return uuid.Nil, nil, err
|
||||
@@ -209,10 +209,25 @@ func (s *Service) Create(ctx context.Context, comment *string, files []CreateFil
|
||||
}
|
||||
accepted = append(accepted, NewFile{ClientID: f.ClientID, Name: name, RelPath: cleanClientPath(f.RelPath), Size: f.Size, SHA256: sum})
|
||||
}
|
||||
batch, res, err := s.store.CreateBatch(ctx, p.UserID, comment, accepted, st)
|
||||
coverPath, err := s.createCover(cover)
|
||||
if err != nil {
|
||||
return uuid.Nil, nil, err
|
||||
}
|
||||
keepCover := false
|
||||
defer func() {
|
||||
if coverPath != nil && !keepCover {
|
||||
_ = os.Remove(s.stagingAbs(*coverPath))
|
||||
}
|
||||
}()
|
||||
batch, res, err := s.store.CreateBatch(ctx, p.UserID, comment, accepted, st, coverPath)
|
||||
if err != nil {
|
||||
return uuid.Nil, nil, err
|
||||
}
|
||||
for _, r := range res {
|
||||
if r.Rejected == "" {
|
||||
keepCover = true
|
||||
}
|
||||
}
|
||||
return batch, append(res, rejected...), nil
|
||||
}
|
||||
|
||||
@@ -240,12 +255,12 @@ func (s *Service) Complete(ctx context.Context, f File) error {
|
||||
// --- Списки ---
|
||||
|
||||
// ListMine — предложения текущего пользователя.
|
||||
func (s *Service) ListMine(ctx context.Context, statuses []Status, cursor string, limit int) ([]File, string, error) {
|
||||
func (s *Service) ListMine(ctx context.Context, statuses []Status, cursor string, limit int, batch *uuid.UUID) ([]File, string, error) {
|
||||
p, err := principal(ctx, auth.PermSuggestTracks)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
return s.list(ctx, ListFilter{AuthorID: &p.UserID, Statuses: statuses, Cursor: cursor, Limit: limit})
|
||||
return s.list(ctx, ListFilter{AuthorID: &p.UserID, BatchID: batch, Statuses: statuses, Cursor: cursor, Limit: limit})
|
||||
}
|
||||
|
||||
// ListAll — вся очередь (админ).
|
||||
@@ -279,7 +294,7 @@ func (s *Service) Withdraw(ctx context.Context, id uuid.UUID) error {
|
||||
if err := s.store.ChangeStatus(ctx, id, []Status{StatusUploading, StatusProcessing, StatusPendingReview}, StatusWithdrawn); err != nil {
|
||||
return notFound(err)
|
||||
}
|
||||
s.removeStaging(f)
|
||||
s.removeStaging(ctx, f)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -380,7 +395,7 @@ func (s *Service) reviewOne(ctx context.Context, reviewer, id uuid.UUID, approve
|
||||
if err := s.store.Decide(ctx, id, reviewer, false, reason); err != nil {
|
||||
return notFound(err)
|
||||
}
|
||||
s.removeStaging(f)
|
||||
s.removeStaging(ctx, f)
|
||||
s.store.Audit(ctx, &reviewer, "upload.reject", id, map[string]any{"reason": reason})
|
||||
return nil
|
||||
}
|
||||
@@ -542,7 +557,7 @@ func reuseExisting(root, rel string) string {
|
||||
}
|
||||
|
||||
// removeStaging удаляет файл staging (и временные копии с тегами).
|
||||
func (s *Service) removeStaging(f File) {
|
||||
func (s *Service) removeStaging(ctx context.Context, f File) {
|
||||
if f.StagingPath == "" {
|
||||
return
|
||||
}
|
||||
@@ -552,6 +567,11 @@ func (s *Service) removeStaging(f File) {
|
||||
_ = os.Remove(m)
|
||||
}
|
||||
_ = os.Remove(filepath.Dir(p)) // каталог пачки, если опустел
|
||||
if f.CoverPath != nil {
|
||||
if cover, err := s.store.UnusedBatchCover(ctx, f.BatchID); err == nil && cover != nil {
|
||||
_ = os.Remove(s.stagingAbs(*cover))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TusURL — адрес tus для файла.
|
||||
|
||||
Reference in new issue
Block a user