Nhúng trình soạn thảo
Xử lý lỗi và giới hạn kết nối
Bảng mã lỗi của lớp nhúng, quy tắc chống SSRF, hành vi khi chạm trần kết nối ở trình duyệt, và các giới hạn thời gian, kích thước.
Trong trang này
Mọi lỗi tới trang của bạn qua onError với {code, message, detail}: code ổn định để rẽ nhánh, message là câu tiếng Việt hiện được cho người dùng. Chạm trần kết nối không phải lỗi và đi qua onLimitReached.
Lỗi từ máy chủ#
| HTTP | code | Khi nào | Nên làm gì |
|---|---|---|---|
| 400 | invalid_config | Thiếu hoặc sai trường; detail.errors = [{path, message}]. | Sửa config; đừng thử lại. |
| 401 | token_required | Máy chủ yêu cầu JWT nhưng config không có. | Ký config ở máy chủ. |
| 401 | invalid_token | Chữ ký sai, không phải HS256, thiếu exp, exp xa quá 24 giờ. | Kiểm khoá và thuật toán. |
| 401 | token_expired | Quá exp. | Xin token mới rồi mở lại. |
| 401 | embed_auth_not_configured | Máy chủ chưa đặt khoá và không bật chế độ không ký. | Quản trị đặt O3O_EMBED_JWT_SECRET. |
| 401 | invalid_session_token | Authorization không khớp phiên. | Tạo phiên mới. |
| 403 | origin_not_allowed | Origin không nằm trong O3O_EMBED_ALLOWED_ORIGINS. | Thêm origin của trang. |
| 403 | invalid_signature | Chữ ký URL tải tệp sai. | Dùng nguyên URL gate trả về. |
| 404 | not_found | Phiên hoặc tài liệu không tồn tại; hoặc lớp nhúng bị tắt. | Kiểm O3O_EMBED_ENABLED. |
| 410 | link_expired, version_gone | URL tải hết hạn; phiên bản đã bị dọn. | Tải sớm hơn, trong thời gian giữ. |
| 413 | file_too_large | Vượt O3O_EMBED_MAX_FILE_MB (mặc định 100 MB). | Không thử lại. |
| 415 | unsupported_file_type | fileType ngoài danh sách hoặc máy chủ soạn thảo không mở được. | Không thử lại. |
| 422 | url_not_allowed | Vi phạm quy tắc chống SSRF. | Dùng URL công cộng hoặc khai O3O_FETCH_ALLOW_HOSTS. |
| 422 | download_failed | Không tải được document.url. | Kiểm máy chủ tệp; có thể thử lại. |
| 503 | editor_unavailable | Gate không lấy được discovery của máy chủ soạn thảo. | Thử lại sau; báo quản trị. |
Lỗi chỉ có ở trình duyệt#
| code | Khi nào |
|---|---|
network_error | Không gọi được máy chủ O3O. |
load_failed | Khung soạn thảo báo nạp hỏng, hoặc tài liệu không hiện sau 120 giây. |
save_failed | Khung soạn thảo báo lưu không thành công; detail chứa nguyên thông điệp. |
save_timeout | save() quá 30 giây. |
Quy tắc chống SSRF#
Áp cho mọi URL mà máy chủ tự gọi (document.url, callbackUrl): chỉ http/https, không có user:pass@, tối đa 2048 ký tự; mọi địa chỉ phân giải ra phải là địa chỉ công cộng (địa chỉ riêng, loopback, link-local, multicast đều bị chặn), và gate nối thẳng tới đúng địa chỉ IP đã kiểm, không phân giải lại (chống DNS rebinding); không tự theo chuyển hướng (tải thì tự xử lý tối đa 3 lần và xét lại từng chặng, callback thì coi chuyển hướng là thất bại); chỉ mã 200 là tải thành công. Ngoại lệ có chủ đích duy nhất: O3O_FETCH_ALLOW_HOSTS.
Khi chạm trần kết nối#
- Trang xin
mode: "edit"khicurrent + pending >= limit: gate vẫn tạo phiên nhưng ởview, trảlimited: true, vàapi.jsphátonLimitReached. api.jschèn phía trên khung một băng nền vàng nhạt có nút tắt, với đúng câumessagecủa máy chủ. Tắt băng này bằngui.limitBanner: false.- Phiên đang sửa không bao giờ bị ngắt, lưu không bao giờ bị chặn.
- Mở lại đúng tài liệu vừa rời trong 120 giây được cho vào kể cả khi đã đầy (mất mạng chớp nhoáng, tải lại trang).
- Trần của bản cộng đồng là 50; bản doanh nghiệp theo số kết nối đã mua. Xem hạn mức và các gói.
{
"session_id": "ses_0c4e9a1b2d3f4a5b6c7d8e9f",
"key": "contract-42-v7",
"doc_id": "doc_9835f61009fc659dfedab8696f2d2665",
"mode": "view",
"requested_mode": "edit",
"limited": true,
"limit": {
"limit": 50,
"current": 50,
"edition": "community"
},
"message": "Hệ thống đã đạt giới hạn 50 phiên soạn thảo đồng thời của bản cộng đồng. Tài liệu của bạn vẫn an toàn. Bạn có thể mở ở chế độ chỉ đọc hoặc thử lại sau ít phút.",
"editor_url": "/browser/825c9caa93/cool.html?WOPISrc=http%3A%2F%2Fo3o-gate%3A8070%2Fo3o%2Fwopi%2Ffiles%2Fdoc_9835f61009fc659dfedab8696f2d2665&lang=vi&permission=readonly",
"form": {
"access_token": "kq3…",
"access_token_ttl": 1790043200000,
"ui_defaults": "UIMode=notebookbar"
},
"version": 0,
"expires_at": "2026-09-21T22:00:00Z"
}<div id="banner" hidden style="background: #fff4d6; padding: 8px 12px"></div>
<div id="o3o-editor" style="height: 90vh"></div>
<script src="http://localhost:8080/o3o/api.js"></script>
<script>
let editor;
let retries = 0;
async function openEditor() {
// Máy chủ của bạn trả config đã ký (xem trang Ký cấu hình bằng JWT)
const config = await (await fetch("/o3o/config?doc=42")).json();
editor = new O3O.Editor("o3o-editor", {
...config,
ui: { ...(config.ui || {}), limitBanner: false }, // tự vẽ băng thông báo
events: { onDocumentLoaded: () => { retries = 0; }, onError, onLimitReached }
});
}
function onError(e) {
switch (e.code) {
case "token_expired": // token quá hạn: xin config mới và mở lại
case "network_error": // không gọi được O3O: thử lại, giãn cách tăng dần
case "load_failed":
if (retries++ < 3) {
editor.destroy();
setTimeout(openEditor, 2000 * retries);
return;
}
break;
case "save_failed":
case "save_timeout": // tài liệu vẫn mở, nhắc người dùng lưu lại
return showBanner("Chưa lưu được. Vui lòng bấm lưu lần nữa.");
case "url_not_allowed":
case "download_failed":
case "file_too_large":
case "unsupported_file_type": // lỗi do tệp hoặc URL: thử lại không giúp gì
return showBanner(e.message);
}
showBanner(e.message);
console.error(e.code, e.detail);
}
function onLimitReached(e) {
// Không phải lỗi: tài liệu đã mở ở chế độ chỉ đọc
showBanner(e.message, "Thử sửa lại", () => editor.setReadOnly(false));
}
function showBanner(text, actionLabel, action) {
const el = document.getElementById("banner");
el.textContent = text;
if (actionLabel) {
const b = document.createElement("button");
b.textContent = actionLabel;
b.onclick = action;
el.append(" ", b);
}
el.hidden = false;
}
openEditor();
</script>Các giới hạn khác#
| Giới hạn | Giá trị mặc định | Biến |
|---|---|---|
| Kích thước tệp nhúng (tải về và lưu lên) | 100 MB | O3O_EMBED_MAX_FILE_MB |
Thời hạn access_token của phiên | 720 phút | O3O_EMBED_SESSION_TTL_MINUTES |
| Phiên tạo xong mà trình soạn thảo không nạp | huỷ sau 300 giây | O3O_EMBED_PENDING_TTL_SECONDS |
| Giữ bản làm việc sau khi đóng | 24 giờ | O3O_EMBED_RETAIN_HOURS |
| Chờ tài liệu hiện | 120 giây, rồi load_failed | cố định |
save() | 30 giây, rồi save_timeout | cố định |
close() chờ lưu | 10 giây | cố định |
Trang nhúng ở origin khác#
Trang trên localhost (cổng bất kỳ) nhúng được mà không cần cấu hình. Trang ở origin khác cần hai việc: thêm origin vào O3O_EMBED_ALLOWED_ORIGINS (CORS của /o3o/embed/*), và đặt O3O_ONLINE_FRAME_ANCESTORS (một giá trị không có dấu cách, ví dụ https://app.example.com hoặc https://*.example.com) để trình duyệt cho phép nhúng khung. Nhiều origin rời nhau trong O3O_ONLINE_FRAME_ANCESTORS: sắp có.