feat(wopi): fetch discover endpoint

pull/1626/head
HFO4 2 years ago
parent 42a31f2fd1
commit c39daeb0d0

@ -1 +1 @@
Subproject commit 01343d7656f839d13cc60adc6b56a238e3160465
Subproject commit d72688d7fceaa0bc916fc6637e928dfd5b5a1c43

@ -0,0 +1,77 @@
package wopi
import (
"encoding/xml"
"fmt"
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
"net/http"
)
type ActonType string
var (
ActionPreview = ActonType("embedview")
ActionEdit = ActonType("edit")
)
const (
DiscoverResponseCacheKey = "wopi_discover"
DiscoverRefreshDuration = 24 * 3600 // 24 hrs
)
// checkDiscovery checks if discovery content is needed to be refreshed.
// If so, it will refresh discovery content.
func (c *client) checkDiscovery() error {
c.mu.RLock()
if c.discovery == nil {
c.mu.RUnlock()
return c.refreshDiscovery()
}
c.mu.RUnlock()
return nil
}
// refresh Discovery action configs.
func (c *client) refreshDiscovery() error {
c.mu.Lock()
defer c.mu.Unlock()
cached, exist := cache.Get(DiscoverResponseCacheKey)
if exist {
cachedDiscovery := cached.(WopiDiscovery)
c.discovery = &cachedDiscovery
} else {
res, err := c.http.Request("GET", c.config.discoveryEndpoint.String(), nil).
CheckHTTPResponse(http.StatusOK).GetResponse()
if err != nil {
return fmt.Errorf("failed to request discovery endpoint: %s", err)
}
if err := xml.Unmarshal([]byte(res), &c.discovery); err != nil {
return fmt.Errorf("failed to parse response discovery endpoint: %s", err)
}
if err := cache.Set(DiscoverResponseCacheKey, *c.discovery, DiscoverRefreshDuration); err != nil {
return err
}
}
// construct actions map
c.actions = make(map[string]map[string]Action)
for _, app := range c.discovery.NetZone.App {
for _, action := range app.Action {
if action.Ext == "" {
continue
}
if _, ok := c.actions["."+action.Ext]; !ok {
c.actions["."+action.Ext] = make(map[string]Action)
}
c.actions["."+action.Ext][action.Name] = action
}
}
return nil
}

@ -0,0 +1,25 @@
package wopi
import (
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"github.com/stretchr/testify/assert"
"net/url"
"testing"
)
func TestDiscovery(t *testing.T) {
a := assert.New(t)
endpoint, _ := url.Parse("http://localhost:8001/hosting/discovery")
client := &client{
cache: cache.Store,
http: request.NewClient(),
config: config{
discoveryEndpoint: endpoint,
},
}
a.NoError(client.refreshDiscovery())
client.NewSession(nil, &model.File{Name: "123.pptx"}, ActionPreview)
}

@ -0,0 +1,58 @@
package wopi
import (
"encoding/gob"
"encoding/xml"
)
// Response content from discovery endpoint.
type WopiDiscovery struct {
XMLName xml.Name `xml:"wopi-discovery"`
Text string `xml:",chardata"`
NetZone struct {
Text string `xml:",chardata"`
Name string `xml:"name,attr"`
App []struct {
Text string `xml:",chardata"`
Name string `xml:"name,attr"`
FavIconUrl string `xml:"favIconUrl,attr"`
BootstrapperUrl string `xml:"bootstrapperUrl,attr"`
AppBootstrapperUrl string `xml:"appBootstrapperUrl,attr"`
ApplicationBaseUrl string `xml:"applicationBaseUrl,attr"`
StaticResourceOrigin string `xml:"staticResourceOrigin,attr"`
CheckLicense string `xml:"checkLicense,attr"`
Action []Action `xml:"action"`
} `xml:"app"`
} `xml:"net-zone"`
ProofKey struct {
Text string `xml:",chardata"`
Oldvalue string `xml:"oldvalue,attr"`
Oldmodulus string `xml:"oldmodulus,attr"`
Oldexponent string `xml:"oldexponent,attr"`
Value string `xml:"value,attr"`
Modulus string `xml:"modulus,attr"`
Exponent string `xml:"exponent,attr"`
} `xml:"proof-key"`
}
type Action struct {
Text string `xml:",chardata"`
Name string `xml:"name,attr"`
Ext string `xml:"ext,attr"`
Default string `xml:"default,attr"`
Urlsrc string `xml:"urlsrc,attr"`
Requires string `xml:"requires,attr"`
Targetext string `xml:"targetext,attr"`
Progid string `xml:"progid,attr"`
UseParent string `xml:"useParent,attr"`
Newprogid string `xml:"newprogid,attr"`
Newext string `xml:"newext,attr"`
}
func init() {
gob.Register(WopiDiscovery{})
gob.Register(Action{})
}
type Session struct {
}

@ -0,0 +1,111 @@
package wopi
import (
"errors"
"fmt"
model "github.com/cloudreve/Cloudreve/v3/models"
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
"github.com/cloudreve/Cloudreve/v3/pkg/request"
"net/url"
"path"
"strings"
"sync"
)
type Client interface {
}
var (
ErrActionNotSupported = errors.New("action not supported by current wopi endpoint")
queryPlaceholders = map[string]string{
"BUSINESS_USER": "",
"DC_LLCC": "lng",
"DISABLE_ASYNC": "",
"DISABLE_CHAT": "",
"EMBEDDED": "true",
"FULLSCREEN": "true",
"HOST_SESSION_ID": "",
"SESSION_CONTEXT": "",
"RECORDING": "",
"THEME_ID": "darkmode",
"UI_LLCC": "lng",
"VALIDATOR_TEST_CATEGORY": "",
}
)
const (
wopiSrcPlaceholder = "WOPI_SOURCE"
)
type client struct {
cache cache.Driver
http request.Client
mu sync.RWMutex
discovery *WopiDiscovery
actions map[string]map[string]Action
config
}
type config struct {
discoveryEndpoint *url.URL
}
func (c *client) NewSession(user *model.User, file *model.File, action ActonType) (*Session, error) {
if err := c.checkDiscovery(); err != nil {
return nil, err
}
ext := path.Ext(file.Name)
availableActions, ok := c.actions[ext]
if !ok {
return nil, ErrActionNotSupported
}
actionConfig, ok := availableActions[string(action)]
if !ok {
return nil, ErrActionNotSupported
}
actionUrl, err := generateActionUrl(actionConfig.Urlsrc, "")
if err != nil {
return nil, err
}
fmt.Println(actionUrl)
return nil, nil
}
func generateActionUrl(src string, fileSrc string) (*url.URL, error) {
src = strings.ReplaceAll(src, "<", "")
src = strings.ReplaceAll(src, ">", "")
actionUrl, err := url.Parse(src)
if err != nil {
return nil, fmt.Errorf("failed to parse action url: %s", err)
}
queries := actionUrl.Query()
queryReplaced := url.Values{}
for k := range queries {
if placeholder, ok := queryPlaceholders[queries.Get(k)]; ok {
if placeholder != "" {
queryReplaced.Set(k, placeholder)
}
continue
}
if queries.Get(k) == wopiSrcPlaceholder {
queryReplaced.Set(k, fileSrc)
continue
}
queryReplaced.Set(k, queries.Get(k))
}
actionUrl.RawQuery = queryReplaced.Encode()
return actionUrl, nil
}
Loading…
Cancel
Save