Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bb1659e076 | ||
|
|
c445061451 | ||
|
|
43fd7df098 | ||
|
|
768ccd7739 | ||
|
|
321a86acf8 | ||
|
|
b5f4f67813 | ||
|
|
697a9416e8 | ||
|
|
d005ae1d88 | ||
|
|
3d8e20de8f |
+2
-1
@@ -1,5 +1,6 @@
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
from importlib.metadata import version as pkg_version
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
from docopt import docopt
|
from docopt import docopt
|
||||||
@@ -30,7 +31,7 @@ def create_startup_box(
|
|||||||
*, folder, url, unix=None, dev=False, paskia_url=None, public=False
|
*, folder, url, unix=None, dev=False, paskia_url=None, public=False
|
||||||
):
|
):
|
||||||
"""Create a framed startup box with server information."""
|
"""Create a framed startup box with server information."""
|
||||||
title = f"Cista {cista.__version__}"
|
title = f"Cista {cista.__version__} (mediapreview {pkg_version('mediapreview')})"
|
||||||
listen = unix or url
|
listen = unix or url
|
||||||
location = f"{folder} @ {listen}"
|
location = f"{folder} @ {listen}"
|
||||||
lines = [title, location]
|
lines = [title, location]
|
||||||
|
|||||||
+1
-1
@@ -226,7 +226,7 @@ def hydrate_request_auth_context(request, *, source: str) -> None:
|
|||||||
|
|
||||||
|
|
||||||
_AUTH_REALM = "cista"
|
_AUTH_REALM = "cista"
|
||||||
_AUTH_CACHE_TTL = 10
|
_AUTH_CACHE_TTL = 300
|
||||||
_auth_cache: dict[str, tuple[float, config.User]] = {}
|
_auth_cache: dict[str, tuple[float, config.User]] = {}
|
||||||
_WINDOWS_UA_HINTS = (
|
_WINDOWS_UA_HINTS = (
|
||||||
"windows",
|
"windows",
|
||||||
|
|||||||
+11
-2
@@ -6,6 +6,7 @@ only bridges cista's config-derived JWT secret into it and wires the
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import mediapreview.office
|
import mediapreview.office
|
||||||
@@ -20,7 +21,7 @@ def configure() -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def setup_docker(confdir: Path | None = None) -> str:
|
def setup_docker(confdir: Path | None = None) -> int:
|
||||||
"""Build and run the patched OnlyOffice Docker image (via mediapreview)."""
|
"""Build and run the patched OnlyOffice Docker image (via mediapreview)."""
|
||||||
if confdir is not None:
|
if confdir is not None:
|
||||||
os.environ["CISTA_HOME"] = confdir.as_posix()
|
os.environ["CISTA_HOME"] = confdir.as_posix()
|
||||||
@@ -36,4 +37,12 @@ def setup_docker(confdir: Path | None = None) -> str:
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
configure()
|
configure()
|
||||||
return mediapreview.office.setup_docker()
|
try:
|
||||||
|
mediapreview.office.setup_docker()
|
||||||
|
finally:
|
||||||
|
# Print regardless of build outcome: the secret is deterministic
|
||||||
|
# (derived from the config).
|
||||||
|
sys.stdout.write(
|
||||||
|
f"ONLYOFFICE_JWT_SECRET={os.environ['ONLYOFFICE_JWT_SECRET']}\n"
|
||||||
|
)
|
||||||
|
return 0
|
||||||
|
|||||||
+15
-4
@@ -18,7 +18,9 @@ from mediapreview.exceptions import (
|
|||||||
PreviewError,
|
PreviewError,
|
||||||
)
|
)
|
||||||
from mediapreview.formats import OFFICE_PREVIEW_SUFFIXES
|
from mediapreview.formats import OFFICE_PREVIEW_SUFFIXES
|
||||||
|
from mediapreview.formats import expected_backend as _expected_preview_backend
|
||||||
from mediapreview.pool import (
|
from mediapreview.pool import (
|
||||||
|
PREVIEW_TIMEOUT,
|
||||||
generate_office_preview,
|
generate_office_preview,
|
||||||
run_preview,
|
run_preview,
|
||||||
)
|
)
|
||||||
@@ -80,14 +82,23 @@ async def preview(req, path):
|
|||||||
logger.debug(f"Preview cache hit: {rel}")
|
logger.debug(f"Preview cache hit: {rel}")
|
||||||
return raw(cached.body, headers=cached.headers)
|
return raw(cached.body, headers=cached.headers)
|
||||||
|
|
||||||
# Generate preview
|
# Generate preview. The outer deadline is strict: pool internals have
|
||||||
|
# their own timeouts, but queueing (workers, the OnlyOffice semaphore)
|
||||||
|
# must not let a request exceed PREVIEW_TIMEOUT.
|
||||||
try:
|
try:
|
||||||
if filepath.suffix.lower() in OFFICE_PREVIEW_SUFFIXES:
|
if filepath.suffix.lower() in OFFICE_PREVIEW_SUFFIXES:
|
||||||
img, preview_resp = await generate_office_preview(
|
img, preview_resp = await asyncio.wait_for(
|
||||||
filepath, quality, maxsize, maxzoom
|
generate_office_preview(filepath, quality, maxsize, maxzoom),
|
||||||
|
timeout=PREVIEW_TIMEOUT,
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
img, preview_resp = await run_preview(filepath, quality, maxsize, maxzoom)
|
img, preview_resp = await asyncio.wait_for(
|
||||||
|
run_preview(filepath, quality, maxsize, maxzoom),
|
||||||
|
timeout=PREVIEW_TIMEOUT,
|
||||||
|
)
|
||||||
|
except TimeoutError:
|
||||||
|
req.ctx.log_extra = f"{_expected_preview_backend(filepath)} timeout"
|
||||||
|
return empty(503)
|
||||||
except PreviewError as e:
|
except PreviewError as e:
|
||||||
# mediapreview is responsible for backend-specific diagnostics; cista only
|
# mediapreview is responsible for backend-specific diagnostics; cista only
|
||||||
# needs the backend name, a short access-log reason, and a response status.
|
# needs the backend name, a short access-log reason, and a response status.
|
||||||
|
|||||||
+1
-1
@@ -68,7 +68,7 @@ async def close_client():
|
|||||||
# Keyed by (credential hash, validation URL) so that entries for different
|
# Keyed by (credential hash, validation URL) so that entries for different
|
||||||
# perms/renew flags coexist and all entries for a credential can be purged
|
# perms/renew flags coexist and all entries for a credential can be purged
|
||||||
# on logout.
|
# on logout.
|
||||||
_VALIDATE_CACHE_TTL = 10
|
_VALIDATE_CACHE_TTL = 300
|
||||||
_validate_cache: dict[tuple[str, str], tuple[float, dict]] = {}
|
_validate_cache: dict[tuple[str, str], tuple[float, dict]] = {}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,28 +0,0 @@
|
|||||||
services:
|
|
||||||
onlyoffice:
|
|
||||||
build:
|
|
||||||
context: ./mediapreview/mediapreview/docker
|
|
||||||
args:
|
|
||||||
ONLYOFFICE_VERSION: "9.3.1"
|
|
||||||
container_name: onlyoffice
|
|
||||||
ports:
|
|
||||||
- "8080:80"
|
|
||||||
environment:
|
|
||||||
# Number of converter workers (default 8).
|
|
||||||
# Set to your CPU count or slightly below.
|
|
||||||
- WORKERS
|
|
||||||
# JWT secret shared with Cista.
|
|
||||||
# OnlyOffice reads it as JWT_SECRET; Cista reads it as ONLYOFFICE_JWT_SECRET.
|
|
||||||
# We use ONLYOFFICE_JWT_SECRET as the canonical name so you only set one variable.
|
|
||||||
- JWT_SECRET=${ONLYOFFICE_JWT_SECRET}
|
|
||||||
- JWT_ENABLED=true
|
|
||||||
- JWT_HEADER=Authorization
|
|
||||||
volumes:
|
|
||||||
# Persist fonts and generated caches across restarts
|
|
||||||
- onlyoffice-data:/var/www/onlyoffice/Data
|
|
||||||
- onlyoffice-lib:/var/lib/onlyoffice
|
|
||||||
restart: unless-stopped
|
|
||||||
|
|
||||||
volumes:
|
|
||||||
onlyoffice-data:
|
|
||||||
onlyoffice-lib:
|
|
||||||
+1
-1
@@ -33,7 +33,7 @@ dependencies = [
|
|||||||
"html5tagger>=1.3.0",
|
"html5tagger>=1.3.0",
|
||||||
"httpx>=0.28.0",
|
"httpx>=0.28.0",
|
||||||
"inotify>=0.2.12",
|
"inotify>=0.2.12",
|
||||||
"mediapreview[standard]>=0.2.0",
|
"mediapreview[standard]>=0.2.3",
|
||||||
"msgspec>=0.19.0",
|
"msgspec>=0.19.0",
|
||||||
"natsort>=8.4.0",
|
"natsort>=8.4.0",
|
||||||
"numpy>=2.3.2",
|
"numpy>=2.3.2",
|
||||||
|
|||||||
Reference in New Issue
Block a user