@tx5dr/contracts 1.7.12 → 2.0.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/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/android-operator-audio.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/android-operator-audio.schema.test.js +27 -0
- package/dist/schema/__tests__/android-operator-audio.schema.test.js.map +1 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.js +113 -0
- package/dist/schema/__tests__/assisted-queue.schema.test.js.map +1 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.js +42 -0
- package/dist/schema/__tests__/audio-sidecar.schema.test.js.map +1 -0
- package/dist/schema/__tests__/audio.schema.test.js +92 -1
- package/dist/schema/__tests__/audio.schema.test.js.map +1 -1
- package/dist/schema/__tests__/diagnostics.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/diagnostics.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/diagnostics.schema.test.js +31 -0
- package/dist/schema/__tests__/diagnostics.schema.test.js.map +1 -0
- package/dist/schema/__tests__/logbook-health.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/logbook-health.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/logbook-health.schema.test.js +104 -0
- package/dist/schema/__tests__/logbook-health.schema.test.js.map +1 -0
- package/dist/schema/__tests__/observability.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/observability.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/observability.schema.test.js +12 -0
- package/dist/schema/__tests__/observability.schema.test.js.map +1 -0
- package/dist/schema/__tests__/operator.schema.test.js +9 -3
- package/dist/schema/__tests__/operator.schema.test.js.map +1 -1
- package/dist/schema/__tests__/plugin-market.schema.test.js +43 -1
- package/dist/schema/__tests__/plugin-market.schema.test.js.map +1 -1
- package/dist/schema/__tests__/radio-power-support.test.js +12 -0
- package/dist/schema/__tests__/radio-power-support.test.js.map +1 -1
- package/dist/schema/__tests__/radio-profile.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/radio-profile.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/radio-profile.schema.test.js +36 -0
- package/dist/schema/__tests__/radio-profile.schema.test.js.map +1 -0
- package/dist/schema/__tests__/radio.schema.test.js +41 -1
- package/dist/schema/__tests__/radio.schema.test.js.map +1 -1
- package/dist/schema/__tests__/realtime-settings.schema.test.js +2 -1
- package/dist/schema/__tests__/realtime-settings.schema.test.js.map +1 -1
- package/dist/schema/__tests__/remote-access.schema.test.d.ts +2 -0
- package/dist/schema/__tests__/remote-access.schema.test.d.ts.map +1 -0
- package/dist/schema/__tests__/remote-access.schema.test.js +18 -0
- package/dist/schema/__tests__/remote-access.schema.test.js.map +1 -0
- package/dist/schema/android-operator-audio.schema.d.ts +174 -0
- package/dist/schema/android-operator-audio.schema.d.ts.map +1 -0
- package/dist/schema/android-operator-audio.schema.js +38 -0
- package/dist/schema/android-operator-audio.schema.js.map +1 -0
- package/dist/schema/api-response.schema.d.ts +268 -44
- package/dist/schema/api-response.schema.d.ts.map +1 -1
- package/dist/schema/audio-sidecar.schema.d.ts +75 -0
- package/dist/schema/audio-sidecar.schema.d.ts.map +1 -1
- package/dist/schema/audio-sidecar.schema.js +32 -0
- package/dist/schema/audio-sidecar.schema.js.map +1 -1
- package/dist/schema/audio.schema.d.ts +4749 -3
- package/dist/schema/audio.schema.d.ts.map +1 -1
- package/dist/schema/audio.schema.js +101 -2
- package/dist/schema/audio.schema.js.map +1 -1
- package/dist/schema/auth.schema.d.ts +171 -12
- package/dist/schema/auth.schema.d.ts.map +1 -1
- package/dist/schema/auth.schema.js +65 -1
- package/dist/schema/auth.schema.js.map +1 -1
- package/dist/schema/cpu-profile.schema.d.ts +2 -2
- package/dist/schema/cw-decoder.schema.d.ts +74 -74
- package/dist/schema/cw-keyer.schema.d.ts +20 -14
- package/dist/schema/cw-keyer.schema.d.ts.map +1 -1
- package/dist/schema/cw-keyer.schema.js +3 -0
- package/dist/schema/cw-keyer.schema.js.map +1 -1
- package/dist/schema/desktop-https.schema.d.ts +6 -6
- package/dist/schema/device-ui.schema.d.ts +6 -6
- package/dist/schema/diagnostics.schema.d.ts +163 -0
- package/dist/schema/diagnostics.schema.d.ts.map +1 -0
- package/dist/schema/diagnostics.schema.js +46 -0
- package/dist/schema/diagnostics.schema.js.map +1 -0
- package/dist/schema/ft8.schema.d.ts +12 -2
- package/dist/schema/ft8.schema.d.ts.map +1 -1
- package/dist/schema/ft8.schema.js +8 -0
- package/dist/schema/ft8.schema.js.map +1 -1
- package/dist/schema/logbook.schema.d.ts +1940 -18
- package/dist/schema/logbook.schema.d.ts.map +1 -1
- package/dist/schema/logbook.schema.js +158 -1
- package/dist/schema/logbook.schema.js.map +1 -1
- package/dist/schema/mode.schema.d.ts +2 -6
- package/dist/schema/mode.schema.d.ts.map +1 -1
- package/dist/schema/mode.schema.js +2 -6
- package/dist/schema/mode.schema.js.map +1 -1
- package/dist/schema/observability.schema.d.ts +66 -0
- package/dist/schema/observability.schema.d.ts.map +1 -0
- package/dist/schema/observability.schema.js +20 -0
- package/dist/schema/observability.schema.js.map +1 -0
- package/dist/schema/operator.schema.d.ts +6 -6
- package/dist/schema/operator.schema.d.ts.map +1 -1
- package/dist/schema/operator.schema.js +6 -1
- package/dist/schema/operator.schema.js.map +1 -1
- package/dist/schema/plugin.schema.d.ts +258 -78
- package/dist/schema/plugin.schema.d.ts.map +1 -1
- package/dist/schema/plugin.schema.js +57 -2
- package/dist/schema/plugin.schema.js.map +1 -1
- package/dist/schema/process-monitor.schema.d.ts +132 -132
- package/dist/schema/pskreporter.schema.d.ts +1 -3
- package/dist/schema/pskreporter.schema.d.ts.map +1 -1
- package/dist/schema/pskreporter.schema.js +1 -3
- package/dist/schema/pskreporter.schema.js.map +1 -1
- package/dist/schema/qso.schema.d.ts +7 -0
- package/dist/schema/qso.schema.d.ts.map +1 -1
- package/dist/schema/qso.schema.js +7 -0
- package/dist/schema/qso.schema.js.map +1 -1
- package/dist/schema/radio-capability.schema.d.ts +31 -40
- package/dist/schema/radio-capability.schema.d.ts.map +1 -1
- package/dist/schema/radio-capability.schema.js +7 -15
- package/dist/schema/radio-capability.schema.js.map +1 -1
- package/dist/schema/radio-power-support.d.ts.map +1 -1
- package/dist/schema/radio-power-support.js +3 -0
- package/dist/schema/radio-power-support.js.map +1 -1
- package/dist/schema/radio-power.schema.d.ts +87 -13
- package/dist/schema/radio-power.schema.d.ts.map +1 -1
- package/dist/schema/radio-power.schema.js +15 -8
- package/dist/schema/radio-power.schema.js.map +1 -1
- package/dist/schema/radio-profile.schema.d.ts +1240 -97
- package/dist/schema/radio-profile.schema.d.ts.map +1 -1
- package/dist/schema/radio.schema.d.ts +271 -48
- package/dist/schema/radio.schema.d.ts.map +1 -1
- package/dist/schema/radio.schema.js +44 -5
- package/dist/schema/radio.schema.js.map +1 -1
- package/dist/schema/realtime.schema.d.ts +60 -52
- package/dist/schema/realtime.schema.d.ts.map +1 -1
- package/dist/schema/realtime.schema.js +9 -1
- package/dist/schema/realtime.schema.js.map +1 -1
- package/dist/schema/slot-info.schema.d.ts +15 -9
- package/dist/schema/slot-info.schema.d.ts.map +1 -1
- package/dist/schema/slot-info.schema.js +15 -9
- package/dist/schema/slot-info.schema.js.map +1 -1
- package/dist/schema/spectrum.schema.d.ts +12 -12
- package/dist/schema/station-info.schema.d.ts +5 -0
- package/dist/schema/station-info.schema.d.ts.map +1 -1
- package/dist/schema/station-info.schema.js +5 -0
- package/dist/schema/station-info.schema.js.map +1 -1
- package/dist/schema/system.schema.d.ts +56 -6
- package/dist/schema/system.schema.d.ts.map +1 -1
- package/dist/schema/system.schema.js +22 -0
- package/dist/schema/system.schema.js.map +1 -1
- package/dist/schema/transmission.schema.d.ts +1 -7
- package/dist/schema/transmission.schema.d.ts.map +1 -1
- package/dist/schema/transmission.schema.js +2 -7
- package/dist/schema/transmission.schema.js.map +1 -1
- package/dist/schema/voice-keyer.schema.d.ts +16 -16
- package/dist/schema/websocket.schema.d.ts +3582 -555
- package/dist/schema/websocket.schema.d.ts.map +1 -1
- package/dist/schema/websocket.schema.js +160 -3
- package/dist/schema/websocket.schema.js.map +1 -1
- package/package.json +6 -1
- package/src/index.ts +3 -0
- package/src/schema/__tests__/android-operator-audio.schema.test.ts +31 -0
- package/src/schema/__tests__/assisted-queue.schema.test.ts +121 -0
- package/src/schema/__tests__/audio-sidecar.schema.test.ts +48 -0
- package/src/schema/__tests__/audio.schema.test.ts +102 -0
- package/src/schema/__tests__/diagnostics.schema.test.ts +32 -0
- package/src/schema/__tests__/logbook-health.schema.test.ts +117 -0
- package/src/schema/__tests__/observability.schema.test.ts +15 -0
- package/src/schema/__tests__/operator.schema.test.ts +11 -3
- package/src/schema/__tests__/plugin-market.schema.test.ts +49 -0
- package/src/schema/__tests__/radio-power-support.test.ts +14 -0
- package/src/schema/__tests__/radio-profile.schema.test.ts +44 -0
- package/src/schema/__tests__/radio.schema.test.ts +49 -1
- package/src/schema/__tests__/realtime-settings.schema.test.ts +2 -1
- package/src/schema/__tests__/remote-access.schema.test.ts +22 -0
- package/src/schema/android-operator-audio.schema.ts +49 -0
- package/src/schema/audio-sidecar.schema.ts +35 -0
- package/src/schema/audio.schema.ts +115 -2
- package/src/schema/auth.schema.ts +79 -1
- package/src/schema/cw-keyer.schema.ts +5 -0
- package/src/schema/diagnostics.schema.ts +55 -0
- package/src/schema/ft8.schema.ts +8 -0
- package/src/schema/logbook.schema.ts +204 -1
- package/src/schema/mode.schema.ts +2 -6
- package/src/schema/observability.schema.ts +26 -0
- package/src/schema/operator.schema.ts +7 -1
- package/src/schema/plugin.schema.ts +67 -2
- package/src/schema/pskreporter.schema.ts +1 -3
- package/src/schema/qso.schema.ts +7 -0
- package/src/schema/radio-capability.schema.ts +7 -15
- package/src/schema/radio-power-support.ts +3 -0
- package/src/schema/radio-power.schema.ts +15 -8
- package/src/schema/radio.schema.ts +55 -5
- package/src/schema/realtime.schema.ts +9 -1
- package/src/schema/slot-info.schema.ts +16 -10
- package/src/schema/station-info.schema.ts +5 -0
- package/src/schema/system.schema.ts +29 -0
- package/src/schema/transmission.schema.ts +2 -7
- package/src/schema/websocket.schema.ts +223 -6
- package/test/radio-capability-schema.test.ts +44 -0
|
@@ -82,6 +82,19 @@ export const LoginResponseSchema = z.object({
|
|
|
82
82
|
|
|
83
83
|
export type LoginResponse = z.infer<typeof LoginResponseSchema>;
|
|
84
84
|
|
|
85
|
+
export const BrowserLoginCodeResponseSchema = z.object({
|
|
86
|
+
code: z.string().min(1),
|
|
87
|
+
expiresAt: z.number(),
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
export type BrowserLoginCodeResponse = z.infer<typeof BrowserLoginCodeResponseSchema>;
|
|
91
|
+
|
|
92
|
+
export const ExchangeBrowserLoginCodeRequestSchema = z.object({
|
|
93
|
+
code: z.string().min(1),
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
export type ExchangeBrowserLoginCodeRequest = z.infer<typeof ExchangeBrowserLoginCodeRequestSchema>;
|
|
97
|
+
|
|
85
98
|
// ===== Token 管理(Admin API) =====
|
|
86
99
|
|
|
87
100
|
export const LoginCredentialSummarySchema = z.object({
|
|
@@ -211,11 +224,76 @@ export const UpdateAuthConfigRequestSchema = z.object({
|
|
|
211
224
|
|
|
212
225
|
export type UpdateAuthConfigRequest = z.infer<typeof UpdateAuthConfigRequestSchema>;
|
|
213
226
|
|
|
227
|
+
export const RemoteAccessPresetSchema = z.enum(['local', 'lan', 'public']);
|
|
228
|
+
export type RemoteAccessPreset = z.infer<typeof RemoteAccessPresetSchema>;
|
|
229
|
+
|
|
230
|
+
export function isPrivateRemoteAccessHostname(hostname: string): boolean {
|
|
231
|
+
if (['localhost', '127.0.0.1', '::1'].includes(hostname)) return true;
|
|
232
|
+
if (hostname.includes(':')) {
|
|
233
|
+
const normalized = hostname.toLowerCase();
|
|
234
|
+
return normalized.startsWith('fc') || normalized.startsWith('fd') || normalized.startsWith('fe8')
|
|
235
|
+
|| normalized.startsWith('fe9') || normalized.startsWith('fea') || normalized.startsWith('feb');
|
|
236
|
+
}
|
|
237
|
+
const parts = hostname.split('.').map(Number);
|
|
238
|
+
if (parts.length !== 4 || parts.some(part => !Number.isInteger(part) || part < 0 || part > 255)) return false;
|
|
239
|
+
return parts[0] === 10
|
|
240
|
+
|| (parts[0] === 172 && parts[1] >= 16 && parts[1] <= 31)
|
|
241
|
+
|| (parts[0] === 192 && parts[1] === 168)
|
|
242
|
+
|| (parts[0] === 100 && parts[1] >= 64 && parts[1] <= 127)
|
|
243
|
+
|| (parts[0] === 169 && parts[1] === 254);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
export function normalizeRemoteAccessOrigin(value: string): string | null {
|
|
247
|
+
try {
|
|
248
|
+
const parsed = new URL(value.trim());
|
|
249
|
+
if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') return null;
|
|
250
|
+
if (parsed.username || parsed.password || parsed.pathname !== '/' || parsed.search || parsed.hash) return null;
|
|
251
|
+
return parsed.origin;
|
|
252
|
+
} catch {
|
|
253
|
+
return null;
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
export function isSecureRemoteAccessOrigin(value: string): boolean {
|
|
258
|
+
const normalized = normalizeRemoteAccessOrigin(value);
|
|
259
|
+
if (!normalized) return false;
|
|
260
|
+
const parsed = new URL(normalized);
|
|
261
|
+
return parsed.protocol === 'https:' || isPrivateRemoteAccessHostname(parsed.hostname);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
export const RemoteAccessSecurityConfigSchema = z.object({
|
|
265
|
+
preset: RemoteAccessPresetSchema.default('lan'),
|
|
266
|
+
allowedOrigins: z.array(z.string().url().refine(
|
|
267
|
+
value => normalizeRemoteAccessOrigin(value) !== null,
|
|
268
|
+
'Expected a complete HTTP(S) origin without a path',
|
|
269
|
+
)).max(32).default([]),
|
|
270
|
+
maxConnections: z.number().int().min(1).max(512).default(32),
|
|
271
|
+
maxConnectionsPerIp: z.number().int().min(1).max(128).default(16),
|
|
272
|
+
maxPendingAuth: z.number().int().min(1).max(128).default(32),
|
|
273
|
+
authTimeoutMs: z.number().int().min(3_000).max(60_000).default(10_000),
|
|
274
|
+
handshakeTimeoutMs: z.number().int().min(3_000).max(60_000).default(10_000),
|
|
275
|
+
});
|
|
276
|
+
export type RemoteAccessSecurityConfig = z.infer<typeof RemoteAccessSecurityConfigSchema>;
|
|
277
|
+
|
|
278
|
+
export const UpdateRemoteAccessSecurityRequestSchema = RemoteAccessSecurityConfigSchema.partial().extend({
|
|
279
|
+
allowPublicViewing: z.boolean().optional(),
|
|
280
|
+
});
|
|
281
|
+
export type UpdateRemoteAccessSecurityRequest = z.infer<typeof UpdateRemoteAccessSecurityRequestSchema>;
|
|
282
|
+
|
|
283
|
+
export const RemoteAccessSecurityStatusSchema = RemoteAccessSecurityConfigSchema.extend({
|
|
284
|
+
allowPublicViewing: z.boolean(),
|
|
285
|
+
activeConnections: z.number().int().nonnegative(),
|
|
286
|
+
pendingConnections: z.number().int().nonnegative(),
|
|
287
|
+
});
|
|
288
|
+
export type RemoteAccessSecurityStatus = z.infer<typeof RemoteAccessSecurityStatusSchema>;
|
|
289
|
+
|
|
214
290
|
// ===== 认证配置(持久化到 auth.json) =====
|
|
215
291
|
|
|
216
292
|
export const AuthConfigSchema = z.object({
|
|
217
293
|
enabled: z.boolean().default(true),
|
|
218
|
-
|
|
294
|
+
// Existing installations persist their explicit value; new installations opt in.
|
|
295
|
+
allowPublicViewing: z.boolean().default(false),
|
|
296
|
+
remoteAccess: RemoteAccessSecurityConfigSchema.default({}),
|
|
219
297
|
jwtSecret: z.string().optional(),
|
|
220
298
|
jwtExpiresInSeconds: z.number().default(7 * 24 * 3600), // 7 days
|
|
221
299
|
tokens: z.array(AuthTokenSchema).default([]),
|
|
@@ -6,6 +6,9 @@ import { z } from 'zod';
|
|
|
6
6
|
export const CWKeyerBackendSchema = z.enum(['cat', 'serial']);
|
|
7
7
|
export type CWKeyerBackend = z.infer<typeof CWKeyerBackendSchema>;
|
|
8
8
|
|
|
9
|
+
export const CWKeyActiveLevelSchema = z.enum(['high', 'low']);
|
|
10
|
+
export type CWKeyActiveLevel = z.infer<typeof CWKeyActiveLevelSchema>;
|
|
11
|
+
|
|
9
12
|
export const CWKeyerConfigSchema = z.object({
|
|
10
13
|
/** CW 发报后端:cat=Hamlib 整报文,serial=DTR/RTS 时序键控 */
|
|
11
14
|
backend: CWKeyerBackendSchema.default('cat'),
|
|
@@ -13,6 +16,8 @@ export const CWKeyerConfigSchema = z.object({
|
|
|
13
16
|
keyPort: z.string(),
|
|
14
17
|
/** CW 键控引脚类型 */
|
|
15
18
|
keyMethod: z.enum(['dtr', 'rts']),
|
|
19
|
+
/** CW 键控有效电平:high=高电平按下,low=低电平按下 */
|
|
20
|
+
keyActiveLevel: CWKeyActiveLevelSchema.default('high'),
|
|
16
21
|
/** 莫尔斯码速度 (WPM, 5-60) */
|
|
17
22
|
wpm: z.number().int().min(5).max(60).default(20),
|
|
18
23
|
});
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const DiagnosticLogSourceIdSchema = z.enum(['server', 'electron-main', 'client-tools']);
|
|
4
|
+
export type DiagnosticLogSourceId = z.infer<typeof DiagnosticLogSourceIdSchema>;
|
|
5
|
+
|
|
6
|
+
export const DiagnosticLogSourceSchema = z.object({
|
|
7
|
+
id: DiagnosticLogSourceIdSchema,
|
|
8
|
+
fileName: z.string().min(1),
|
|
9
|
+
fileCount: z.number().int().positive(),
|
|
10
|
+
totalBytes: z.number().int().nonnegative(),
|
|
11
|
+
availableFromMs: z.number().int().positive().nullable(),
|
|
12
|
+
availableToMs: z.number().int().positive().nullable(),
|
|
13
|
+
});
|
|
14
|
+
export type DiagnosticLogSource = z.infer<typeof DiagnosticLogSourceSchema>;
|
|
15
|
+
|
|
16
|
+
export const DiagnosticLogSourcesResponseSchema = z.object({
|
|
17
|
+
sources: z.array(DiagnosticLogSourceSchema),
|
|
18
|
+
limits: z.object({
|
|
19
|
+
maxRangeMs: z.number().int().positive(),
|
|
20
|
+
maxUncompressedBytes: z.number().int().positive(),
|
|
21
|
+
maxCompressedBytes: z.number().int().positive(),
|
|
22
|
+
feedbackMaxCharacters: z.number().int().positive(),
|
|
23
|
+
}),
|
|
24
|
+
});
|
|
25
|
+
export type DiagnosticLogSourcesResponse = z.infer<typeof DiagnosticLogSourcesResponseSchema>;
|
|
26
|
+
|
|
27
|
+
export const CreateDiagnosticUploadRequestSchema = z.object({
|
|
28
|
+
sourceId: DiagnosticLogSourceIdSchema,
|
|
29
|
+
fromMs: z.number().int().positive(),
|
|
30
|
+
toMs: z.number().int().positive(),
|
|
31
|
+
feedback: z.string().trim().max(2000).optional(),
|
|
32
|
+
}).strict().superRefine((value, context) => {
|
|
33
|
+
if (value.toMs <= value.fromMs) {
|
|
34
|
+
context.addIssue({ code: z.ZodIssueCode.custom, path: ['toMs'], message: 'toMs must be later than fromMs' });
|
|
35
|
+
}
|
|
36
|
+
if (value.toMs - value.fromMs > 7 * 24 * 60 * 60 * 1000) {
|
|
37
|
+
context.addIssue({ code: z.ZodIssueCode.custom, path: ['toMs'], message: 'time range exceeds seven days' });
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
export type CreateDiagnosticUploadRequest = z.infer<typeof CreateDiagnosticUploadRequestSchema>;
|
|
41
|
+
|
|
42
|
+
export const DiagnosticUploadReceiptSchema = z.object({
|
|
43
|
+
uploadId: z.string().uuid(),
|
|
44
|
+
sourceId: DiagnosticLogSourceIdSchema,
|
|
45
|
+
requestedFromMs: z.number().int().positive(),
|
|
46
|
+
requestedToMs: z.number().int().positive(),
|
|
47
|
+
includedFromMs: z.number().int().positive(),
|
|
48
|
+
includedToMs: z.number().int().positive(),
|
|
49
|
+
lineCount: z.number().int().positive(),
|
|
50
|
+
uncompressedBytes: z.number().int().positive(),
|
|
51
|
+
compressedBytes: z.number().int().positive(),
|
|
52
|
+
acceptedAt: z.string().datetime(),
|
|
53
|
+
retainedUntil: z.string().datetime(),
|
|
54
|
+
});
|
|
55
|
+
export type DiagnosticUploadReceipt = z.infer<typeof DiagnosticUploadReceiptSchema>;
|
package/src/schema/ft8.schema.ts
CHANGED
|
@@ -276,6 +276,9 @@ export const FT8MessageSchema = z.discriminatedUnion('type', [
|
|
|
276
276
|
* This is the main plugin-facing FT8 payload. It combines RF metrics from the
|
|
277
277
|
* decoder (`snr`, `dt`, `df`), the original unparsed text (`rawMessage`), the
|
|
278
278
|
* normalized semantic form (`message`) and optional logbook enrichment.
|
|
279
|
+
* `snr` is dB, `dt` is seconds from the expected decode timing, `df` is audio
|
|
280
|
+
* offset in hertz, and `timestamp` is Unix epoch milliseconds. `slotId` ties
|
|
281
|
+
* the decode back to its `SlotInfo`/`SlotPack`.
|
|
279
282
|
*/
|
|
280
283
|
export const ParsedFT8MessageSchema = z.object({
|
|
281
284
|
snr: z.number(),
|
|
@@ -285,6 +288,11 @@ export const ParsedFT8MessageSchema = z.object({
|
|
|
285
288
|
message: FT8MessageSchema,
|
|
286
289
|
slotId: z.string(),
|
|
287
290
|
timestamp: z.number(),
|
|
291
|
+
/**
|
|
292
|
+
* 标记该消息为 WSJT-X 部分解码(含 `<...>` 未解码呼号占位符)。
|
|
293
|
+
* 部分解码消息不得作为自动呼叫/回复目标,仅供监控与防御使用。
|
|
294
|
+
*/
|
|
295
|
+
isPartialDecode: z.boolean().optional(),
|
|
288
296
|
logbookAnalysis: LogbookAnalysisSchema.optional(),
|
|
289
297
|
});
|
|
290
298
|
|
|
@@ -1,4 +1,49 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
|
+
import { QSORecordSchema } from './qso.schema.js';
|
|
3
|
+
|
|
4
|
+
export const LOGBOOK_OPERATION_ERROR_CODES = [
|
|
5
|
+
'LOGBOOK_LOADING',
|
|
6
|
+
'LOGBOOK_READ_ONLY',
|
|
7
|
+
'LOGBOOK_UNAVAILABLE',
|
|
8
|
+
'LOGBOOK_WRITE_FAILED',
|
|
9
|
+
'LOGBOOK_WRITE_STATE_UNCERTAIN',
|
|
10
|
+
'LOGBOOK_UNSAVED_QSO_NOT_FOUND',
|
|
11
|
+
'LOGBOOK_BACKUP_FAILED',
|
|
12
|
+
'LOGBOOK_BACKUP_UNAVAILABLE',
|
|
13
|
+
'LOGBOOK_BACKUP_CHANGED',
|
|
14
|
+
'LOGBOOK_MAINTENANCE',
|
|
15
|
+
'LOGBOOK_REVISION_MISMATCH',
|
|
16
|
+
'LOGBOOK_RESTORE_PRECONDITION_FAILED',
|
|
17
|
+
'LOGBOOK_PRECONDITION_REQUIRED',
|
|
18
|
+
'LOGBOOK_IDEMPOTENCY_CONFLICT',
|
|
19
|
+
] as const;
|
|
20
|
+
|
|
21
|
+
export const LogbookOperationErrorCodeSchema = z.enum(LOGBOOK_OPERATION_ERROR_CODES);
|
|
22
|
+
|
|
23
|
+
export const LogbookHealthStateSchema = z.enum([
|
|
24
|
+
'loading',
|
|
25
|
+
'healthy',
|
|
26
|
+
'degraded',
|
|
27
|
+
'read_only',
|
|
28
|
+
'unavailable',
|
|
29
|
+
]);
|
|
30
|
+
|
|
31
|
+
export const LogbookHealthIssueSchema = z.object({
|
|
32
|
+
code: z.string(),
|
|
33
|
+
message: z.string(),
|
|
34
|
+
affectedRecords: z.number().int().nonnegative().optional(),
|
|
35
|
+
affectedBytes: z.number().int().nonnegative().optional(),
|
|
36
|
+
recoveryFileName: z.string().optional(),
|
|
37
|
+
occurredAt: z.number(),
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
export const LogbookHealthSchema = z.object({
|
|
41
|
+
state: LogbookHealthStateSchema,
|
|
42
|
+
readable: z.boolean(),
|
|
43
|
+
writable: z.boolean(),
|
|
44
|
+
issues: z.array(LogbookHealthIssueSchema),
|
|
45
|
+
updatedAt: z.number(),
|
|
46
|
+
});
|
|
2
47
|
|
|
3
48
|
export const DxccCountSummarySchema = z.object({
|
|
4
49
|
current: z.number().default(0),
|
|
@@ -27,10 +72,12 @@ export const LogBookInfoSchema = z.object({
|
|
|
27
72
|
id: z.string(),
|
|
28
73
|
name: z.string(),
|
|
29
74
|
description: z.string().optional(),
|
|
30
|
-
|
|
75
|
+
fileName: z.string().min(1).max(255),
|
|
76
|
+
storageKind: z.enum(['managed', 'custom']),
|
|
31
77
|
createdAt: z.number(),
|
|
32
78
|
lastUsed: z.number(),
|
|
33
79
|
isActive: z.boolean(),
|
|
80
|
+
health: LogbookHealthSchema,
|
|
34
81
|
});
|
|
35
82
|
|
|
36
83
|
/**
|
|
@@ -102,6 +149,141 @@ export const LogBookActionResponseSchema = z.object({
|
|
|
102
149
|
data: LogBookInfoSchema.optional(),
|
|
103
150
|
});
|
|
104
151
|
|
|
152
|
+
export const LogbookRecoveryRetryResponseSchema = z.object({
|
|
153
|
+
success: z.boolean(),
|
|
154
|
+
data: z.object({
|
|
155
|
+
logBookId: z.string(),
|
|
156
|
+
health: LogbookHealthSchema,
|
|
157
|
+
}),
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const LogbookRevisionSchema = z.string()
|
|
161
|
+
.min(1)
|
|
162
|
+
.max(256)
|
|
163
|
+
.regex(/^[A-Za-z0-9._:"-]+$/);
|
|
164
|
+
const LogbookIdempotencyKeySchema = z.string()
|
|
165
|
+
.min(8)
|
|
166
|
+
.max(128)
|
|
167
|
+
.regex(/^[A-Za-z0-9._:-]+$/);
|
|
168
|
+
const LogbookRecoveryTokenSchema = z.string()
|
|
169
|
+
.min(8)
|
|
170
|
+
.max(128)
|
|
171
|
+
.regex(/^[A-Za-z0-9._:-]+$/);
|
|
172
|
+
const UnsavedQsoAttemptIdSchema = z.string()
|
|
173
|
+
.min(8)
|
|
174
|
+
.max(128)
|
|
175
|
+
.regex(/^[A-Za-z0-9._:-]+$/);
|
|
176
|
+
|
|
177
|
+
export const LogbookUnsavedQsoSummarySchema = z.object({
|
|
178
|
+
attemptId: UnsavedQsoAttemptIdSchema,
|
|
179
|
+
operatorId: z.string().min(1).max(128),
|
|
180
|
+
createdAt: z.number(),
|
|
181
|
+
callsign: z.string().max(64),
|
|
182
|
+
mode: z.string().max(32),
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
export const LogbookBackupArtifactSchema = z.object({
|
|
186
|
+
createdAt: z.number(),
|
|
187
|
+
size: z.number().int().nonnegative(),
|
|
188
|
+
recordCount: z.number().int().nonnegative().optional(),
|
|
189
|
+
opaqueRecordCount: z.number().int().nonnegative().optional(),
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
export const LogbookBackupOperationSchema = z.object({
|
|
193
|
+
id: z.string().min(1).max(128),
|
|
194
|
+
kind: z.enum(['backup', 'restore-prepare', 'restore']),
|
|
195
|
+
state: z.enum(['queued', 'running', 'succeeded', 'failed']),
|
|
196
|
+
phase: z.string().min(1).max(128),
|
|
197
|
+
processedBytes: z.number().int().nonnegative().optional(),
|
|
198
|
+
totalBytes: z.number().int().nonnegative().optional(),
|
|
199
|
+
errorCode: z.string().min(1).max(128).optional(),
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
export const LogbookBackupStatusSchema = z.object({
|
|
203
|
+
logBookId: z.string(),
|
|
204
|
+
revision: LogbookRevisionSchema,
|
|
205
|
+
mainHealth: LogbookHealthSchema,
|
|
206
|
+
dirty: z.boolean(),
|
|
207
|
+
pendingMutations: z.number().int().nonnegative(),
|
|
208
|
+
latest: LogbookBackupArtifactSchema.optional(),
|
|
209
|
+
preRestore: LogbookBackupArtifactSchema.optional(),
|
|
210
|
+
operation: LogbookBackupOperationSchema.optional(),
|
|
211
|
+
unsaved: z.array(LogbookUnsavedQsoSummarySchema).optional(),
|
|
212
|
+
capabilities: z.object({
|
|
213
|
+
canCreate: z.boolean(),
|
|
214
|
+
canDownload: z.boolean(),
|
|
215
|
+
canRestore: z.boolean(),
|
|
216
|
+
canDownloadPreRestore: z.boolean(),
|
|
217
|
+
}),
|
|
218
|
+
error: z.object({
|
|
219
|
+
code: z.string().min(1).max(128),
|
|
220
|
+
message: z.string(),
|
|
221
|
+
}).optional(),
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
export const LogbookBackupStatusResponseSchema = z.object({
|
|
225
|
+
success: z.literal(true),
|
|
226
|
+
data: LogbookBackupStatusSchema,
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
export const CreateLogbookBackupRequestSchema = z.object({
|
|
230
|
+
}).strict();
|
|
231
|
+
|
|
232
|
+
export const PrepareLogbookRestoreRequestSchema = z.object({
|
|
233
|
+
}).strict();
|
|
234
|
+
|
|
235
|
+
export const RestoreLogbookRequestSchema = z.object({
|
|
236
|
+
preflightToken: LogbookRecoveryTokenSchema,
|
|
237
|
+
confirmation: z.string().min(1).max(128),
|
|
238
|
+
}).strict();
|
|
239
|
+
|
|
240
|
+
export const LogbookConditionalMutationHeadersSchema = z.object({
|
|
241
|
+
revision: LogbookRevisionSchema,
|
|
242
|
+
idempotencyKey: LogbookIdempotencyKeySchema,
|
|
243
|
+
}).strict();
|
|
244
|
+
|
|
245
|
+
export const LogbookIdempotentMutationHeadersSchema = z.object({
|
|
246
|
+
idempotencyKey: LogbookIdempotencyKeySchema,
|
|
247
|
+
}).strict();
|
|
248
|
+
|
|
249
|
+
export const LogbookRestoreFileSummarySchema = z.object({
|
|
250
|
+
size: z.number().int().nonnegative(),
|
|
251
|
+
recordCount: z.number().int().nonnegative(),
|
|
252
|
+
opaqueRecordCount: z.number().int().nonnegative(),
|
|
253
|
+
incompleteTail: z.boolean(),
|
|
254
|
+
issueCount: z.number().int().nonnegative(),
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
export const LogbookRestorePreflightSchema = z.object({
|
|
258
|
+
preflightToken: LogbookRecoveryTokenSchema,
|
|
259
|
+
expiresAt: z.number(),
|
|
260
|
+
revision: LogbookRevisionSchema,
|
|
261
|
+
main: LogbookRestoreFileSummarySchema,
|
|
262
|
+
backup: LogbookRestoreFileSummarySchema,
|
|
263
|
+
recordDelta: z.number().int(),
|
|
264
|
+
estimatedLoss: z.number().int().nonnegative(),
|
|
265
|
+
highRisk: z.boolean(),
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
export const LogbookRestorePreflightResponseSchema = z.object({
|
|
269
|
+
success: z.literal(true),
|
|
270
|
+
data: LogbookRestorePreflightSchema,
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
export const LogbookUnsavedQsoRetryResponseSchema = z.object({
|
|
274
|
+
success: z.literal(true),
|
|
275
|
+
data: QSORecordSchema,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
export const LogbookUnsavedQsoDiscardResponseSchema = z.object({
|
|
279
|
+
success: z.literal(true),
|
|
280
|
+
data: z.object({ attemptId: UnsavedQsoAttemptIdSchema }),
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
export const LogbookUnsavedQsoAttemptParamsSchema = z.object({
|
|
284
|
+
attemptId: UnsavedQsoAttemptIdSchema,
|
|
285
|
+
}).strict();
|
|
286
|
+
|
|
105
287
|
/**
|
|
106
288
|
* 日志本QSO查询选项Schema
|
|
107
289
|
*/
|
|
@@ -293,6 +475,10 @@ export const QSOActionResponseSchema = z.object({
|
|
|
293
475
|
// ========== 类型导出 ==========
|
|
294
476
|
|
|
295
477
|
export type LogBookInfo = z.infer<typeof LogBookInfoSchema>;
|
|
478
|
+
export type LogbookOperationErrorCode = z.infer<typeof LogbookOperationErrorCodeSchema>;
|
|
479
|
+
export type LogbookHealthState = z.infer<typeof LogbookHealthStateSchema>;
|
|
480
|
+
export type LogbookHealthIssue = z.infer<typeof LogbookHealthIssueSchema>;
|
|
481
|
+
export type LogbookHealth = z.infer<typeof LogbookHealthSchema>;
|
|
296
482
|
export type CreateLogBookRequest = z.infer<typeof CreateLogBookRequestSchema>;
|
|
297
483
|
export type UpdateLogBookRequest = z.infer<typeof UpdateLogBookRequestSchema>;
|
|
298
484
|
export type ConnectOperatorToLogBookRequest = z.infer<typeof ConnectOperatorToLogBookRequestSchema>;
|
|
@@ -301,6 +487,23 @@ export type LogBookDxccSummary = z.infer<typeof LogBookDxccSummarySchema>;
|
|
|
301
487
|
export type LogBookListResponse = z.infer<typeof LogBookListResponseSchema>;
|
|
302
488
|
export type LogBookDetailResponse = z.infer<typeof LogBookDetailResponseSchema>;
|
|
303
489
|
export type LogBookActionResponse = z.infer<typeof LogBookActionResponseSchema>;
|
|
490
|
+
export type LogbookRecoveryRetryResponse = z.infer<typeof LogbookRecoveryRetryResponseSchema>;
|
|
491
|
+
export type LogbookUnsavedQsoSummary = z.infer<typeof LogbookUnsavedQsoSummarySchema>;
|
|
492
|
+
export type LogbookBackupArtifact = z.infer<typeof LogbookBackupArtifactSchema>;
|
|
493
|
+
export type LogbookBackupOperation = z.infer<typeof LogbookBackupOperationSchema>;
|
|
494
|
+
export type LogbookBackupStatus = z.infer<typeof LogbookBackupStatusSchema>;
|
|
495
|
+
export type LogbookBackupStatusResponse = z.infer<typeof LogbookBackupStatusResponseSchema>;
|
|
496
|
+
export type CreateLogbookBackupRequest = z.infer<typeof CreateLogbookBackupRequestSchema>;
|
|
497
|
+
export type PrepareLogbookRestoreRequest = z.infer<typeof PrepareLogbookRestoreRequestSchema>;
|
|
498
|
+
export type RestoreLogbookRequest = z.infer<typeof RestoreLogbookRequestSchema>;
|
|
499
|
+
export type LogbookConditionalMutationHeaders = z.infer<typeof LogbookConditionalMutationHeadersSchema>;
|
|
500
|
+
export type LogbookIdempotentMutationHeaders = z.infer<typeof LogbookIdempotentMutationHeadersSchema>;
|
|
501
|
+
export type LogbookRestoreFileSummary = z.infer<typeof LogbookRestoreFileSummarySchema>;
|
|
502
|
+
export type LogbookRestorePreflight = z.infer<typeof LogbookRestorePreflightSchema>;
|
|
503
|
+
export type LogbookRestorePreflightResponse = z.infer<typeof LogbookRestorePreflightResponseSchema>;
|
|
504
|
+
export type LogbookUnsavedQsoRetryResponse = z.infer<typeof LogbookUnsavedQsoRetryResponseSchema>;
|
|
505
|
+
export type LogbookUnsavedQsoDiscardResponse = z.infer<typeof LogbookUnsavedQsoDiscardResponseSchema>;
|
|
506
|
+
export type LogbookUnsavedQsoAttemptParams = z.infer<typeof LogbookUnsavedQsoAttemptParamsSchema>;
|
|
304
507
|
export type LogBookQSOQueryOptions = z.infer<typeof LogBookQSOQueryOptionsSchema>;
|
|
305
508
|
export type LogBookRecentGlobeQuery = z.infer<typeof LogBookRecentGlobeQuerySchema>;
|
|
306
509
|
export type LogBookRecentGlobeHomeSource = z.infer<typeof LogBookRecentGlobeHomeSourceSchema>;
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
|
|
3
|
-
/**
|
|
4
|
-
* 模式描述符 - 定义 FT8/FT4 等模式的时序参数
|
|
5
|
-
*/
|
|
3
|
+
/** Timing, decoder and display parameters for a digital mode such as FT8 or FT4. */
|
|
6
4
|
export const ModeDescriptorSchema = z.object({
|
|
7
5
|
/** 模式名称,如 "FT8", "FT4" */
|
|
8
6
|
name: z.string(),
|
|
@@ -109,9 +107,7 @@ export const FT4_WINDOW_PRESETS: Record<string, number[]> = {
|
|
|
109
107
|
lightweight: [0], // 1 轮:仅末端
|
|
110
108
|
};
|
|
111
109
|
|
|
112
|
-
/**
|
|
113
|
-
* 解码窗口设置 Schema
|
|
114
|
-
*/
|
|
110
|
+
/** Per-mode decode-window presets and optional custom timing windows. */
|
|
115
111
|
export const DecodeWindowSettingsSchema = z.object({
|
|
116
112
|
ft8: z.object({
|
|
117
113
|
preset: z.enum(['maximum', 'balanced', 'lightweight', 'minimum', 'custom']).default('balanced'),
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
|
|
3
|
+
export const OBSERVABILITY_NOTICE_VERSION = 1;
|
|
4
|
+
|
|
5
|
+
export const ObservabilitySettingsSchema = z.object({
|
|
6
|
+
enabled: z.boolean(),
|
|
7
|
+
noticeVersion: z.number().int().nonnegative(),
|
|
8
|
+
});
|
|
9
|
+
export type ObservabilitySettings = z.infer<typeof ObservabilitySettingsSchema>;
|
|
10
|
+
|
|
11
|
+
export const UpdateObservabilitySettingsSchema = z.object({
|
|
12
|
+
enabled: z.boolean(),
|
|
13
|
+
noticeVersion: z.literal(OBSERVABILITY_NOTICE_VERSION),
|
|
14
|
+
});
|
|
15
|
+
export type UpdateObservabilitySettingsRequest = z.infer<typeof UpdateObservabilitySettingsSchema>;
|
|
16
|
+
|
|
17
|
+
export const ObservabilityStatusSchema = z.object({
|
|
18
|
+
settings: ObservabilitySettingsSchema,
|
|
19
|
+
effectiveEnabled: z.boolean(),
|
|
20
|
+
noticeRequired: z.boolean(),
|
|
21
|
+
endpointConfigured: z.boolean(),
|
|
22
|
+
queueDepth: z.number().int().nonnegative(),
|
|
23
|
+
lastSentAt: z.number().int().nonnegative().nullable(),
|
|
24
|
+
lastError: z.string().nullable(),
|
|
25
|
+
});
|
|
26
|
+
export type ObservabilityStatus = z.infer<typeof ObservabilityStatusSchema>;
|
|
@@ -1,18 +1,24 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { ModeDescriptorSchema } from './mode.schema.js';
|
|
3
3
|
import { sanitizeCallsignInput } from '../utils/callsign.js';
|
|
4
|
+
import { sanitizeGridInput } from '../utils/grid.js';
|
|
4
5
|
|
|
5
6
|
const OperatorCallsignSchema = z
|
|
6
7
|
.string()
|
|
7
8
|
.transform(sanitizeCallsignInput)
|
|
8
9
|
.pipe(z.string().min(1, '呼号不能为空').max(10, '呼号不能超过10个字符'));
|
|
9
10
|
|
|
11
|
+
const OperatorGridSchema = z
|
|
12
|
+
.string()
|
|
13
|
+
.transform(sanitizeGridInput)
|
|
14
|
+
.pipe(z.string().min(4, '网格坐标至少4位').max(8, '网格坐标不能超过8位'));
|
|
15
|
+
|
|
10
16
|
// 操作员配置 Schema (重命名以避免冲突)
|
|
11
17
|
export const RadioOperatorConfigSchema = z.object({
|
|
12
18
|
id: z.string().min(1, '操作员ID不能为空'),
|
|
13
19
|
createdByTokenId: z.string().optional(), // 创建者 Token ID(仅用于审计,不参与权限判断)
|
|
14
20
|
myCallsign: OperatorCallsignSchema,
|
|
15
|
-
myGrid:
|
|
21
|
+
myGrid: OperatorGridSchema.optional(),
|
|
16
22
|
frequency: z.number().min(0).max(1000000000, '频率必须在0-1GHz之间').default(1000), // 音频偏移频率(Hz),默认1000Hz,创建时自动分配不重复值
|
|
17
23
|
transmitCycles: z.array(z.number().min(0).max(1)).default([0]), // 0=偶数周期,1=奇数周期
|
|
18
24
|
mode: ModeDescriptorSchema.optional(),
|