Documentation
¶
Overview ¶
Package onlyoffice integrates a self-hosted OnlyOffice Document Server with filex storage backends.
Three pieces cooperate:
- Config: builds a JWT-signed editor descriptor for the embed iframe. Includes a signed fetch URL the document server uses to pull source bytes from filex.
- Fetch: serves source bytes back to the document server (HMAC-signed URL). Public — no session required, but the URL is unguessable and short-lived.
- Callback: receives save events from the document server, validates the JWT it sends, downloads the saved revision, and writes back to storage.
All signing uses HS256 (HMAC-SHA256) with the JWTSecret loaded from config (FILEX_ONLYOFFICE_JWT). That same secret is what the document server has in its `local.json` -> `services.CoAuthoring.token.enable` stanza.
Index ¶
- Constants
- func DocumentType(ext string) string
- type CallbackPayload
- type EditorConfig
- type ReverseResult
- type Service
- func (s *Service) AttachSync(sy *protocolsync.Syncer)
- func (s *Service) BuildConfigForNode(ctx context.Context, node *model.Node, user *model.User, lang, mode string) (*EditorConfig, error)
- func (s *Service) Enabled() bool
- func (s *Service) EnabledCtx(ctx context.Context) bool
- func (s *Service) HandleCallback(r *http.Request, nodeID int64) (map[string]any, error)
- func (s *Service) ServeProbe(token string) (body string, ok bool)
- func (s *Service) SignedFetchURL(nodeID, exp int64) string
- func (s *Service) VerifyFetchSignature(nodeID, exp int64, sig string) error
- func (s *Service) VerifyFetchSignatureCtx(ctx context.Context, nodeID, exp int64, sig string) error
- func (s *Service) VerifyReversePath(ctx context.Context) ReverseResult
Constants ¶
const ( StatusBeingEdited = 1 StatusReadyForSaving = 2 StatusSavingError = 3 StatusClosedNoChange = 4 StatusForceSave = 6 StatusForceSaveError = 7 )
Status codes per OnlyOffice callback spec.
const ProbePath = "/api/files/onlyoffice/probe"
ProbePath is the endpoint the document server is asked to download.
Variables ¶
This section is empty.
Functions ¶
func DocumentType ¶
DocumentType returns "word", "cell", "slide", or "" for an extension.
Matches the official OnlyOffice mapping. If the extension is unknown we return "" so callers can 415.
Types ¶
type CallbackPayload ¶
type CallbackPayload struct {
Key string `json:"key"`
Status int `json:"status"`
URL string `json:"url"`
Token string `json:"token,omitempty"`
}
CallbackPayload is the body the document server posts on save / status transitions. See https://api.onlyoffice.com/editors/callback for the full schema; we use the fields filex actually needs.
type EditorConfig ¶
type EditorConfig struct {
DocumentServerURL string `json:"documentServerUrl"`
Config map[string]any `json:"config"`
}
EditorConfig is the descriptor we send to the embed iframe.
type ReverseResult ¶
type ReverseResult struct {
// Checked is false when the question could not be put at all — nothing
// configured, or the document server did not answer. A false here must
// never be rendered as a failure of the route.
Checked bool `json:"checked"`
// OK is true only when the probe request actually ARRIVED at filex.
OK bool `json:"ok"`
// URL is the address the document server was asked to fetch, so an
// operator reading a red result can try it themselves.
URL string `json:"url,omitempty"`
// Code is the document server's own error number when it returned one.
// -4 is "error while downloading the document file" — the shape of a
// broken reverse path. 0 means it did not report an error.
Code int `json:"code,omitempty"`
// Detail is the sentence to show the operator.
Detail string `json:"detail,omitempty"`
}
ReverseResult is one answer to "can the document server reach filex?".
type Service ¶
type Service struct {
Store db.Store
StorageResolver func(int64) (storage.Driver, error)
// DocumentServerURL / JWTSecret are the BOOT-TIME values (env/YAML). They
// are the fallback only: when Live is wired the running configuration is
// read from the `external_services` row on every call, so an operator who
// configures OnlyOffice in the admin UI does not have to restart filex.
// See internal/external and issue #17.
DocumentServerURL string // e.g. https://docs.example.com
JWTSecret string // shared secret with the doc server
// Live, when non-nil, returns the current url + secret. Nil means "use the
// fields", which is what the unit tests and any no-DB caller want.
Live func(ctx context.Context) (url, secret string)
PublicURL string // filex public base URL — used to build callbacks
// LiveCallbackURL, when non-nil, returns the address the DOCUMENT SERVER
// should use to reach filex. Empty result means "the public URL".
//
// ⚠ These are two different addresses for two different readers. PublicURL
// is what a person's browser uses and what every share link is built from;
// this one is only ever read by the document server's container. They are
// usually the same string, and on podman or a private network they cannot
// be — which is why a document opened and its save never came back with
// nothing in filex's log to show for it (issue #17).
LiveCallbackURL func(ctx context.Context) string
FetchTTL time.Duration
// Sync is the shared post-write gate every other write surface in filex
// goes through: it upserts the node row, re-indexes the document,
// dispatches a thumbnail, tells open explorers, emits the canonical
// `file.updated` webhook event and schedules the antivirus scan.
//
// ⚠ Optional, and its absence degrades gracefully rather than silently:
// an unwired Service falls back to a Syncer with no search index (see
// syncer), so a save still announces itself and is still scanned — only
// the re-index is skipped. Wire it with AttachSync at boot.
Sync *protocolsync.Syncer
// contains filtered or unexported fields
}
Service builds editor configs and validates document-server callbacks.
func New ¶
func New(store db.Store, resolver func(int64) (storage.Driver, error), docURL, jwtSecret, publicURL string, fetchTTL time.Duration) *Service
New constructs a Service. fetchTTL defaults to 1 hour when zero.
func (*Service) AttachSync ¶
func (s *Service) AttachSync(sy *protocolsync.Syncer)
AttachSync wires the post-write gate. ⚠ Without it an edited document keeps its PRE-EDIT text in content search, because nothing else ever revisits a file whose path did not change.
func (*Service) BuildConfigForNode ¶
func (s *Service) BuildConfigForNode(ctx context.Context, node *model.Node, user *model.User, lang, mode string) (*EditorConfig, error)
BuildConfigForNode resolves the node, presigns the fetch URL, and signs the JSON descriptor with HS256. `mode` selects "edit" or "view"; any value other than "edit" is treated as read-only and toggles the permissions block so OnlyOffice renders the document with toolbars disabled.
func (*Service) Enabled ¶
Enabled reports whether the service has the minimum config to run, as of this instant — not as of the last restart.
func (*Service) EnabledCtx ¶
EnabledCtx is Enabled with a caller-supplied context, so the DB read that backs it inherits the request's deadline and cancellation.
func (*Service) HandleCallback ¶
HandleCallback validates the JWT, downloads the saved version, and writes it back to storage.
Returns the OnlyOffice expected JSON envelope.
func (*Service) ServeProbe ¶
ServeProbe answers the document server's download of a probe URL. It reports whether the token was one filex issued and is still live; an unknown or expired token is served as not-found, so the endpoint says nothing to a stranger who guesses at it.
func (*Service) SignedFetchURL ¶
SignedFetchURL returns an HMAC-signed URL the document server can use to pull document bytes from filex without authenticating.
The URL embeds node id + expiry + sig=base64-url(HMAC-SHA256(secret, "n=<id>&exp=<exp>")).
func (*Service) VerifyFetchSignature ¶
VerifyFetchSignature validates a query against the fetch HMAC.
func (*Service) VerifyFetchSignatureCtx ¶
VerifyFetchSignatureCtx is VerifyFetchSignature with the request context, so the secret is the one in force now.
func (*Service) VerifyReversePath ¶
func (s *Service) VerifyReversePath(ctx context.Context) ReverseResult
VerifyReversePath asks the document server to download a one-shot URL from filex and reports whether that request arrived.
⚠ The verdict is the ARRIVAL. The conversion result is a hint for the message, never the answer: a document server that downloaded our probe and then failed to convert it has still proved the only thing being asked.