- inkreach-cli: subscription command in bin, built dist - env.development: public origin, plugin service registry, cache provider=redis, cold retention days - skill docs: subscription quick reference, deploy and ops notes
297 lines
12 KiB
JavaScript
297 lines
12 KiB
JavaScript
"use strict";
|
||
Object.defineProperty(exports, "__esModule", { value: true });
|
||
exports.ApiError = exports.Client = void 0;
|
||
/**
|
||
* High-level API client. Each method maps to one REST endpoint of the
|
||
* inkreach-teable backend, using the SELF-BUILT contract (not official teable).
|
||
*
|
||
* Base/table/field/record/view endpoints are compatible with official teable
|
||
* open-api; workflow/automation endpoints use the project's own contract.
|
||
*/
|
||
const request_1 = require("./request");
|
||
Object.defineProperty(exports, "ApiError", { enumerable: true, get: function () { return request_1.ApiError; } });
|
||
class Client {
|
||
cfg;
|
||
constructor(cfg) {
|
||
this.cfg = cfg;
|
||
}
|
||
get headers() {
|
||
return this.cfg.token ? { Authorization: `Bearer ${this.cfg.token}` } : {};
|
||
}
|
||
async call(method, p, body) {
|
||
const res = await (0, request_1.request)(this.cfg.endpoint, p, { method, headers: this.headers, body });
|
||
return res.data;
|
||
}
|
||
// ---- auth / user ----
|
||
getMe() {
|
||
// GET /api/auth/user — @TokenAccess() enabled, works with PAT.
|
||
// (/api/auth/user/me is session-only and 403s for PAT.)
|
||
return this.call('GET', '/api/auth/user');
|
||
}
|
||
// ---- access tokens (PAT) ----
|
||
listAccessTokens() {
|
||
return this.call('GET', '/api/access-token');
|
||
}
|
||
createAccessToken(body) {
|
||
return this.call('POST', '/api/access-token', body);
|
||
}
|
||
deleteAccessToken(id) {
|
||
return this.call('DELETE', `/api/access-token/${id}`);
|
||
}
|
||
// ---- base ----
|
||
listBases() {
|
||
// GET /api/base/access/all — lists bases the user can access.
|
||
return this.call('GET', '/api/base/access/all');
|
||
}
|
||
getBase(baseId) {
|
||
return this.call('GET', `/api/base/${baseId}`);
|
||
}
|
||
// ---- table ----
|
||
listTables(baseId) {
|
||
return this.call('GET', `/api/base/${baseId}/table`);
|
||
}
|
||
getTable(tableId) {
|
||
return this.call('GET', `/api/table/${tableId}`);
|
||
}
|
||
createTable(baseId, body) {
|
||
return this.call('POST', `/api/base/${baseId}/table`, body);
|
||
}
|
||
updateTable(tableId, body) {
|
||
return this.call('PUT', `/api/table/${tableId}`, body);
|
||
}
|
||
deleteTable(tableId, baseId) {
|
||
const base = baseId ?? this.cfg.baseId;
|
||
return this.call('DELETE', `/api/base/${base}/table/${tableId}`);
|
||
}
|
||
// ---- field ----
|
||
listFields(tableId) {
|
||
return this.call('GET', `/api/table/${tableId}/field`);
|
||
}
|
||
getField(tableId, fieldId) {
|
||
return this.call('GET', `/api/table/${tableId}/field/${fieldId}`);
|
||
}
|
||
createField(tableId, body) {
|
||
return this.call('POST', `/api/table/${tableId}/field`, body);
|
||
}
|
||
/** Rename/describe. NOTE: the update API accepts name/description only —
|
||
* options (incl. select choice visibilityFilter) must go through convertField. */
|
||
updateField(tableId, fieldId, body) {
|
||
return this.call('PATCH', `/api/table/${tableId}/field/${fieldId}`, body);
|
||
}
|
||
/** PUT convert: the ONLY API path that persists field options (choices,
|
||
* visibilityFilter dependency config, etc.). Requires the field's current type. */
|
||
convertField(tableId, fieldId, body) {
|
||
return this.call('PUT', `/api/table/${tableId}/field/${fieldId}/convert`, body);
|
||
}
|
||
deleteField(tableId, fieldId) {
|
||
return this.call('DELETE', `/api/table/${tableId}/field/${fieldId}`);
|
||
}
|
||
/** Evaluate select-choice visibility dependency for one record+field.
|
||
* GET /api/table/:tableId/select-visibility/:recordId/:fieldId */
|
||
getVisibleChoices(tableId, recordId, fieldId) {
|
||
return this.call('GET', `/api/table/${tableId}/select-visibility/${recordId}/${fieldId}`);
|
||
}
|
||
// ---- record ----
|
||
getRecords(tableId, query) {
|
||
const qs = query ? '?' + new URLSearchParams(this.flattenQuery(query)).toString() : '';
|
||
return this.call('GET', `/api/table/${tableId}/record${qs}`);
|
||
}
|
||
/** Flatten search[] tuple arrays into repeated query keys (Teable expects search[]=...). */
|
||
flattenQuery(query) {
|
||
const out = {};
|
||
for (const [k, v] of Object.entries(query)) {
|
||
if (v === undefined || v === null)
|
||
continue;
|
||
if (Array.isArray(v)) {
|
||
v.forEach((item) => {
|
||
out[`${k}[]`] = typeof item === 'object' ? JSON.stringify(item) : String(item);
|
||
});
|
||
}
|
||
else if (typeof v === 'object') {
|
||
out[k] = JSON.stringify(v);
|
||
}
|
||
else if (k === 'search' && typeof v === 'string') {
|
||
// The records list API expects `search[]=value` (tuple), not a plain
|
||
// `search=value` string. https://github.com/colinhacks/zod
|
||
out['search[]'] = v;
|
||
}
|
||
else {
|
||
out[k] = String(v);
|
||
}
|
||
}
|
||
return out;
|
||
}
|
||
getRecord(recordId, query) {
|
||
const qs = query ? '?' + new URLSearchParams(this.flattenQuery(query)).toString() : '';
|
||
return this.call('GET', `/api/record/${recordId}${qs}`);
|
||
}
|
||
createRecord(tableId, body) {
|
||
return this.call('POST', `/api/table/${tableId}/record`, body);
|
||
}
|
||
updateRecord(tableId, body) {
|
||
return this.call('PATCH', `/api/table/${tableId}/record`, body);
|
||
}
|
||
updateRecordOne(recordId, body) {
|
||
return this.call('PATCH', `/api/record/${recordId}`, body);
|
||
}
|
||
deleteRecords(tableId, recordIds) {
|
||
const qs = recordIds.map((id) => `recordIds=${encodeURIComponent(id)}`).join('&');
|
||
return this.call('DELETE', `/api/table/${tableId}/record?${qs}`);
|
||
}
|
||
// ---- view ----
|
||
listViews(tableId) {
|
||
return this.call('GET', `/api/table/${tableId}/view`);
|
||
}
|
||
getView(viewId) {
|
||
return this.call('GET', `/api/view/${viewId}`);
|
||
}
|
||
createView(tableId, body) {
|
||
return this.call('POST', `/api/table/${tableId}/view`, body);
|
||
}
|
||
updateView(viewId, body) {
|
||
return this.call('PATCH', `/api/view/${viewId}`, body);
|
||
}
|
||
deleteView(viewId) {
|
||
return this.call('DELETE', `/api/view/${viewId}`);
|
||
}
|
||
// ---- workflow (self-built contract) ----
|
||
listWorkflows(baseId) {
|
||
return this.call('GET', `/api/base/${baseId}/workflow`);
|
||
}
|
||
getWorkflow(workflowId) {
|
||
return this.call('GET', `/api/base/any/workflow/${workflowId}`);
|
||
}
|
||
/**
|
||
* Get workflow by explicit baseId + workflowId. getWorkflow() above hits a
|
||
* route that resolves base from the workflow itself, but some auth/permission
|
||
* paths need the base-scoped route. Prefer this when baseId is known.
|
||
*/
|
||
getWorkflowInBase(baseId, workflowId) {
|
||
return this.call('GET', `/api/base/${baseId}/workflow/${workflowId}`);
|
||
}
|
||
createWorkflow(baseId, body) {
|
||
return this.call('POST', `/api/base/${baseId}/workflow`, body);
|
||
}
|
||
updateWorkflow(baseId, workflowId, body) {
|
||
return this.call('PATCH', `/api/base/${baseId}/workflow/${workflowId}`, body);
|
||
}
|
||
deleteWorkflow(baseId, workflowId) {
|
||
return this.call('DELETE', `/api/base/${baseId}/workflow/${workflowId}`);
|
||
}
|
||
activateWorkflow(baseId, workflowId) {
|
||
return this.call('POST', `/api/base/${baseId}/workflow/${workflowId}/activate`);
|
||
}
|
||
deactivateWorkflow(baseId, workflowId) {
|
||
return this.call('POST', `/api/base/${baseId}/workflow/${workflowId}/deactivate`);
|
||
}
|
||
testWorkflow(baseId, workflowId) {
|
||
return this.call('POST', `/api/base/${baseId}/workflow/${workflowId}/test`);
|
||
}
|
||
/** PUT /node — whole-graph replacement (delete-all + create-all in a txn). */
|
||
replaceNodes(baseId, workflowId, body) {
|
||
return this.call('PUT', `/api/base/${baseId}/workflow/${workflowId}/node`, body);
|
||
}
|
||
// ---- secrets ----
|
||
listSecrets(baseId, workflowId) {
|
||
return this.call('GET', `/api/base/${baseId}/workflow/${workflowId}/secret`);
|
||
}
|
||
createSecret(baseId, workflowId, body) {
|
||
return this.call('POST', `/api/base/${baseId}/workflow/${workflowId}/secret`, body);
|
||
}
|
||
updateSecret(baseId, workflowId, secretId, body) {
|
||
return this.call('PATCH', `/api/base/${baseId}/workflow/${workflowId}/secret/${secretId}`, body);
|
||
}
|
||
deleteSecret(baseId, workflowId, secretId) {
|
||
return this.call('DELETE', `/api/base/${baseId}/workflow/${workflowId}/secret/${secretId}`);
|
||
}
|
||
// ---- execution history ----
|
||
listExecutions(baseId, workflowId) {
|
||
return this.call('GET', `/api/base/${baseId}/workflow/${workflowId}/execution`);
|
||
}
|
||
getExecution(baseId, workflowId, executionId) {
|
||
return this.call('GET', `/api/base/${baseId}/workflow/${workflowId}/execution/${executionId}`);
|
||
}
|
||
rerunExecution(baseId, workflowId, executionId) {
|
||
return this.call('POST', `/api/base/${baseId}/workflow/${workflowId}/execution/${executionId}/rerun`);
|
||
}
|
||
// ---- trigger (manual fire for testing) ----
|
||
fireButtonClick(tableId, recordId, fieldId) {
|
||
return this.call('POST', `/api/table/${tableId}/record/${recordId}/${fieldId}/button-click`);
|
||
}
|
||
fireWebhook(baseId, workflowId, body) {
|
||
// NOTE: real backend route is /api/webhook/{baseId}/{workflowId} (no /base/ /workflow/ segments)
|
||
return this.call('POST', `/api/webhook/${baseId}/${workflowId}`, body);
|
||
}
|
||
// ---- SQL (read-only) ----
|
||
sql(baseId, sql) {
|
||
return this.call('POST', `/api/base/${baseId}/sql`, { sql });
|
||
}
|
||
// ---- plugin ----
|
||
getPlugins() {
|
||
return this.call('GET', '/api/plugin');
|
||
}
|
||
getPlugin(pluginId) {
|
||
return this.call('GET', `/api/plugin/${encodeURIComponent(pluginId)}`);
|
||
}
|
||
updatePlugin(pluginId, body) {
|
||
return this.call('PUT', `/api/plugin/${encodeURIComponent(pluginId)}`, body);
|
||
}
|
||
getCspOrigins() {
|
||
return this.call('GET', '/api/plugin/csp-origins');
|
||
}
|
||
registerPlugin(body) {
|
||
return this.call('POST', '/api/plugin', body);
|
||
}
|
||
listPluginConfigs(baseId) {
|
||
return this.call('GET', `/api/base/${baseId}/plugin-config`);
|
||
}
|
||
upsertPluginConfig(baseId, pluginId, body) {
|
||
return this.call('PUT', `/api/base/${baseId}/plugin-config/${pluginId}`, body);
|
||
}
|
||
// ---- plugin installs ----
|
||
listInstalls(baseId) {
|
||
return this.call('GET', `/api/base/${baseId}/plugin-installs`);
|
||
}
|
||
createInstall(baseId, body) {
|
||
return this.call('POST', `/api/base/${baseId}/plugin-installs`, body);
|
||
}
|
||
deleteInstall(baseId, installId) {
|
||
return this.call('DELETE', `/api/base/${baseId}/plugin-installs/${installId}`);
|
||
}
|
||
// ---- plugin developers ----
|
||
listDevelopers() {
|
||
return this.call('GET', '/api/plugin-developers');
|
||
}
|
||
addDeveloper(userIdOrEmail) {
|
||
// Backend reads @Body('userId') or @Body('email'). Send whichever looks right.
|
||
const isEmail = userIdOrEmail.includes('@');
|
||
const body = isEmail ? { email: userIdOrEmail } : { userId: userIdOrEmail };
|
||
return this.call('POST', '/api/plugin-developers', body);
|
||
}
|
||
removeDeveloper(userId) {
|
||
return this.call('DELETE', `/api/plugin-developers/${encodeURIComponent(userId)}`);
|
||
}
|
||
// ---- subscription center (record subscriptions) ----
|
||
listSubscriptions(baseId, tableId) {
|
||
const q = tableId ? `?tableId=${encodeURIComponent(tableId)}` : '';
|
||
return this.call('GET', `/api/base/${baseId}/subscription${q}`);
|
||
}
|
||
getSubscriptionEvents(baseId, subscriptionId) {
|
||
return this.call('GET', `/api/base/${baseId}/subscription/${subscriptionId}/event`);
|
||
}
|
||
createSubscription(baseId, body) {
|
||
return this.call('POST', `/api/base/${baseId}/subscription`, body);
|
||
}
|
||
updateSubscription(baseId, subscriptionId, body) {
|
||
return this.call('PATCH', `/api/base/${baseId}/subscription/${subscriptionId}`, body);
|
||
}
|
||
deleteSubscription(baseId, subscriptionId) {
|
||
return this.call('DELETE', `/api/base/${baseId}/subscription/${subscriptionId}`);
|
||
}
|
||
/** 立即发送(flush pending digest events) */
|
||
sendSubscriptionNow(baseId, subscriptionId) {
|
||
return this.call('POST', `/api/base/${baseId}/subscription/${subscriptionId}/send`);
|
||
}
|
||
}
|
||
exports.Client = Client;
|