feat(backend): impl share expiry update

This commit is contained in:
2025-12-28 19:01:53 +00:00
parent 67320f8090
commit e3d78497e8
3 changed files with 123 additions and 1 deletions

View File

@@ -0,0 +1,28 @@
package nullable
import (
"bytes"
"encoding/json"
"time"
)
// Time tracks whether a JSON field was set and stores an optional time value.
type Time struct {
Time *time.Time
Set bool
}
func (nt *Time) UnmarshalJSON(b []byte) error {
nt.Set = true
if bytes.Equal(bytes.TrimSpace(b), []byte("null")) {
nt.Time = nil
return nil
}
var t time.Time
if err := json.Unmarshal(b, &t); err != nil {
return err
}
nt.Time = &t
return nil
}

View File

@@ -6,6 +6,7 @@ import (
"github.com/get-drexa/drexa/internal/account"
"github.com/get-drexa/drexa/internal/httperr"
"github.com/get-drexa/drexa/internal/nullable"
"github.com/get-drexa/drexa/internal/reqctx"
"github.com/get-drexa/drexa/internal/user"
"github.com/get-drexa/drexa/internal/virtualfs"
@@ -31,6 +32,13 @@ type createShareRequest struct {
ExpiresAt *time.Time `json:"expiresAt" example:"2025-01-15T00:00:00Z"`
}
// patchShareRequest represents a request to update a share link
// @Description Request to update a share link. Omit expiresAt to keep the current value. Use null to remove the expiry.
type patchShareRequest struct {
// Optional expiration time for the share (ISO 8601), null clears it.
ExpiresAt nullable.Time `json:"expiresAt" example:"2025-01-15T00:00:00Z"`
}
func NewHTTPHandler(sharingService *Service, accountService *account.Service, vfs *virtualfs.VirtualFS, db *bun.DB, authMiddleware fiber.Handler) *HTTPHandler {
return &HTTPHandler{
sharingService: sharingService,
@@ -48,8 +56,9 @@ func (h *HTTPHandler) RegisterShareConsumeRoutes(r fiber.Router) *virtualfs.Scop
func (h *HTTPHandler) RegisterShareManagementRoutes(api *account.ScopedRouter) {
g := api.Group("/shares")
g.Get("/:shareID", h.getShare)
g.Post("/", h.createShare)
g.Get("/:shareID", h.getShare)
g.Patch("/:shareID", h.updateShare)
g.Delete("/:shareID", h.deleteShare)
}
@@ -216,6 +225,52 @@ func (h *HTTPHandler) createShare(c *fiber.Ctx) error {
return c.JSON(share)
}
func (h *HTTPHandler) updateShare(c *fiber.Ctx) error {
shareID := c.Params("shareID")
share, err := h.sharingService.FindShareByPublicID(c.Context(), h.db, shareID)
if err != nil {
if errors.Is(err, ErrShareNotFound) {
return c.SendStatus(fiber.StatusNotFound)
}
return httperr.Internal(err)
}
var req patchShareRequest
if err := c.BodyParser(&req); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
"error": "invalid request",
})
}
tx, err := h.db.BeginTx(c.Context(), nil)
if err != nil {
return httperr.Internal(err)
}
defer tx.Rollback()
opts := UpdateShareOptions{}
if req.ExpiresAt.Set {
if req.ExpiresAt.Time == nil {
opts.ExpiresAt = &time.Time{}
} else {
opts.ExpiresAt = req.ExpiresAt.Time
}
}
err = h.sharingService.UpdateShare(c.Context(), tx, share, opts)
if err != nil {
return httperr.Internal(err)
}
err = tx.Commit()
if err != nil {
return httperr.Internal(err)
}
return c.JSON(share)
}
// deleteShare deletes a share link
// @Summary Delete share
// @Description Delete a share link, revoking access for all users

View File

@@ -34,6 +34,11 @@ type ListSharesOptions struct {
IncludesExpired bool
}
type UpdateShareOptions struct {
// ExpiresAt sets the expiration time for the share. If nil, the expiration time is not changed. If it is a zero time, the expiration time is cleared.
ExpiresAt *time.Time
}
var (
ErrShareNotFound = errors.New("share not found")
ErrShareExpired = errors.New("share expired")
@@ -317,3 +322,37 @@ func (s *Service) generatePublicID() (string, error) {
n := binary.BigEndian.Uint64(b[:])
return s.sqid.Encode([]uint64{n})
}
func (s *Service) UpdateShare(ctx context.Context, db bun.IDB, share *Share, opts UpdateShareOptions) error {
now := time.Now()
cols := make([]string, 0, 2)
if opts.ExpiresAt != nil {
newExpiresAt := opts.ExpiresAt
if opts.ExpiresAt.IsZero() {
newExpiresAt = nil
}
if !timePtrEqual(share.ExpiresAt, newExpiresAt) {
share.ExpiresAt = newExpiresAt
share.UpdatedAt = now
cols = append(cols, "expires_at", "updated_at")
}
}
if len(cols) > 0 {
_, err := db.NewUpdate().Model(share).Column(cols...).WherePK().Exec(ctx)
if err != nil {
return err
}
}
return nil
}
func timePtrEqual(a, b *time.Time) bool {
if a == nil || b == nil {
return a == b
}
return a.Equal(*b)
}