REST API 參考

本頁內容

EmDash 在 /_emdash/api/ 下公開其支援的應用程式設計介面(API)。請使用產生的 OpenAPI 3.1 文件查看請求參數、請求主體、回應 schema、狀態碼以及用戶端產生資訊:

GET /_emdash/api/openapi.json

該文件由 API 使用的同一套 Zod schema 產生,並反映所設定的最大媒體上傳大小。

公開契約邊界

OpenAPI 文件是所支援 REST API 的邊界。EmDash 還有管理介面與協定工作流的路由。原始碼中存在但未出現在 OpenAPI 中的路由,對外部用戶端而言不是受支援的 REST 操作。

這一區分適用於備份、署名管理、關係遍歷、外掛管理、安裝、匯入與認證等路由。備份請參閱備份指南;受支援的署名管理請使用 MCP 署名工具。OAuth 端點是協定端點,請透過 MCP OAuth 章節 中的詮釋資料探索,而不要將其當作應用 REST 端點。

認證與授權

大多數操作接受 EmDash 工作階段 cookie 或 Bearer 權杖。在 Authorization 標頭中傳送個人存取權杖或 OAuth 存取權杖:

Authorization: Bearer $EMDASH_TOKEN

Bearer 權杖受其 scope 與關聯使用者角色限制。工作階段請求使用使用者角色。授權模型請參閱使用者角色與權杖 scope。

GET 與 POST /_emdash/api/comments/{collection}/{contentId} 為公開介面。GET 傳回已核准評論;POST 提交待審核評論。其餘評論審核操作需要認證。

跨站請求偽造防護

透過工作階段 cookie 認證的狀態變更請求需包含此標頭:

X-EmDash-Request: 1

Bearer 權杖請求不需要該標頭,因其不使用瀏覽器隱式憑證。瀏覽器對公開寫入操作的請求須傳送該標頭,或 Origin 與 EmDash 站點的公開/請求來源一致。

回應封裝

成功的 JSON 回應會將 success 設為 true,並在 data 中放置操作特定結果:

{
	"success": true,
	"data": {
		"items": []
	}
}

錯誤時 success 為 false,並包含穩定的機器可讀程式碼與訊息。部分錯誤還包含結構化 details:

{
	"success": false,
	"error": {
		"code": "NOT_FOUND",
		"message": "Content item not found"
	}
}

請使用各 OpenAPI 操作的狀態碼與錯誤 schema。常見狀態:400 無效輸入,401 憑證缺失或無效,403 scope 或權限不足,404 資源不存在,409 狀態衝突,413 上傳過大,422 外掛拒絕儲存,500 內部錯誤。

端點清單

操作 ID 在產生的契約中穩定,OpenAPI 用戶端產生器常將其用作方法名。以下清單與產生的 OpenAPI 文件對照校驗。

內容

方法路徑操作摘要
GET/_emdash/api/content/{collection}listContentList content items
POST/_emdash/api/content/{collection}createContentCreate a content item
GET/_emdash/api/content/{collection}/{id}getContentGet a content item
PUT/_emdash/api/content/{collection}/{id}updateContentUpdate a content item
DELETE/_emdash/api/content/{collection}/{id}deleteContentDelete a content item (soft delete)
POST/_emdash/api/content/{collection}/{id}/publishpublishContentPublish a content item
POST/_emdash/api/content/{collection}/{id}/unpublishunpublishContentUnpublish a content item
POST/_emdash/api/content/{collection}/{id}/schedulescheduleContentSchedule content for future publishing
DELETE/_emdash/api/content/{collection}/{id}/scheduleunscheduleContentCancel scheduled publishing
POST/_emdash/api/content/{collection}/{id}/duplicateduplicateContentDuplicate a content item
POST/_emdash/api/content/{collection}/{id}/restorerestoreContentRestore a content item from trash
DELETE/_emdash/api/content/{collection}/{id}/permanentpermanentDeleteContentPermanently delete a content item
GET/_emdash/api/content/{collection}/{id}/comparecompareContentCompare live and draft revisions
POST/_emdash/api/content/{collection}/{id}/discard-draftdiscardDraftDiscard draft changes
GET/_emdash/api/content/{collection}/{id}/lockgetEntryLockRead the entry’s edit lock
POST/_emdash/api/content/{collection}/{id}/lockacquireEntryLockTake or refresh the entry’s edit lock
DELETE/_emdash/api/content/{collection}/{id}/lockreleaseEntryLockRelease the caller’s edit lock
GET/_emdash/api/content/{collection}/{id}/translationsgetContentTranslationsGet translations for a content item
GET/_emdash/api/content/{collection}/{id}/terms/{taxonomy}getContentTermsGet taxonomy terms assigned to a content item
POST/_emdash/api/content/{collection}/{id}/terms/{taxonomy}setContentTermsSet taxonomy terms on a content item
GET/_emdash/api/content/{collection}/authorslistContentAuthorsList distinct authors of a collection’s content
GET/_emdash/api/content/{collection}/trashlistTrashedContentList trashed content items

媒體

方法路徑操作摘要
GET/_emdash/api/medialistMediaList media items
POST/_emdash/api/mediauploadMediaUpload a media item
GET/_emdash/api/media/folderslistMediaFoldersList media folders
POST/_emdash/api/media/folderscreateMediaFolderCreate a media folder
GET/_emdash/api/media/folders/{id}getMediaFolderGet a media folder
PUT/_emdash/api/media/folders/{id}updateMediaFolderUpdate a media folder
DELETE/_emdash/api/media/folders/{id}deleteMediaFolderDelete a media folder
GET/_emdash/api/media/{id}getMediaGet a media item
PUT/_emdash/api/media/{id}updateMediaUpdate media metadata
DELETE/_emdash/api/media/{id}deleteMediaDelete a media item
GET/_emdash/api/media/{id}/usagegetMediaUsageGet media usage details
PUT/_emdash/api/media/{id}/replacereplaceMediaImageReplace a media image
POST/_emdash/api/admin/media-usage/repairrepairMediaUsageRepair media usage indexes
GET/_emdash/api/admin/media-usage/progressgetMediaUsageProgressGet media usage indexing progress
POST/_emdash/api/admin/media-usage/progressadvanceMediaUsageProgressAdvance media usage indexing
GET/_emdash/api/admin/media-usage/worklistMediaUsageWorkList durable media usage work
GET/_emdash/api/admin/media-usage/activationgetMediaUsageActivationGet media usage activation status
POST/_emdash/api/admin/media-usage/activationadvanceMediaUsageActivationAdvance media usage activation
POST/_emdash/api/admin/media-usage/work/retryretryMediaUsageWorkRetry one durable media usage job
GET/_emdash/api/admin/media-usage/collection-deletionslistMediaUsageCollectionDeletionsList durable collection deletions
POST/_emdash/api/admin/media-usage/collection-deletions/retryretryMediaUsageCollectionDeletionRetry one collection deletion
POST/_emdash/api/media/upload-urlgetMediaUploadUrlGet a media upload target
POST/_emdash/api/media/{id}/confirmconfirmMediaUploadConfirm a media upload
PUT/_emdash/api/media/{id}/uploaduploadPendingMediaUpload a pending media file through EmDash

Schema

方法路徑操作摘要
GET/_emdash/api/schema/block-typeslistBlockTypesList block types
POST/_emdash/api/schema/block-typescreateBlockTypeCreate a block type
GET/_emdash/api/schema/block-types/{slug}getBlockTypeGet a block type
PUT/_emdash/api/schema/block-types/{slug}updateBlockTypeUpdate a block type
POST/_emdash/api/schema/block-types/{slug}/versions/{version}/activateactivateBlockTypeVersionActivate a block type version
GET/_emdash/api/schema/collectionslistCollectionsList all collections
POST/_emdash/api/schema/collectionscreateCollectionCreate a collection
GET/_emdash/api/schema/collections/{slug}getCollectionGet a collection
PUT/_emdash/api/schema/collections/{slug}updateCollectionUpdate a collection
DELETE/_emdash/api/schema/collections/{slug}deleteCollectionDelete a collection
GET/_emdash/api/schema/collections/{slug}/fieldslistFieldsList fields for a collection
POST/_emdash/api/schema/collections/{slug}/fieldscreateFieldCreate a field
GET/_emdash/api/schema/collections/{slug}/fields/{fieldSlug}getFieldGet a field
PUT/_emdash/api/schema/collections/{slug}/fields/{fieldSlug}updateFieldUpdate a field
DELETE/_emdash/api/schema/collections/{slug}/fields/{fieldSlug}deleteFieldDelete a field
POST/_emdash/api/schema/collections/reorderreorderCollectionsReorder collections in the admin sidebar
POST/_emdash/api/schema/collections/{slug}/fields/reorderreorderFieldsReorder fields in a collection
GET/_emdash/api/schema/orphanslistOrphanedTablesList orphaned content tables
POST/_emdash/api/schema/orphans/{slug}registerOrphanedTableRegister an orphaned table as a collection

評論

方法路徑操作摘要
GET/_emdash/api/comments/{collection}/{contentId}listPublicCommentsList approved comments for content
POST/_emdash/api/comments/{collection}/{contentId}createCommentSubmit a new comment
GET/_emdash/api/admin/commentslistAdminCommentsList comments for moderation
GET/_emdash/api/admin/comments/countsgetCommentCountsGet comment status counts
POST/_emdash/api/admin/comments/bulkbulkCommentActionBulk approve, spam, trash, or delete comments
GET/_emdash/api/admin/comments/{id}getCommentGet a single comment
DELETE/_emdash/api/admin/comments/{id}deleteCommentPermanently delete a comment
PUT/_emdash/api/admin/comments/{id}/statusupdateCommentStatusChange comment status

分類法

方法路徑操作摘要
GET/_emdash/api/taxonomieslistTaxonomiesList all taxonomy definitions
GET/_emdash/api/taxonomies/{name}getTaxonomyGet a taxonomy definition
PUT/_emdash/api/taxonomies/{name}updateTaxonomyUpdate a taxonomy definition
DELETE/_emdash/api/taxonomies/{name}deleteTaxonomyDelete a taxonomy, its terms, and their content assignments
GET/_emdash/api/taxonomies/{name}/translationslistTaxonomyTranslationsList every locale variant of a taxonomy definition
POST/_emdash/api/taxonomies/{name}/reorderreorderTermsSet the manual order of one sibling group of terms
GET/_emdash/api/taxonomies/{name}/termslistTermsList terms for a taxonomy
POST/_emdash/api/taxonomies/{name}/termscreateTermCreate a term
GET/_emdash/api/taxonomies/{name}/terms/{slug}getTermGet a term by slug
PUT/_emdash/api/taxonomies/{name}/terms/{slug}updateTermUpdate a term
DELETE/_emdash/api/taxonomies/{name}/terms/{slug}deleteTermDelete a term

選單

方法路徑操作摘要
GET/_emdash/api/menuslistMenusList all menus with item counts
POST/_emdash/api/menuscreateMenuCreate a menu
GET/_emdash/api/menus/{name}getMenuGet a menu with all items
PUT/_emdash/api/menus/{name}updateMenuUpdate a menu
DELETE/_emdash/api/menus/{name}deleteMenuDelete a menu and its items
POST/_emdash/api/menus/{name}/itemscreateMenuItemAdd an item to a menu
PUT/_emdash/api/menus/{name}/items/{id}updateMenuItemUpdate a menu item
DELETE/_emdash/api/menus/{name}/items/{id}deleteMenuItemDelete a menu item
POST/_emdash/api/menus/{name}/reorderreorderMenuItemsBatch reorder menu items

區段

方法路徑操作摘要
GET/_emdash/api/sectionslistSectionsList sections
POST/_emdash/api/sectionscreateSectionCreate a section
GET/_emdash/api/sections/{slug}getSectionGet a section by slug
PUT/_emdash/api/sections/{slug}updateSectionUpdate a section
DELETE/_emdash/api/sections/{slug}deleteSectionDelete a section

小工具

方法路徑操作摘要
GET/_emdash/api/widget-areaslistWidgetAreasList all widget areas
POST/_emdash/api/widget-areascreateWidgetAreaCreate a widget area
GET/_emdash/api/widget-areas/{name}getWidgetAreaGet a widget area with widgets
DELETE/_emdash/api/widget-areas/{name}deleteWidgetAreaDelete a widget area and its widgets
POST/_emdash/api/widget-areas/{name}/widgetscreateWidgetAdd a widget to an area
PUT/_emdash/api/widget-areas/{name}/widgets/{id}updateWidgetUpdate a widget
DELETE/_emdash/api/widget-areas/{name}/widgets/{id}deleteWidgetDelete a widget
POST/_emdash/api/widget-areas/{name}/reorderreorderWidgetsReorder widgets in an area

設定

方法路徑操作摘要
GET/_emdash/api/settingsgetSettingsGet site settings
PUT/_emdash/api/settingsupdateSettingsUpdate site settings

搜尋

方法路徑操作摘要
GET/_emdash/api/searchsearchFull-text search across collections
GET/_emdash/api/search/suggestsearchSuggestAutocomplete search suggestions
POST/_emdash/api/search/rebuildrebuildSearchIndexRebuild the search index for a collection
POST/_emdash/api/search/enableenableSearchEnable or disable search for a collection
GET/_emdash/api/search/statsgetSearchStatsGet search index statistics

重新導向

方法路徑操作摘要
GET/_emdash/api/redirectslistRedirectsList redirects
POST/_emdash/api/redirectscreateRedirectCreate a redirect rule
GET/_emdash/api/redirects/{id}getRedirectGet a redirect
PUT/_emdash/api/redirects/{id}updateRedirectUpdate a redirect
DELETE/_emdash/api/redirects/{id}deleteRedirectDelete a redirect
GET/_emdash/api/redirects/404slistNotFoundEntriesList 404 log entries
POST/_emdash/api/redirects/404spruneNotFoundLogPrune old 404 log entries
DELETE/_emdash/api/redirects/404sclearNotFoundLogClear all 404 log entries
GET/_emdash/api/redirects/404s/summarygetNotFoundSummaryGet 404 summary grouped by path

使用者

方法路徑操作摘要
GET/_emdash/api/admin/userslistUsersList users
GET/_emdash/api/admin/users/{id}getUserGet user details
PUT/_emdash/api/admin/users/{id}updateUserUpdate a user
POST/_emdash/api/admin/users/{id}/disabledisableUserDisable a user account
POST/_emdash/api/admin/users/{id}/enableenableUserEnable a user account
GET/_emdash/api/admin/allowed-domainslistAllowedDomainsList allowed email domains
POST/_emdash/api/admin/allowed-domainscreateAllowedDomainAdd an allowed email domain
PUT/_emdash/api/admin/allowed-domains/{domain}updateAllowedDomainUpdate an allowed domain
DELETE/_emdash/api/admin/allowed-domains/{domain}deleteAllowedDomainRemove an allowed domain

站點遷移

方法路徑操作摘要
GET/_emdash/api/admin/transfer/capabilitiesgetTransferCapabilitiesGet site transfer capabilities
GET/_emdash/api/admin/transfer/importslistTransferImportsList site imports
POST/_emdash/api/admin/transfer/importscreateTransferImportCreate a site import
GET/_emdash/api/admin/transfer/imports/{id}getTransferImportGet a site import
GET/_emdash/api/admin/transfer/imports/{id}/missinglistTransferImportMissingFilesList package files still to upload
PUT/_emdash/api/admin/transfer/imports/{id}/files/{path}uploadTransferImportFileUpload one package file
POST/_emdash/api/admin/transfer/imports/{id}/analyzeanalyzeTransferImportAdvance import analysis
GET/_emdash/api/admin/transfer/imports/{id}/plangetTransferImportPlanGet an import plan
POST/_emdash/api/admin/transfer/imports/{id}/cancelcancelTransferImportCancel a site import
POST/_emdash/api/admin/transfer/imports/{id}/abandonabandonTransferImportAbandon a failed or cancelled import
POST/_emdash/api/admin/transfer/imports/{id}/executeexecuteTransferImportStart a planned import
POST/_emdash/api/admin/transfer/imports/{id}/advanceadvanceTransferImportAdvance an executing import
GET/_emdash/api/admin/transfer/imports/{id}/receiptgetTransferImportReceiptGet an import receipt
GET/_emdash/api/admin/transfer/exportslistTransferExportsList site exports
POST/_emdash/api/admin/transfer/exportscreateTransferExportStart a site export
GET/_emdash/api/admin/transfer/exports/{id}getTransferExportGet a site export
POST/_emdash/api/admin/transfer/exports/{id}/advanceadvanceTransferExportAdvance a site export
GET/_emdash/api/admin/transfer/exports/{id}/manifestgetTransferExportManifestDownload an export manifest
GET/_emdash/api/admin/transfer/exports/{id}/files/{path}downloadTransferExportFileDownload one export file
GET/_emdash/api/admin/transfer/exports/{id}/archivedownloadTransferExportArchiveDownload an export archive
GET/_emdash/api/admin/transfer/approvalslistTransferApprovalsList transfer approvals
POST/_emdash/api/admin/transfer/approvals/{id}/approveapproveTransferApprovalApprove a transfer request
POST/_emdash/api/admin/transfer/approvals/{id}/denydenyTransferApprovalDeny a transfer request

內容生命週期與署名

內容生命週期參考 定義了 REST、MCP、CLI 與管理後台共享的狀態、修訂、權限、衝突與掛鉤行為。

內容讀取在可用時會傳回不透明的 _rev 權杖。在 PUT /content/{collection}/{id} 時附带 _rev,以免覆蓋自讀取以来发生的变更。過期的權杖會產生冲突;重试前請再次讀取項目。CLI 對 content update 強制此檢查,而 REST 欄位對有意選擇无条件寫入的用戶端仍為可选。

讀取並更新項目

修改前先讀取項目:

GET /_emdash/api/content/articles/01JARTICLE0000000000000000
Authorization: Bearer $EMDASH_TOKEN

回應包含其欄位、發佈狀態與修订權杖:

{
	"success": true,
	"data": {
		"item": {
			"id": "01JARTICLE0000000000000000",
			"type": "articles",
			"slug": "launch-notes",
			"status": "published",
			"data": { "title": "Launch notes" }
		},
		"_rev": "opaque-revision-token"
	}
}

仅傳送需要变更的欄位,以及該次讀取得到的權杖:

PUT /_emdash/api/content/articles/01JARTICLE0000000000000000
Authorization: Bearer $EMDASH_TOKEN
Content-Type: application/json

{
	"data": { "title": "Updated launch notes" },
	"_rev": "opaque-revision-token"
}

修改已發佈項目會创建草稿,先前版本仍保持线上。调用 compare 操作查看两個版本,然后發佈草稿或丢弃。取消發佈會保留內容及其發佈日期、取消任何待执行计划,並將項目从线上站點移除。

创建與更新請求体可接受署名 credits,內容回應包含主署名與有序 credits。內容列表可按存储的署名 ID 筛选,並可選擇包含作者推断的署名。署名记录本身的创建與管理通過 MCP 署名工具 提供,不在公開 REST 契約内。

生命周期操作区分软删除與永久删除。還原會將回收站內容恢复為无计划的草稿;永久删除會移除回收站中的項目且不可撤销。發佈、取消發佈、计划、取消计划、比较、丢弃草稿與复制是独立操作,用戶端可一次請求一种狀態转换。

項目編輯鎖

編輯者打开項目時,集合可获取七分钟編輯锁。使用 /content/{collection}/{id}/lock 上的三個操作来讀取、获取或刷新以及释放租约。

讀取或获取回應會显示是否启用锁定、调用方是否持有租约以及当前持有者:

{
	"success": true,
	"data": {
		"enabled": true,
		"heldByCaller": false,
		"holder": {
			"userId": "01JUSER000000000000000000",
			"userName": "Ada",
			"acquiredAt": "2026-05-01T09:12:04.117Z",
			"expiresAt": "2026-05-01T09:19:04.117Z"
		}
	}
}

集合禁用編輯锁時,enabled 為 false 且不會占用租约。再次获取同一把锁並保存項目都會延长调用方持有的租约。

获取請求体可包含标识单次編輯工作階段的不透明 token,以及使用者選擇取代其他編輯者租约時的 takeover: true。释放锁時將同一 token 作為查询参数传入。同一账户的第二個标签页便无法误释放第一個标签页的租约。

当其他使用者持有租约時,受保护的內容寫入傳回 409 ENTRY_LOCKED。錯誤 details 标明持有者與過期時间。要覆蓋锁,在有請求体的寫入 JSON 中傳送 "overrideLock": true,或對无請求体的 DELETE 操作使用 ?overrideLock=true。

引用選擇

reference 欄位 通過关系將項目链接到另一集合中的項目。其值不属于 data,且按翻译组键控,因此項目的各语言翻译共享同一選擇。

创建與更新請求体在 references 下携带選擇,按欄位 slug 键控,每项為最多 1000 個項目 ID 的数组,顺序為展示顺序。EmDash 在與項目同一事务中寫入選擇。以下更新替换項目的作者:

PUT /_emdash/api/content/articles/01JARTICLE0000000000000000
Authorization: Bearer $EMDASH_TOKEN
Content-Type: application/json

{
	"data": { "title": "Updated launch notes" },
	"references": { "author": ["01JAUTHOR00000000000000000"] },
	"_rev": "opaque-revision-token"
}

绑定到关系子端的欄位會選擇指向当前寫入項目的項目,這些項目本身没有顺序。关系两端都會強制执行限制,因此若選擇會使链接項目拥有超過关系允许的上级数量,會與链接過多項目一样被拒绝。

在保留修订的集合上,已發佈項目上变更的選擇會與項目其他待处理編輯一起暂存于草稿。項目發佈時生效,随草稿丢弃。發佈會针對关系限制重新檢查整個選擇。

单項目讀取傳回按欄位 slug 键控的 references。每個欄位包含其链接項目的第一页(50 条),若還有更多则带 nextCursor,並报告各項目的 ID、slug、集合、展示标题、解析后的 locale 與翻译组。可讀取草稿的调用方會看到草稿中暂存的選擇。內容列表操作不包含 references。

关系定义與链接遍历是管理路由,不在 OpenAPI 中,也不属于公開契約。通過上述內容操作寫入與讀取選擇,並在站點上使用 getEmDashEntry() 與 getEmDashReferences() 渲染。

翻譯

公開 REST 契約暴露內容翻译與分类法定义翻译。內容创建接受 translationOf,分类法创建使用同一欄位添加 locale 变体。內容-术语操作傳回感知 locale 的分配。

省略 locale 時,GET /taxonomies/{name} 傳回站點默认 locale 的定义;仅当默认 locale 无定义時才回退到最低 locale 代码。更新行為不同:省略 locale 時會修改最低 locale 代码的定义。請传入 locale,使翻译分类法的更新作用于目标定义。若該 locale 无定义,更新傳回 NOT_FOUND 而非回退到其他 locale。translations 操作傳回共享组中的全部定义,並提供 translationOf 接受的 ID。

label 與 labelSingular 属于某一 locale 的定义。hierarchical 與 collections 属于分类法:各 locale 傳回相同值,更新中傳送任一项會改变所有 locale。為已在其他 locale 存在的名称创建定义會加入該分类法,无论請求是否傳送 translationOf。新定义采用分类法的 hierarchical 與 collections;创建時傳送不同值會傳回 VALIDATION_ERROR。

删除分类法會移除其定义的所有 locale、全部术语以及這些术语對內容的全部分配,不會删除內容項目本身。

术语重排操作改变一個兄弟组。ids 数组可只包含該组的一部分;列出的术语交换现有位置,未列出的术语保持原位。例如對 [A, B, C] 用 ids: ["C", "A"] 重排得到 [C, B, A]。重排不改变父子关系,同一术语顺序适用于其翻译组中的各 locale。

選單、分类法术语與署名翻译路由不在公開 REST 契約内。其受支持的翻译列表可通過 MCP 服务器 上的 menu_translations、taxonomy_term_translations 與 byline_translations 获取。

媒體端點

媒體操作涵盖列表、上傳、元数据更新、图片替换、資料夾、使用信息與使用索引维护。媒體库指南 说明面向使用者的工作流及使用覆蓋的含义。

列出與檢視媒體

GET /media 支持游标或页码分页、MIME 类型與文件名筛选、資料夾以及可选的使用摘要。省略 folderId 包含所有資料夾,或传入 folderId=unfiled 仅傳回主库。列表或单项讀取時设置 includeUsage=1 以包含使用信息;其他值无效。

usage.count 统计当前索引源引用該媒體项的不同活跃內容行或 locale,以及選擇它的各站點设置(logo、favicon、seo.defaultOgImage)。同一項目内重复引用只计一次,回收站項目不计入。仅可讀取草稿的调用方可见該数字;其他已授權媒體读者收到 count: null,因為计数可能泄露草稿內容。

GET /media/{id}/usage 分页傳回引用該媒體的內容項目。每页還包含 siteSettings(選擇該媒體项的站點设置),例如 [{ "setting": "favicon" }]。站點设置每次請求从存储的设置讀取,不依赖使用索引。

每個使用结果包含覆蓋狀態:

狀態含义
complete每個已注册集合都有当前使用覆蓋。
never没有已注册集合完成初始使用修復。
running修復进行中。
partial仅部分已注册集合有当前覆蓋。
failed已注册集合集上的覆蓋失败。
stale索引早于其所描述的內容。
unknown存储狀態不被此 EmDash 版本识别。

仅 complete 支持在已索引欄位类型内將零计数视為完整。並发寫入期间计数仅供参考;不會锁定媒體项或保证删除安全。使用索引覆蓋图片與文件欄位、Repeater 图片欄位、Portable Text 图片與图库块,以及 EmDash 集合中保留块版本声明的媒體。站點 logo、favicon 與默认社交图设置也會报告。使用不包括自定义 Portable Text 块、应用代码、渲染 HTML、其他设置、選單、小部件、插件数据、外部站點或仅提供商资产。

直接 multipart 上傳

通過 multipart 請求將文件作為 file 欄位 POST 到 EmDash:

curl --request POST \
	--header "Authorization: Bearer $EMDASH_TOKEN" \
	--form "file=@./cover.jpg;type=image/jpeg" \
	https://example.com/_emdash/api/media

curl 會自动添加 multipart 边界。請勿手动设置 Content-Type 標頭。OpenAPI MediaDirectUploadBody schema 列出可选元数据欄位,回應 schema 区分新上傳與去重后的现有项。

multipart 請求体還可包含图片 width、height、应应用 MIME 类型 allowlist 的 fieldId,以及用于创建低质量占位符的缩小 thumbnail。新文件傳回 201 Created 並立即可用。相同字节傳回现有媒體项,200 OK 且 deduplicated: true。

上傳目標流程

当用戶端可直接上傳到 S3 兼容存储時使用上傳目标流程。媒體项保持 pending,确认成功前不會出现在标准库中。

  1. 請求上傳目标

    POST /_emdash/api/media/upload-url
    Authorization: Bearer $EMDASH_TOKEN
    Content-Type: application/json
    
    {
    	"filename": "cover.jpg",
    	"contentType": "image/jpeg",
    	"size": 102400
    }

    回應提供 uploadUrl、method、headers、mediaId、storageKey 與過期時间。当 contentHash 與相同 MIME 类型與大小的现有文件匹配時,回應改為设置 existing: true;使用該媒體项,勿再上傳或确认另一份副本。

  2. 上傳字节

    使用傳回的 method 與 headers。將根相對 URL 解析為 EmDash 站點並包含 Bearer 權杖。向其他源的绝對 URL 仅傳送傳回的上傳 headers。

  3. 确认上傳

    POST /_emdash/api/media/01JMEDIA000000000000000000/confirm
    Authorization: Bearer $EMDASH_TOKEN
    Content-Type: application/json
    
    {
    	"size": 102400,
    	"width": 1920,
    	"height": 1080
    }

    确认會校验存储對象並將项从 pending 变為 ready。提供的 size 與尺寸必须與上傳文件一致。

本地存储與原生 R2 傳回同源的 EmDash 上傳目标。S3 兼容存储可能傳回签名的外部 URL。pending 项在确认成功前不會出现在标准媒體列表中。

上傳錯誤

以下錯誤需要不同的恢复操作:

狀態代码操作
400NO_FILE在 multipart 請求中添加 file 欄位,或傳送缺失的上傳体。
400INVALID_TYPE使用與 pending 媒體项匹配的允许 MIME 类型。
400VALIDATION_ERROR修正缺失或无效的元数据,包括超過配置大小限制的值。
400FILE_NOT_FOUND确认前先將對象上傳到傳回的目标。
400UPLOAD_SIZE_MISMATCH以正确 size 重新开始流程;声明、上傳與确认的 size 必须一致。
400 或 409INVALID_STATE重试前讀取媒體项。它可能已非 pending,或在确认期间被其他請求更改。
404NOT_FOUND使用现有的 pending 媒體 ID。
413PAYLOAD_TOO_LARGE减小文件大小或增大 maxUploadSize 后再开始上傳。

媒體資料夾

資料夾名称會 trim、限制 200 字符,並在 Unicode 规范化與小写后比较。因此 Photos、photos 與 PHOTOS 等名称會冲突。删除資料夾會將其媒體移回主库;不會删除媒體、更改媒體 ID 或 URL,或更改使用记录。

修復媒體使用

媒體使用激活、进度、工作队列、删除清理與修復是 /_emdash/api/admin/media-usage/ 下的运维操作。工作階段使用者需要 schema:manage;Bearer 權杖還需要 admin scope。

跟踪关闭時,激活前暂停直接写数据库的进程。EmDash 在设置运行期间會暂時阻止通過其 API 傳送的內容與 schema 寫入,但无法阻止直接写数据库的其他进程。

  1. 停止直接写数据库的进程,等待进行中的寫入完成。
  2. 讀取激活狀態。expanded 表示跟踪关闭,activating 表示 EmDash 正在准备集合,active 表示新的媒體引用变更會被跟踪。
  3. 傳送一次激活請求 { "writersDrained": true }。
  4. 逐次傳送进度請求,等待每次傳回的 nextRequestInMs,直到激活為 active。
  5. 恢复直接写数据库。
  6. 继续进度請求,直到历史索引為 ready 且 nextRequestInMs 為 null。

若寫入請求超時或傳回 409 或 500,重试前讀取激活與进度狀態。已完成的批次仍會记录。当设置 lastErrorCode 時,保持直接寫入停止,解决报告的问题,並傳送一次确认的重试。激活开始后无法取消或重置,請在 staging 副本上测试此流程並保持当前数据库备份。

工作列表操作暴露失败或延迟的項目索引,不傳回內容、媒體引用、租约權杖、原始数据库錯誤或精确积压计数。重试单项是幂等的。409 WORK_LEASE_ACTIVE 表示 worker 仍在处理該项;回應包含 details.leaseExpiresAt,請等到該時间並再次讀取项后再重试。409 WORK_CHANGED 表示其他請求更改了工作项;讀取其当前狀態,勿覆蓋较新的工作。

修復操作接受 { "scope": "collection", "collection": "articles" } 或 { "scope": "all" }。全集合修復同步且顺序执行,大型站點可能耗時很长。200 回應仍可能报告 partial、failed 或 stale;在视為修復完成前檢查 data.status、各集合狀態與源计数。

站點遷移

/_emdash/api/admin/transfer/ 下的遷移操作將站點导出為站點包並导入到空站點。站點遷移指南 描述导出、上傳、分析、执行與回执工作流。

工作階段使用者需要 transfer:export 或 transfer:import 权限,仅管理员持有。Bearer 權杖需要 admin 或各操作指定的 transfer:export、transfer:analyze 或 transfer:execute scope。审批操作仅接受已登录工作階段。它们裁决 MCP site_export_start 與 site_import_start 工具发起的請求;REST 导出與执行操作不需要审批。

导入执行期间,以及失败或取消后直至放弃之前,大多数其他寫入操作傳回 503 TRANSFER_IMPORT_IN_PROGRESS。指南列出导入期间仍可用的操作。

分頁

列表操作在 OpenAPI 中描述其分页参数。大多数游标分页操作接受不透明 cursor 與 1 至 100 的 limit,默认 50。原样傳回上一回應的 nextCursor;請勿解析或构造它。部分媒體操作還支持页码,部分专用列表使用不同 limit,生成的用戶端应遵循各操作的 schema。

搜尋分詞器

search-enable 操作為每個集合存储分词器。在已启用搜索的集合上更改它會重建該集合索引。

值用途
porter unicode61适合 Porter 词干提取的英文內容的默认值。
unicode61使用词分隔符但不应使用英文词干提取的语言。
trigram无空格文本(含日文、中文、泰文、高棉文、老挝文、缅甸文)或需要子串匹配的集合。少于三個 Unicode 字符的查询无匹配。

禁用搜索會保留存储的分词器供下次启用。rebuild 操作使用集合存储的分词器與欄位权重。

評論與重新導向

公開評論提交进入审核队列。管理評論操作列出所有狀態、傳回计数、更新单一狀態、批量审核並永久删除評論。429 回應表示达到提交速率限制。

重定向操作分别管理重定向规则與记录的 404 日志。修剪按請求体所选項目移除,而 DELETE /redirects/404s 清空整個日志。两者均不删除重定向规则。