github.com/cloudreve/Cloudreve/v3@v3.0.0-20240224133659-3edb00a6484c/pkg/wopi/discovery.go (about)

     1  package wopi
     2  
     3  import (
     4  	"encoding/xml"
     5  	"fmt"
     6  	"github.com/cloudreve/Cloudreve/v3/pkg/util"
     7  	"net/http"
     8  	"strings"
     9  )
    10  
    11  type ActonType string
    12  
    13  var (
    14  	ActionPreview         = ActonType("embedview")
    15  	ActionPreviewFallback = ActonType("view")
    16  	ActionEdit            = ActonType("edit")
    17  )
    18  
    19  const (
    20  	DiscoverResponseCacheKey = "wopi_discover"
    21  	DiscoverRefreshDuration  = 24 * 3600 // 24 hrs
    22  )
    23  
    24  func (c *client) AvailableExts() []string {
    25  	if err := c.checkDiscovery(); err != nil {
    26  		util.Log().Error("Failed to check WOPI discovery: %s", err)
    27  		return nil
    28  	}
    29  
    30  	c.mu.RLock()
    31  	defer c.mu.RUnlock()
    32  	exts := make([]string, 0, len(c.actions))
    33  	for ext, actions := range c.actions {
    34  		_, previewable := actions[string(ActionPreview)]
    35  		_, editable := actions[string(ActionEdit)]
    36  		_, previewableFallback := actions[string(ActionPreviewFallback)]
    37  
    38  		if previewable || editable || previewableFallback {
    39  			exts = append(exts, strings.TrimPrefix(ext, "."))
    40  		}
    41  	}
    42  
    43  	return exts
    44  }
    45  
    46  // checkDiscovery checks if discovery content is needed to be refreshed.
    47  // If so, it will refresh discovery content.
    48  func (c *client) checkDiscovery() error {
    49  	c.mu.RLock()
    50  	if c.discovery == nil {
    51  		c.mu.RUnlock()
    52  		return c.refreshDiscovery()
    53  	}
    54  
    55  	c.mu.RUnlock()
    56  	return nil
    57  }
    58  
    59  // refresh Discovery action configs.
    60  func (c *client) refreshDiscovery() error {
    61  	c.mu.Lock()
    62  	defer c.mu.Unlock()
    63  
    64  	cached, exist := c.cache.Get(DiscoverResponseCacheKey)
    65  	if exist {
    66  		cachedDiscovery := cached.(WopiDiscovery)
    67  		c.discovery = &cachedDiscovery
    68  	} else {
    69  		res, err := c.http.Request("GET", c.config.discoveryEndpoint.String(), nil).
    70  			CheckHTTPResponse(http.StatusOK).GetResponse()
    71  		if err != nil {
    72  			return fmt.Errorf("failed to request discovery endpoint: %w", err)
    73  		}
    74  
    75  		if err := xml.Unmarshal([]byte(res), &c.discovery); err != nil {
    76  			return fmt.Errorf("failed to parse response discovery endpoint: %w", err)
    77  		}
    78  
    79  		if err := c.cache.Set(DiscoverResponseCacheKey, *c.discovery, DiscoverRefreshDuration); err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	// construct actions map
    85  	c.actions = make(map[string]map[string]Action)
    86  	for _, app := range c.discovery.NetZone.App {
    87  		for _, action := range app.Action {
    88  			if action.Ext == "" {
    89  				continue
    90  			}
    91  
    92  			if _, ok := c.actions["."+action.Ext]; !ok {
    93  				c.actions["."+action.Ext] = make(map[string]Action)
    94  			}
    95  
    96  			c.actions["."+action.Ext][action.Name] = action
    97  		}
    98  	}
    99  
   100  	return nil
   101  }