@tx5dr/contracts 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +41 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +86 -0
- package/dist/index.js.map +1 -0
- package/dist/schema/__tests__/plugin-runtime-log.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/plugin-runtime-log.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/plugin-runtime-log.schema.test.js +78 -0
- package/dist/schema/__tests__/plugin-runtime-log.schema.test.js.map +1 -0
- package/dist/schema/ability.d.ts +85 -0
- package/dist/schema/ability.d.ts.map +1 -0
- package/dist/schema/ability.js +119 -0
- package/dist/schema/ability.js.map +1 -0
- package/dist/schema/api-response.schema.d.ts +1017 -0
- package/dist/schema/api-response.schema.d.ts.map +1 -0
- package/dist/schema/api-response.schema.js +137 -0
- package/dist/schema/api-response.schema.js.map +1 -0
- package/dist/schema/audio-sidecar.schema.d.ts +106 -0
- package/dist/schema/audio-sidecar.schema.d.ts.map +1 -0
- package/dist/schema/audio-sidecar.schema.js +47 -0
- package/dist/schema/audio-sidecar.schema.js.map +1 -0
- package/dist/schema/audio.schema.d.ts +227 -0
- package/dist/schema/audio.schema.d.ts.map +1 -0
- package/dist/schema/audio.schema.js +56 -0
- package/dist/schema/audio.schema.js.map +1 -0
- package/dist/schema/auth.schema.d.ts +738 -0
- package/dist/schema/auth.schema.d.ts.map +1 -0
- package/dist/schema/auth.schema.js +161 -0
- package/dist/schema/auth.schema.js.map +1 -0
- package/dist/schema/cycle.schema.d.ts +33 -0
- package/dist/schema/cycle.schema.d.ts.map +1 -0
- package/dist/schema/cycle.schema.js +23 -0
- package/dist/schema/cycle.schema.js.map +1 -0
- package/dist/schema/desktop-https.schema.d.ts +146 -0
- package/dist/schema/desktop-https.schema.d.ts.map +1 -0
- package/dist/schema/desktop-https.schema.js +31 -0
- package/dist/schema/desktop-https.schema.js.map +1 -0
- package/dist/schema/ft8.schema.d.ts +1107 -0
- package/dist/schema/ft8.schema.d.ts.map +1 -0
- package/dist/schema/ft8.schema.js +268 -0
- package/dist/schema/ft8.schema.js.map +1 -0
- package/dist/schema/hello.schema.d.ts +10 -0
- package/dist/schema/hello.schema.d.ts.map +1 -0
- package/dist/schema/hello.schema.js +5 -0
- package/dist/schema/hello.schema.js.map +1 -0
- package/dist/schema/logbook.schema.d.ts +1472 -0
- package/dist/schema/logbook.schema.d.ts.map +1 -0
- package/dist/schema/logbook.schema.js +256 -0
- package/dist/schema/logbook.schema.js.map +1 -0
- package/dist/schema/mode.schema.d.ts +130 -0
- package/dist/schema/mode.schema.d.ts.map +1 -0
- package/dist/schema/mode.schema.js +141 -0
- package/dist/schema/mode.schema.js.map +1 -0
- package/dist/schema/openwebrx.schema.d.ts +352 -0
- package/dist/schema/openwebrx.schema.d.ts.map +1 -0
- package/dist/schema/openwebrx.schema.js +139 -0
- package/dist/schema/openwebrx.schema.js.map +1 -0
- package/dist/schema/operator.schema.d.ts +883 -0
- package/dist/schema/operator.schema.d.ts.map +1 -0
- package/dist/schema/operator.schema.js +97 -0
- package/dist/schema/operator.schema.js.map +1 -0
- package/dist/schema/plugin.schema.d.ts +1579 -0
- package/dist/schema/plugin.schema.d.ts.map +1 -0
- package/dist/schema/plugin.schema.js +336 -0
- package/dist/schema/plugin.schema.js.map +1 -0
- package/dist/schema/process-monitor.schema.d.ts +282 -0
- package/dist/schema/process-monitor.schema.d.ts.map +1 -0
- package/dist/schema/process-monitor.schema.js +31 -0
- package/dist/schema/process-monitor.schema.js.map +1 -0
- package/dist/schema/pskreporter.schema.d.ts +185 -0
- package/dist/schema/pskreporter.schema.d.ts.map +1 -0
- package/dist/schema/pskreporter.schema.js +80 -0
- package/dist/schema/pskreporter.schema.js.map +1 -0
- package/dist/schema/qso.schema.d.ts +419 -0
- package/dist/schema/qso.schema.d.ts.map +1 -0
- package/dist/schema/qso.schema.js +137 -0
- package/dist/schema/qso.schema.js.map +1 -0
- package/dist/schema/radio-capability.schema.d.ts +625 -0
- package/dist/schema/radio-capability.schema.d.ts.map +1 -0
- package/dist/schema/radio-capability.schema.js +191 -0
- package/dist/schema/radio-capability.schema.js.map +1 -0
- package/dist/schema/radio-power-support.d.ts +45 -0
- package/dist/schema/radio-power-support.d.ts.map +1 -0
- package/dist/schema/radio-power-support.js +64 -0
- package/dist/schema/radio-power-support.js.map +1 -0
- package/dist/schema/radio-power.schema.d.ts +319 -0
- package/dist/schema/radio-power.schema.d.ts.map +1 -0
- package/dist/schema/radio-power.schema.js +87 -0
- package/dist/schema/radio-power.schema.js.map +1 -0
- package/dist/schema/radio-profile.schema.d.ts +2694 -0
- package/dist/schema/radio-profile.schema.d.ts.map +1 -0
- package/dist/schema/radio-profile.schema.js +71 -0
- package/dist/schema/radio-profile.schema.js.map +1 -0
- package/dist/schema/radio.schema.d.ts +1438 -0
- package/dist/schema/radio.schema.d.ts.map +1 -0
- package/dist/schema/radio.schema.js +314 -0
- package/dist/schema/radio.schema.js.map +1 -0
- package/dist/schema/realtime.schema.d.ts +1299 -0
- package/dist/schema/realtime.schema.d.ts.map +1 -0
- package/dist/schema/realtime.schema.js +184 -0
- package/dist/schema/realtime.schema.js.map +1 -0
- package/dist/schema/rigctld.schema.d.ts +151 -0
- package/dist/schema/rigctld.schema.d.ts.map +1 -0
- package/dist/schema/rigctld.schema.js +42 -0
- package/dist/schema/rigctld.schema.js.map +1 -0
- package/dist/schema/server-message-keys.d.ts +21 -0
- package/dist/schema/server-message-keys.d.ts.map +1 -0
- package/dist/schema/server-message-keys.js +22 -0
- package/dist/schema/server-message-keys.js.map +1 -0
- package/dist/schema/slot-info.schema.d.ts +696 -0
- package/dist/schema/slot-info.schema.d.ts.map +1 -0
- package/dist/schema/slot-info.schema.js +144 -0
- package/dist/schema/slot-info.schema.js.map +1 -0
- package/dist/schema/spectrum.schema.d.ts +716 -0
- package/dist/schema/spectrum.schema.d.ts.map +1 -0
- package/dist/schema/spectrum.schema.js +128 -0
- package/dist/schema/spectrum.schema.js.map +1 -0
- package/dist/schema/station-info.schema.d.ts +202 -0
- package/dist/schema/station-info.schema.d.ts.map +1 -0
- package/dist/schema/station-info.schema.js +32 -0
- package/dist/schema/station-info.schema.js.map +1 -0
- package/dist/schema/system.schema.d.ts +113 -0
- package/dist/schema/system.schema.d.ts.map +1 -0
- package/dist/schema/system.schema.js +95 -0
- package/dist/schema/system.schema.js.map +1 -0
- package/dist/schema/transmission.schema.d.ts +413 -0
- package/dist/schema/transmission.schema.d.ts.map +1 -0
- package/dist/schema/transmission.schema.js +78 -0
- package/dist/schema/transmission.schema.js.map +1 -0
- package/dist/schema/voice.schema.d.ts +38 -0
- package/dist/schema/voice.schema.d.ts.map +1 -0
- package/dist/schema/voice.schema.js +24 -0
- package/dist/schema/voice.schema.js.map +1 -0
- package/dist/schema/websocket.schema.d.ts +16916 -0
- package/dist/schema/websocket.schema.d.ts.map +1 -0
- package/dist/schema/websocket.schema.js +1050 -0
- package/dist/schema/websocket.schema.js.map +1 -0
- package/dist/utils/grid.d.ts +13 -0
- package/dist/utils/grid.d.ts.map +1 -0
- package/dist/utils/grid.js +71 -0
- package/dist/utils/grid.js.map +1 -0
- package/eslint.config.js +15 -0
- package/package.json +28 -0
- package/src/index.ts +152 -0
- package/src/schema/__tests__/plugin-runtime-log.schema.test.ts +90 -0
- package/src/schema/ability.ts +205 -0
- package/src/schema/api-response.schema.ts +187 -0
- package/src/schema/audio-sidecar.schema.ts +53 -0
- package/src/schema/audio.schema.ts +73 -0
- package/src/schema/auth.schema.ts +224 -0
- package/src/schema/cycle.schema.ts +25 -0
- package/src/schema/desktop-https.schema.ts +43 -0
- package/src/schema/ft8.schema.ts +362 -0
- package/src/schema/hello.schema.ts +7 -0
- package/src/schema/logbook.schema.ts +310 -0
- package/src/schema/mode.schema.ts +158 -0
- package/src/schema/openwebrx.schema.ts +160 -0
- package/src/schema/operator.schema.ts +112 -0
- package/src/schema/plugin.schema.ts +503 -0
- package/src/schema/process-monitor.schema.ts +41 -0
- package/src/schema/pskreporter.schema.ts +102 -0
- package/src/schema/qso.schema.ts +173 -0
- package/src/schema/radio-capability.schema.ts +244 -0
- package/src/schema/radio-power-support.ts +107 -0
- package/src/schema/radio-power.schema.ts +101 -0
- package/src/schema/radio-profile.schema.ts +91 -0
- package/src/schema/radio.schema.ts +385 -0
- package/src/schema/realtime.schema.ts +262 -0
- package/src/schema/rigctld.schema.ts +51 -0
- package/src/schema/server-message-keys.ts +26 -0
- package/src/schema/slot-info.schema.ts +162 -0
- package/src/schema/spectrum.schema.ts +166 -0
- package/src/schema/station-info.schema.ts +40 -0
- package/src/schema/system.schema.ts +121 -0
- package/src/schema/transmission.schema.ts +105 -0
- package/src/schema/voice.schema.ts +28 -0
- package/src/schema/websocket.schema.ts +1480 -0
- package/src/utils/grid.ts +94 -0
- package/test/mode-schema.test.ts +48 -0
- package/tsconfig.json +9 -0
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import {
|
|
3
|
+
HamlibConfigSchema,
|
|
4
|
+
RadioInfoSchema,
|
|
5
|
+
RadioConnectionStatusSchema,
|
|
6
|
+
PresetFrequencySchema,
|
|
7
|
+
SupportedRigSchema,
|
|
8
|
+
SerialPortSchema,
|
|
9
|
+
TunerCapabilitiesSchema,
|
|
10
|
+
TunerStatusSchema,
|
|
11
|
+
} from './radio.schema.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* API 响应基础类型定义
|
|
15
|
+
* 用于统一 API 响应格式
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
// ========== 通用响应 Schema ==========
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 基础成功响应
|
|
22
|
+
*/
|
|
23
|
+
export const BaseSuccessResponseSchema = z.object({
|
|
24
|
+
success: z.literal(true),
|
|
25
|
+
message: z.string().optional(),
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 基础错误响应
|
|
30
|
+
*/
|
|
31
|
+
export const BaseErrorResponseSchema = z.object({
|
|
32
|
+
success: z.literal(false),
|
|
33
|
+
message: z.string(),
|
|
34
|
+
code: z.string().optional(),
|
|
35
|
+
userMessage: z.string().optional(),
|
|
36
|
+
suggestions: z.array(z.string()).optional(),
|
|
37
|
+
severity: z.enum(['info', 'warning', 'error', 'critical']).optional(),
|
|
38
|
+
context: z.record(z.any()).optional(),
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// ========== 电台配置相关响应 ==========
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 获取电台配置响应 (已存在于 radio.schema.ts)
|
|
45
|
+
* RadioConfigResponseSchema
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* 更新电台配置响应
|
|
50
|
+
*/
|
|
51
|
+
export const UpdateRadioConfigResponseSchema = z.object({
|
|
52
|
+
success: z.boolean(),
|
|
53
|
+
config: HamlibConfigSchema,
|
|
54
|
+
message: z.string().optional(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 支持的电台列表响应 (已存在于 radio.schema.ts)
|
|
59
|
+
* SupportedRigsResponseSchema
|
|
60
|
+
*/
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* 串口列表响应 (已存在于 radio.schema.ts)
|
|
64
|
+
* SerialPortsResponseSchema
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* 电台测试响应 (已存在于 radio.schema.ts)
|
|
69
|
+
* TestResponseSchema
|
|
70
|
+
*/
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* 电台状态响应
|
|
74
|
+
*/
|
|
75
|
+
export const RadioStatusResponseSchema = z.object({
|
|
76
|
+
success: z.boolean(),
|
|
77
|
+
status: z.object({
|
|
78
|
+
connected: z.boolean(),
|
|
79
|
+
connectionStatus: RadioConnectionStatusSchema.optional(),
|
|
80
|
+
radioInfo: RadioInfoSchema.nullable(),
|
|
81
|
+
radioConfig: HamlibConfigSchema.optional(),
|
|
82
|
+
connectionHealth: z.object({
|
|
83
|
+
connectionHealthy: z.boolean(),
|
|
84
|
+
}).optional(),
|
|
85
|
+
}),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* 连接电台响应
|
|
90
|
+
*/
|
|
91
|
+
export const ConnectRadioResponseSchema = z.object({
|
|
92
|
+
success: z.boolean(),
|
|
93
|
+
message: z.string(),
|
|
94
|
+
radioInfo: RadioInfoSchema.optional(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* 断开电台响应
|
|
99
|
+
*/
|
|
100
|
+
export const DisconnectRadioResponseSchema = z.object({
|
|
101
|
+
success: z.boolean(),
|
|
102
|
+
message: z.string(),
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// ========== 频率管理相关响应 ==========
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* 预设频率列表响应 (已存在于 radio.schema.ts)
|
|
109
|
+
* FrequencyListResponseSchema
|
|
110
|
+
*/
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* 最后使用频率响应
|
|
114
|
+
*/
|
|
115
|
+
export const LastFrequencyResponseSchema = z.object({
|
|
116
|
+
success: z.boolean(),
|
|
117
|
+
lastFrequency: PresetFrequencySchema.nullable(),
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* 设置频率响应
|
|
122
|
+
*/
|
|
123
|
+
export const SetFrequencyResponseSchema = z.object({
|
|
124
|
+
success: z.boolean(),
|
|
125
|
+
message: z.string(),
|
|
126
|
+
frequency: z.number(),
|
|
127
|
+
mode: z.string().optional(),
|
|
128
|
+
band: z.string().optional(),
|
|
129
|
+
radioMode: z.string().optional(),
|
|
130
|
+
radioConnected: z.boolean().optional(),
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// ========== 时隙包相关响应 ==========
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* 时隙包列表响应
|
|
137
|
+
*/
|
|
138
|
+
export const SlotPacksResponseSchema = z.object({
|
|
139
|
+
success: z.boolean(),
|
|
140
|
+
data: z.array(z.any()), // 使用 SlotPackSchema,但这里暂时用 any 避免循环依赖
|
|
141
|
+
timestamp: z.number(),
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 单个时隙包响应
|
|
146
|
+
*/
|
|
147
|
+
export const SlotPackResponseSchema = z.object({
|
|
148
|
+
success: z.boolean(),
|
|
149
|
+
data: z.any(), // 使用 SlotPackSchema
|
|
150
|
+
timestamp: z.number(),
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 时隙包统计响应
|
|
155
|
+
*/
|
|
156
|
+
export const SlotPackStatsResponseSchema = z.object({
|
|
157
|
+
success: z.boolean(),
|
|
158
|
+
data: z.object({
|
|
159
|
+
activeSlotPacks: z.number(),
|
|
160
|
+
totalProcessed: z.number(),
|
|
161
|
+
totalFrames: z.number(),
|
|
162
|
+
averageFramesPerSlot: z.number(),
|
|
163
|
+
lastActivity: z.number(),
|
|
164
|
+
}),
|
|
165
|
+
timestamp: z.number(),
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// ========== 天调相关响应 (已存在于 radio.schema.ts) ==========
|
|
169
|
+
// TunerCapabilitiesResponseSchema
|
|
170
|
+
// TunerStatusResponseSchema
|
|
171
|
+
|
|
172
|
+
// ========== 导出 TypeScript 类型 ==========
|
|
173
|
+
|
|
174
|
+
export type BaseSuccessResponse = z.infer<typeof BaseSuccessResponseSchema>;
|
|
175
|
+
export type BaseErrorResponse = z.infer<typeof BaseErrorResponseSchema>;
|
|
176
|
+
|
|
177
|
+
export type UpdateRadioConfigResponse = z.infer<typeof UpdateRadioConfigResponseSchema>;
|
|
178
|
+
export type RadioStatusResponse = z.infer<typeof RadioStatusResponseSchema>;
|
|
179
|
+
export type ConnectRadioResponse = z.infer<typeof ConnectRadioResponseSchema>;
|
|
180
|
+
export type DisconnectRadioResponse = z.infer<typeof DisconnectRadioResponseSchema>;
|
|
181
|
+
|
|
182
|
+
export type LastFrequencyResponse = z.infer<typeof LastFrequencyResponseSchema>;
|
|
183
|
+
export type SetFrequencyResponse = z.infer<typeof SetFrequencyResponseSchema>;
|
|
184
|
+
|
|
185
|
+
export type SlotPacksResponse = z.infer<typeof SlotPacksResponseSchema>;
|
|
186
|
+
export type SlotPackResponse = z.infer<typeof SlotPackResponseSchema>;
|
|
187
|
+
export type SlotPackStatsResponse = z.infer<typeof SlotPackStatsResponseSchema>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* AudioSidecar 状态:描述本地音频子系统的旁路生命周期。
|
|
5
|
+
* 与电台 CAT 连接解耦——主引擎已进入 RUNNING,但音频可能仍在重试或已停用。
|
|
6
|
+
*/
|
|
7
|
+
export enum AudioSidecarStatus {
|
|
8
|
+
/** 引擎未运行或 sidecar 未启动 */
|
|
9
|
+
IDLE = 'idle',
|
|
10
|
+
/** 首次尝试启动音频流中 */
|
|
11
|
+
CONNECTING = 'connecting',
|
|
12
|
+
/** 音频输入/输出/监听均已就绪 */
|
|
13
|
+
CONNECTED = 'connected',
|
|
14
|
+
/** 上一次尝试失败,等待下一次退避重试 */
|
|
15
|
+
RETRYING = 'retrying',
|
|
16
|
+
/** 配置错误或不可恢复的失败,已停止重试 */
|
|
17
|
+
DISABLED = 'disabled',
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export const AudioSidecarStatusSchema = z.nativeEnum(AudioSidecarStatus);
|
|
21
|
+
|
|
22
|
+
export const AudioSidecarErrorSchema = z.object({
|
|
23
|
+
code: z.string().optional(),
|
|
24
|
+
message: z.string(),
|
|
25
|
+
userMessage: z.string().optional(),
|
|
26
|
+
userMessageKey: z.string().optional(),
|
|
27
|
+
userMessageParams: z.record(z.union([z.string(), z.number()])).optional(),
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
export type AudioSidecarError = z.infer<typeof AudioSidecarErrorSchema>;
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* AudioSidecar 状态变化载荷。
|
|
34
|
+
* 前端以此渲染 RadioControl 中的音频副状态 spinner + Popover。
|
|
35
|
+
*/
|
|
36
|
+
export const AudioSidecarStatusPayloadSchema = z.object({
|
|
37
|
+
/** 当前状态 */
|
|
38
|
+
status: AudioSidecarStatusSchema,
|
|
39
|
+
/** 音频输入/输出是否已就绪可用 */
|
|
40
|
+
isConnected: z.boolean(),
|
|
41
|
+
/** 当前重试尝试计数(0 表示首次未重试过) */
|
|
42
|
+
retryAttempt: z.number().int().min(0),
|
|
43
|
+
/** 下次重试延迟 ms(仅 RETRYING 有值) */
|
|
44
|
+
nextRetryMs: z.number().int().min(0).nullable(),
|
|
45
|
+
/** 连续失败超过阈值时置 true,用于前端提示"长时间未就绪" */
|
|
46
|
+
longRunning: z.boolean(),
|
|
47
|
+
/** 最近一次错误摘要 */
|
|
48
|
+
lastError: AudioSidecarErrorSchema.nullable(),
|
|
49
|
+
/** 当前目标设备名(来自 AudioConfig.inputDeviceName) */
|
|
50
|
+
deviceName: z.string().nullable(),
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type AudioSidecarStatusPayload = z.infer<typeof AudioSidecarStatusPayloadSchema>;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
// 音频设备信息
|
|
4
|
+
export const AudioDeviceSchema = z.object({
|
|
5
|
+
id: z.string(),
|
|
6
|
+
name: z.string(),
|
|
7
|
+
isDefault: z.boolean(),
|
|
8
|
+
channels: z.number(),
|
|
9
|
+
sampleRate: z.number(),
|
|
10
|
+
type: z.enum(['input', 'output']),
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
// 音频设备列表响应
|
|
14
|
+
export const AudioDevicesResponseSchema = z.object({
|
|
15
|
+
inputDevices: z.array(AudioDeviceSchema),
|
|
16
|
+
outputDevices: z.array(AudioDeviceSchema),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
// 音频设备设置请求
|
|
20
|
+
export const AudioDeviceSettingsSchema = z.object({
|
|
21
|
+
inputDeviceName: z.string().optional(), // 使用设备名称而非ID
|
|
22
|
+
outputDeviceName: z.string().optional(), // 使用设备名称而非ID
|
|
23
|
+
sampleRate: z.number().optional(),
|
|
24
|
+
bufferSize: z.number().optional(),
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// 音频设备设置响应
|
|
28
|
+
export const AudioDeviceSettingsResponseSchema = z.object({
|
|
29
|
+
success: z.boolean(),
|
|
30
|
+
message: z.string().optional(),
|
|
31
|
+
currentSettings: AudioDeviceSettingsSchema,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
// 音频流配置参数 (用于 Audify/RtAudio)
|
|
35
|
+
export const AudioStreamConfigSchema = z.object({
|
|
36
|
+
channelCount: z.number().int().min(1).max(32),
|
|
37
|
+
sampleFormat: z.number().int(), // RtAudio SampleFormat 枚举值
|
|
38
|
+
sampleRate: z.number().int().min(8000).max(192000),
|
|
39
|
+
deviceId: z.string().optional(),
|
|
40
|
+
maxQueue: z.number().int().optional(),
|
|
41
|
+
queueHighWaterMark: z.number().int().optional(),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// 音频流事件数据
|
|
45
|
+
export const AudioStreamEventDataSchema = z.object({
|
|
46
|
+
type: z.enum(['started', 'stopped', 'error', 'audioData']),
|
|
47
|
+
error: z.string().optional(),
|
|
48
|
+
samples: z.number().optional(), // audioData 事件的样本数
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
// 音频混音配置
|
|
52
|
+
export const AudioMixerConfigSchema = z.object({
|
|
53
|
+
operatorId: z.string(),
|
|
54
|
+
audioData: z.instanceof(Float32Array).optional(), // 实际数据在运行时传递
|
|
55
|
+
targetPlaybackTime: z.number(), // 目标播放时间戳 (ms)
|
|
56
|
+
volumeGain: z.number().min(0).max(10).default(1.0),
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
// 音频音量控制
|
|
60
|
+
export const VolumeGainSchema = z.object({
|
|
61
|
+
gain: z.number().min(0.001).max(10), // 线性增益
|
|
62
|
+
gainDb: z.number().min(-60).max(20), // dB 单位增益
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// 导出类型
|
|
66
|
+
export type AudioDevice = z.infer<typeof AudioDeviceSchema>;
|
|
67
|
+
export type AudioDevicesResponse = z.infer<typeof AudioDevicesResponseSchema>;
|
|
68
|
+
export type AudioDeviceSettings = z.infer<typeof AudioDeviceSettingsSchema>;
|
|
69
|
+
export type AudioDeviceSettingsResponse = z.infer<typeof AudioDeviceSettingsResponseSchema>;
|
|
70
|
+
export type AudioStreamConfig = z.infer<typeof AudioStreamConfigSchema>;
|
|
71
|
+
export type AudioStreamEventData = z.infer<typeof AudioStreamEventDataSchema>;
|
|
72
|
+
export type AudioMixerConfig = z.infer<typeof AudioMixerConfigSchema>;
|
|
73
|
+
export type VolumeGain = z.infer<typeof VolumeGainSchema>;
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { PermissionGrantSchema } from './ability.js';
|
|
3
|
+
|
|
4
|
+
// ===== 角色枚举 =====
|
|
5
|
+
|
|
6
|
+
export enum UserRole {
|
|
7
|
+
VIEWER = 'viewer',
|
|
8
|
+
OPERATOR = 'operator',
|
|
9
|
+
ADMIN = 'admin',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
/** 角色权限等级(用于比较) */
|
|
13
|
+
export const USER_ROLE_LEVEL: Record<UserRole, number> = {
|
|
14
|
+
[UserRole.VIEWER]: 0,
|
|
15
|
+
[UserRole.OPERATOR]: 1,
|
|
16
|
+
[UserRole.ADMIN]: 2,
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// ===== Token 数据结构(持久化) =====
|
|
20
|
+
|
|
21
|
+
export const AuthTokenSchema = z.object({
|
|
22
|
+
id: z.string(),
|
|
23
|
+
tokenHash: z.string(),
|
|
24
|
+
tokenPlain: z.string().optional(), // plaintext stored so admins can retrieve it later
|
|
25
|
+
label: z.string(),
|
|
26
|
+
role: z.nativeEnum(UserRole),
|
|
27
|
+
operatorIds: z.array(z.string()),
|
|
28
|
+
createdBy: z.string().nullable(),
|
|
29
|
+
createdAt: z.number(),
|
|
30
|
+
expiresAt: z.number().optional(),
|
|
31
|
+
lastUsedAt: z.number().optional(),
|
|
32
|
+
revoked: z.boolean(),
|
|
33
|
+
system: z.boolean().optional(),
|
|
34
|
+
maxOperators: z.number().min(0).optional(), // 该 Token 可创建的操作员上限
|
|
35
|
+
permissionGrants: z.array(PermissionGrantSchema).optional(), // CASL permission grants
|
|
36
|
+
allowSelfLoginCredential: z.boolean().optional(),
|
|
37
|
+
loginCredential: z.object({
|
|
38
|
+
username: z.string(),
|
|
39
|
+
usernameNormalized: z.string(),
|
|
40
|
+
passwordHash: z.string(),
|
|
41
|
+
updatedAt: z.number(),
|
|
42
|
+
}).optional(),
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
export type AuthToken = z.infer<typeof AuthTokenSchema>;
|
|
46
|
+
|
|
47
|
+
// ===== JWT Payload =====
|
|
48
|
+
|
|
49
|
+
export const JWTPayloadSchema = z.object({
|
|
50
|
+
tokenId: z.string(),
|
|
51
|
+
role: z.nativeEnum(UserRole),
|
|
52
|
+
operatorIds: z.array(z.string()),
|
|
53
|
+
iat: z.number(),
|
|
54
|
+
exp: z.number(),
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
export type JWTPayload = z.infer<typeof JWTPayloadSchema>;
|
|
58
|
+
|
|
59
|
+
// ===== 登录请求/响应 =====
|
|
60
|
+
|
|
61
|
+
export const LoginRequestSchema = z.object({
|
|
62
|
+
token: z.string().min(1),
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
export type LoginRequest = z.infer<typeof LoginRequestSchema>;
|
|
66
|
+
|
|
67
|
+
export const PasswordLoginRequestSchema = z.object({
|
|
68
|
+
username: z.string().trim().min(3).max(32),
|
|
69
|
+
password: z.string().min(1),
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
export type PasswordLoginRequest = z.infer<typeof PasswordLoginRequestSchema>;
|
|
73
|
+
|
|
74
|
+
export const LoginResponseSchema = z.object({
|
|
75
|
+
jwt: z.string(),
|
|
76
|
+
role: z.nativeEnum(UserRole),
|
|
77
|
+
label: z.string(),
|
|
78
|
+
operatorIds: z.array(z.string()),
|
|
79
|
+
maxOperators: z.number().optional(),
|
|
80
|
+
permissionGrants: z.array(PermissionGrantSchema).optional(),
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
export type LoginResponse = z.infer<typeof LoginResponseSchema>;
|
|
84
|
+
|
|
85
|
+
// ===== Token 管理(Admin API) =====
|
|
86
|
+
|
|
87
|
+
export const LoginCredentialSummarySchema = z.object({
|
|
88
|
+
username: z.string(),
|
|
89
|
+
allowSelfService: z.boolean(),
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
export type LoginCredentialSummary = z.infer<typeof LoginCredentialSummarySchema>;
|
|
93
|
+
|
|
94
|
+
export const AuthMeLoginCredentialSchema = z.object({
|
|
95
|
+
configured: z.boolean(),
|
|
96
|
+
username: z.string().nullable(),
|
|
97
|
+
allowSelfService: z.boolean(),
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
export type AuthMeLoginCredential = z.infer<typeof AuthMeLoginCredentialSchema>;
|
|
101
|
+
|
|
102
|
+
export const CreateTokenLoginCredentialRequestSchema = z.object({
|
|
103
|
+
username: z.string().trim().min(3).max(32).regex(/^[A-Za-z0-9._-]+$/),
|
|
104
|
+
password: z.string().min(8).max(128),
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
export type CreateTokenLoginCredentialRequest = z.infer<typeof CreateTokenLoginCredentialRequestSchema>;
|
|
108
|
+
|
|
109
|
+
export const UpdateTokenLoginCredentialRequestSchema = z.object({
|
|
110
|
+
username: z.string().trim().min(3).max(32).regex(/^[A-Za-z0-9._-]+$/),
|
|
111
|
+
password: z.string().min(8).max(128).optional(),
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
export type UpdateTokenLoginCredentialRequest = z.infer<typeof UpdateTokenLoginCredentialRequestSchema>;
|
|
115
|
+
|
|
116
|
+
export const CreateTokenRequestSchema = z.object({
|
|
117
|
+
label: z.string().min(1).max(100),
|
|
118
|
+
role: z.nativeEnum(UserRole),
|
|
119
|
+
operatorIds: z.array(z.string()),
|
|
120
|
+
expiresAt: z.number().optional(),
|
|
121
|
+
maxOperators: z.number().min(0), // 必选,0 表示不限制
|
|
122
|
+
permissionGrants: z.array(PermissionGrantSchema).optional(),
|
|
123
|
+
allowSelfLoginCredential: z.boolean().optional(),
|
|
124
|
+
loginCredential: CreateTokenLoginCredentialRequestSchema.optional(),
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
export type CreateTokenRequest = z.infer<typeof CreateTokenRequestSchema>;
|
|
128
|
+
|
|
129
|
+
export const CreateTokenResponseSchema = z.object({
|
|
130
|
+
id: z.string(),
|
|
131
|
+
token: z.string(),
|
|
132
|
+
label: z.string(),
|
|
133
|
+
role: z.nativeEnum(UserRole),
|
|
134
|
+
operatorIds: z.array(z.string()),
|
|
135
|
+
maxOperators: z.number().optional(),
|
|
136
|
+
permissionGrants: z.array(PermissionGrantSchema).optional(),
|
|
137
|
+
allowSelfLoginCredential: z.boolean().optional(),
|
|
138
|
+
loginCredential: LoginCredentialSummarySchema.optional(),
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
export type CreateTokenResponse = z.infer<typeof CreateTokenResponseSchema>;
|
|
142
|
+
|
|
143
|
+
export const TokenInfoSchema = z.object({
|
|
144
|
+
id: z.string(),
|
|
145
|
+
token: z.string().optional(), // plaintext token value (admin only)
|
|
146
|
+
label: z.string(),
|
|
147
|
+
role: z.nativeEnum(UserRole),
|
|
148
|
+
operatorIds: z.array(z.string()),
|
|
149
|
+
createdBy: z.string().nullable(),
|
|
150
|
+
createdAt: z.number(),
|
|
151
|
+
expiresAt: z.number().optional(),
|
|
152
|
+
lastUsedAt: z.number().optional(),
|
|
153
|
+
revoked: z.boolean(),
|
|
154
|
+
system: z.boolean().optional(),
|
|
155
|
+
maxOperators: z.number().optional(),
|
|
156
|
+
permissionGrants: z.array(PermissionGrantSchema).optional(),
|
|
157
|
+
allowSelfLoginCredential: z.boolean().optional(),
|
|
158
|
+
loginCredential: LoginCredentialSummarySchema.optional(),
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
export type TokenInfo = z.infer<typeof TokenInfoSchema>;
|
|
162
|
+
|
|
163
|
+
export const UpdateTokenRequestSchema = z.object({
|
|
164
|
+
label: z.string().min(1).max(100).optional(),
|
|
165
|
+
role: z.nativeEnum(UserRole).optional(),
|
|
166
|
+
operatorIds: z.array(z.string()).optional(),
|
|
167
|
+
expiresAt: z.number().nullable().optional(),
|
|
168
|
+
maxOperators: z.number().min(0).nullable().optional(), // null 表示移除限制
|
|
169
|
+
permissionGrants: z.array(PermissionGrantSchema).nullable().optional(), // null to clear
|
|
170
|
+
allowSelfLoginCredential: z.boolean().optional(),
|
|
171
|
+
loginCredential: UpdateTokenLoginCredentialRequestSchema.nullable().optional(),
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
export type UpdateTokenRequest = z.infer<typeof UpdateTokenRequestSchema>;
|
|
175
|
+
|
|
176
|
+
// ===== 认证状态(GET /api/auth/status) =====
|
|
177
|
+
|
|
178
|
+
export const AuthStatusSchema = z.object({
|
|
179
|
+
enabled: z.boolean(),
|
|
180
|
+
allowPublicViewing: z.boolean(),
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
export type AuthStatus = z.infer<typeof AuthStatusSchema>;
|
|
184
|
+
|
|
185
|
+
// ===== 当前用户信息(GET /api/auth/me) =====
|
|
186
|
+
|
|
187
|
+
export const AuthMeResponseSchema = z.object({
|
|
188
|
+
role: z.nativeEnum(UserRole),
|
|
189
|
+
label: z.string(),
|
|
190
|
+
operatorIds: z.array(z.string()),
|
|
191
|
+
tokenId: z.string(),
|
|
192
|
+
maxOperators: z.number().optional(),
|
|
193
|
+
permissionGrants: z.array(PermissionGrantSchema).optional(),
|
|
194
|
+
loginCredential: AuthMeLoginCredentialSchema,
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
export type AuthMeResponse = z.infer<typeof AuthMeResponseSchema>;
|
|
198
|
+
|
|
199
|
+
export const UpdateSelfLoginCredentialRequestSchema = z.object({
|
|
200
|
+
username: z.string().trim().min(3).max(32).regex(/^[A-Za-z0-9._-]+$/),
|
|
201
|
+
password: z.string().min(8).max(128).optional(),
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
export type UpdateSelfLoginCredentialRequest = z.infer<typeof UpdateSelfLoginCredentialRequestSchema>;
|
|
205
|
+
|
|
206
|
+
// ===== 更新认证配置请求(PATCH /api/auth/config) =====
|
|
207
|
+
|
|
208
|
+
export const UpdateAuthConfigRequestSchema = z.object({
|
|
209
|
+
allowPublicViewing: z.boolean().optional(),
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
export type UpdateAuthConfigRequest = z.infer<typeof UpdateAuthConfigRequestSchema>;
|
|
213
|
+
|
|
214
|
+
// ===== 认证配置(持久化到 auth.json) =====
|
|
215
|
+
|
|
216
|
+
export const AuthConfigSchema = z.object({
|
|
217
|
+
enabled: z.boolean().default(true),
|
|
218
|
+
allowPublicViewing: z.boolean().default(true),
|
|
219
|
+
jwtSecret: z.string().optional(),
|
|
220
|
+
jwtExpiresInSeconds: z.number().default(7 * 24 * 3600), // 7 days
|
|
221
|
+
tokens: z.array(AuthTokenSchema).default([]),
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
export type AuthConfig = z.infer<typeof AuthConfigSchema>;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* FT8周期枚举
|
|
5
|
+
*/
|
|
6
|
+
export enum FT8Cycle {
|
|
7
|
+
EVEN = 0,
|
|
8
|
+
ODD = 1
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* 周期信息
|
|
13
|
+
*/
|
|
14
|
+
export const CycleInfoSchema = z.object({
|
|
15
|
+
/** 当前周期 */
|
|
16
|
+
cycle: z.number(),
|
|
17
|
+
/** 是否为发射周期 */
|
|
18
|
+
isTransmit: z.boolean(),
|
|
19
|
+
/** 周期开始时间(毫秒时间戳) */
|
|
20
|
+
startTime: z.number(),
|
|
21
|
+
/** 周期结束时间(毫秒时间戳) */
|
|
22
|
+
endTime: z.number()
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
export type CycleInfo = z.infer<typeof CycleInfoSchema>;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const DesktopHttpsModeSchema = z.enum(['self-signed', 'imported-pem']);
|
|
4
|
+
export type DesktopHttpsMode = z.infer<typeof DesktopHttpsModeSchema>;
|
|
5
|
+
|
|
6
|
+
export const DesktopHttpsCertificateStatusSchema = z.enum(['missing', 'valid', 'invalid']);
|
|
7
|
+
export type DesktopHttpsCertificateStatus = z.infer<typeof DesktopHttpsCertificateStatusSchema>;
|
|
8
|
+
|
|
9
|
+
export const DesktopHttpsAccessSchemeSchema = z.enum(['http', 'https']);
|
|
10
|
+
export type DesktopHttpsAccessScheme = z.infer<typeof DesktopHttpsAccessSchemeSchema>;
|
|
11
|
+
|
|
12
|
+
export const DesktopHttpsCertificateMetaSchema = z.object({
|
|
13
|
+
source: DesktopHttpsModeSchema,
|
|
14
|
+
subject: z.string().nullable(),
|
|
15
|
+
issuer: z.string().nullable(),
|
|
16
|
+
validFrom: z.string().nullable(),
|
|
17
|
+
validTo: z.string().nullable(),
|
|
18
|
+
fingerprintSha256: z.string().nullable(),
|
|
19
|
+
altNames: z.array(z.string()).default([]),
|
|
20
|
+
createdAt: z.string().nullable(),
|
|
21
|
+
updatedAt: z.string().nullable(),
|
|
22
|
+
});
|
|
23
|
+
export type DesktopHttpsCertificateMeta = z.infer<typeof DesktopHttpsCertificateMetaSchema>;
|
|
24
|
+
|
|
25
|
+
export const DesktopHttpsSettingsSchema = z.object({
|
|
26
|
+
enabled: z.boolean().default(false),
|
|
27
|
+
mode: DesktopHttpsModeSchema.default('self-signed'),
|
|
28
|
+
httpsPort: z.number().int().min(1).max(65535).default(8443),
|
|
29
|
+
redirectExternalHttp: z.boolean().default(true),
|
|
30
|
+
});
|
|
31
|
+
export type DesktopHttpsSettings = z.infer<typeof DesktopHttpsSettingsSchema>;
|
|
32
|
+
|
|
33
|
+
export const DesktopHttpsStatusSchema = DesktopHttpsSettingsSchema.extend({
|
|
34
|
+
activeScheme: DesktopHttpsAccessSchemeSchema.default('http'),
|
|
35
|
+
browserAccessUrl: z.string().nullable(),
|
|
36
|
+
shareUrls: z.array(z.string()).default([]),
|
|
37
|
+
certificateStatus: DesktopHttpsCertificateStatusSchema.default('missing'),
|
|
38
|
+
certificateMeta: DesktopHttpsCertificateMetaSchema.nullable(),
|
|
39
|
+
usingSelfSigned: z.boolean().default(false),
|
|
40
|
+
needsCertificate: z.boolean().default(true),
|
|
41
|
+
});
|
|
42
|
+
export type DesktopHttpsStatus = z.infer<typeof DesktopHttpsStatusSchema>;
|
|
43
|
+
|