feat: OpsxAPI v1.0 全协议接口工作台

- 六协议调试:HTTP/GraphQL/WebSocket/SSE/Socket.IO/MQTT
- 集合树/多标签/多环境变量/历史记录,localStorage 持久化
- 导入:OpenAPI 2/3/3.1、Postman、Apifox、Apipost、cURL、HAR、
  Insomnia、Hoppscotch、Markdown(自动识别,21 个单测覆盖)
- 导出:OpenAPI 3.0 JSON/YAML、Postman v2.1、Markdown 文档、
  全量备份;单请求生成 cURL/fetch/Python/Go 代码
- Gin 服务:HTTP 代理、NDJSON 流式代理(SSE 透传)、
  WebSocket 隧道(浏览器自定义握手 Header)
- 内置产品官网 /home 与用户文档站 /docs(SSR,随二进制交付)
- lucide 图标体系 + 品牌标识(Logo/favicon),明暗双主题
- 环境变量:彩色徽标切换器、管理弹窗、{{ 自动补全
This commit is contained in:
logan committed 2026-09-18 00:16:59 +08:00
commit dec66db5aa
182 files changed
+23377

No files matched your search

+250
View File
@@ -0,0 +1,250 @@
/**
* OpsxAPI 集合数据模型 —— UI / 导入导出 / 持久化共用的唯一事实源。
*/
export type RequestMethod =
| "GET"
| "POST"
| "PUT"
| "PATCH"
| "DELETE"
| "HEAD"
| "OPTIONS";
export const REQUEST_METHODS: RequestMethod[] = [
"GET",
"POST",
"PUT",
"PATCH",
"DELETE",
"HEAD",
"OPTIONS",
];
export type RequestKind = "http" | "graphql" | "websocket" | "sse" | "socketio" | "mqtt";
export const REQUEST_KINDS: RequestKind[] = [
"http",
"graphql",
"websocket",
"sse",
"socketio",
"mqtt",
];
export const KIND_LABELS: Record<RequestKind, string> = {
http: "HTTP",
graphql: "GraphQL",
websocket: "WebSocket",
sse: "SSE",
socketio: "Socket.IO",
mqtt: "MQTT",
};
/** 键值行:Params / Headers / Cookies / 表单 / 环境变量共用 */
export type KVRow = {
id: string;
key: string;
value: string;
enabled: boolean;
description?: string;
};
export type BodyMode =
| "none"
| "json"
| "xml"
| "text"
| "html"
| "urlencoded"
| "form-data";
export const BODY_MODE_LABELS: Record<BodyMode, string> = {
none: "none",
json: "JSON",
xml: "XML",
text: "Text",
html: "HTML",
urlencoded: "x-www-form-urlencoded",
"form-data": "form-data",
};
export const RAW_BODY_MODES: BodyMode[] = ["json", "xml", "text", "html"];
export type AuthMode = "none" | "bearer" | "basic" | "apikey";
export type AuthConfig = {
mode: AuthMode;
bearer?: string;
basicUser?: string;
basicPass?: string;
apiKeyName?: string;
apiKeyValue?: string;
apiKeyIn?: "header" | "query";
};
export type HttpRequestData = {
kind: "http";
method: RequestMethod;
url: string;
query: KVRow[];
headers: KVRow[];
cookies: KVRow[];
auth: AuthConfig;
bodyMode: BodyMode;
bodyRaw: string;
bodyForm: KVRow[];
};
export type GraphqlRequestData = {
kind: "graphql";
url: string;
headers: KVRow[];
auth: AuthConfig;
query: string;
variables: string;
};
export type WebsocketRequestData = {
kind: "websocket";
url: string;
headers: KVRow[];
protocols: string;
};
export type SseRequestData = {
kind: "sse";
url: string;
headers: KVRow[];
query: KVRow[];
auth: AuthConfig;
lastEventId: string;
/** 仅解析 data: 行为 JSON */
dataOnly: boolean;
};
export type SocketIoRequestData = {
kind: "socketio";
url: string;
/** 额外连接参数(JSON:auth / transports / path …) */
options: string;
/** 监听的服务端事件名 */
listenEvents: string[];
sendEvent: string;
sendArgs: string;
};
export type MqttRequestData = {
kind: "mqtt";
brokerUrl: string;
username: string;
password: string;
clientId: string;
/** 订阅主题,逗号分隔 */
subscribeTopics: string;
publishTopic: string;
publishPayload: string;
qos: 0 | 1 | 2;
};
export type RequestData =
| HttpRequestData
| GraphqlRequestData
| WebsocketRequestData
| SseRequestData
| SocketIoRequestData
| MqttRequestData;
export type RequestDataOf<K extends RequestKind> = Extract<RequestData, { kind: K }>;
export type RequestNodeBase = {
id: string;
type: "request";
name: string;
description?: string;
createdAt: number;
updatedAt: number;
};
export type HttpApiNode = RequestNodeBase & { kind: "http"; data: HttpRequestData };
export type GraphqlApiNode = RequestNodeBase & { kind: "graphql"; data: GraphqlRequestData };
export type WebsocketApiNode = RequestNodeBase & { kind: "websocket"; data: WebsocketRequestData };
export type SseApiNode = RequestNodeBase & { kind: "sse"; data: SseRequestData };
export type SocketIoApiNode = RequestNodeBase & { kind: "socketio"; data: SocketIoRequestData };
export type MqttApiNode = RequestNodeBase & { kind: "mqtt"; data: MqttRequestData };
/** 判别联合:`node.kind === "http"` 时自动收窄 data 类型 */
export type RequestNode =
| HttpApiNode
| GraphqlApiNode
| WebsocketApiNode
| SseApiNode
| SocketIoApiNode
| MqttApiNode;
export type FolderNode = {
id: string;
type: "folder";
name: string;
description?: string;
children: CollectionNode[];
createdAt: number;
updatedAt: number;
};
export type CollectionNode = RequestNode | FolderNode;
/** 一个集合(项目),本身是一个顶层目录 */
export type Collection = {
id: string;
type: "collection";
name: string;
description?: string;
children: CollectionNode[];
createdAt: number;
updatedAt: number;
};
export type Environment = {
id: string;
name: string;
variables: KVRow[];
};
export type HistoryEntry = {
id: string;
at: number;
kind: RequestKind;
label: string;
method?: RequestMethod;
url: string;
status?: number;
durationMs?: number;
ok: boolean;
/** 关联的请求节点 id(可回跳) */
nodeId?: string;
};
export type ThemeMode = "dark" | "light";
export type WorkspaceSettings = {
theme: ThemeMode;
/** 请求超时(毫秒) */
timeoutMs: number;
};
export type WorkspaceData = {
version: 2;
collections: Collection[];
environments: Environment[];
activeEnvironmentId: string | null;
history: HistoryEntry[];
settings: WorkspaceSettings;
};
/** 导入结果:集合 + 建议写入环境的前缀变量(如 baseUrl) */
export type ImportResult = {
collections: Collection[];
environmentVariables: { key: string; value: string }[];
warnings: string[];
};