Compare commits
5 Commits
e1793d1314
...
20d27653ad
Author | SHA1 | Date |
---|---|---|
Alan Castro | 20d27653ad | |
古大羊 | be62f56782 | |
Alan Castro | 764147c443 | |
Alan Castro | d294cacbe4 | |
DrosoCode | 08f37b90ce |
|
@ -48,6 +48,9 @@ func addConfigFlags(flags *pflag.FlagSet) {
|
|||
flags.String("branding.files", "", "path to directory with images and custom styles")
|
||||
flags.Bool("branding.disableExternal", false, "disable external links such as GitHub links")
|
||||
flags.Bool("branding.disableUsedPercentage", false, "disable used disk percentage graph")
|
||||
|
||||
flags.String("onlyoffice.url", "", "onlyoffice integration url")
|
||||
flags.String("onlyoffice.jwtSecret", "", "onlyoffice integration secret")
|
||||
}
|
||||
|
||||
//nolint:gocyclo
|
||||
|
|
|
@ -42,6 +42,10 @@ override the options.`,
|
|||
Theme: mustGetString(flags, "branding.theme"),
|
||||
Files: mustGetString(flags, "branding.files"),
|
||||
},
|
||||
OnlyOffice: settings.OnlyOffice{
|
||||
URL: mustGetString(flags, "onlyoffice.url"),
|
||||
JWTSecret: mustGetString(flags, "onlyoffice.jwtSecret"),
|
||||
},
|
||||
}
|
||||
|
||||
ser := &settings.Server{
|
||||
|
|
|
@ -63,6 +63,10 @@ you want to change. Other options will remain unchanged.`,
|
|||
set.Branding.DisableUsedPercentage = mustGetBool(flags, flag.Name)
|
||||
case "branding.files":
|
||||
set.Branding.Files = mustGetString(flags, flag.Name)
|
||||
case "onlyoffice.url":
|
||||
set.OnlyOffice.URL = mustGetString(flags, flag.Name)
|
||||
case "onlyoffice.jwtSecret":
|
||||
set.OnlyOffice.JWTSecret = mustGetString(flags, flag.Name)
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -205,19 +205,10 @@ func (i *FileInfo) Checksum(algo string) error {
|
|||
}
|
||||
|
||||
func (i *FileInfo) RealPath() string {
|
||||
if realPathFs, ok := i.Fs.(interface {
|
||||
RealPath(name string) (fPath string, err error)
|
||||
}); ok {
|
||||
realPath, err := realPathFs.RealPath(i.Path)
|
||||
if err == nil {
|
||||
return realPath
|
||||
}
|
||||
}
|
||||
|
||||
return i.Path
|
||||
return GetRealPath(i.Fs, i.Path)
|
||||
}
|
||||
|
||||
//nolint:goconst
|
||||
//nolint:goconst,gocyclo
|
||||
func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
||||
if IsNamedPipe(i.Mode) {
|
||||
i.Type = "blob"
|
||||
|
@ -276,6 +267,10 @@ func (i *FileInfo) detectType(modify, saveContent, readHeader bool) error {
|
|||
i.Content = string(content)
|
||||
}
|
||||
return nil
|
||||
case strings.HasPrefix(mimetype, "application/vnd.openxmlformats-officedocument"),
|
||||
strings.HasPrefix(mimetype, "application/vnd.oasis.opendocument"):
|
||||
i.Type = "officedocument"
|
||||
return nil
|
||||
default:
|
||||
i.Type = "blob"
|
||||
}
|
||||
|
|
|
@ -0,0 +1,608 @@
|
|||
package files
|
||||
|
||||
// This file contains code primarily sourced from::
|
||||
// github.com/kataras/iris
|
||||
|
||||
import (
|
||||
"mime"
|
||||
)
|
||||
|
||||
const (
|
||||
// ContentBinaryHeaderValue header value for binary data.
|
||||
ContentBinaryHeaderValue = "application/octet-stream"
|
||||
// ContentWebassemblyHeaderValue header value for web assembly files.
|
||||
ContentWebassemblyHeaderValue = "application/wasm"
|
||||
// ContentHTMLHeaderValue is the string of text/html response header's content type value.
|
||||
ContentHTMLHeaderValue = "text/html"
|
||||
// ContentJSONHeaderValue header value for JSON data.
|
||||
ContentJSONHeaderValue = "application/json"
|
||||
// ContentJSONProblemHeaderValue header value for JSON API problem error.
|
||||
// Read more at: https://tools.ietf.org/html/rfc7807
|
||||
ContentJSONProblemHeaderValue = "application/problem+json"
|
||||
// ContentXMLProblemHeaderValue header value for XML API problem error.
|
||||
// Read more at: https://tools.ietf.org/html/rfc7807
|
||||
ContentXMLProblemHeaderValue = "application/problem+xml"
|
||||
// ContentJavascriptHeaderValue header value for JSONP & Javascript data.
|
||||
ContentJavascriptHeaderValue = "text/javascript"
|
||||
// ContentTextHeaderValue header value for Text data.
|
||||
ContentTextHeaderValue = "text/plain"
|
||||
// ContentXMLHeaderValue header value for XML data.
|
||||
ContentXMLHeaderValue = "text/xml"
|
||||
// ContentXMLUnreadableHeaderValue obselete header value for XML.
|
||||
ContentXMLUnreadableHeaderValue = "application/xml"
|
||||
// ContentMarkdownHeaderValue custom key/content type, the real is the text/html.
|
||||
ContentMarkdownHeaderValue = "text/markdown"
|
||||
// ContentYAMLHeaderValue header value for YAML data.
|
||||
ContentYAMLHeaderValue = "application/x-yaml"
|
||||
// ContentYAMLTextHeaderValue header value for YAML plain text.
|
||||
ContentYAMLTextHeaderValue = "text/yaml"
|
||||
// ContentProtobufHeaderValue header value for Protobuf messages data.
|
||||
ContentProtobufHeaderValue = "application/x-protobuf"
|
||||
// ContentMsgPackHeaderValue header value for MsgPack data.
|
||||
ContentMsgPackHeaderValue = "application/msgpack"
|
||||
// ContentMsgPack2HeaderValue alternative header value for MsgPack data.
|
||||
ContentMsgPack2HeaderValue = "application/x-msgpack"
|
||||
// ContentFormHeaderValue header value for post form data.
|
||||
ContentFormHeaderValue = "application/x-www-form-urlencoded"
|
||||
// ContentFormMultipartHeaderValue header value for post multipart form data.
|
||||
ContentFormMultipartHeaderValue = "multipart/form-data"
|
||||
// ContentMultipartRelatedHeaderValue header value for multipart related data.
|
||||
ContentMultipartRelatedHeaderValue = "multipart/related"
|
||||
// ContentGRPCHeaderValue Content-Type header value for gRPC.
|
||||
ContentGRPCHeaderValue = "application/grpc"
|
||||
)
|
||||
|
||||
var types = map[string]string{
|
||||
".3dm": "x-world/x-3dmf",
|
||||
".3dmf": "x-world/x-3dmf",
|
||||
".7z": "application/x-7z-compressed",
|
||||
".a": "application/octet-stream",
|
||||
".aab": "application/x-authorware-bin",
|
||||
".aam": "application/x-authorware-map",
|
||||
".aas": "application/x-authorware-seg",
|
||||
".abc": "text/vndabc",
|
||||
".ace": "application/x-ace-compressed",
|
||||
".acgi": "text/html",
|
||||
".afl": "video/animaflex",
|
||||
".ai": "application/postscript",
|
||||
".aif": "audio/aiff",
|
||||
".aifc": "audio/aiff",
|
||||
".aiff": "audio/aiff",
|
||||
".aim": "application/x-aim",
|
||||
".aip": "text/x-audiosoft-intra",
|
||||
".alz": "application/x-alz-compressed",
|
||||
".ani": "application/x-navi-animation",
|
||||
".aos": "application/x-nokia-9000-communicator-add-on-software",
|
||||
".aps": "application/mime",
|
||||
".apk": "application/vnd.android.package-archive",
|
||||
".arc": "application/x-arc-compressed",
|
||||
".arj": "application/arj",
|
||||
".art": "image/x-jg",
|
||||
".asf": "video/x-ms-asf",
|
||||
".asm": "text/x-asm",
|
||||
".asp": "text/asp",
|
||||
".asx": "application/x-mplayer2",
|
||||
".au": "audio/basic",
|
||||
".avi": "video/x-msvideo",
|
||||
".avs": "video/avs-video",
|
||||
".bcpio": "application/x-bcpio",
|
||||
".bin": "application/mac-binary",
|
||||
".bmp": "image/bmp",
|
||||
".boo": "application/book",
|
||||
".book": "application/book",
|
||||
".boz": "application/x-bzip2",
|
||||
".bsh": "application/x-bsh",
|
||||
".bz2": "application/x-bzip2",
|
||||
".bz": "application/x-bzip",
|
||||
".c++": ContentTextHeaderValue,
|
||||
".c": "text/x-c",
|
||||
".cab": "application/vnd.ms-cab-compressed",
|
||||
".cat": "application/vndms-pkiseccat",
|
||||
".cc": "text/x-c",
|
||||
".ccad": "application/clariscad",
|
||||
".cco": "application/x-cocoa",
|
||||
".cdf": "application/cdf",
|
||||
".cer": "application/pkix-cert",
|
||||
".cha": "application/x-chat",
|
||||
".chat": "application/x-chat",
|
||||
".chrt": "application/vnd.kde.kchart",
|
||||
".class": "application/java",
|
||||
".com": ContentTextHeaderValue,
|
||||
".conf": ContentTextHeaderValue,
|
||||
".cpio": "application/x-cpio",
|
||||
".cpp": "text/x-c",
|
||||
".cpt": "application/mac-compactpro",
|
||||
".crl": "application/pkcs-crl",
|
||||
".crt": "application/pkix-cert",
|
||||
".crx": "application/x-chrome-extension",
|
||||
".csh": "text/x-scriptcsh",
|
||||
".css": "text/css",
|
||||
".csv": "text/csv",
|
||||
".cxx": ContentTextHeaderValue,
|
||||
".dar": "application/x-dar",
|
||||
".dcr": "application/x-director",
|
||||
".deb": "application/x-debian-package",
|
||||
".deepv": "application/x-deepv",
|
||||
".def": ContentTextHeaderValue,
|
||||
".der": "application/x-x509-ca-cert",
|
||||
".dif": "video/x-dv",
|
||||
".dir": "application/x-director",
|
||||
".divx": "video/divx",
|
||||
".dl": "video/dl",
|
||||
".dmg": "application/x-apple-diskimage",
|
||||
".doc": "application/msword",
|
||||
".dot": "application/msword",
|
||||
".dp": "application/commonground",
|
||||
".drw": "application/drafting",
|
||||
".dump": "application/octet-stream",
|
||||
".dv": "video/x-dv",
|
||||
".dvi": "application/x-dvi",
|
||||
".dwf": "drawing/x-dwf=(old)",
|
||||
".dwg": "application/acad",
|
||||
".dxf": "application/dxf",
|
||||
".dxr": "application/x-director",
|
||||
".el": "text/x-scriptelisp",
|
||||
".elc": "application/x-bytecodeelisp=(compiled=elisp)",
|
||||
".eml": "message/rfc822",
|
||||
".env": "application/x-envoy",
|
||||
".eps": "application/postscript",
|
||||
".es": "application/x-esrehber",
|
||||
".etx": "text/x-setext",
|
||||
".evy": "application/envoy",
|
||||
".exe": "application/octet-stream",
|
||||
".f77": "text/x-fortran",
|
||||
".f90": "text/x-fortran",
|
||||
".f": "text/x-fortran",
|
||||
".fdf": "application/vndfdf",
|
||||
".fif": "application/fractals",
|
||||
".fli": "video/fli",
|
||||
".flo": "image/florian",
|
||||
".flv": "video/x-flv",
|
||||
".flx": "text/vndfmiflexstor",
|
||||
".fmf": "video/x-atomic3d-feature",
|
||||
".for": "text/x-fortran",
|
||||
".fpx": "image/vndfpx",
|
||||
".frl": "application/freeloader",
|
||||
".funk": "audio/make",
|
||||
".g3": "image/g3fax",
|
||||
".g": ContentTextHeaderValue,
|
||||
".gif": "image/gif",
|
||||
".gl": "video/gl",
|
||||
".gsd": "audio/x-gsm",
|
||||
".gsm": "audio/x-gsm",
|
||||
".gsp": "application/x-gsp",
|
||||
".gss": "application/x-gss",
|
||||
".gtar": "application/x-gtar",
|
||||
".gz": "application/x-compressed",
|
||||
".gzip": "application/x-gzip",
|
||||
".h": "text/x-h",
|
||||
".hdf": "application/x-hdf",
|
||||
".help": "application/x-helpfile",
|
||||
".hgl": "application/vndhp-hpgl",
|
||||
".hh": "text/x-h",
|
||||
".hlb": "text/x-script",
|
||||
".hlp": "application/hlp",
|
||||
".hpg": "application/vndhp-hpgl",
|
||||
".hpgl": "application/vndhp-hpgl",
|
||||
".hqx": "application/binhex",
|
||||
".hta": "application/hta",
|
||||
".htc": "text/x-component",
|
||||
".htm": "text/html",
|
||||
".html": "text/html",
|
||||
".htmls": "text/html",
|
||||
".htt": "text/webviewhtml",
|
||||
".htx": "text/html",
|
||||
".ice": "x-conference/x-cooltalk",
|
||||
".ico": "image/x-icon",
|
||||
".ics": "text/calendar",
|
||||
".icz": "text/calendar",
|
||||
".idc": ContentTextHeaderValue,
|
||||
".ief": "image/ief",
|
||||
".iefs": "image/ief",
|
||||
".iges": "application/iges",
|
||||
".igs": "application/iges",
|
||||
".ima": "application/x-ima",
|
||||
".imap": "application/x-httpd-imap",
|
||||
".inf": "application/inf",
|
||||
".ins": "application/x-internett-signup",
|
||||
".ip": "application/x-ip2",
|
||||
".isu": "video/x-isvideo",
|
||||
".it": "audio/it",
|
||||
".iv": "application/x-inventor",
|
||||
".ivr": "i-world/i-vrml",
|
||||
".ivy": "application/x-livescreen",
|
||||
".jam": "audio/x-jam",
|
||||
".jav": "text/x-java-source",
|
||||
".java": "text/x-java-source",
|
||||
".jcm": "application/x-java-commerce",
|
||||
".jfif-tbnl": "image/jpeg",
|
||||
".jfif": "image/jpeg",
|
||||
".jnlp": "application/x-java-jnlp-file",
|
||||
".jpe": "image/jpeg",
|
||||
".jpeg": "image/jpeg",
|
||||
".jpg": "image/jpeg",
|
||||
".jps": "image/x-jps",
|
||||
".js": ContentJavascriptHeaderValue,
|
||||
".mjs": ContentJavascriptHeaderValue,
|
||||
".json": ContentJSONHeaderValue,
|
||||
".vue": ContentJavascriptHeaderValue,
|
||||
".jut": "image/jutvision",
|
||||
".kar": "audio/midi",
|
||||
".karbon": "application/vnd.kde.karbon",
|
||||
".kfo": "application/vnd.kde.kformula",
|
||||
".flw": "application/vnd.kde.kivio",
|
||||
".kml": "application/vnd.google-earth.kml+xml",
|
||||
".kmz": "application/vnd.google-earth.kmz",
|
||||
".kon": "application/vnd.kde.kontour",
|
||||
".kpr": "application/vnd.kde.kpresenter",
|
||||
".kpt": "application/vnd.kde.kpresenter",
|
||||
".ksp": "application/vnd.kde.kspread",
|
||||
".kwd": "application/vnd.kde.kword",
|
||||
".kwt": "application/vnd.kde.kword",
|
||||
".ksh": "text/x-scriptksh",
|
||||
".la": "audio/nspaudio",
|
||||
".lam": "audio/x-liveaudio",
|
||||
".latex": "application/x-latex",
|
||||
".lha": "application/lha",
|
||||
".lhx": "application/octet-stream",
|
||||
".list": ContentTextHeaderValue,
|
||||
".lma": "audio/nspaudio",
|
||||
".log": ContentTextHeaderValue,
|
||||
".lsp": "text/x-scriptlisp",
|
||||
".lst": ContentTextHeaderValue,
|
||||
".lsx": "text/x-la-asf",
|
||||
".ltx": "application/x-latex",
|
||||
".lzh": "application/octet-stream",
|
||||
".lzx": "application/lzx",
|
||||
".m1v": "video/mpeg",
|
||||
".m2a": "audio/mpeg",
|
||||
".m2v": "video/mpeg",
|
||||
".m3u": "audio/x-mpegurl",
|
||||
".m": "text/x-m",
|
||||
".man": "application/x-troff-man",
|
||||
".manifest": "text/cache-manifest",
|
||||
".map": "application/x-navimap",
|
||||
".mar": ContentTextHeaderValue,
|
||||
".mbd": "application/mbedlet",
|
||||
".mc$": "application/x-magic-cap-package-10",
|
||||
".mcd": "application/mcad",
|
||||
".mcf": "text/mcf",
|
||||
".mcp": "application/netmc",
|
||||
".me": "application/x-troff-me",
|
||||
".mht": "message/rfc822",
|
||||
".mhtml": "message/rfc822",
|
||||
".mid": "application/x-midi",
|
||||
".midi": "application/x-midi",
|
||||
".mif": "application/x-frame",
|
||||
".mime": "message/rfc822",
|
||||
".mjf": "audio/x-vndaudioexplosionmjuicemediafile",
|
||||
".mjpg": "video/x-motion-jpeg",
|
||||
".mm": "application/base64",
|
||||
".mme": "application/base64",
|
||||
".mod": "audio/mod",
|
||||
".moov": "video/quicktime",
|
||||
".mov": "video/quicktime",
|
||||
".movie": "video/x-sgi-movie",
|
||||
".mp2": "audio/mpeg",
|
||||
".mp3": "audio/mpeg",
|
||||
".mp4": "video/mp4",
|
||||
".mpa": "audio/mpeg",
|
||||
".mpc": "application/x-project",
|
||||
".mpe": "video/mpeg",
|
||||
".mpeg": "video/mpeg",
|
||||
".mpg": "video/mpeg",
|
||||
".mpga": "audio/mpeg",
|
||||
".mpp": "application/vndms-project",
|
||||
".mpt": "application/x-project",
|
||||
".mpv": "application/x-project",
|
||||
".mpx": "application/x-project",
|
||||
".mrc": "application/marc",
|
||||
".ms": "application/x-troff-ms",
|
||||
".mv": "video/x-sgi-movie",
|
||||
".my": "audio/make",
|
||||
".mzz": "application/x-vndaudioexplosionmzz",
|
||||
".nap": "image/naplps",
|
||||
".naplps": "image/naplps",
|
||||
".nc": "application/x-netcdf",
|
||||
".ncm": "application/vndnokiaconfiguration-message",
|
||||
".nif": "image/x-niff",
|
||||
".niff": "image/x-niff",
|
||||
".nix": "application/x-mix-transfer",
|
||||
".nsc": "application/x-conference",
|
||||
".nvd": "application/x-navidoc",
|
||||
".o": "application/octet-stream",
|
||||
".oda": "application/oda",
|
||||
".odb": "application/vnd.oasis.opendocument.database",
|
||||
".odc": "application/vnd.oasis.opendocument.chart",
|
||||
".odf": "application/vnd.oasis.opendocument.formula",
|
||||
".odg": "application/vnd.oasis.opendocument.graphics",
|
||||
".odi": "application/vnd.oasis.opendocument.image",
|
||||
".odm": "application/vnd.oasis.opendocument.text-master",
|
||||
".odp": "application/vnd.oasis.opendocument.presentation",
|
||||
".ods": "application/vnd.oasis.opendocument.spreadsheet",
|
||||
".odt": "application/vnd.oasis.opendocument.text",
|
||||
".oga": "audio/ogg",
|
||||
".ogg": "audio/ogg",
|
||||
".ogv": "video/ogg",
|
||||
".omc": "application/x-omc",
|
||||
".omcd": "application/x-omcdatamaker",
|
||||
".omcr": "application/x-omcregerator",
|
||||
".otc": "application/vnd.oasis.opendocument.chart-template",
|
||||
".otf": "application/vnd.oasis.opendocument.formula-template",
|
||||
".otg": "application/vnd.oasis.opendocument.graphics-template",
|
||||
".oth": "application/vnd.oasis.opendocument.text-web",
|
||||
".oti": "application/vnd.oasis.opendocument.image-template",
|
||||
".otm": "application/vnd.oasis.opendocument.text-master",
|
||||
".otp": "application/vnd.oasis.opendocument.presentation-template",
|
||||
".ots": "application/vnd.oasis.opendocument.spreadsheet-template",
|
||||
".ott": "application/vnd.oasis.opendocument.text-template",
|
||||
".p10": "application/pkcs10",
|
||||
".p12": "application/pkcs-12",
|
||||
".p7a": "application/x-pkcs7-signature",
|
||||
".p7c": "application/pkcs7-mime",
|
||||
".p7m": "application/pkcs7-mime",
|
||||
".p7r": "application/x-pkcs7-certreqresp",
|
||||
".p7s": "application/pkcs7-signature",
|
||||
".p": "text/x-pascal",
|
||||
".part": "application/pro_eng",
|
||||
".pas": "text/pascal",
|
||||
".pbm": "image/x-portable-bitmap",
|
||||
".pcl": "application/vndhp-pcl",
|
||||
".pct": "image/x-pict",
|
||||
".pcx": "image/x-pcx",
|
||||
".pdb": "chemical/x-pdb",
|
||||
".pdf": "application/pdf",
|
||||
".pfunk": "audio/make",
|
||||
".pgm": "image/x-portable-graymap",
|
||||
".pic": "image/pict",
|
||||
".pict": "image/pict",
|
||||
".pkg": "application/x-newton-compatible-pkg",
|
||||
".pko": "application/vndms-pkipko",
|
||||
".pl": "text/x-scriptperl",
|
||||
".plx": "application/x-pixclscript",
|
||||
".pm4": "application/x-pagemaker",
|
||||
".pm5": "application/x-pagemaker",
|
||||
".pm": "text/x-scriptperl-module",
|
||||
".png": "image/png",
|
||||
".pnm": "application/x-portable-anymap",
|
||||
".pot": "application/mspowerpoint",
|
||||
".pov": "model/x-pov",
|
||||
".ppa": "application/vndms-powerpoint",
|
||||
".ppm": "image/x-portable-pixmap",
|
||||
".pps": "application/mspowerpoint",
|
||||
".ppt": "application/mspowerpoint",
|
||||
".ppz": "application/mspowerpoint",
|
||||
".pre": "application/x-freelance",
|
||||
".prt": "application/pro_eng",
|
||||
".ps": "application/postscript",
|
||||
".psd": "application/octet-stream",
|
||||
".pvu": "paleovu/x-pv",
|
||||
".pwz": "application/vndms-powerpoint",
|
||||
".py": "text/x-scriptphyton",
|
||||
".pyc": "application/x-bytecodepython",
|
||||
".qcp": "audio/vndqcelp",
|
||||
".qd3": "x-world/x-3dmf",
|
||||
".qd3d": "x-world/x-3dmf",
|
||||
".qif": "image/x-quicktime",
|
||||
".qt": "video/quicktime",
|
||||
".qtc": "video/x-qtc",
|
||||
".qti": "image/x-quicktime",
|
||||
".qtif": "image/x-quicktime",
|
||||
".ra": "audio/x-pn-realaudio",
|
||||
".ram": "audio/x-pn-realaudio",
|
||||
".rar": "application/x-rar-compressed",
|
||||
".ras": "application/x-cmu-raster",
|
||||
".rast": "image/cmu-raster",
|
||||
".rexx": "text/x-scriptrexx",
|
||||
".rf": "image/vndrn-realflash",
|
||||
".rgb": "image/x-rgb",
|
||||
".rm": "application/vndrn-realmedia",
|
||||
".rmi": "audio/mid",
|
||||
".rmm": "audio/x-pn-realaudio",
|
||||
".rmp": "audio/x-pn-realaudio",
|
||||
".rng": "application/ringing-tones",
|
||||
".rnx": "application/vndrn-realplayer",
|
||||
".roff": "application/x-troff",
|
||||
".rp": "image/vndrn-realpix",
|
||||
".rpm": "audio/x-pn-realaudio-plugin",
|
||||
".rt": "text/vndrn-realtext",
|
||||
".rtf": "text/richtext",
|
||||
".rtx": "text/richtext",
|
||||
".rv": "video/vndrn-realvideo",
|
||||
".s": "text/x-asm",
|
||||
".s3m": "audio/s3m",
|
||||
".s7z": "application/x-7z-compressed",
|
||||
".saveme": "application/octet-stream",
|
||||
".sbk": "application/x-tbook",
|
||||
".scm": "text/x-scriptscheme",
|
||||
".sdml": ContentTextHeaderValue,
|
||||
".sdp": "application/sdp",
|
||||
".sdr": "application/sounder",
|
||||
".sea": "application/sea",
|
||||
".set": "application/set",
|
||||
".sgm": "text/x-sgml",
|
||||
".sgml": "text/x-sgml",
|
||||
".sh": "text/x-scriptsh",
|
||||
".shar": "application/x-bsh",
|
||||
".shtml": "text/x-server-parsed-html",
|
||||
".sid": "audio/x-psid",
|
||||
".skd": "application/x-koan",
|
||||
".skm": "application/x-koan",
|
||||
".skp": "application/x-koan",
|
||||
".skt": "application/x-koan",
|
||||
".sit": "application/x-stuffit",
|
||||
".sitx": "application/x-stuffitx",
|
||||
".sl": "application/x-seelogo",
|
||||
".smi": "application/smil",
|
||||
".smil": "application/smil",
|
||||
".snd": "audio/basic",
|
||||
".sol": "application/solids",
|
||||
".spc": "text/x-speech",
|
||||
".spl": "application/futuresplash",
|
||||
".spr": "application/x-sprite",
|
||||
".sprite": "application/x-sprite",
|
||||
".spx": "audio/ogg",
|
||||
".src": "application/x-wais-source",
|
||||
".ssi": "text/x-server-parsed-html",
|
||||
".ssm": "application/streamingmedia",
|
||||
".sst": "application/vndms-pkicertstore",
|
||||
".step": "application/step",
|
||||
".stl": "application/sla",
|
||||
".stp": "application/step",
|
||||
".sv4cpio": "application/x-sv4cpio",
|
||||
".sv4crc": "application/x-sv4crc",
|
||||
".svf": "image/vnddwg",
|
||||
".svg": "image/svg+xml",
|
||||
".svr": "application/x-world",
|
||||
".swf": "application/x-shockwave-flash",
|
||||
".t": "application/x-troff",
|
||||
".talk": "text/x-speech",
|
||||
".tar": "application/x-tar",
|
||||
".tbk": "application/toolbook",
|
||||
".tcl": "text/x-scripttcl",
|
||||
".tcsh": "text/x-scripttcsh",
|
||||
".tex": "application/x-tex",
|
||||
".texi": "application/x-texinfo",
|
||||
".texinfo": "application/x-texinfo",
|
||||
".text": ContentTextHeaderValue,
|
||||
".tgz": "application/gnutar",
|
||||
".tif": "image/tiff",
|
||||
".tiff": "image/tiff",
|
||||
".tr": "application/x-troff",
|
||||
".tsi": "audio/tsp-audio",
|
||||
".tsp": "application/dsptype",
|
||||
".tsv": "text/tab-separated-values",
|
||||
".turbot": "image/florian",
|
||||
".txt": ContentTextHeaderValue,
|
||||
".uil": "text/x-uil",
|
||||
".uni": "text/uri-list",
|
||||
".unis": "text/uri-list",
|
||||
".unv": "application/i-deas",
|
||||
".uri": "text/uri-list",
|
||||
".uris": "text/uri-list",
|
||||
".ustar": "application/x-ustar",
|
||||
".uu": "text/x-uuencode",
|
||||
".uue": "text/x-uuencode",
|
||||
".vcd": "application/x-cdlink",
|
||||
".vcf": "text/x-vcard",
|
||||
".vcard": "text/x-vcard",
|
||||
".vcs": "text/x-vcalendar",
|
||||
".vda": "application/vda",
|
||||
".vdo": "video/vdo",
|
||||
".vew": "application/groupwise",
|
||||
".viv": "video/vivo",
|
||||
".vivo": "video/vivo",
|
||||
".vmd": "application/vocaltec-media-desc",
|
||||
".vmf": "application/vocaltec-media-file",
|
||||
".voc": "audio/voc",
|
||||
".vos": "video/vosaic",
|
||||
".vox": "audio/voxware",
|
||||
".vqe": "audio/x-twinvq-plugin",
|
||||
".vqf": "audio/x-twinvq",
|
||||
".vql": "audio/x-twinvq-plugin",
|
||||
".vrml": "application/x-vrml",
|
||||
".vrt": "x-world/x-vrt",
|
||||
".vsd": "application/x-visio",
|
||||
".vst": "application/x-visio",
|
||||
".vsw": "application/x-visio",
|
||||
".w60": "application/wordperfect60",
|
||||
".w61": "application/wordperfect61",
|
||||
".w6w": "application/msword",
|
||||
".wav": "audio/wav",
|
||||
".wb1": "application/x-qpro",
|
||||
".wbmp": "image/vnd.wap.wbmp",
|
||||
".web": "application/vndxara",
|
||||
".wiz": "application/msword",
|
||||
".wk1": "application/x-123",
|
||||
".wmf": "windows/metafile",
|
||||
".wml": "text/vnd.wap.wml",
|
||||
".wmlc": "application/vnd.wap.wmlc",
|
||||
".wmls": "text/vnd.wap.wmlscript",
|
||||
".wmlsc": "application/vnd.wap.wmlscriptc",
|
||||
".word": "application/msword",
|
||||
".wp5": "application/wordperfect",
|
||||
".wp6": "application/wordperfect",
|
||||
".wp": "application/wordperfect",
|
||||
".wpd": "application/wordperfect",
|
||||
".wq1": "application/x-lotus",
|
||||
".wri": "application/mswrite",
|
||||
".wrl": "application/x-world",
|
||||
".wrz": "model/vrml",
|
||||
".wsc": "text/scriplet",
|
||||
".wsrc": "application/x-wais-source",
|
||||
".wtk": "application/x-wintalk",
|
||||
".x-png": "image/png",
|
||||
".xbm": "image/x-xbitmap",
|
||||
".xdr": "video/x-amt-demorun",
|
||||
".xgz": "xgl/drawing",
|
||||
".xif": "image/vndxiff",
|
||||
".xl": "application/excel",
|
||||
".xla": "application/excel",
|
||||
".xlb": "application/excel",
|
||||
".xlc": "application/excel",
|
||||
".xld": "application/excel",
|
||||
".xlk": "application/excel",
|
||||
".xll": "application/excel",
|
||||
".xlm": "application/excel",
|
||||
".xls": "application/excel",
|
||||
".xlt": "application/excel",
|
||||
".xlv": "application/excel",
|
||||
".xlw": "application/excel",
|
||||
".xm": "audio/xm",
|
||||
".xml": ContentXMLHeaderValue,
|
||||
".xmz": "xgl/movie",
|
||||
".xpix": "application/x-vndls-xpix",
|
||||
".xpm": "image/x-xpixmap",
|
||||
".xsr": "video/x-amt-showrun",
|
||||
".xwd": "image/x-xwd",
|
||||
".xyz": "chemical/x-pdb",
|
||||
".z": "application/x-compress",
|
||||
".zip": "application/zip",
|
||||
".zoo": "application/octet-stream",
|
||||
".zsh": "text/x-scriptzsh",
|
||||
".docx": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||
".docm": "application/vnd.ms-word.document.macroEnabled.12",
|
||||
".dotx": "application/vnd.openxmlformats-officedocument.wordprocessingml.template",
|
||||
".dotm": "application/vnd.ms-word.template.macroEnabled.12",
|
||||
".xlsx": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
".xlsm": "application/vnd.ms-excel.sheet.macroEnabled.12",
|
||||
".xltx": "application/vnd.openxmlformats-officedocument.spreadsheetml.template",
|
||||
".xltm": "application/vnd.ms-excel.template.macroEnabled.12",
|
||||
".xlsb": "application/vnd.ms-excel.sheet.binary.macroEnabled.12",
|
||||
".xlam": "application/vnd.ms-excel.addin.macroEnabled.12",
|
||||
".pptx": "application/vnd.openxmlformats-officedocument.presentationml.presentation",
|
||||
".pptm": "application/vnd.ms-powerpoint.presentation.macroEnabled.12",
|
||||
".ppsx": "application/vnd.openxmlformats-officedocument.presentationml.slideshow",
|
||||
".ppsm": "application/vnd.ms-powerpoint.slideshow.macroEnabled.12",
|
||||
".potx": "application/vnd.openxmlformats-officedocument.presentationml.template",
|
||||
".potm": "application/vnd.ms-powerpoint.template.macroEnabled.12",
|
||||
".ppam": "application/vnd.ms-powerpoint.addin.macroEnabled.12",
|
||||
".sldx": "application/vnd.openxmlformats-officedocument.presentationml.slide",
|
||||
".sldm": "application/vnd.ms-powerpoint.slide.macroEnabled.12",
|
||||
".thmx": "application/vnd.ms-officetheme",
|
||||
".onetoc": "application/onenote",
|
||||
".onetoc2": "application/onenote",
|
||||
".onetmp": "application/onenote",
|
||||
".onepkg": "application/onenote",
|
||||
".xpi": "application/x-xpinstall",
|
||||
".wasm": "application/wasm",
|
||||
".m4a": "audio/mp4",
|
||||
".flac": "audio/x-flac",
|
||||
".amr": "audio/amr",
|
||||
".aac": "audio/aac",
|
||||
".opus": "video/ogg",
|
||||
".m4v": "video/mp4",
|
||||
".mkv": "video/x-matroska",
|
||||
".caf": "audio/x-caf",
|
||||
".m3u8": "application/x-mpegURL",
|
||||
".mpd": "application/dash+xml",
|
||||
".webp": "image/webp",
|
||||
}
|
||||
|
||||
//nolint:gochecknoinits
|
||||
func init() {
|
||||
for ext, typ := range types {
|
||||
// skip errors
|
||||
_ = mime.AddExtensionType(ext, typ)
|
||||
}
|
||||
}
|
|
@ -3,6 +3,8 @@ package files
|
|||
import (
|
||||
"os"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
func isBinary(content []byte) bool {
|
||||
|
@ -57,3 +59,16 @@ func IsNamedPipe(mode os.FileMode) bool {
|
|||
func IsSymlink(mode os.FileMode) bool {
|
||||
return mode&os.ModeSymlink != 0
|
||||
}
|
||||
|
||||
func GetRealPath(fs afero.Fs, path string) string {
|
||||
if realPathFs, ok := fs.(interface {
|
||||
RealPath(name string) (fPath string, err error)
|
||||
}); ok {
|
||||
realPath, err := realPathFs.RealPath(path)
|
||||
if err == nil {
|
||||
return realPath
|
||||
}
|
||||
}
|
||||
|
||||
return path
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
"version": "3.0.0",
|
||||
"dependencies": {
|
||||
"@chenfengyuan/vue-number-input": "^2.0.1",
|
||||
"@onlyoffice/document-editor-vue": "^1.4.0",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/integrations": "^10.9.0",
|
||||
"ace-builds": "^1.32.9",
|
||||
|
@ -2506,6 +2507,14 @@
|
|||
"node": ">= 8"
|
||||
}
|
||||
},
|
||||
"node_modules/@onlyoffice/document-editor-vue": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/@onlyoffice/document-editor-vue/-/document-editor-vue-1.4.0.tgz",
|
||||
"integrity": "sha512-Fg5gSc1zF6bmpRapUd7rMpm7kEDF7mQIHQKfcsfJcILdFX9bwIhnkXEucETEA9zdt92nWMS6qiAgVeT61TdCyw==",
|
||||
"peerDependencies": {
|
||||
"vue": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@pkgr/core": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz",
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"@chenfengyuan/vue-number-input": "^2.0.1",
|
||||
"@onlyoffice/document-editor-vue": "^1.4.0",
|
||||
"@vueuse/core": "^10.9.0",
|
||||
"@vueuse/integrations": "^10.9.0",
|
||||
"ace-builds": "^1.32.9",
|
||||
|
|
|
@ -195,6 +195,8 @@
|
|||
"newPassword": "كلمة المرور الجديدة",
|
||||
"newPasswordConfirm": "تأكيد كلمة المرور",
|
||||
"newUser": "مستخدم جديد",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "كلمة المرور",
|
||||
"passwordUpdated": "تم تغيير كلمة المرور!",
|
||||
"path": "المسار",
|
||||
|
|
|
@ -182,6 +182,8 @@
|
|||
"newPassword": "Ihr neues Passwort.",
|
||||
"newPasswordConfirm": "Bestätigen Sie Ihr neues Passwort",
|
||||
"newUser": "Neuer Benutzer",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Passwort",
|
||||
"passwordUpdated": "Passwort aktualisiert!",
|
||||
"path": "Pfad",
|
||||
|
@ -247,4 +249,4 @@
|
|||
"seconds": "Sekunden",
|
||||
"unit": "Zeiteinheit"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -195,6 +195,9 @@
|
|||
"newPassword": "Your new password",
|
||||
"newPasswordConfirm": "Confirm your new password",
|
||||
"newUser": "New User",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"onlyOfficeJwtSecret": "Only Office JWT Secret (works only with https, leave blank to disable)",
|
||||
"password": "Password",
|
||||
"passwordUpdated": "Password updated!",
|
||||
"path": "Path",
|
||||
|
@ -261,4 +264,4 @@
|
|||
"seconds": "Seconds",
|
||||
"unit": "Time Unit"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -182,6 +182,8 @@
|
|||
"newPassword": "Tu nueva contraseña",
|
||||
"newPasswordConfirm": "Confirma tu contraseña",
|
||||
"newUser": "Nuevo usuario",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Contraseña",
|
||||
"passwordUpdated": "¡Contraseña actualizada!",
|
||||
"path": "Ruta",
|
||||
|
@ -247,4 +249,4 @@
|
|||
"seconds": "Segundos",
|
||||
"unit": "Unidad"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -181,6 +181,8 @@
|
|||
"newPassword": "Votre nouveau mot de passe",
|
||||
"newPasswordConfirm": "Confirmation du nouveau mot de passe",
|
||||
"newUser": "Nouvel Utilisateur",
|
||||
"onlyOffice": "Intégration Only Office",
|
||||
"onlyOfficeUrl": "URL vers OnlyOffice (laisser vide pour désactiver)",
|
||||
"password": "Mot de passe",
|
||||
"passwordUpdated": "Mot de passe mis à jour !",
|
||||
"path": "",
|
||||
|
@ -246,4 +248,4 @@
|
|||
"seconds": "Secondes",
|
||||
"unit": "Unité de temps"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -169,6 +169,8 @@
|
|||
"newPassword": "Nýja lykilorðið þitt",
|
||||
"newPasswordConfirm": "Staðfestu nýja lykilorðið",
|
||||
"newUser": "Nýr notandi",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Lykilorð",
|
||||
"passwordUpdated": "Lykilorð vistað!",
|
||||
"path": "",
|
||||
|
@ -232,4 +234,4 @@
|
|||
"seconds": "Sekúndur",
|
||||
"unit": "Tímastilling"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -170,6 +170,8 @@
|
|||
"newPassword": "La tua nuova password",
|
||||
"newPasswordConfirm": "Conferma la password",
|
||||
"newUser": "Nuovo utente",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Password",
|
||||
"passwordUpdated": "Password aggiornata!",
|
||||
"path": "Percorso",
|
||||
|
@ -233,4 +235,4 @@
|
|||
"seconds": "Secondi",
|
||||
"unit": "Unità di tempo"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -191,6 +191,8 @@
|
|||
"newPassword": "新しいパスワード",
|
||||
"newPasswordConfirm": "新しいパスワード(再入力)",
|
||||
"newUser": "新規ユーザー作成",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "パスワード",
|
||||
"passwordUpdated": "パスワードを更新しました!",
|
||||
"path": "パス",
|
||||
|
@ -256,4 +258,4 @@
|
|||
"seconds": "秒",
|
||||
"unit": "時間の単位"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -169,6 +169,8 @@
|
|||
"newPassword": "새로운 비밀번호",
|
||||
"newPasswordConfirm": "새로운 비밀번호 확인",
|
||||
"newUser": "새로운 사용자",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "비밀번호",
|
||||
"passwordUpdated": "비밀번호 수정 완료!",
|
||||
"path": "",
|
||||
|
@ -232,4 +234,4 @@
|
|||
"seconds": "초",
|
||||
"unit": "Time Unit"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -169,6 +169,8 @@
|
|||
"newPassword": "Uw nieuw wachtwoord",
|
||||
"newPasswordConfirm": "Bevestig uw nieuw wachtwoord",
|
||||
"newUser": "Nieuwe gebruiker",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Wachtwoord",
|
||||
"passwordUpdated": "Wachtwoord bijgewerkt!",
|
||||
"path": "",
|
||||
|
@ -232,4 +234,4 @@
|
|||
"seconds": "Seconden",
|
||||
"unit": "Tijdseenheid"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -169,6 +169,8 @@
|
|||
"newPassword": "Twoje nowe hasło",
|
||||
"newPasswordConfirm": "Potwierdź swoje hasło",
|
||||
"newUser": "Nowy Użytkownik",
|
||||
"onlyOffice": "Integracja z Only Office",
|
||||
"onlyOfficeUrl": "Link do Only Office (w celu wyłączenia usługi, pozostaw puste)",
|
||||
"password": "Hasło",
|
||||
"passwordUpdated": "Hasło zostało zapisane!",
|
||||
"path": "Ścieżka",
|
||||
|
@ -232,4 +234,4 @@
|
|||
"seconds": "Sekundy",
|
||||
"unit": "Jednostka czasu"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -182,6 +182,8 @@
|
|||
"newPassword": "Nova senha",
|
||||
"newPasswordConfirm": "Confirme a nova senha",
|
||||
"newUser": "Novo usuário",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Senha",
|
||||
"passwordUpdated": "Senha atualizada!",
|
||||
"path": "",
|
||||
|
@ -247,4 +249,4 @@
|
|||
"seconds": "Segundos",
|
||||
"unit": "Unidades de Tempo"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -170,6 +170,8 @@
|
|||
"newPassword": "Nova palavra-passe",
|
||||
"newPasswordConfirm": "Confirme a nova palavra-passe",
|
||||
"newUser": "Novo utilizador",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Palavra-passe",
|
||||
"passwordUpdated": "Palavra-passe atualizada!",
|
||||
"path": "",
|
||||
|
@ -233,4 +235,4 @@
|
|||
"seconds": "Segundos",
|
||||
"unit": "Unidades de tempo"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -169,6 +169,8 @@
|
|||
"newPassword": "Noua ta parolă",
|
||||
"newPasswordConfirm": "Confirmă noua parolă",
|
||||
"newUser": "Utilizator nou",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Parola",
|
||||
"passwordUpdated": "Parola actualizată!",
|
||||
"path": "",
|
||||
|
@ -232,4 +234,4 @@
|
|||
"seconds": "Secunde",
|
||||
"unit": "Unitate de timp"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -177,6 +177,8 @@
|
|||
"newPassword": "Новый пароль",
|
||||
"newPasswordConfirm": "Повтор нового пароля",
|
||||
"newUser": "Новый пользователь",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Пароль",
|
||||
"passwordUpdated": "Пароль обновлен!",
|
||||
"path": "Путь",
|
||||
|
@ -242,4 +244,4 @@
|
|||
"seconds": "Секунды",
|
||||
"unit": "Единица времени"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -169,6 +169,8 @@
|
|||
"newPassword": "Ditt nya lösenord",
|
||||
"newPasswordConfirm": "Bekräfta ditt nya lösenord",
|
||||
"newUser": "Ny användare",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "Lösenord",
|
||||
"passwordUpdated": "Lösenord uppdaterat",
|
||||
"path": "",
|
||||
|
@ -232,4 +234,4 @@
|
|||
"seconds": "Sekunder",
|
||||
"unit": "Tidsenhet"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -195,6 +195,8 @@
|
|||
"newPassword": "你的新密码",
|
||||
"newPasswordConfirm": "再次输入以确认你的新密码",
|
||||
"newUser": "新建用户",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "密码",
|
||||
"passwordUpdated": "密码已更新!",
|
||||
"path": "路径",
|
||||
|
@ -261,4 +263,4 @@
|
|||
"seconds": "秒",
|
||||
"unit": "时间单位"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -195,6 +195,8 @@
|
|||
"newPassword": "您的新密碼",
|
||||
"newPasswordConfirm": "重輸一遍新密碼",
|
||||
"newUser": "建立使用者",
|
||||
"onlyOffice": "Only Office Integration",
|
||||
"onlyOfficeUrl": "Only Office URL (leave blank to disable)",
|
||||
"password": "密碼",
|
||||
"passwordUpdated": "密碼已更新!",
|
||||
"path": "路徑",
|
||||
|
@ -261,4 +263,4 @@
|
|||
"seconds": "秒",
|
||||
"unit": "時間單位"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,8 @@ type ResourceType =
|
|||
| "pdf"
|
||||
| "text"
|
||||
| "blob"
|
||||
| "textImmutable";
|
||||
| "textImmutable"
|
||||
| "officedocument";
|
||||
|
||||
type DownloadFormat =
|
||||
| "zip"
|
||||
|
|
|
@ -8,6 +8,7 @@ interface ISettings {
|
|||
tus: SettingsTus;
|
||||
shell: string[];
|
||||
commands: SettingsCommand;
|
||||
onlyoffice: SettingsOnlyOffice;
|
||||
}
|
||||
|
||||
interface SettingsDefaults {
|
||||
|
@ -55,3 +56,8 @@ interface SettingsUnit {
|
|||
GB: number;
|
||||
TB: number;
|
||||
}
|
||||
|
||||
interface SettingsOnlyOffice {
|
||||
url: string;
|
||||
jwtSecret: string;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ const enableExec: boolean = window.FileBrowser.EnableExec;
|
|||
const tusSettings = window.FileBrowser.TusSettings;
|
||||
const origin = window.location.origin;
|
||||
const tusEndpoint = `/api/tus`;
|
||||
const onlyOfficeUrl = window.FileBrowser.OnlyOfficeUrl;
|
||||
|
||||
export {
|
||||
name,
|
||||
|
@ -39,4 +40,5 @@ export {
|
|||
tusSettings,
|
||||
origin,
|
||||
tusEndpoint,
|
||||
onlyOfficeUrl,
|
||||
};
|
||||
|
|
|
@ -37,6 +37,7 @@ import { storeToRefs } from "pinia";
|
|||
import { useFileStore } from "@/stores/file";
|
||||
import { useLayoutStore } from "@/stores/layout";
|
||||
import { useUploadStore } from "@/stores/upload";
|
||||
import { onlyOfficeUrl } from "@/utils/constants";
|
||||
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||
|
@ -47,6 +48,9 @@ import FileListing from "@/views/files/FileListing.vue";
|
|||
import { StatusError } from "@/api/utils";
|
||||
const Editor = defineAsyncComponent(() => import("@/views/files/Editor.vue"));
|
||||
const Preview = defineAsyncComponent(() => import("@/views/files/Preview.vue"));
|
||||
const OnlyOfficeEditor = defineAsyncComponent(
|
||||
() => import("@/views/files/OnlyOfficeEditor.vue")
|
||||
);
|
||||
|
||||
const layoutStore = useLayoutStore();
|
||||
const fileStore = useFileStore();
|
||||
|
@ -77,6 +81,8 @@ const currentView = computed(() => {
|
|||
fileStore.req.type === "textImmutable"
|
||||
) {
|
||||
return Editor;
|
||||
} else if (fileStore.req.type === "officedocument" && onlyOfficeUrl) {
|
||||
return OnlyOfficeEditor;
|
||||
} else {
|
||||
return Preview;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
<template>
|
||||
<div id="editor-container">
|
||||
<header-bar>
|
||||
<action icon="close" :label="$t('buttons.close')" @action="close()" />
|
||||
<title>{{ fileStore.req?.name ?? "" }}</title>
|
||||
</header-bar>
|
||||
<breadcrumbs base="/files" noLink />
|
||||
<errors v-if="error" :errorCode="error.status" />
|
||||
<div id="editor" v-if="clientConfig">
|
||||
<DocumentEditor
|
||||
v-if="clientConfig"
|
||||
id="onlyoffice-editor"
|
||||
:documentServerUrl="onlyOfficeUrl"
|
||||
:config="clientConfig"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import url from "@/utils/url";
|
||||
import { onlyOfficeUrl } from "@/utils/constants";
|
||||
import HeaderBar from "@/components/header/HeaderBar.vue";
|
||||
import Action from "@/components/header/Action.vue";
|
||||
import Breadcrumbs from "@/components/Breadcrumbs.vue";
|
||||
import Errors from "@/views/Errors.vue";
|
||||
import { fetchJSON, StatusError } from "@/api/utils";
|
||||
import { useFileStore } from "@/stores/file";
|
||||
import { useRoute } from "vue-router";
|
||||
import { useRouter } from "vue-router";
|
||||
import { onMounted, ref } from "vue";
|
||||
import { DocumentEditor } from "@onlyoffice/document-editor-vue";
|
||||
|
||||
const fileStore = useFileStore();
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const error = ref<StatusError | null>(null);
|
||||
const clientConfig = ref<any>(null);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
const isMobile = window.innerWidth <= 736;
|
||||
clientConfig.value = await fetchJSON(
|
||||
`/api/onlyoffice/client-config${fileStore.req!.path}?isMobile=${isMobile}`
|
||||
);
|
||||
} catch (err) {
|
||||
if (err instanceof Error) {
|
||||
error.value = err;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
fileStore.updateRequest(null);
|
||||
let uri = url.removeLastDir(route.path) + "/";
|
||||
router.push({ path: uri });
|
||||
};
|
||||
</script>
|
|
@ -136,6 +136,31 @@
|
|||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<h3>{{ $t("settings.onlyOffice") }}</h3>
|
||||
|
||||
<p>
|
||||
<label for="onlyoffice-url">{{
|
||||
$t("settings.onlyOfficeUrl")
|
||||
}}</label>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="text"
|
||||
v-model="settings.onlyoffice.url"
|
||||
id="onlyoffice-url"
|
||||
/>
|
||||
</p>
|
||||
<p>
|
||||
<label for="onlyoffice-jwt">{{
|
||||
$t("settings.onlyOfficeJwtSecret")
|
||||
}}</label>
|
||||
<input
|
||||
class="input input--block"
|
||||
type="text"
|
||||
v-model="settings.onlyoffice.jwtSecret"
|
||||
id="onlyoffice-jwt"
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="card-action">
|
||||
|
|
1
go.mod
1
go.mod
|
@ -32,6 +32,7 @@ require (
|
|||
)
|
||||
|
||||
require (
|
||||
github.com/allegro/bigcache v1.2.1 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/asticode/go-astikit v0.42.0 // indirect
|
||||
github.com/asticode/go-astits v1.13.0 // indirect
|
||||
|
|
2
go.sum
2
go.sum
|
@ -2,6 +2,8 @@ github.com/DataDog/zstd v1.4.1 h1:3oxKN3wbHibqx897utPC2LTQU4J+IHWWJO+glkAkpFM=
|
|||
github.com/DataDog/zstd v1.4.1/go.mod h1:1jcaCB/ufaK+sKp1NBhlGmpz41jOoPQ35bpF36t7BBo=
|
||||
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863 h1:BRrxwOZBolJN4gIwvZMJY1tzqBvQgpaZiQRuIDD40jM=
|
||||
github.com/Sereal/Sereal v0.0.0-20190618215532-0b8ac451a863/go.mod h1:D0JMgToj/WdxCgd30Kc1UcA9E+WdZoJqeVOuYW7iTBM=
|
||||
github.com/allegro/bigcache v1.2.1 h1:hg1sY1raCwic3Vnsvje6TT7/pnZba83LeFck5NrFKSc=
|
||||
github.com/allegro/bigcache v1.2.1/go.mod h1:Cb/ax3seSYIx7SuZdm2G2xzfwmv3TPSk2ucNfQESPXM=
|
||||
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
|
||||
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
|
||||
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
|
||||
|
|
|
@ -84,6 +84,7 @@ func withUser(fn handleFunc) handleFunc {
|
|||
w.Header().Add("X-Renew-Token", "true")
|
||||
}
|
||||
|
||||
d.authToken = token.Raw
|
||||
d.user, err = d.store.Users.Get(d.server.Root, tk.User.ID)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
|
|
11
http/data.go
11
http/data.go
|
@ -18,11 +18,12 @@ type handleFunc func(w http.ResponseWriter, r *http.Request, d *data) (int, erro
|
|||
|
||||
type data struct {
|
||||
*runner.Runner
|
||||
settings *settings.Settings
|
||||
server *settings.Server
|
||||
store *storage.Storage
|
||||
user *users.User
|
||||
raw interface{}
|
||||
authToken string
|
||||
settings *settings.Settings
|
||||
server *settings.Server
|
||||
store *storage.Storage
|
||||
user *users.User
|
||||
raw interface{}
|
||||
}
|
||||
|
||||
// Check implements rules.Checker.
|
||||
|
|
|
@ -60,6 +60,9 @@ func NewHandler(
|
|||
users.Handle("/{id:[0-9]+}", monkey(userGetHandler, "")).Methods("GET")
|
||||
users.Handle("/{id:[0-9]+}", monkey(userDeleteHandler, "")).Methods("DELETE")
|
||||
|
||||
api.PathPrefix("/onlyoffice").Handler(monkey(onlyofficeClientConfigGetHandler, "/api/onlyoffice/client-config")).Methods("GET")
|
||||
api.PathPrefix("/onlyoffice").Handler(monkey(onlyofficeCallbackHandler, "/api/onlyoffice/callback")).Methods("POST")
|
||||
|
||||
api.PathPrefix("/resources").Handler(monkey(resourceGetHandler, "/api/resources")).Methods("GET")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourceDeleteHandler(fileCache), "/api/resources")).Methods("DELETE")
|
||||
api.PathPrefix("/resources").Handler(monkey(resourcePostHandler(fileCache), "/api/resources")).Methods("POST")
|
||||
|
|
|
@ -0,0 +1,216 @@
|
|||
package http
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/allegro/bigcache"
|
||||
"github.com/golang-jwt/jwt/v4"
|
||||
|
||||
"github.com/filebrowser/filebrowser/v2/files"
|
||||
)
|
||||
|
||||
const (
|
||||
onlyOfficeStatusDocumentClosedWithChanges = 2
|
||||
onlyOfficeStatusDocumentClosedWithNoChanges = 4
|
||||
onlyOfficeStatusForceSaveWhileDocumentStillOpen = 6
|
||||
trueString = "true" // linter-enforced constant
|
||||
twoDays = 48 * time.Hour // linter enforced constant
|
||||
)
|
||||
|
||||
var (
|
||||
// Refer to only-office documentation on co-editing
|
||||
// https://api.onlyoffice.com/editors/coedit
|
||||
//
|
||||
// a 48 hour TTL here is not required, because the document server will notify
|
||||
// us when keys should be evicted. However, it is added defensively in order to
|
||||
// prevent potential memory leaks.
|
||||
coeditingDocumentKeys, _ = bigcache.NewBigCache(bigcache.DefaultConfig(twoDays))
|
||||
)
|
||||
|
||||
type OnlyOfficeCallback struct {
|
||||
ChangesURL string `json:"changesurl,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
Status int `json:"status,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
Users []string `json:"users,omitempty"`
|
||||
UserData string `json:"userdata,omitempty"`
|
||||
}
|
||||
|
||||
var onlyofficeClientConfigGetHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
if d.settings.OnlyOffice.JWTSecret == "" {
|
||||
return http.StatusInternalServerError, errors.New("only-office integration must be configured in settings")
|
||||
}
|
||||
|
||||
if !d.user.Perm.Modify || !d.Check(r.URL.Path) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
referrer, err := getReferer(r)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, errors.Join(errors.New("could not determine request referrer"), err)
|
||||
}
|
||||
|
||||
file, err := files.NewFileInfo(&files.FileOptions{
|
||||
Fs: d.user.Fs,
|
||||
Path: r.URL.Path,
|
||||
Modify: d.user.Perm.Modify,
|
||||
Expand: false,
|
||||
ReadHeader: d.server.TypeDetectionByHeader,
|
||||
Checker: d,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return errToStatus(err), err
|
||||
}
|
||||
|
||||
clientConfig := map[string]interface{}{
|
||||
"document": map[string]interface{}{
|
||||
"fileType": file.Extension[1:],
|
||||
"key": getDocumentKey(file.RealPath()),
|
||||
"title": file.Name,
|
||||
"url": (&url.URL{
|
||||
Scheme: referrer.Scheme,
|
||||
Host: referrer.Host,
|
||||
RawQuery: "auth=" + url.QueryEscape(d.authToken),
|
||||
}).JoinPath(d.server.BaseURL, "/api/raw", file.Path).String(),
|
||||
"permissions": map[string]interface{}{
|
||||
"edit": d.user.Perm.Modify,
|
||||
"download": d.user.Perm.Download,
|
||||
"print": d.user.Perm.Download,
|
||||
},
|
||||
},
|
||||
"editorConfig": map[string]interface{}{
|
||||
"callbackUrl": (&url.URL{
|
||||
Scheme: referrer.Scheme,
|
||||
Host: referrer.Host,
|
||||
RawQuery: "auth=" + url.QueryEscape(d.authToken) + "&save=" + url.QueryEscape(file.Path),
|
||||
}).JoinPath(d.server.BaseURL, "/api/onlyoffice/callback").String(),
|
||||
"user": map[string]interface{}{
|
||||
"id": strconv.FormatUint(uint64(d.user.ID), 10),
|
||||
"name": d.user.Username,
|
||||
},
|
||||
"customization": map[string]interface{}{
|
||||
"autosave": true,
|
||||
"forcesave": true,
|
||||
"uiTheme": ternary(d.Settings.Branding.Theme == "dark", "default-dark", "default-light"),
|
||||
},
|
||||
"lang": d.user.Locale,
|
||||
"mode": ternary(d.user.Perm.Modify, "edit", "view"),
|
||||
},
|
||||
"type": ternary(r.URL.Query().Get("isMobile") == trueString, "mobile", "desktop"),
|
||||
}
|
||||
|
||||
signature, err := jwt.
|
||||
NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(clientConfig)).
|
||||
SignedString([]byte(d.Settings.OnlyOffice.JWTSecret))
|
||||
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, errors.Join(errors.New("could not sign only-office client-config"), err)
|
||||
}
|
||||
clientConfig["token"] = signature
|
||||
|
||||
return renderJSON(w, r, clientConfig)
|
||||
})
|
||||
|
||||
var onlyofficeCallbackHandler = withUser(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
var data OnlyOfficeCallback
|
||||
err = json.Unmarshal(body, &data)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
|
||||
docPath := r.URL.Query().Get("save")
|
||||
if docPath == "" {
|
||||
return http.StatusInternalServerError, errors.New("unable to get file save path")
|
||||
}
|
||||
|
||||
if data.Status == onlyOfficeStatusDocumentClosedWithChanges ||
|
||||
data.Status == onlyOfficeStatusDocumentClosedWithNoChanges {
|
||||
// Refer to only-office documentation
|
||||
// - https://api.onlyoffice.com/editors/coedit
|
||||
// - https://api.onlyoffice.com/editors/callback
|
||||
//
|
||||
// When the document is fully closed by all editors,
|
||||
// then the document key should no longer be re-used.
|
||||
realPath := files.GetRealPath(d.user.Fs, docPath)
|
||||
_ = coeditingDocumentKeys.Delete(realPath)
|
||||
}
|
||||
|
||||
if data.Status == onlyOfficeStatusDocumentClosedWithChanges ||
|
||||
data.Status == onlyOfficeStatusForceSaveWhileDocumentStillOpen {
|
||||
if !d.user.Perm.Modify || !d.Check(docPath) {
|
||||
return http.StatusForbidden, nil
|
||||
}
|
||||
|
||||
doc, err := http.Get(data.URL)
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
defer doc.Body.Close()
|
||||
|
||||
err = d.Runner.RunHook(func() error {
|
||||
_, writeErr := writeFile(d.user.Fs, docPath, doc.Body)
|
||||
if writeErr != nil {
|
||||
return writeErr
|
||||
}
|
||||
return nil
|
||||
}, "save", docPath, "", d.user)
|
||||
|
||||
if err != nil {
|
||||
return http.StatusInternalServerError, err
|
||||
}
|
||||
}
|
||||
|
||||
resp := map[string]int{
|
||||
"error": 0,
|
||||
}
|
||||
return renderJSON(w, r, resp)
|
||||
})
|
||||
|
||||
func getReferer(r *http.Request) (*url.URL, error) {
|
||||
if len(r.Header["Referer"]) != 1 {
|
||||
return nil, errors.New("expected exactly one Referer header")
|
||||
}
|
||||
|
||||
return url.ParseRequestURI(r.Header["Referer"][0])
|
||||
}
|
||||
|
||||
func getDocumentKey(realPath string) string {
|
||||
// error is intentionally ignored in order treat errors
|
||||
// the same as a cache-miss
|
||||
cachedDocumentKey, _ := coeditingDocumentKeys.Get(realPath)
|
||||
|
||||
if cachedDocumentKey != nil {
|
||||
return string(cachedDocumentKey)
|
||||
}
|
||||
|
||||
timestamp := strconv.FormatInt(time.Now().UnixMilli(), 10)
|
||||
documentKey := hashSHA256(realPath + timestamp)
|
||||
_ = coeditingDocumentKeys.Set(realPath, []byte(documentKey))
|
||||
return documentKey
|
||||
}
|
||||
|
||||
func hashSHA256(data string) string {
|
||||
bytes := sha256.Sum256([]byte(data))
|
||||
return hex.EncodeToString(bytes[:])
|
||||
}
|
||||
|
||||
func ternary(condition bool, trueValue, falseValue string) string {
|
||||
if condition {
|
||||
return trueValue
|
||||
}
|
||||
return falseValue
|
||||
}
|
|
@ -18,6 +18,7 @@ type settingsData struct {
|
|||
Tus settings.Tus `json:"tus"`
|
||||
Shell []string `json:"shell"`
|
||||
Commands map[string][]string `json:"commands"`
|
||||
OnlyOffice settings.OnlyOffice `json:"onlyoffice"`
|
||||
}
|
||||
|
||||
var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request, d *data) (int, error) {
|
||||
|
@ -31,6 +32,7 @@ var settingsGetHandler = withAdmin(func(w http.ResponseWriter, r *http.Request,
|
|||
Tus: d.settings.Tus,
|
||||
Shell: d.settings.Shell,
|
||||
Commands: d.settings.Commands,
|
||||
OnlyOffice: d.settings.OnlyOffice,
|
||||
}
|
||||
|
||||
return renderJSON(w, r, data)
|
||||
|
@ -52,6 +54,7 @@ var settingsPutHandler = withAdmin(func(_ http.ResponseWriter, r *http.Request,
|
|||
d.settings.Tus = req.Tus
|
||||
d.settings.Shell = req.Shell
|
||||
d.settings.Commands = req.Commands
|
||||
d.settings.OnlyOffice = req.OnlyOffice
|
||||
|
||||
err = d.store.Settings.Save(d.settings)
|
||||
return errToStatus(err), err
|
||||
|
|
|
@ -46,6 +46,7 @@ func handleWithStaticData(w http.ResponseWriter, _ *http.Request, d *data, fSys
|
|||
"ResizePreview": d.server.ResizePreview,
|
||||
"EnableExec": d.server.EnableExec,
|
||||
"TusSettings": d.settings.Tus,
|
||||
"OnlyOfficeUrl": d.settings.OnlyOffice.URL,
|
||||
}
|
||||
|
||||
if d.settings.Branding.Files != "" {
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
package settings
|
||||
|
||||
// OnlyOffice contains the onlyoffice server connection settings of the app.
|
||||
type OnlyOffice struct {
|
||||
URL string `json:"url"`
|
||||
JWTSecret string `json:"jwtSecret"`
|
||||
}
|
|
@ -27,6 +27,7 @@ type Settings struct {
|
|||
Commands map[string][]string `json:"commands"`
|
||||
Shell []string `json:"shell"`
|
||||
Rules []rules.Rule `json:"rules"`
|
||||
OnlyOffice OnlyOffice `json:"onlyoffice"`
|
||||
}
|
||||
|
||||
// GetRules implements rules.Provider.
|
||||
|
|
Loading…
Reference in New Issue