feat: expand commercial ai drama platform
This commit is contained in:
+317
-7
@@ -15,9 +15,11 @@ import {
|
||||
buildContextPayload,
|
||||
createAsset,
|
||||
createAssetVersion,
|
||||
ensureOrganizationEntitlements,
|
||||
getAsset,
|
||||
httpError,
|
||||
orgMembers,
|
||||
organizationEntitlements,
|
||||
organizationRolePolicies,
|
||||
updateOrganizationRolePolicy,
|
||||
previewInvitation,
|
||||
@@ -41,7 +43,11 @@ import {
|
||||
apiClients,
|
||||
identityCenter,
|
||||
publicIdentityProviders,
|
||||
createModelCatalogEntry,
|
||||
createModelRoute,
|
||||
updateIdentityPolicy,
|
||||
updateModelCatalogEntry,
|
||||
updateModelRoute,
|
||||
saveIdentityProvider,
|
||||
probeIdentityProvider,
|
||||
createDirectorySync,
|
||||
@@ -59,8 +65,10 @@ import {
|
||||
scimPatchUser,
|
||||
scimDeleteUser,
|
||||
serviceHealth,
|
||||
scanAssetGovernance,
|
||||
updateAssetLock,
|
||||
updateAssetRights,
|
||||
listAssetGovernanceReviews,
|
||||
restoreAssetVersion,
|
||||
updateServiceHealth,
|
||||
usageSummary,
|
||||
@@ -82,7 +90,17 @@ import {
|
||||
workspaceMembers,
|
||||
listAuditEvents,
|
||||
getAuditEvent,
|
||||
exportAuditEvents
|
||||
exportAuditEvents,
|
||||
scopedModelCatalog,
|
||||
scopedModelRoutes,
|
||||
subscriptionPlanTemplates,
|
||||
createSubscriptionPlanTemplate,
|
||||
updateSubscriptionPlanTemplate,
|
||||
updateOrganizationEntitlement,
|
||||
requireEntitlement,
|
||||
listCommercialApprovalRequests,
|
||||
createCommercialApprovalRequest,
|
||||
decideCommercialApprovalRequest
|
||||
} from "./tenant.mjs";
|
||||
import { platformData, platformResearchMatrix } from "../src/platform/platformData.js";
|
||||
import { authMode, authenticate, cancelMfaSetup, changePassword, completeMfaChallenge, completeMfaEnrollment, createMfaChallenge, createMfaEnrollmentChallenge, createSession, disableMfa, enableMfa, listSecurityEvents, listUserDevices, listUserSessions, mfaRequiredForUser, mfaStatus, recordSecurityEvent, revokeAllUserSessions, revokeOtherUserSessions, revokeSession, revokeUserSession, safeUser, sessionIdentity, startMfaEnrollment, startMfaSetup, trustUserDevice, untrustUserDevice } from "./auth.mjs";
|
||||
@@ -99,6 +117,7 @@ import {
|
||||
createSeason,
|
||||
createShot,
|
||||
decideReview,
|
||||
getDeliveryClearance,
|
||||
importScript,
|
||||
listDeliveryBatches,
|
||||
listDeliveryChannels,
|
||||
@@ -112,6 +131,7 @@ import {
|
||||
runMediaQa,
|
||||
restoreShotVersion,
|
||||
rollbackDeliveryBatch,
|
||||
runDeliveryClearance,
|
||||
publishDeliveryRelease,
|
||||
decideDeliveryRelease,
|
||||
savePromptVersion,
|
||||
@@ -123,11 +143,16 @@ import {
|
||||
} from "./production.mjs";
|
||||
import {
|
||||
createGenerationJob,
|
||||
previewGenerationJob,
|
||||
executeGenerationJob,
|
||||
getGenerationJob,
|
||||
listGenerationJobs,
|
||||
decideModelRouteApproval,
|
||||
endpointInfo,
|
||||
listModelRouteApprovalRequests,
|
||||
previewModelRouteResolution,
|
||||
probeModelConnector,
|
||||
requestModelRouteApproval,
|
||||
updateModelConnector
|
||||
} from "./execution.mjs";
|
||||
import { reclaimStorage, requireStorageQuota, storageCleanupPreview, storageSummary } from "./storage.mjs";
|
||||
@@ -145,6 +170,23 @@ import { addTaskLink, createProjectTask, createTaskComment, getProjectTask, list
|
||||
import { searchPlatform } from "./search.mjs";
|
||||
import { consumeRateLimit, rateLimitHeaders, rateLimitIdentity } from "./rate-limit.mjs";
|
||||
import { createDeliveryAccessLink, listDeliveryAccessFeedback, listDeliveryAccessLinks, readPublicDeliveryFile, resolvePublicDeliveryPortal, revokeDeliveryAccessLink, submitPublicDeliveryFeedback } from "./delivery-portal.mjs";
|
||||
import {
|
||||
archiveKnowledgeDocument,
|
||||
createKnowledgeContextPack,
|
||||
getKnowledgeContextPack,
|
||||
getKnowledgeDocument,
|
||||
importKnowledgeDocument,
|
||||
listKnowledgeDocumentVersions,
|
||||
listKnowledgeContextPacks,
|
||||
listKnowledgeDocuments,
|
||||
materializeKnowledgeContextPack,
|
||||
materializeKnowledgeDocument,
|
||||
restoreKnowledgeDocument,
|
||||
restoreKnowledgeDocumentVersion,
|
||||
reviewKnowledgeDocument,
|
||||
updateKnowledgeDocument,
|
||||
searchKnowledge
|
||||
} from "./knowledge.mjs";
|
||||
|
||||
const port = Number(process.env.AI_DRAMA_API_PORT || 8787);
|
||||
const root = resolve(import.meta.dirname, "..");
|
||||
@@ -667,6 +709,7 @@ function createOrganization(context, body) {
|
||||
dbRun("INSERT INTO quota_allocations(id, organization_id, workspace_id, metric, limit_value, used_value, unit, period_start, period_end, created_at, updated_at) VALUES (?, ?, ?, 'clip', 2400, 0, 'clips', datetime('now', 'start of month'), datetime('now', 'start of month', '+1 month', '-1 second'), ?, ?)", [createId("quota"), organizationId, workspaceId, timestamp, timestamp]);
|
||||
dbRun("INSERT INTO audit_logs(id, organization_id, workspace_id, actor_user_id, action, target_type, target_id, result, metadata_json, created_at) VALUES (?, ?, ?, ?, 'organization.created', 'organization', ?, 'ok', ?, ?)", [createId("aud"), organizationId, workspaceId, context.user.id, organizationId, JSON.stringify({ createdFrom: context.organization.id }), timestamp]);
|
||||
});
|
||||
ensureOrganizationEntitlements(organizationId);
|
||||
return dbGet("SELECT * FROM organizations WHERE id = ?", [organizationId]);
|
||||
}
|
||||
|
||||
@@ -688,6 +731,7 @@ function updateOrganization(context, organizationId, body) {
|
||||
|
||||
function createWorkspace(context, body) {
|
||||
requirePermission(context, "workspace:create");
|
||||
requireEntitlement(context, "limit.workspaces", 1);
|
||||
const name = String(body.name || "").trim();
|
||||
if (!name) throw httpError(400, "name_required", "工作区名称不能为空");
|
||||
const id = body.id || createId("ws");
|
||||
@@ -742,6 +786,7 @@ function updateWorkspace(context, workspaceId, body) {
|
||||
|
||||
function createProject(context, body) {
|
||||
requirePermission(context, "project:create");
|
||||
requireEntitlement(context, "limit.projects", 1);
|
||||
const name = String(body.name || "").trim();
|
||||
if (!name) throw httpError(400, "name_required", "项目名称不能为空");
|
||||
const id = body.id || createId("project");
|
||||
@@ -836,7 +881,13 @@ async function uploadAssetVersion(context, assetId, body) {
|
||||
contentSha256,
|
||||
mimeType: body.mimeType || "application/octet-stream",
|
||||
rightsStatus: body.rightsStatus || "needs-evidence",
|
||||
versionNote: body.versionNote || "本地文件版本上传"
|
||||
versionNote: body.versionNote || "本地文件版本上传",
|
||||
provenance: body.provenance,
|
||||
risk: body.risk,
|
||||
tags: body.tags,
|
||||
licenseScope: body.licenseScope,
|
||||
expiresAt: body.expiresAt,
|
||||
metadata: body.metadata
|
||||
});
|
||||
}
|
||||
|
||||
@@ -868,7 +919,7 @@ function mimeForPath(pathname) {
|
||||
}
|
||||
|
||||
async function assetContent(context, assetId) {
|
||||
if (!hasPermission(context, "asset:edit") && !hasPermission(context, "voice:edit")) {
|
||||
if (!hasPermission(context, "asset:edit") && !hasPermission(context, "voice:edit") && !hasPermission(context, "compliance:manage")) {
|
||||
throw httpError(403, "permission_denied", "当前角色没有资产预览权限");
|
||||
}
|
||||
const asset = getAsset(context, assetId);
|
||||
@@ -894,7 +945,9 @@ async function assetContent(context, assetId) {
|
||||
}
|
||||
|
||||
async function verifyAssetContent(context, assetId) {
|
||||
requirePermission(context, "asset:edit");
|
||||
if (!hasPermission(context, "asset:edit") && !hasPermission(context, "compliance:manage")) {
|
||||
requirePermission(context, "asset:edit");
|
||||
}
|
||||
const asset = getAsset(context, assetId);
|
||||
if (!asset) throw httpError(404, "asset_not_found", "资产不存在或不属于当前项目");
|
||||
const version = asset.currentVersion;
|
||||
@@ -1054,11 +1107,13 @@ function updateProjectMember(context, projectId, userId, body) {
|
||||
|
||||
function registerModel(context, body) {
|
||||
requirePermission(context, "model:manage");
|
||||
requireEntitlement(context, "limit.model_connectors", 1);
|
||||
const id = body.id || createId("model");
|
||||
const timestamp = new Date().toISOString();
|
||||
const label = String(body.label || "未命名本地模型").trim();
|
||||
const endpoint = String(body.endpoint || "http://127.0.0.1:7860").trim();
|
||||
const costMode = String(body.costMode || "local").trim();
|
||||
const kind = String(body.kind || "http-json").trim();
|
||||
if (!label || !endpoint) throw httpError(400, "adapter_fields_required", "连接器名称和地址不能为空");
|
||||
if (!["local", "mixed", "cloud"].includes(costMode)) throw httpError(400, "cost_mode_invalid", "成本策略无效");
|
||||
const { local } = endpointInfo(endpoint);
|
||||
@@ -1066,9 +1121,11 @@ function registerModel(context, body) {
|
||||
const requestedApproval = Boolean(body.approvalRequired);
|
||||
if (costMode !== "local" && !requestedApproval) throw httpError(400, "external_connector_approval_required", "混合或外部连接器必须开启审批策略");
|
||||
const approvalRequired = costMode === "local" ? requestedApproval : true;
|
||||
if (costMode !== "local") requireEntitlement(context, "feature.external_cloud_connectors", 1);
|
||||
if (kind === "comfyui") requireEntitlement(context, "feature.comfyui_adapter", 1);
|
||||
const protocol = body.protocol && typeof body.protocol === "object" ? body.protocol : {};
|
||||
const authEnv = String(body.authEnv || protocol.authEnv || "").trim();
|
||||
dbRun("INSERT INTO model_connectors(id, organization_id, workspace_id, label, kind, capabilities_json, endpoint, status, cost_mode, approval_required, protocol_json, auth_env, created_by, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, 'not-connected', ?, ?, ?, ?, ?, ?, ?)", [id, context.organization.id, context.workspace.id, label, body.kind || "http-json", JSON.stringify(body.capability || ["custom"]), endpoint, costMode, approvalRequired ? 1 : 0, JSON.stringify(protocol), authEnv, context.user.id, timestamp, timestamp]);
|
||||
dbRun("INSERT INTO model_connectors(id, organization_id, workspace_id, label, kind, capabilities_json, endpoint, status, cost_mode, approval_required, protocol_json, auth_env, created_by, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, 'not-connected', ?, ?, ?, ?, ?, ?, ?)", [id, context.organization.id, context.workspace.id, label, kind, JSON.stringify(body.capability || ["custom"]), endpoint, costMode, approvalRequired ? 1 : 0, JSON.stringify(protocol), authEnv, context.user.id, timestamp, timestamp]);
|
||||
addAudit({ context, action: "model.registered", targetType: "model_connector", targetId: id, metadata: { label, endpoint, costMode, approvalRequired } });
|
||||
return parseModelRow(dbGet("SELECT * FROM model_connectors WHERE id = ?", [id]));
|
||||
}
|
||||
@@ -1077,6 +1134,7 @@ function systemConfigPayload() {
|
||||
return {
|
||||
settings: systemSettings(),
|
||||
featureFlags: featureFlags(),
|
||||
planTemplates: subscriptionPlanTemplates({ includeArchived: true }),
|
||||
notifications: notificationChannels(),
|
||||
notificationDeliveries: [],
|
||||
apiClients: apiClients(),
|
||||
@@ -1149,6 +1207,7 @@ function updateNotificationChannel(context, body) {
|
||||
|
||||
function createApiClient(context, body) {
|
||||
requirePermission(context, "api_client:manage");
|
||||
requireEntitlement(context, "limit.api_clients", 1);
|
||||
const timestamp = new Date().toISOString();
|
||||
const id = `client-${Date.now()}-${Math.random().toString(16).slice(2, 8)}`;
|
||||
const issued = issueApiClientKey();
|
||||
@@ -1656,6 +1715,50 @@ createServer(async (req, res) => {
|
||||
return send(res, 200, organizationCommercial(context));
|
||||
}
|
||||
|
||||
const organizationEntitlementsMatch = pathname.match(/^\/api\/organizations\/([^/]+)\/entitlements$/);
|
||||
if (req.method === "GET" && organizationEntitlementsMatch) {
|
||||
const organizationId = decodeURIComponent(organizationEntitlementsMatch[1]);
|
||||
const context = contextWith({ organizationId, workspaceId: "", projectId: "" }, req);
|
||||
if (!hasPermission(context, "usage:view") && !hasPermission(context, "billing:manage") && !hasPermission(context, "quota:manage")) {
|
||||
throw httpError(403, "permission_denied", "当前角色没有查看组织套餐权益的权限");
|
||||
}
|
||||
return send(res, 200, organizationEntitlements(organizationId));
|
||||
}
|
||||
|
||||
const organizationEntitlementMatch = pathname.match(/^\/api\/organizations\/([^/]+)\/entitlements\/([^/]+)$/);
|
||||
if (req.method === "PATCH" && organizationEntitlementMatch) {
|
||||
const organizationId = decodeURIComponent(organizationEntitlementMatch[1]);
|
||||
const entitlementKey = decodeURIComponent(organizationEntitlementMatch[2]);
|
||||
const context = contextWith({ organizationId, workspaceId: "", projectId: "" }, req);
|
||||
return send(res, 200, updateOrganizationEntitlement(context, organizationId, entitlementKey, await readBody(req)));
|
||||
}
|
||||
|
||||
const organizationCommercialApprovalsMatch = pathname.match(/^\/api\/organizations\/([^/]+)\/commercial-approvals$/);
|
||||
if (req.method === "GET" && organizationCommercialApprovalsMatch) {
|
||||
const organizationId = decodeURIComponent(organizationCommercialApprovalsMatch[1]);
|
||||
const context = contextWith({ organizationId, workspaceId: "", projectId: "" }, req);
|
||||
return send(res, 200, listCommercialApprovalRequests(context, organizationId, {
|
||||
status: url.searchParams.get("status") || "",
|
||||
type: url.searchParams.get("type") || "",
|
||||
mine: url.searchParams.get("mine") === "1",
|
||||
limit: url.searchParams.get("limit") || 80
|
||||
}));
|
||||
}
|
||||
|
||||
if (req.method === "POST" && organizationCommercialApprovalsMatch) {
|
||||
const organizationId = decodeURIComponent(organizationCommercialApprovalsMatch[1]);
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, createCommercialApprovalRequest(context, organizationId, await readBody(req)));
|
||||
}
|
||||
|
||||
const organizationCommercialApprovalDecisionMatch = pathname.match(/^\/api\/organizations\/([^/]+)\/commercial-approvals\/([^/]+)\/decision$/);
|
||||
if (req.method === "POST" && organizationCommercialApprovalDecisionMatch) {
|
||||
const organizationId = decodeURIComponent(organizationCommercialApprovalDecisionMatch[1]);
|
||||
const approvalId = decodeURIComponent(organizationCommercialApprovalDecisionMatch[2]);
|
||||
const context = contextWith({ organizationId, workspaceId: "", projectId: "" }, req);
|
||||
return send(res, 200, decideCommercialApprovalRequest(context, organizationId, approvalId, await readBody(req)));
|
||||
}
|
||||
|
||||
const organizationCommercialExportMatch = pathname.match(/^\/api\/organizations\/([^/]+)\/commercial\/export$/);
|
||||
if (req.method === "GET" && organizationCommercialExportMatch) {
|
||||
const organizationId = decodeURIComponent(organizationCommercialExportMatch[1]);
|
||||
@@ -2045,7 +2148,7 @@ createServer(async (req, res) => {
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/assets") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (!hasPermission(context, "asset:edit") && !hasPermission(context, "voice:edit")) {
|
||||
if (!hasPermission(context, "asset:edit") && !hasPermission(context, "voice:edit") && !hasPermission(context, "compliance:manage")) {
|
||||
throw httpError(403, "permission_denied", "当前角色没有资产库访问权限");
|
||||
}
|
||||
return send(res, 200, { assets: scopedAssets(context) });
|
||||
@@ -2065,7 +2168,7 @@ createServer(async (req, res) => {
|
||||
const assetMatch = pathname.match(/^\/api\/assets\/([^/]+)$/);
|
||||
if (req.method === "GET" && assetMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (!hasPermission(context, "asset:edit") && !hasPermission(context, "voice:edit")) {
|
||||
if (!hasPermission(context, "asset:edit") && !hasPermission(context, "voice:edit") && !hasPermission(context, "compliance:manage")) {
|
||||
throw httpError(403, "permission_denied", "当前角色没有资产库访问权限");
|
||||
}
|
||||
const asset = getAsset(context, decodeURIComponent(assetMatch[1]));
|
||||
@@ -2098,6 +2201,18 @@ createServer(async (req, res) => {
|
||||
return send(res, 200, { verification: await verifyAssetContent(context, decodeURIComponent(assetVerifyMatch[1])) });
|
||||
}
|
||||
|
||||
const assetGovernanceMatch = pathname.match(/^\/api\/assets\/([^/]+)\/governance$/);
|
||||
if (req.method === "GET" && assetGovernanceMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, listAssetGovernanceReviews(context, decodeURIComponent(assetGovernanceMatch[1])));
|
||||
}
|
||||
|
||||
const assetGovernanceScanMatch = pathname.match(/^\/api\/assets\/([^/]+)\/governance\/scan$/);
|
||||
if (req.method === "POST" && assetGovernanceScanMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, scanAssetGovernance(context, decodeURIComponent(assetGovernanceScanMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
const assetVersionRestoreMatch = pathname.match(/^\/api\/assets\/([^/]+)\/versions\/([^/]+)\/restore$/);
|
||||
if (req.method === "POST" && assetVersionRestoreMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
@@ -2144,6 +2259,20 @@ createServer(async (req, res) => {
|
||||
return send(res, 200, { models: scopedModels(context), runners: runtimeRunners() });
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/platform/model-catalog") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:read");
|
||||
else requirePermission(context, "model:manage");
|
||||
return send(res, 200, { catalog: scopedModelCatalog(context), connectors: scopedModels(context) });
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/platform/model-routes") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:read");
|
||||
else requirePermission(context, "model:manage");
|
||||
return send(res, 200, { routes: scopedModelRoutes(context), catalog: scopedModelCatalog(context) });
|
||||
}
|
||||
|
||||
const modelConnectorMatch = pathname.match(/^\/api\/platform\/models\/([^/]+)$/);
|
||||
if (req.method === "PATCH" && modelConnectorMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
@@ -2396,6 +2525,23 @@ createServer(async (req, res) => {
|
||||
return send(res, 200, { featureFlags: featureFlags() });
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/system/plan-templates") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
requirePermission(context, "system:settings:view");
|
||||
return send(res, 200, { planTemplates: subscriptionPlanTemplates({ includeArchived: true }) });
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/system/plan-templates") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, createSubscriptionPlanTemplate(context, await readBody(req)));
|
||||
}
|
||||
|
||||
const planTemplateMatch = pathname.match(/^\/api\/system\/plan-templates\/([^/]+)$/);
|
||||
if (req.method === "PATCH" && planTemplateMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, updateSubscriptionPlanTemplate(context, decodeURIComponent(planTemplateMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/system/feature-flags") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, { featureFlags: updateFeatureFlag(context, await readBody(req)) });
|
||||
@@ -2457,6 +2603,154 @@ createServer(async (req, res) => {
|
||||
return send(res, 201, { model, models: scopedModels(context) });
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/platform/model-catalog") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:write");
|
||||
const entry = createModelCatalogEntry(context, await readBody(req));
|
||||
return send(res, 201, { entry, catalog: scopedModelCatalog(context) });
|
||||
}
|
||||
|
||||
const modelCatalogMatch = pathname.match(/^\/api\/platform\/model-catalog\/([^/]+)$/);
|
||||
if (req.method === "PATCH" && modelCatalogMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:write");
|
||||
const entry = updateModelCatalogEntry(context, decodeURIComponent(modelCatalogMatch[1]), await readBody(req));
|
||||
return send(res, 200, { entry, catalog: scopedModelCatalog(context) });
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/platform/model-routes") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:write");
|
||||
const route = createModelRoute(context, await readBody(req));
|
||||
return send(res, 201, { route, routes: scopedModelRoutes(context) });
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/platform/model-routes/resolve") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:read");
|
||||
return send(res, 200, { resolution: previewModelRouteResolution(context, await readBody(req)) });
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/platform/model-route-approvals") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:read");
|
||||
return send(res, 200, listModelRouteApprovalRequests(context, {
|
||||
status: url.searchParams.get("status") || "",
|
||||
mine: url.searchParams.get("mine") === "1",
|
||||
limit: url.searchParams.get("limit") || 80
|
||||
}));
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/platform/model-route-approvals") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:write");
|
||||
return send(res, 201, requestModelRouteApproval(context, await readBody(req)));
|
||||
}
|
||||
|
||||
const modelRouteApprovalDecisionMatch = pathname.match(/^\/api\/platform\/model-route-approvals\/([^/]+)\/decision$/);
|
||||
if (req.method === "POST" && modelRouteApprovalDecisionMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:write");
|
||||
return send(res, 200, decideModelRouteApproval(context, decodeURIComponent(modelRouteApprovalDecisionMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
const modelRouteMatch = pathname.match(/^\/api\/platform\/model-routes\/([^/]+)$/);
|
||||
if (req.method === "PATCH" && modelRouteMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "models:write");
|
||||
const route = updateModelRoute(context, decodeURIComponent(modelRouteMatch[1]), await readBody(req));
|
||||
return send(res, 200, { route, routes: scopedModelRoutes(context) });
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/knowledge/library") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, listKnowledgeDocuments(context));
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/knowledge/library/import") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, importKnowledgeDocument(context, await readBody(req)));
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/knowledge/search") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, searchKnowledge(context, {
|
||||
query: url.searchParams.get("q") || url.searchParams.get("query") || "",
|
||||
sourceType: url.searchParams.get("sourceType") || url.searchParams.get("source_type") || "all",
|
||||
scopeMode: url.searchParams.get("scopeMode") || url.searchParams.get("scope_mode") || "all",
|
||||
limit: url.searchParams.get("limit") || 24
|
||||
}));
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/knowledge/context-packs") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, listKnowledgeContextPacks(context));
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/knowledge/context-packs") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, createKnowledgeContextPack(context, await readBody(req)));
|
||||
}
|
||||
|
||||
const knowledgePackMatch = pathname.match(/^\/api\/knowledge\/context-packs\/([^/]+)$/);
|
||||
if (req.method === "GET" && knowledgePackMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, getKnowledgeContextPack(context, decodeURIComponent(knowledgePackMatch[1])));
|
||||
}
|
||||
|
||||
const knowledgePackMaterializeMatch = pathname.match(/^\/api\/knowledge\/context-packs\/([^/]+)\/materialize$/);
|
||||
if (req.method === "POST" && knowledgePackMaterializeMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, materializeKnowledgeContextPack(context, decodeURIComponent(knowledgePackMaterializeMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
const knowledgeDocumentMatch = pathname.match(/^\/api\/knowledge\/library\/([^/]+)$/);
|
||||
if (req.method === "GET" && knowledgeDocumentMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, getKnowledgeDocument(context, decodeURIComponent(knowledgeDocumentMatch[1])));
|
||||
}
|
||||
|
||||
if (req.method === "PATCH" && knowledgeDocumentMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, updateKnowledgeDocument(context, decodeURIComponent(knowledgeDocumentMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
const knowledgeReviewMatch = pathname.match(/^\/api\/knowledge\/library\/([^/]+)\/review$/);
|
||||
if (req.method === "POST" && knowledgeReviewMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, reviewKnowledgeDocument(context, decodeURIComponent(knowledgeReviewMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
const knowledgeArchiveMatch = pathname.match(/^\/api\/knowledge\/library\/([^/]+)\/archive$/);
|
||||
if (req.method === "POST" && knowledgeArchiveMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, archiveKnowledgeDocument(context, decodeURIComponent(knowledgeArchiveMatch[1])));
|
||||
}
|
||||
|
||||
const knowledgeRestoreMatch = pathname.match(/^\/api\/knowledge\/library\/([^/]+)\/restore$/);
|
||||
if (req.method === "POST" && knowledgeRestoreMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, restoreKnowledgeDocument(context, decodeURIComponent(knowledgeRestoreMatch[1])));
|
||||
}
|
||||
|
||||
const knowledgeVersionsMatch = pathname.match(/^\/api\/knowledge\/library\/([^/]+)\/versions$/);
|
||||
if (req.method === "GET" && knowledgeVersionsMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, listKnowledgeDocumentVersions(context, decodeURIComponent(knowledgeVersionsMatch[1])));
|
||||
}
|
||||
|
||||
const knowledgeVersionRestoreMatch = pathname.match(/^\/api\/knowledge\/library\/([^/]+)\/versions\/([^/]+)\/restore$/);
|
||||
if (req.method === "POST" && knowledgeVersionRestoreMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, restoreKnowledgeDocumentVersion(context, decodeURIComponent(knowledgeVersionRestoreMatch[1]), decodeURIComponent(knowledgeVersionRestoreMatch[2])));
|
||||
}
|
||||
|
||||
const knowledgeMaterializeMatch = pathname.match(/^\/api\/knowledge\/library\/([^/]+)\/materialize$/);
|
||||
if (req.method === "POST" && knowledgeMaterializeMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, materializeKnowledgeDocument(context, decodeURIComponent(knowledgeMaterializeMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
if (req.method === "GET" && pathname === "/api/qa") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
requirePermission(context, "qa:review");
|
||||
@@ -2581,6 +2875,16 @@ createServer(async (req, res) => {
|
||||
return send(res, 201, createDelivery(context, await readBody(req)));
|
||||
}
|
||||
|
||||
const deliveryClearanceMatch = pathname.match(/^\/api\/production\/deliveries\/([^/]+)\/clearance$/);
|
||||
if (req.method === "GET" && deliveryClearanceMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 200, getDeliveryClearance(context, decodeURIComponent(deliveryClearanceMatch[1])));
|
||||
}
|
||||
if (req.method === "POST" && deliveryClearanceMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
return send(res, 201, runDeliveryClearance(context, decodeURIComponent(deliveryClearanceMatch[1]), await readBody(req)));
|
||||
}
|
||||
|
||||
const deliveryReleasesMatch = pathname.match(/^\/api\/production\/deliveries\/([^/]+)\/releases$/);
|
||||
if (req.method === "GET" && deliveryReleasesMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
@@ -2659,6 +2963,12 @@ createServer(async (req, res) => {
|
||||
return send(res, 201, await createGenerationJob(context, await readBody(req)));
|
||||
}
|
||||
|
||||
if (req.method === "POST" && pathname === "/api/jobs/preview") {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
if (context.apiClient) requireApiScope(context, "jobs:read");
|
||||
return send(res, 200, previewGenerationJob(context, await readBody(req)));
|
||||
}
|
||||
|
||||
const jobDetailMatch = pathname.match(/^\/api\/jobs\/([^/]+)$/);
|
||||
if (req.method === "GET" && jobDetailMatch) {
|
||||
const context = resolveContext(req.headers, url.searchParams);
|
||||
|
||||
Reference in New Issue
Block a user