| 1 | package release |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "crypto/rand" |
| 6 | "encoding/hex" |
| 7 | "encoding/json" |
| 8 | "errors" |
| 9 | "time" |
| 10 | ) |
| 11 | |
| 12 | type ClaimPurgeRequest struct { |
| 13 | V int `json:"v"` |
| 14 | ID string `json:"id"` |
| 15 | AppID string `json:"app_id"` |
| 16 | Account string `json:"account"` |
| 17 | App string `json:"app"` |
| 18 | TombstoneKey string `json:"tombstone_key"` |
| 19 | TombstoneETag string `json:"tombstone_etag"` |
| 20 | Tombstone json.RawMessage `json:"tombstone"` |
| 21 | } |
| 22 | |
| 23 | func RequestClaimAutoPurge(ctx context.Context, s Store, account, app string, now time.Time) error { |
| 24 | if !ValidAccount(account) || !ValidApp(app) { |
| 25 | return ErrNotTrashed |
| 26 | } |
| 27 | tomb, err := ReadAppTombstone(ctx, s, account, app) |
| 28 | if err != nil { |
| 29 | return err |
| 30 | } |
| 31 | if tomb == nil || now.Before(tomb.DeletedAt.Add(TrashCooloff)) { |
| 32 | return ErrNotTrashed |
| 33 | } |
| 34 | key := AppTombstoneKey(account, app) |
| 35 | body, tag, err := s.Get(ctx, key) |
| 36 | if err != nil { |
| 37 | return err |
| 38 | } |
| 39 | prefix := "apps/" + account + "/" + app + "/" |
| 40 | admission, _, err := s.Get(ctx, prefix+"admission.json") |
| 41 | if err != nil { |
| 42 | return err |
| 43 | } |
| 44 | var identity struct { |
| 45 | V int `json:"v"` |
| 46 | AppID string `json:"app_id"` |
| 47 | State string `json:"state"` |
| 48 | Op json.RawMessage `json:"op"` |
| 49 | } |
| 50 | if err = json.Unmarshal(admission, &identity); err != nil { |
| 51 | return err |
| 52 | } |
| 53 | if identity.V != 1 || identity.AppID == "" || tag == "" { |
| 54 | return ErrNotTrashed |
| 55 | } |
| 56 | if identity.State != "open" || (len(identity.Op) > 0 && string(identity.Op) != "null") { |
| 57 | return nil |
| 58 | } |
| 59 | var nonce [16]byte |
| 60 | if _, err = rand.Read(nonce[:]); err != nil { |
| 61 | return err |
| 62 | } |
| 63 | request := ClaimPurgeRequest{V: 1, ID: hex.EncodeToString(nonce[:]), AppID: identity.AppID, Account: account, App: app, TombstoneKey: key, TombstoneETag: tag, Tombstone: body} |
| 64 | raw, err := json.Marshal(request) |
| 65 | if err != nil { |
| 66 | return err |
| 67 | } |
| 68 | _, err = s.Put(ctx, prefix+"purge-request.json", raw, "") |
| 69 | if errors.Is(err, ErrCASConflict) { |
| 70 | return nil |
| 71 | } |
| 72 | return err |
| 73 | } |
| 74 | |