chore: 初始化 Cloudreve 源码仓库
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
package explorer
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/cloudreve/Cloudreve/v4/application/dependency"
|
||||
"github.com/cloudreve/Cloudreve/v4/inventory"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/auth/requestinfo"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/fs"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/filemanager/manager"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/logging"
|
||||
"github.com/cloudreve/Cloudreve/v4/pkg/serializer"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gofrs/uuid"
|
||||
)
|
||||
|
||||
type (
|
||||
ExplorerEventService struct {
|
||||
Uri string `form:"uri" binding:"required"`
|
||||
}
|
||||
ExplorerEventParamCtx struct{}
|
||||
)
|
||||
|
||||
func (s *ExplorerEventService) HandleExplorerEventsPush(c *gin.Context) error {
|
||||
dep := dependency.FromContext(c)
|
||||
user := inventory.UserFromContext(c)
|
||||
m := manager.NewFileManager(dep, user)
|
||||
l := logging.FromContext(c)
|
||||
defer m.Recycle()
|
||||
|
||||
uri, err := fs.NewUriFromString(s.Uri)
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeParamErr, "Unknown uri", err)
|
||||
}
|
||||
|
||||
// Make sure target is a valid folder that the user can listen to
|
||||
parent, listRes, err := m.List(c, uri, &manager.ListArgs{
|
||||
Page: 0,
|
||||
PageSize: 1,
|
||||
})
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeParamErr, "Requested uri not available", err)
|
||||
}
|
||||
|
||||
// Reject event subscriptions on single-file views (e.g. single-file shares).
|
||||
// The listed parent is the underlying owner-side folder containing the file,
|
||||
// while the subscriber is only authorized to observe the shared file itself.
|
||||
// Subscribing to that folder topic would leak events about unshared siblings.
|
||||
if listRes != nil && listRes.SingleFileView {
|
||||
return serializer.NewError(serializer.CodeNoPermissionErr, "Event subscriptions are not supported on this view", nil)
|
||||
}
|
||||
|
||||
requestInfo := requestinfo.RequestInfoFromContext(c)
|
||||
if requestInfo.ClientID == "" {
|
||||
return serializer.NewError(serializer.CodeParamErr, "Client ID is required", nil)
|
||||
}
|
||||
|
||||
// Client ID must be a valid UUID
|
||||
if _, err := uuid.FromString(requestInfo.ClientID); err != nil {
|
||||
return serializer.NewError(serializer.CodeParamErr, "Invalid client ID", err)
|
||||
}
|
||||
|
||||
// Subscribe
|
||||
eventHub := dep.EventHub()
|
||||
rx, resumed, err := eventHub.Subscribe(c, parent.ID(), requestInfo.ClientID)
|
||||
if err != nil {
|
||||
return serializer.NewError(serializer.CodeInternalSetting, "Failed to subscribe to events", err)
|
||||
}
|
||||
|
||||
// SSE Headers
|
||||
c.Writer.Header().Set("Content-Type", "text/event-stream")
|
||||
c.Writer.Header().Set("Cache-Control", "no-cache")
|
||||
c.Writer.Header().Set("Connection", "keep-alive")
|
||||
c.Writer.Header().Set("X-Accel-Buffering", "no")
|
||||
|
||||
keepAliveTicker := time.NewTicker(30 * time.Second)
|
||||
defer keepAliveTicker.Stop()
|
||||
|
||||
if resumed {
|
||||
c.SSEvent("resumed", nil)
|
||||
c.Writer.Flush()
|
||||
} else {
|
||||
c.SSEvent("subscribed", nil)
|
||||
c.Writer.Flush()
|
||||
}
|
||||
|
||||
for {
|
||||
select {
|
||||
// TODO: close connection after access token expired
|
||||
case <-c.Request.Context().Done():
|
||||
// Server shutdown or request cancelled
|
||||
eventHub.Unsubscribe(c, parent.ID(), requestInfo.ClientID)
|
||||
l.Debug("Request context done, unsubscribed from event hub")
|
||||
return nil
|
||||
case <-c.Writer.CloseNotify():
|
||||
eventHub.Unsubscribe(c, parent.ID(), requestInfo.ClientID)
|
||||
l.Debug("Unsubscribed from event hub")
|
||||
return nil
|
||||
case evt, ok := <-rx:
|
||||
if !ok {
|
||||
// Channel closed, EventHub is shutting down
|
||||
l.Debug("Event hub closed, disconnecting client")
|
||||
return nil
|
||||
}
|
||||
c.SSEvent("event", evt)
|
||||
l.Debug("Event sent: %+v", evt)
|
||||
c.Writer.Flush()
|
||||
if c.Errors.Last() != nil {
|
||||
l.Error("Error occurred: %+v", c.Errors.Last().Error())
|
||||
eventHub.Unsubscribe(c, parent.ID(), requestInfo.ClientID)
|
||||
l.Debug("Unsubscribed from event hub")
|
||||
return nil
|
||||
}
|
||||
case <-keepAliveTicker.C:
|
||||
c.SSEvent("keep-alive", nil)
|
||||
c.Writer.Flush()
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user