feat(wopi): change doc preview config based on WOPI discovery results
This commit is contained in:
parent
c39daeb0d0
commit
4541400755
7 changed files with 107 additions and 22 deletions
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/cloudreve/Cloudreve/v3/pkg/email"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/mq"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/task"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/wopi"
|
||||
"github.com/gin-gonic/gin"
|
||||
"io/fs"
|
||||
)
|
||||
|
@ -95,6 +96,12 @@ func Init(path string, statics fs.FS) {
|
|||
auth.Init()
|
||||
},
|
||||
},
|
||||
{
|
||||
"master",
|
||||
func() {
|
||||
wopi.Init()
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, dependency := range dependencies {
|
||||
|
|
|
@ -115,4 +115,8 @@ Neue',Helvetica,Arial,sans-serif; box-sizing: border-box; font-size: 14px; verti
|
|||
{Name: "office_preview_service", Value: "https://view.officeapps.live.com/op/view.aspx?src={$src}", Type: "preview"},
|
||||
{Name: "show_app_promotion", Value: "1", Type: "mobile"},
|
||||
{Name: "public_resource_maxage", Value: "86400", Type: "timeout"},
|
||||
{Name: "wopi_enabled", Value: "0", Type: "wopi"},
|
||||
{Name: "wopi_endpoint", Value: "", Type: "wopi"},
|
||||
{Name: "wopi_max_size", Value: "52428800", Type: "wopi"},
|
||||
{Name: "wopi_session_timeout", Value: "43200", Type: "wopi"},
|
||||
}
|
||||
|
|
|
@ -7,22 +7,23 @@ import (
|
|||
|
||||
// SiteConfig 站点全局设置序列
|
||||
type SiteConfig struct {
|
||||
SiteName string `json:"title"`
|
||||
LoginCaptcha bool `json:"loginCaptcha"`
|
||||
RegCaptcha bool `json:"regCaptcha"`
|
||||
ForgetCaptcha bool `json:"forgetCaptcha"`
|
||||
EmailActive bool `json:"emailActive"`
|
||||
Themes string `json:"themes"`
|
||||
DefaultTheme string `json:"defaultTheme"`
|
||||
HomepageViewMethod string `json:"home_view_method"`
|
||||
ShareViewMethod string `json:"share_view_method"`
|
||||
Authn bool `json:"authn"`
|
||||
User User `json:"user"`
|
||||
ReCaptchaKey string `json:"captcha_ReCaptchaKey"`
|
||||
CaptchaType string `json:"captcha_type"`
|
||||
TCaptchaCaptchaAppId string `json:"tcaptcha_captcha_app_id"`
|
||||
RegisterEnabled bool `json:"registerEnabled"`
|
||||
AppPromotion bool `json:"app_promotion"`
|
||||
SiteName string `json:"title"`
|
||||
LoginCaptcha bool `json:"loginCaptcha"`
|
||||
RegCaptcha bool `json:"regCaptcha"`
|
||||
ForgetCaptcha bool `json:"forgetCaptcha"`
|
||||
EmailActive bool `json:"emailActive"`
|
||||
Themes string `json:"themes"`
|
||||
DefaultTheme string `json:"defaultTheme"`
|
||||
HomepageViewMethod string `json:"home_view_method"`
|
||||
ShareViewMethod string `json:"share_view_method"`
|
||||
Authn bool `json:"authn"`
|
||||
User User `json:"user"`
|
||||
ReCaptchaKey string `json:"captcha_ReCaptchaKey"`
|
||||
CaptchaType string `json:"captcha_type"`
|
||||
TCaptchaCaptchaAppId string `json:"tcaptcha_captcha_app_id"`
|
||||
RegisterEnabled bool `json:"registerEnabled"`
|
||||
AppPromotion bool `json:"app_promotion"`
|
||||
WopiExts []string `json:"wopi_exts"`
|
||||
}
|
||||
|
||||
type task struct {
|
||||
|
@ -60,7 +61,7 @@ func checkSettingValue(setting map[string]string, key string) string {
|
|||
}
|
||||
|
||||
// BuildSiteConfig 站点全局设置
|
||||
func BuildSiteConfig(settings map[string]string, user *model.User) Response {
|
||||
func BuildSiteConfig(settings map[string]string, user *model.User, wopiExts []string) Response {
|
||||
var userRes User
|
||||
if user != nil {
|
||||
userRes = BuildUser(*user)
|
||||
|
@ -85,6 +86,7 @@ func BuildSiteConfig(settings map[string]string, user *model.User) Response {
|
|||
TCaptchaCaptchaAppId: checkSettingValue(settings, "captcha_TCaptcha_CaptchaAppId"),
|
||||
RegisterEnabled: model.IsTrueVal(checkSettingValue(settings, "register_enabled")),
|
||||
AppPromotion: model.IsTrueVal(checkSettingValue(settings, "show_app_promotion")),
|
||||
WopiExts: wopiExts,
|
||||
}}
|
||||
return res
|
||||
}
|
||||
|
|
|
@ -18,10 +18,10 @@ func TestCheckSettingValue(t *testing.T) {
|
|||
func TestBuildSiteConfig(t *testing.T) {
|
||||
asserts := assert.New(t)
|
||||
|
||||
res := BuildSiteConfig(map[string]string{"not exist": ""}, &model.User{})
|
||||
res := BuildSiteConfig(map[string]string{"not exist": ""}, &model.User{}, nil)
|
||||
asserts.Equal("", res.Data.(SiteConfig).SiteName)
|
||||
|
||||
res = BuildSiteConfig(map[string]string{"siteName": "123"}, &model.User{})
|
||||
res = BuildSiteConfig(map[string]string{"siteName": "123"}, &model.User{}, nil)
|
||||
asserts.Equal("123", res.Data.(SiteConfig).SiteName)
|
||||
|
||||
// 非空用户
|
||||
|
@ -29,7 +29,7 @@ func TestBuildSiteConfig(t *testing.T) {
|
|||
Model: gorm.Model{
|
||||
ID: 5,
|
||||
},
|
||||
})
|
||||
}, nil)
|
||||
asserts.Len(res.Data.(SiteConfig).User.ID, 4)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,7 +4,9 @@ import (
|
|||
"encoding/xml"
|
||||
"fmt"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type ActonType string
|
||||
|
@ -19,6 +21,27 @@ const (
|
|||
DiscoverRefreshDuration = 24 * 3600 // 24 hrs
|
||||
)
|
||||
|
||||
func (c *client) AvailableExts() []string {
|
||||
if err := c.checkDiscovery(); err != nil {
|
||||
util.Log().Error("Failed to check WOPI discovery: %s", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
c.mu.RUnlock()
|
||||
defer c.mu.RUnlock()
|
||||
exts := make([]string, 0, len(c.actions))
|
||||
for ext, actions := range c.actions {
|
||||
_, previewable := actions[string(ActionPreview)]
|
||||
_, editable := actions[string(ActionEdit)]
|
||||
|
||||
if previewable || editable {
|
||||
exts = append(exts, strings.TrimPrefix(ext, "."))
|
||||
}
|
||||
}
|
||||
|
||||
return exts
|
||||
}
|
||||
|
||||
// checkDiscovery checks if discovery content is needed to be refreshed.
|
||||
// If so, it will refresh discovery content.
|
||||
func (c *client) checkDiscovery() error {
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
model "github.com/cloudreve/Cloudreve/v3/models"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/cache"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/request"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
|
@ -13,11 +14,18 @@ import (
|
|||
)
|
||||
|
||||
type Client interface {
|
||||
// NewSession creates a new document session with access token.
|
||||
NewSession(user *model.User, file *model.File, action ActonType) (*Session, error)
|
||||
// AvailableExts returns a list of file extensions that are supported by WOPI.
|
||||
AvailableExts() []string
|
||||
}
|
||||
|
||||
var (
|
||||
ErrActionNotSupported = errors.New("action not supported by current wopi endpoint")
|
||||
|
||||
Default Client
|
||||
DefaultMu sync.Mutex
|
||||
|
||||
queryPlaceholders = map[string]string{
|
||||
"BUSINESS_USER": "",
|
||||
"DC_LLCC": "lng",
|
||||
|
@ -38,6 +46,24 @@ const (
|
|||
wopiSrcPlaceholder = "WOPI_SOURCE"
|
||||
)
|
||||
|
||||
// Init initializes a new global WOPI client.
|
||||
func Init() {
|
||||
settings := model.GetSettingByNames("wopi_endpoint", "wopi_enabled")
|
||||
if !model.IsTrueVal(settings["wopi_enabled"]) {
|
||||
return
|
||||
}
|
||||
|
||||
wopiClient, err := NewClient(settings["wopi_endpoint"], cache.Store, request.NewClient())
|
||||
if err != nil {
|
||||
util.Log().Error("Failed to initialize WOPI client: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
DefaultMu.Lock()
|
||||
Default = wopiClient
|
||||
DefaultMu.Unlock()
|
||||
}
|
||||
|
||||
type client struct {
|
||||
cache cache.Driver
|
||||
http request.Client
|
||||
|
@ -53,6 +79,21 @@ type config struct {
|
|||
discoveryEndpoint *url.URL
|
||||
}
|
||||
|
||||
func NewClient(endpoint string, cache cache.Driver, http request.Client) (Client, error) {
|
||||
endpointUrl, err := url.Parse(endpoint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to parse WOPI endpoint: %s", err)
|
||||
}
|
||||
|
||||
return &client{
|
||||
cache: cache,
|
||||
http: http,
|
||||
config: config{
|
||||
discoveryEndpoint: endpointUrl,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *client) NewSession(user *model.User, file *model.File, action ActonType) (*Session, error) {
|
||||
if err := c.checkDiscovery(); err != nil {
|
||||
return nil, err
|
||||
|
@ -79,6 +120,8 @@ func (c *client) NewSession(user *model.User, file *model.File, action ActonType
|
|||
return nil, nil
|
||||
}
|
||||
|
||||
// Replace query parameters in action URL template. Some placeholders need to be replaced
|
||||
// at the frontend, e.g. `THEME_ID`.
|
||||
func generateActionUrl(src string, fileSrc string) (*url.URL, error) {
|
||||
src = strings.ReplaceAll(src, "<", "")
|
||||
src = strings.ReplaceAll(src, ">", "")
|
||||
|
|
|
@ -5,6 +5,7 @@ import (
|
|||
"github.com/cloudreve/Cloudreve/v3/pkg/conf"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/serializer"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/util"
|
||||
"github.com/cloudreve/Cloudreve/v3/pkg/wopi"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/mojocn/base64Captcha"
|
||||
)
|
||||
|
@ -30,14 +31,19 @@ func SiteConfig(c *gin.Context) {
|
|||
"show_app_promotion",
|
||||
)
|
||||
|
||||
var wopiExts []string
|
||||
if wopi.Default != nil {
|
||||
wopiExts = wopi.Default.AvailableExts()
|
||||
}
|
||||
|
||||
// 如果已登录,则同时返回用户信息和标签
|
||||
user, _ := c.Get("user")
|
||||
if user, ok := user.(*model.User); ok {
|
||||
c.JSON(200, serializer.BuildSiteConfig(siteConfig, user))
|
||||
c.JSON(200, serializer.BuildSiteConfig(siteConfig, user, wopiExts))
|
||||
return
|
||||
}
|
||||
|
||||
c.JSON(200, serializer.BuildSiteConfig(siteConfig, nil))
|
||||
c.JSON(200, serializer.BuildSiteConfig(siteConfig, nil, wopiExts))
|
||||
}
|
||||
|
||||
// Ping 状态检查页面
|
||||
|
|
Loading…
Add table
Reference in a new issue