Skip to content

Content Versions & Activity

Overview

Hệ thống versions cho phép tạo các snapshot có tên của các content item phục vụ luồng branching và review. Một version lưu trữ một delta (JSON diff) so với item chính và có thể được promote để trở thành nội dung trực tiếp.

Activity log ghi lại toàn bộ các thao tác tạo/cập nhật/xóa trên mọi collection, cung cấp một audit trail đầy đủ.


Versions

Data Model

Một version được gắn với một cặp collection + item cụ thể và có một key dễ đọc (slug). Nhiều version có thể tồn tại đồng thời cho cùng một item.

odp_versions

ColumnTypeDescription
idUUID PK
keystring(64)Slug/tên dễ đọc
namestring(255)Tên hiển thị
collectionstring(255) FK → odp_collectionsTên collection
itemstring(255)Khóa chính của item (dạng chuỗi)
hashstring(255)Hash của nội dung delta
date_createdtimestamp
date_updatedtimestamp
user_createdUUID FK → odp_users
user_updatedUUID FK → odp_users
deltatext (JSON)Nội dung diff so với item chính

Endpoints

POST /versions

Tạo một version mới.

Yêu cầu xác thực: Có (quyền được kiểm tra ở service layer)

Request Body:

json
{
  "key": "draft-v2",
  "name": "Draft Version 2",
  "collection": "articles",
  "item": "item-uuid",
  "delta": {
    "title": "Updated Title",
    "body": "New body content"
  }
}
FieldTypeRequiredDescription
keystringSlug duy nhất cho version này trong phạm vi (collection, item)
namestringKhôngNhãn hiển thị
collectionstringTên collection
itemstringKhóa chính của item
deltaobjectKhôngJSON object chứa các field được ghi đè

Response:

json
{
  "data": "new-version-uuid"
}

GET /versions

Lấy danh sách version với bộ lọc tùy chọn.

Yêu cầu xác thực:

Query Parameters: Các query param chuẩn của ODP (filter, sort, fields, limit, offset, meta).

Ví dụ truy vấn:

GET /versions?filter[collection][_eq]=articles&filter[item][_eq]=article-uuid

Response:

json
{
  "data": [
    {
      "id": "version-uuid",
      "key": "draft-v2",
      "name": "Draft Version 2",
      "collection": "articles",
      "item": "article-uuid",
      "hash": "abc123...",
      "date_created": "2024-01-01T00:00:00.000Z",
      "date_updated": null,
      "user_created": "user-uuid",
      "user_updated": null,
      "delta": { "title": "Updated Title" }
    }
  ],
  "meta": { "total_count": 1 }
}

GET /versions/:id

Đọc một version cụ thể.

Response: Một object version duy nhất.


PATCH /versions/:id

Cập nhật metadata hoặc delta của một version.

Request Body:

json
{
  "name": "Renamed Draft",
  "delta": {
    "title": "Even More Updated Title"
  }
}

Response:

json
{
  "data": "version-uuid"
}

DELETE /versions/:id

Xóa một version.

Response: 204 No Content


POST /versions/:id/promote

Promote một version để trở thành nội dung trực tiếp của item cha.

Response: 204 No Content

Business Logic (được triển khai trong VersionsService.promote):

  1. Đọc bản ghi version bao gồm delta (toàn bộ tập field cho item mới, hoặc các field đã thay đổi cho lần chỉnh sửa).
  2. Xác định item đích thông qua version.item (khóa chính mà version liên kết đến).
  3. Tạo mới hoặc cập nhật:
    • Nếu item chưa tồn tạiItemsService.createOne({ [pk]: version.item, ...delta }) — item được tạo khi approval.
    • Nếu item đã tồn tạiItemsService.updateOne(version.item, delta).
  4. Thao tác ghi đi qua ItemsService (không phải cập nhật column trực tiếp), do đó các relational field (o2m / m2m / translations) và nested write được bảo toàn khi promote.
  5. Nếu collection có field status và delta không thiết lập nó, status sẽ được đặt là published.
  6. Đánh dấu version là approved.

**Không có placeholder item cho nội dung mới.** Việc tạo item mới theo luồng approval không còn chèn một row trống placeholder từ trước nữa. Frontend tự tạo id (uuid PKs) và liên kết draft version với nó; row thực sự chỉ được tạo **khi version được promote**. Các PK auto-increment vẫn fallback về cách tạo placeholder (vì client không thể tự tạo id). Một version bị rejected sẽ không tạo bất kỳ thứ gì.

**Promotion nằm trong transaction của approval.** Khi một workflow promote một version lúc approval cuối cùng, `promote` chạy **bên trong transaction database của approval**. Nếu promotion thất bại, toàn bộ approval sẽ rollback — instance **không** được đánh dấu là hoàn thành và version vẫn ở trạng thái chưa approved (không có trạng thái "hoàn thành nhưng chưa promote"). Xem [[workflow]] → *Approval Outcome & Version Promotion*.


Activity Log

Activity log (odp_activity) ghi lại toàn bộ các thao tác thay đổi dữ liệu trong hệ thống. Đây là log chỉ ghi thêm (append-only) và có thể được truy vấn để xây dựng audit trail.

odp_activity

ColumnTypeDescription
idinteger PK (auto-increment)
actionstring(45)Loại hành động (xem bên dưới)
userUUID FK → odp_usersNgười dùng thực hiện hành động
timestamptimestampThời điểm thực hiện hành động
ipstring(50)IP của client
user_agenttextTrình duyệt/client
collectionstring(255)Collection bị ảnh hưởng
itemstring(255)Khóa chính của item bị ảnh hưởng
originstring(255)Request origin header
impersonated_byUUIDNếu đang impersonate, đây là user admin thực sự

Common Action Types

ActionDescription
createItem được tạo mới
updateItem được cập nhật
deleteItem bị xóa
loginNgười dùng đăng nhập (qua sự kiện auth.login)
logoutNgười dùng đăng xuất
impersonation.startedPhiên impersonation bắt đầu
impersonation.stoppedPhiên impersonation kết thúc

Revisions

Mỗi bản ghi activity có thể có dữ liệu revision liên quan trong odp_revisions:

odp_revisions

ColumnTypeDescription
idinteger PK
activityinteger FK → odp_activityActivity cha (CASCADE delete)
collectionstring(255)Tên collection
itemstring(255)Khóa chính của item
datatext (JSON)Snapshot đầy đủ sau khi thay đổi
deltatext (JSON)Chỉ các field đã thay đổi
parentinteger FK → odp_revisionsRevision cha
versionUUID FK → odp_versionsVersion liên kết (nếu thông qua promote)

Activity Endpoints

Các endpoint activity chỉ đọc. Xem tài liệu system server để biết danh sách đầy đủ các route CRUD. Activity có thể truy cập tại GET /activityGET /activity/:id.

Ví dụ: Lấy activity đăng nhập gần đây

GET /activity?filter[action][_eq]=login&sort=-timestamp&limit=20

ODP Internal API Documentation