116 lines
3.2 KiB
Go
116 lines
3.2 KiB
Go
package office
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type PublicShareAccess struct {
|
|
Token string
|
|
FilePath string
|
|
Password string
|
|
}
|
|
|
|
func (s *Service) PublicEditorConfig(ctx context.Context, token, filePath, mode, password, guestID, guestName string) (map[string]any, error) {
|
|
token = strings.TrimSpace(token)
|
|
filePath = normalizePath(filePath)
|
|
if token == "" || filePath == "" {
|
|
return nil, fmt.Errorf("invalid public office session")
|
|
}
|
|
if mode == "" {
|
|
mode = "edit"
|
|
}
|
|
|
|
rev, err := s.nc.PublicShareFileRevision(ctx, token, filePath, password)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("resolve public file revision: %w", err)
|
|
}
|
|
|
|
apiBase := strings.TrimRight(s.Cfg.APIInternalURL, "/")
|
|
sig, err := signPublicDocAccess(token, filePath, password, s.Cfg.JWTSecret)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
downloadURL := buildPublicOfficeEndpointURL(apiBase, token, "/office/document", filePath, password, sig)
|
|
callbackURL := buildPublicOfficeEndpointURL(apiBase, token, "/office/callback", filePath, password, sig)
|
|
|
|
editorUserID := strings.TrimSpace(guestID)
|
|
if editorUserID == "" {
|
|
editorUserID = "public-guest"
|
|
} else {
|
|
editorUserID = "public:" + editorUserID
|
|
}
|
|
|
|
if guestName == "" {
|
|
guestName = "Invité"
|
|
}
|
|
|
|
config, err := buildEditorConfig(buildEditorConfigInput{
|
|
filePath: filePath,
|
|
mode: mode,
|
|
editorUserID: editorUserID,
|
|
userName: guestName,
|
|
documentKey: s.keys.current(rev.FileID),
|
|
downloadURL: downloadURL,
|
|
callbackURL: callbackURL,
|
|
})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return wrapConfig(config, s.Cfg.JWTSecret)
|
|
}
|
|
|
|
func (s *Service) OpenPublicDocument(ctx context.Context, access PublicShareAccess) (io.ReadCloser, string, error) {
|
|
return s.nc.DownloadPublicShare(ctx, access.Token, access.FilePath, access.Password)
|
|
}
|
|
|
|
func (s *Service) SavePublicDocument(ctx context.Context, access PublicShareAccess, body io.Reader, contentType string) error {
|
|
return s.nc.UploadPublicShare(ctx, access.Token, access.FilePath, access.Password, body, contentType)
|
|
}
|
|
|
|
func buildPublicOfficeEndpointURL(base, token, endpoint, filePath, password, sig string) string {
|
|
q := url.Values{}
|
|
q.Set("path", normalizePath(filePath))
|
|
if password != "" {
|
|
q.Set("password", password)
|
|
}
|
|
if sig != "" {
|
|
q.Set("sig", sig)
|
|
}
|
|
return strings.TrimRight(base, "/") + "/api/v1/drive/public/shares/" + url.PathEscape(token) + endpoint + "?" + q.Encode()
|
|
}
|
|
|
|
func signPublicDocAccess(token, filePath, password, secret string) (string, error) {
|
|
payload := map[string]any{
|
|
"token": strings.TrimSpace(token),
|
|
"path": normalizePath(filePath),
|
|
"password": password,
|
|
"exp": time.Now().Add(2 * time.Hour).Unix(),
|
|
}
|
|
return signJWT(payload, secret)
|
|
}
|
|
|
|
func VerifyPublicDocAccess(token, filePath, password, sig, secret string) bool {
|
|
if secret == "" {
|
|
return true
|
|
}
|
|
payload, err := verifyJWT(sig, secret)
|
|
if err != nil {
|
|
return false
|
|
}
|
|
if payload["token"] != strings.TrimSpace(token) || payload["path"] != normalizePath(filePath) {
|
|
return false
|
|
}
|
|
if pw, _ := payload["password"].(string); pw != password {
|
|
return false
|
|
}
|
|
if exp, ok := payload["exp"].(float64); ok && int64(exp) < time.Now().Unix() {
|
|
return false
|
|
}
|
|
return true
|
|
}
|