This document covers disaster recovery procedures for OnlyOffice document editing, including backup restoration, corruption handling, and manual intervention tools.
Every document save creates a backup before overwriting:
/{company_id}/{project_id}/{artefact_id}/{filename}.{timestamp}.bak
Example:
/123/456/789/Project_Proposal.docx.1704067200.bak
cleanup_sftp_backups)# Connect to SFTP
sftp user@sftp.rapidpm.uk
# Navigate to artefact folder
cd /{company_id}/{project_id}/{artefact_id}/
# List all backups
ls -la *.bak
# Example output:
# Project_Proposal.docx.1704067200.bak 45KB Jan 1 10:00
# Project_Proposal.docx.1704153600.bak 46KB Jan 2 10:00
# Project_Proposal.docx.1704240000.bak 47KB Jan 3 10:00
1. Identify the backup to restore:
# List available backups sorted by date
sftp> ls -lt /{company_id}/{project_id}/{artefact_id}/*.bak
2. Backup current file (safety):
sftp> rename document.docx document.docx.pre-restore.bak
3. Restore the backup:
sftp> rename document.docx.1704067200.bak document.docx
4. Bump OnlyOffice version (forces fresh cache):
-- In MySQL
UPDATE Artefact
SET onlyoffice_version = UNIX_TIMESTAMP()
WHERE artefact_id = 789;
5. Verify restoration:
For bulk restoration or automated recovery:
# Run from app container
from app.services.onlyoffice_safety import restore_from_backup
# Restore specific artefact to specific backup
restore_from_backup(
artefact_id=789,
backup_timestamp=1704067200 # Unix timestamp from filename
)
Every save is validated before writing:
| Check | What It Catches |
|---|---|
| File size | Empty/truncated files |
| Magic bytes | Wrong file type |
| ZIP integrity | Corrupted archives |
| XML well-formedness | Malformed Office XML |
| Zombie detection | Empty documents (no content) |
# Check for blocked saves
grep "SAVE BLOCKED" /var/log/rapidpm/app.log
# Example output:
# SAVE BLOCKED: Office file too small: 500 bytes for artefact 789
# SAVE BLOCKED: Invalid ZIP signature for docx
# SAVE BLOCKED: Zombie document: docx has no paragraphs
# Run from app container
from app.services.onlyoffice_safety import (
validate_file_content,
validate_zip_integrity,
validate_xml_wellformedness
)
# Read file content
with open('/path/to/document.docx', 'rb') as f:
content = f.read()
# Run validation
is_valid, error = validate_file_content(content, 'docx')
if not is_valid:
print(f"Validation failed: {error}")
If a document is stuck with stale OnlyOffice cache:
-- Get current version
SELECT artefact_id, artefact_name, onlyoffice_version
FROM Artefact WHERE artefact_id = 789;
-- Force new version (clears OnlyOffice cache)
UPDATE Artefact
SET onlyoffice_version = UNIX_TIMESTAMP()
WHERE artefact_id = 789;
If users are shown as "editing" but aren't:
# Connect to Redis
redis-cli
# Check active session
SMEMBERS session:789:users
# Returns: ["42", "57"]
# Clear session
DEL session:789:users
DEL session:789:last_activity
# Clear any transition locks
DEL lock:transition:789
# Run from app container
from app.redis_client import get_redis
from app.services.onlyoffice_safety import force_close_stale_session
from flask import current_app
redis = get_redis()
force_close_stale_session(redis, artefact_id=789, config=current_app.config)
If OnlyOffice has unsaved changes:
from app.services.onlyoffice_safety import trigger_force_save
from flask import current_app
# Trigger force save for document
trigger_force_save(artefact_id=789, config=current_app.config)
Check file exists on SFTP:
sftp> ls /{company}/{project}/{artefact}/
Check OnlyOffice connectivity:
curl -f https://onlyoffice.rapidpm.uk/healthcheck
Check for stale session:
redis-cli SMEMBERS session:{artefact_id}:users
Force version bump if needed (see above)
Check callback logs:
grep "artefact_id={ID}" /var/log/rapidpm/app.log | grep -i callback
Check validation failures:
grep "SAVE BLOCKED" /var/log/rapidpm/app.log | grep {artefact_id}
Check SFTP connectivity:
grep "SFTP" /var/log/rapidpm/app.log | grep -i error
Check actual OnlyOffice state:
from app.services.onlyoffice_safety import query_onlyoffice_active_users
users = query_onlyoffice_active_users(artefact_id, config)
print(f"OnlyOffice reports users: {users}")
Compare with Redis:
redis-cli SMEMBERS session:{artefact_id}:users
If mismatch, clear Redis state (see above)
| Issue | Contact |
|---|---|
| SFTP server down | Infrastructure team |
| OnlyOffice server issues | OnlyOffice admin |
| Database corruption | DBA team |
| Application errors | Development team |