@krischoichoi/channel-web 0.6.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/README.md +84 -0
- package/lib/client.js +5005 -0
- package/lib/client.js.map +7 -0
- package/lib/host/routes-v2.d.ts +149 -0
- package/lib/host/routes-v2.d.ts.map +1 -0
- package/lib/host/routes-v2.js +524 -0
- package/lib/host/routes-v2.js.map +1 -0
- package/lib/host/routes.d.ts +87 -0
- package/lib/host/routes.d.ts.map +1 -0
- package/lib/host/routes.js +250 -0
- package/lib/host/routes.js.map +1 -0
- package/lib/host/security.d.ts +47 -0
- package/lib/host/security.d.ts.map +1 -0
- package/lib/host/security.js +78 -0
- package/lib/host/security.js.map +1 -0
- package/lib/index.d.ts +22 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +129 -0
- package/lib/index.js.map +1 -0
- package/lib/protocol.d.ts +152 -0
- package/lib/protocol.d.ts.map +1 -0
- package/lib/protocol.js +12 -0
- package/lib/protocol.js.map +1 -0
- package/package.json +73 -0
|
@@ -0,0 +1,524 @@
|
|
|
1
|
+
import { channelAccessPolicySchema, isChannelError } from '@krischoichoi/channel-core';
|
|
2
|
+
import { isControlError } from '@krischoichoi/channel-control';
|
|
3
|
+
import { z } from 'zod';
|
|
4
|
+
import { errorBody } from './security.js';
|
|
5
|
+
const AUTH_METHODS = ['qr', 'device', 'portal-login', 'credentials', 'hybrid'];
|
|
6
|
+
/** URL-decode safety: decodeURIComponent of malformed escapes is a URIError. */
|
|
7
|
+
function safeDecode(s) {
|
|
8
|
+
try {
|
|
9
|
+
return decodeURIComponent(s);
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return s;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
/** Any JSON object (never an array / null / primitive). */
|
|
16
|
+
const jsonObjectSchema = z.object({}).loose();
|
|
17
|
+
/** Non-empty string check shared by path params. */
|
|
18
|
+
const nonEmptyString = z.string().min(1);
|
|
19
|
+
/** A 400 INVALID_INPUT result for a malformed body/path value. */
|
|
20
|
+
function badInput(message) {
|
|
21
|
+
return { ok: false, result: { status: 400, body: errorBody('INVALID_INPUT', message) } };
|
|
22
|
+
}
|
|
23
|
+
/** Validate that a value is a non-empty string, or a 400 result. */
|
|
24
|
+
function requireString(value, field) {
|
|
25
|
+
const parsed = nonEmptyString.safeParse(value);
|
|
26
|
+
if (!parsed.success)
|
|
27
|
+
return badInput(field + ' must be a non-empty string');
|
|
28
|
+
return { ok: true, value: parsed.data };
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Map a control-plane error to an HTTP status + structured body. Unknown
|
|
32
|
+
* channels surface as 404; expired sessions as 410; every other recognized
|
|
33
|
+
* control code gets 400; anything unexpected becomes a redacted 500.
|
|
34
|
+
*/
|
|
35
|
+
function mapControlError(error) {
|
|
36
|
+
if (isControlError(error)) {
|
|
37
|
+
switch (error.code) {
|
|
38
|
+
case 'CONTROL_DEFINITION_NOT_FOUND':
|
|
39
|
+
case 'UNKNOWN_FIELD':
|
|
40
|
+
case 'AUTH_SESSION_NOT_FOUND':
|
|
41
|
+
case 'AUTH_SESSION_CANCELLED':
|
|
42
|
+
case 'CLAIM_NOT_FOUND':
|
|
43
|
+
return { status: 404, body: errorBody(error.code, error.message) };
|
|
44
|
+
case 'AUTH_SESSION_EXPIRED':
|
|
45
|
+
case 'CLAIM_EXPIRED':
|
|
46
|
+
return { status: 410, body: errorBody(error.code, error.message) };
|
|
47
|
+
case 'SECRET_FIELD_REJECTED':
|
|
48
|
+
case 'NOT_A_SECRET_FIELD':
|
|
49
|
+
case 'INVALID_CREDENTIAL':
|
|
50
|
+
case 'CREDENTIAL_READONLY':
|
|
51
|
+
case 'CREDENTIAL_NOT_SUPPORTED':
|
|
52
|
+
case 'AUTH_NOT_SUPPORTED':
|
|
53
|
+
case 'AUTH_NOT_READY':
|
|
54
|
+
case 'ENABLE_NOT_SUPPORTED':
|
|
55
|
+
case 'INVALID_ACCESS_POLICY':
|
|
56
|
+
case 'CLAIM_CANCELLED':
|
|
57
|
+
case 'CLAIM_NOT_SUPPORTED':
|
|
58
|
+
case 'CLAIM_INVALID':
|
|
59
|
+
return { status: 400, body: errorBody(error.code, error.message) };
|
|
60
|
+
default:
|
|
61
|
+
return {
|
|
62
|
+
status: 500,
|
|
63
|
+
body: errorBody('CONTROL_ERROR', 'channel control failed'),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (isChannelError(error)) {
|
|
68
|
+
// The only ChannelError the control surface raises here is an unknown
|
|
69
|
+
// channel ("definition not registered"); map it to 404.
|
|
70
|
+
return {
|
|
71
|
+
status: 404,
|
|
72
|
+
body: errorBody('CONTROL_DEFINITION_NOT_FOUND', 'unknown channel'),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
// Never leak an arbitrary message.
|
|
76
|
+
return { status: 500, body: errorBody('INTERNAL_ERROR', 'internal error') };
|
|
77
|
+
}
|
|
78
|
+
/** Run `fn` and translate any control-plane throw into an ApiResult. */
|
|
79
|
+
async function run(fn, ok) {
|
|
80
|
+
try {
|
|
81
|
+
const value = await fn();
|
|
82
|
+
return ok(value);
|
|
83
|
+
}
|
|
84
|
+
catch (error) {
|
|
85
|
+
return mapControlError(error);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export class ChannelApiV2 {
|
|
89
|
+
control;
|
|
90
|
+
constructor(control) {
|
|
91
|
+
this.control = control;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Resolve whether a channel id exists (via the setup descriptor). Returns a
|
|
95
|
+
* 404 result for unknown channels so every channel-scoped endpoint reports a
|
|
96
|
+
* stable code.
|
|
97
|
+
*/
|
|
98
|
+
async channelOr404(channelId) {
|
|
99
|
+
try {
|
|
100
|
+
await this.control.getSetup(channelId);
|
|
101
|
+
return { ok: true };
|
|
102
|
+
}
|
|
103
|
+
catch (error) {
|
|
104
|
+
if (isControlError(error, 'CONTROL_DEFINITION_NOT_FOUND') || isChannelError(error)) {
|
|
105
|
+
return {
|
|
106
|
+
ok: false,
|
|
107
|
+
result: { status: 404, body: errorBody('CHANNEL_NOT_FOUND', 'unknown channel: ' + channelId) },
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
/** GET /channels → { channels: ChannelSummary[] } (doc §29). */
|
|
114
|
+
async listChannels() {
|
|
115
|
+
return run(() => this.control.listChannels(), (channels) => ({ status: 200, body: { channels } }));
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* GET /update-check → BundleUpdateStatus. Panel-level read-only DTO (no
|
|
119
|
+
* secrets); the host performs the registry check so the browser never
|
|
120
|
+
* contacts npm itself.
|
|
121
|
+
*/
|
|
122
|
+
async getUpdateCheck() {
|
|
123
|
+
return run(() => this.control.getUpdateStatus(), (status) => ({ status: 200, body: status }));
|
|
124
|
+
}
|
|
125
|
+
/** GET /channels/:channelId/setup → ChannelSetupDescriptor (doc §29). */
|
|
126
|
+
async getSetup(channelId) {
|
|
127
|
+
const found = await this.channelOr404(channelId);
|
|
128
|
+
if (!found.ok)
|
|
129
|
+
return found.result;
|
|
130
|
+
return run(() => this.control.getSetup(channelId), (setup) => ({ status: 200, body: setup }));
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* PUT /channels/:channelId/enabled → ChannelSummary (doc §23).
|
|
134
|
+
* Body: `{ enabled: boolean }`. The control plane persists the intent and
|
|
135
|
+
* starts/stops the runtime accordingly.
|
|
136
|
+
*/
|
|
137
|
+
async setEnabled(channelId, body) {
|
|
138
|
+
const found = await this.channelOr404(channelId);
|
|
139
|
+
if (!found.ok)
|
|
140
|
+
return found.result;
|
|
141
|
+
const parsed = enabledPatchSchema.safeParse(body);
|
|
142
|
+
if (!parsed.success)
|
|
143
|
+
return badInput('enabled must be a boolean').result;
|
|
144
|
+
return run(() => this.control.setEnabled(channelId, parsed.data.enabled), (summary) => ({
|
|
145
|
+
status: 200,
|
|
146
|
+
body: summary,
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
/** PUT /channels/:channelId/setup → save the whole form and reconcile. */
|
|
150
|
+
async applySetup(channelId, body) {
|
|
151
|
+
const found = await this.channelOr404(channelId);
|
|
152
|
+
if (!found.ok)
|
|
153
|
+
return found.result;
|
|
154
|
+
const input = this.setupInput(body);
|
|
155
|
+
if (!input.ok)
|
|
156
|
+
return input.result;
|
|
157
|
+
return run(() => this.control.applySetup(channelId, input.value), (result) => ({
|
|
158
|
+
status: 200,
|
|
159
|
+
body: result,
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
/** PATCH /channels/:channelId/config → { configured, fields } (doc §30). */
|
|
163
|
+
async saveConfig(channelId, body) {
|
|
164
|
+
const found = await this.channelOr404(channelId);
|
|
165
|
+
if (!found.ok)
|
|
166
|
+
return found.result;
|
|
167
|
+
const patch = this.configPatch(body);
|
|
168
|
+
if (!patch.ok)
|
|
169
|
+
return patch.result;
|
|
170
|
+
const direct = async () => {
|
|
171
|
+
await this.control.saveConfig(channelId, patch.value);
|
|
172
|
+
return this.control.getConfiguredState(channelId);
|
|
173
|
+
};
|
|
174
|
+
return run(direct, (state) => ({
|
|
175
|
+
status: 200,
|
|
176
|
+
body: { configured: state.configured, fields: state.fields },
|
|
177
|
+
}));
|
|
178
|
+
}
|
|
179
|
+
/** PUT /channels/:channelId/credentials/:field → { configured, writable } (doc §31). */
|
|
180
|
+
async saveCredential(channelId, field, body) {
|
|
181
|
+
const found = await this.channelOr404(channelId);
|
|
182
|
+
if (!found.ok)
|
|
183
|
+
return found.result;
|
|
184
|
+
const value = this.credentialValue(body);
|
|
185
|
+
if (!value.ok)
|
|
186
|
+
return value.result;
|
|
187
|
+
return run(() => this.control.saveCredential(channelId, field, value.value), (result) => ({
|
|
188
|
+
status: 200,
|
|
189
|
+
body: { configured: result.configured, writable: result.writable },
|
|
190
|
+
}));
|
|
191
|
+
}
|
|
192
|
+
/** POST /channels/:channelId/auth/sessions → 201 PublicAuthSession (doc §32). */
|
|
193
|
+
async beginAuth(channelId, body) {
|
|
194
|
+
const found = await this.channelOr404(channelId);
|
|
195
|
+
if (!found.ok)
|
|
196
|
+
return found.result;
|
|
197
|
+
const begin = this.authBeginInput(body);
|
|
198
|
+
if (!begin.ok)
|
|
199
|
+
return begin.result;
|
|
200
|
+
return run(() => this.control.beginAuth(channelId, begin.value), (session) => ({
|
|
201
|
+
status: 201,
|
|
202
|
+
body: session,
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
/** GET /channels/:channelId/auth/sessions/:sessionId → PublicAuthStatus (doc §32). */
|
|
206
|
+
async pollAuth(channelId, sessionId) {
|
|
207
|
+
const found = await this.channelOr404(channelId);
|
|
208
|
+
if (!found.ok)
|
|
209
|
+
return found.result;
|
|
210
|
+
const sid = requireString(sessionId, 'sessionId');
|
|
211
|
+
if (!sid.ok)
|
|
212
|
+
return sid.result;
|
|
213
|
+
return run(() => this.control.pollAuth(sid.value), (status) => ({ status: 200, body: status }));
|
|
214
|
+
}
|
|
215
|
+
/** POST /channels/:channelId/auth/sessions/:sessionId/input → PublicAuthStatus (doc §32). */
|
|
216
|
+
async submitInput(channelId, sessionId, body) {
|
|
217
|
+
const found = await this.channelOr404(channelId);
|
|
218
|
+
if (!found.ok)
|
|
219
|
+
return found.result;
|
|
220
|
+
const sid = requireString(sessionId, 'sessionId');
|
|
221
|
+
if (!sid.ok)
|
|
222
|
+
return sid.result;
|
|
223
|
+
const input = this.authInput(body);
|
|
224
|
+
if (!input.ok)
|
|
225
|
+
return input.result;
|
|
226
|
+
return run(() => this.control.submitAuthInput(sid.value, input.value), (status) => ({
|
|
227
|
+
status: 200,
|
|
228
|
+
body: status,
|
|
229
|
+
}));
|
|
230
|
+
}
|
|
231
|
+
/** DELETE /channels/:channelId/auth/sessions/:sessionId → 204 (doc §32). */
|
|
232
|
+
async cancelAuth(channelId, sessionId) {
|
|
233
|
+
const found = await this.channelOr404(channelId);
|
|
234
|
+
if (!found.ok)
|
|
235
|
+
return found.result;
|
|
236
|
+
const sid = requireString(sessionId, 'sessionId');
|
|
237
|
+
if (!sid.ok)
|
|
238
|
+
return sid.result;
|
|
239
|
+
return run(() => this.control.cancelAuth(sid.value), () => ({ status: 204, body: undefined }));
|
|
240
|
+
}
|
|
241
|
+
/** GET /channels/:channelId/access → ChannelAccessState. */
|
|
242
|
+
async getAccess(channelId) {
|
|
243
|
+
const found = await this.channelOr404(channelId);
|
|
244
|
+
if (!found.ok)
|
|
245
|
+
return found.result;
|
|
246
|
+
return run(() => this.control.getAccess(channelId), (state) => ({ status: 200, body: state }));
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* PUT /channels/:channelId/access → ChannelAccessState.
|
|
250
|
+
* Body is a strict ChannelAccessPolicy; malformed input is a 400 INVALID_INPUT
|
|
251
|
+
* before it reaches the control plane.
|
|
252
|
+
*/
|
|
253
|
+
async saveAccess(channelId, body) {
|
|
254
|
+
const found = await this.channelOr404(channelId);
|
|
255
|
+
if (!found.ok)
|
|
256
|
+
return found.result;
|
|
257
|
+
const parsed = accessPolicySchema.safeParse(body);
|
|
258
|
+
if (!parsed.success)
|
|
259
|
+
return badInput(accessPolicyMessage(parsed.error)).result;
|
|
260
|
+
return run(() => this.control.saveAccess(channelId, parsed.data), (state) => ({
|
|
261
|
+
status: 200,
|
|
262
|
+
body: state,
|
|
263
|
+
}));
|
|
264
|
+
}
|
|
265
|
+
/** POST /channels/:channelId/access/owner-claims → 201 PublicOwnerClaimSession. */
|
|
266
|
+
async beginOwnerClaim(channelId) {
|
|
267
|
+
const found = await this.channelOr404(channelId);
|
|
268
|
+
if (!found.ok)
|
|
269
|
+
return found.result;
|
|
270
|
+
return run(() => this.control.beginOwnerClaim(channelId), (session) => ({
|
|
271
|
+
status: 201,
|
|
272
|
+
body: session,
|
|
273
|
+
}));
|
|
274
|
+
}
|
|
275
|
+
/** GET /channels/:channelId/access/owner-claims/:claimId → PublicOwnerClaimSession. */
|
|
276
|
+
async getOwnerClaim(channelId, claimId) {
|
|
277
|
+
const found = await this.channelOr404(channelId);
|
|
278
|
+
if (!found.ok)
|
|
279
|
+
return found.result;
|
|
280
|
+
const cid = requireString(claimId, 'claimId');
|
|
281
|
+
if (!cid.ok)
|
|
282
|
+
return cid.result;
|
|
283
|
+
return run(() => this.control.getOwnerClaim(channelId, cid.value), (session) => ({
|
|
284
|
+
status: 200,
|
|
285
|
+
body: session,
|
|
286
|
+
}));
|
|
287
|
+
}
|
|
288
|
+
/** POST /channels/:channelId/access/owner-claims/:claimId/confirm → ChannelAccessState. */
|
|
289
|
+
async confirmOwnerClaim(channelId, claimId) {
|
|
290
|
+
const found = await this.channelOr404(channelId);
|
|
291
|
+
if (!found.ok)
|
|
292
|
+
return found.result;
|
|
293
|
+
const cid = requireString(claimId, 'claimId');
|
|
294
|
+
if (!cid.ok)
|
|
295
|
+
return cid.result;
|
|
296
|
+
return run(() => this.control.confirmOwnerClaim(channelId, cid.value), (state) => ({
|
|
297
|
+
status: 200,
|
|
298
|
+
body: state,
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
/** DELETE /channels/:channelId/access/owner-claims/:claimId → 204. */
|
|
302
|
+
async cancelOwnerClaim(channelId, claimId) {
|
|
303
|
+
const found = await this.channelOr404(channelId);
|
|
304
|
+
if (!found.ok)
|
|
305
|
+
return found.result;
|
|
306
|
+
const cid = requireString(claimId, 'claimId');
|
|
307
|
+
if (!cid.ok)
|
|
308
|
+
return cid.result;
|
|
309
|
+
return run(() => this.control.cancelOwnerClaim(channelId, cid.value), () => ({
|
|
310
|
+
status: 204,
|
|
311
|
+
body: undefined,
|
|
312
|
+
}));
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* GET /channels/:channelId/conversations → { conversations } — observed
|
|
316
|
+
* conversation identities (plan §35). Display/mapping metadata only.
|
|
317
|
+
*/
|
|
318
|
+
async listConversations(channelId) {
|
|
319
|
+
const found = await this.channelOr404(channelId);
|
|
320
|
+
if (!found.ok)
|
|
321
|
+
return found.result;
|
|
322
|
+
return run(() => this.control.listConversations(channelId), (conversations) => ({
|
|
323
|
+
status: 200,
|
|
324
|
+
body: { conversations },
|
|
325
|
+
}));
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* GET /channels/:channelId/conversations/by-external/:externalId → the one
|
|
329
|
+
* observed identity matching an optional human-facing id, or 404 when
|
|
330
|
+
* the bot has never seen it. Callers must not guess a canonical id.
|
|
331
|
+
*/
|
|
332
|
+
async findConversation(channelId, externalId) {
|
|
333
|
+
const found = await this.channelOr404(channelId);
|
|
334
|
+
if (!found.ok)
|
|
335
|
+
return found.result;
|
|
336
|
+
const eid = requireString(externalId, 'externalId');
|
|
337
|
+
if (!eid.ok)
|
|
338
|
+
return eid.result;
|
|
339
|
+
return run(() => this.control.findConversation(channelId, eid.value), (identity) => identity
|
|
340
|
+
? { status: 200, body: identity }
|
|
341
|
+
: { status: 404, body: errorBody('CONVERSATION_NOT_FOUND', 'no observed conversation for that external id') });
|
|
342
|
+
}
|
|
343
|
+
/**
|
|
344
|
+
* Match a path under `/dsh-channels/api/v2` (the prefix is stripped by the
|
|
345
|
+
* caller) and dispatch. Returns 404 for unknown routes.
|
|
346
|
+
*/
|
|
347
|
+
async handle(method, pathname, body) {
|
|
348
|
+
const clean = pathname.replace(/\/+$/, '');
|
|
349
|
+
if (method === 'GET' && clean === '/channels')
|
|
350
|
+
return this.listChannels();
|
|
351
|
+
if (method === 'GET' && clean === '/update-check')
|
|
352
|
+
return this.getUpdateCheck();
|
|
353
|
+
const setup = /^\/channels\/([^/]+)\/setup$/.exec(clean);
|
|
354
|
+
if (method === 'GET' && setup)
|
|
355
|
+
return this.getSetup(safeDecode(setup[1]));
|
|
356
|
+
if (method === 'PUT' && setup)
|
|
357
|
+
return this.applySetup(safeDecode(setup[1]), body);
|
|
358
|
+
const enabled = /^\/channels\/([^/]+)\/enabled$/.exec(clean);
|
|
359
|
+
if (method === 'PUT' && enabled)
|
|
360
|
+
return this.setEnabled(safeDecode(enabled[1]), body);
|
|
361
|
+
const config = /^\/channels\/([^/]+)\/config$/.exec(clean);
|
|
362
|
+
if (method === 'PATCH' && config)
|
|
363
|
+
return this.saveConfig(safeDecode(config[1]), body);
|
|
364
|
+
const credential = /^\/channels\/([^/]+)\/credentials\/([^/]+)$/.exec(clean);
|
|
365
|
+
if (method === 'PUT' && credential) {
|
|
366
|
+
return this.saveCredential(safeDecode(credential[1]), safeDecode(credential[2]), body);
|
|
367
|
+
}
|
|
368
|
+
const begin = /^\/channels\/([^/]+)\/auth\/sessions$/.exec(clean);
|
|
369
|
+
if (method === 'POST' && begin)
|
|
370
|
+
return this.beginAuth(safeDecode(begin[1]), body);
|
|
371
|
+
const session = /^\/channels\/([^/]+)\/auth\/sessions\/([^/]+)$/.exec(clean);
|
|
372
|
+
if (session) {
|
|
373
|
+
if (method === 'GET')
|
|
374
|
+
return this.pollAuth(safeDecode(session[1]), safeDecode(session[2]));
|
|
375
|
+
if (method === 'DELETE')
|
|
376
|
+
return this.cancelAuth(safeDecode(session[1]), safeDecode(session[2]));
|
|
377
|
+
}
|
|
378
|
+
const input = /^\/channels\/([^/]+)\/auth\/sessions\/([^/]+)\/input$/.exec(clean);
|
|
379
|
+
if (method === 'POST' && input) {
|
|
380
|
+
return this.submitInput(safeDecode(input[1]), safeDecode(input[2]), body);
|
|
381
|
+
}
|
|
382
|
+
// ---- access control ---------------------------------------
|
|
383
|
+
const access = /^\/channels\/([^/]+)\/access$/.exec(clean);
|
|
384
|
+
if (access) {
|
|
385
|
+
const id = safeDecode(access[1]);
|
|
386
|
+
if (method === 'GET')
|
|
387
|
+
return this.getAccess(id);
|
|
388
|
+
if (method === 'PUT')
|
|
389
|
+
return this.saveAccess(id, body);
|
|
390
|
+
}
|
|
391
|
+
const beginClaim = /^\/channels\/([^/]+)\/access\/owner-claims$/.exec(clean);
|
|
392
|
+
if (method === 'POST' && beginClaim)
|
|
393
|
+
return this.beginOwnerClaim(safeDecode(beginClaim[1]));
|
|
394
|
+
const claimConfirm = /^\/channels\/([^/]+)\/access\/owner-claims\/([^/]+)\/confirm$/.exec(clean);
|
|
395
|
+
if (method === 'POST' && claimConfirm) {
|
|
396
|
+
return this.confirmOwnerClaim(safeDecode(claimConfirm[1]), safeDecode(claimConfirm[2]));
|
|
397
|
+
}
|
|
398
|
+
const claim = /^\/channels\/([^/]+)\/access\/owner-claims\/([^/]+)$/.exec(clean);
|
|
399
|
+
if (claim) {
|
|
400
|
+
const id = safeDecode(claim[1]);
|
|
401
|
+
const claimId = safeDecode(claim[2]);
|
|
402
|
+
if (method === 'GET')
|
|
403
|
+
return this.getOwnerClaim(id, claimId);
|
|
404
|
+
if (method === 'DELETE')
|
|
405
|
+
return this.cancelOwnerClaim(id, claimId);
|
|
406
|
+
}
|
|
407
|
+
// ---- conversation identity directory ----------------------
|
|
408
|
+
const conversations = /^\/channels\/([^/]+)\/conversations$/.exec(clean);
|
|
409
|
+
if (method === 'GET' && conversations) {
|
|
410
|
+
return this.listConversations(safeDecode(conversations[1]));
|
|
411
|
+
}
|
|
412
|
+
const byExternal = /^\/channels\/([^/]+)\/conversations\/by-external\/(.+)$/.exec(clean);
|
|
413
|
+
if (method === 'GET' && byExternal) {
|
|
414
|
+
return this.findConversation(safeDecode(byExternal[1]), safeDecode(byExternal[2]));
|
|
415
|
+
}
|
|
416
|
+
return { status: 404, body: errorBody('NOT_FOUND', 'no such endpoint') };
|
|
417
|
+
}
|
|
418
|
+
// ---- strict body schema validation -------------------------------------
|
|
419
|
+
/** PATCH config body: a flat JSON object (never nested). */
|
|
420
|
+
configPatch(value) {
|
|
421
|
+
const parsed = jsonObjectSchema.safeParse(value);
|
|
422
|
+
if (!parsed.success)
|
|
423
|
+
return badInput('config patch must be a JSON object');
|
|
424
|
+
return { ok: true, value: parsed.data };
|
|
425
|
+
}
|
|
426
|
+
/** PUT credentials body: `{ value: string }` (empty string clears). */
|
|
427
|
+
credentialValue(body) {
|
|
428
|
+
const parsed = credentialValueSchema.safeParse(body);
|
|
429
|
+
if (!parsed.success)
|
|
430
|
+
return badInput('value must be a string (empty clears it)');
|
|
431
|
+
return { ok: true, value: parsed.data.value };
|
|
432
|
+
}
|
|
433
|
+
/** PUT setup body: `{ config: object, credentials: Record<string,string> }`. */
|
|
434
|
+
setupInput(body) {
|
|
435
|
+
const root = jsonObjectSchema.safeParse(body);
|
|
436
|
+
if (!root.success)
|
|
437
|
+
return badInput('setup must be a JSON object');
|
|
438
|
+
// Legacy tolerance: an absent or null config/credentials section means `{}`.
|
|
439
|
+
const record = root.data;
|
|
440
|
+
const config = record.config ?? {};
|
|
441
|
+
const credentials = record.credentials ?? {};
|
|
442
|
+
const reconcile = record.reconcile;
|
|
443
|
+
const parsedConfig = jsonObjectSchema.safeParse(config);
|
|
444
|
+
if (!parsedConfig.success) {
|
|
445
|
+
return badInput('config and credentials must be JSON objects');
|
|
446
|
+
}
|
|
447
|
+
const parsedCredentials = credentialsRecordSchema.safeParse(credentials);
|
|
448
|
+
if (!parsedCredentials.success) {
|
|
449
|
+
const first = parsedCredentials.error.issues[0];
|
|
450
|
+
if (first && first.path.length === 1) {
|
|
451
|
+
return badInput(`credential ${String(first.path[0])} must be a string (empty clears it)`);
|
|
452
|
+
}
|
|
453
|
+
return badInput('config and credentials must be JSON objects');
|
|
454
|
+
}
|
|
455
|
+
if (reconcile !== undefined && typeof reconcile !== 'boolean') {
|
|
456
|
+
return badInput('reconcile must be a boolean when present');
|
|
457
|
+
}
|
|
458
|
+
return { ok: true, value: { config: parsedConfig.data, credentials: parsedCredentials.data, reconcile } };
|
|
459
|
+
}
|
|
460
|
+
/** POST auth/sessions body: `{ method: AuthMethod, accountId?: string }`. */
|
|
461
|
+
authBeginInput(body) {
|
|
462
|
+
const parsed = authBeginSchema.safeParse(body);
|
|
463
|
+
if (!parsed.success) {
|
|
464
|
+
const first = parsed.error.issues[0];
|
|
465
|
+
const message = first?.path[0] === 'accountId'
|
|
466
|
+
? 'accountId must be a non-empty string when present'
|
|
467
|
+
: 'method must be one of ' + AUTH_METHODS.join(', ');
|
|
468
|
+
return badInput(message);
|
|
469
|
+
}
|
|
470
|
+
return { ok: true, value: parsed.data };
|
|
471
|
+
}
|
|
472
|
+
/** POST session/input body: `{ kind: 'verification-code', value: string }`. */
|
|
473
|
+
authInput(body) {
|
|
474
|
+
const parsed = authInputSchema.safeParse(body);
|
|
475
|
+
if (!parsed.success) {
|
|
476
|
+
const first = parsed.error.issues[0];
|
|
477
|
+
const message = first?.path[0] === 'value' ? 'value must be a non-empty string' : 'kind must be "verification-code"';
|
|
478
|
+
return badInput(message);
|
|
479
|
+
}
|
|
480
|
+
return { ok: true, value: parsed.data };
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
/**
|
|
484
|
+
* POST session/input body: `{ kind: 'verification-code', value: string }`.
|
|
485
|
+
* Shared with the v1 routes so both API generations check the same shape.
|
|
486
|
+
*/
|
|
487
|
+
export const authInputSchema = z.object({
|
|
488
|
+
kind: z.literal('verification-code'),
|
|
489
|
+
value: z.string().min(1),
|
|
490
|
+
}, 'kind must be "verification-code"').loose();
|
|
491
|
+
/** PUT credentials body: `{ value: string }`. An empty string clears the credential. */
|
|
492
|
+
const credentialValueSchema = z.object({
|
|
493
|
+
value: z.string(),
|
|
494
|
+
}, 'value must be a string').loose();
|
|
495
|
+
/** setup `credentials`: map of field name → value (empty string clears the credential). */
|
|
496
|
+
const credentialsRecordSchema = z.record(z.string(), z.string().trim());
|
|
497
|
+
/** POST auth/sessions body: `{ method: AuthMethod, accountId?: string }`. */
|
|
498
|
+
const authBeginSchema = z.object({
|
|
499
|
+
method: z.enum(AUTH_METHODS),
|
|
500
|
+
accountId: z.string().min(1).optional(),
|
|
501
|
+
}, 'method must be one of ' + AUTH_METHODS.join(', ')).loose();
|
|
502
|
+
/** PUT channels/:id/enabled body: `{ enabled: boolean }`. */
|
|
503
|
+
const enabledPatchSchema = z.object({
|
|
504
|
+
enabled: z.boolean(),
|
|
505
|
+
}, 'enabled must be a boolean').loose();
|
|
506
|
+
// ---------------------------------------------------------------------------
|
|
507
|
+
// Access control body schema
|
|
508
|
+
// ---------------------------------------------------------------------------
|
|
509
|
+
/**
|
|
510
|
+
* PUT /channels/:id/access reuses the shared strict policy schema so every
|
|
511
|
+
* process boundary accepts exactly the same policy versions and semantics.
|
|
512
|
+
* The parsed value is passed to `control.saveAccess`, which re-validates it
|
|
513
|
+
* against the channel's declared descriptor (mentions/groups/DM capability).
|
|
514
|
+
*/
|
|
515
|
+
const accessPolicySchema = channelAccessPolicySchema;
|
|
516
|
+
/** Human-friendly message for the first zod issue of an access policy body. */
|
|
517
|
+
function accessPolicyMessage(err) {
|
|
518
|
+
const first = err.issues[0];
|
|
519
|
+
if (!first)
|
|
520
|
+
return 'invalid access policy';
|
|
521
|
+
const at = first.path.length > 0 ? ` (${first.path.join('.')})` : '';
|
|
522
|
+
return `invalid access policy${at}: ${first.message}`;
|
|
523
|
+
}
|
|
524
|
+
//# sourceMappingURL=routes-v2.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes-v2.js","sourceRoot":"","sources":["../../src/host/routes-v2.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-channels host API routes (M1). Read-only dashboard + Weixin QR auth loop.
|
|
3
|
+
*
|
|
4
|
+
* Lives on `/dsh-channels/api/v1` and talks only to `ctx.channels`
|
|
5
|
+
* (ChannelService `get` / `list`) and each adapter's beginAuth / pollAuth /
|
|
6
|
+
* submitAuthInput / getHealth hooks. No lifecycle refactor (M2), no other platform
|
|
7
|
+
* config work.
|
|
8
|
+
*
|
|
9
|
+
* Auth challenges are stored host-side in a Map keyed by challenge id; only the
|
|
10
|
+
* public subset (id/instruction/qrUrl/expiresAt) is ever put on the wire.
|
|
11
|
+
* Adapter payloads and credentials never leave the process.
|
|
12
|
+
*/
|
|
13
|
+
import type { AuthInput, ChannelAdapter, ChannelHealth } from '@krischoichoi/channel-core';
|
|
14
|
+
/** Client-safe view status enum (spec §23). */
|
|
15
|
+
export type ChannelStatus = 'connected' | 'degraded' | 'unconfigured' | 'error';
|
|
16
|
+
/** The fixed four-channel catalog the dashboard always shows, mounted or not. */
|
|
17
|
+
export declare const CHANNEL_CATALOG: readonly ["weixin", "qq", "dingtalk", "lark"];
|
|
18
|
+
export type ChannelCatalogId = (typeof CHANNEL_CATALOG)[number];
|
|
19
|
+
/** Client-safe view of one channel (spec: id/enabled/configured/mounted/status/health/capabilities/lastError). */
|
|
20
|
+
export interface ChannelView {
|
|
21
|
+
id: string;
|
|
22
|
+
enabled: boolean;
|
|
23
|
+
configured: boolean;
|
|
24
|
+
mounted: boolean;
|
|
25
|
+
status: ChannelStatus;
|
|
26
|
+
health?: ChannelHealth | null;
|
|
27
|
+
capabilities?: Record<string, boolean | string> | null;
|
|
28
|
+
lastError?: string | null;
|
|
29
|
+
}
|
|
30
|
+
/** Public auth challenge — never includes the adapter's payload. */
|
|
31
|
+
export interface PublicAuthChallenge {
|
|
32
|
+
id: string;
|
|
33
|
+
instruction: string;
|
|
34
|
+
qrUrl?: string;
|
|
35
|
+
expiresAt?: number;
|
|
36
|
+
}
|
|
37
|
+
export interface PublicAuthPoll {
|
|
38
|
+
state: 'pending' | 'authenticated' | 'expired' | 'failed';
|
|
39
|
+
detail?: string;
|
|
40
|
+
/** Client hint: 'verify-code' | 'confirm' | 'waiting-scan' | undefined. */
|
|
41
|
+
prompt?: string;
|
|
42
|
+
}
|
|
43
|
+
/** Minimal structural view of the ChannelService surface we consume. */
|
|
44
|
+
export interface ChannelsLike {
|
|
45
|
+
get(id: string): ChannelAdapter | undefined;
|
|
46
|
+
list(): ChannelAdapter[];
|
|
47
|
+
}
|
|
48
|
+
export interface ChannelRoutesOptions {
|
|
49
|
+
/** Injectable clock (tests). */
|
|
50
|
+
now?: () => number;
|
|
51
|
+
}
|
|
52
|
+
export interface ApiResult {
|
|
53
|
+
status: number;
|
|
54
|
+
body: unknown;
|
|
55
|
+
}
|
|
56
|
+
export declare class ChannelApi {
|
|
57
|
+
private readonly channels;
|
|
58
|
+
private readonly now;
|
|
59
|
+
/** Host-side store of full auth challenges: never exposes payload. */
|
|
60
|
+
private readonly challenges;
|
|
61
|
+
constructor(channels: ChannelsLike, options?: ChannelRoutesOptions);
|
|
62
|
+
private adapter;
|
|
63
|
+
private projectCapabilities;
|
|
64
|
+
/** Map a ChannelHealth to the spec §23 status enum. */
|
|
65
|
+
private mapStatus;
|
|
66
|
+
private buildView;
|
|
67
|
+
/** A neutral, unmounted view for a known catalog id with no live adapter. */
|
|
68
|
+
private offlineView;
|
|
69
|
+
/** GET /channels → ChannelView[] for the fixed four-channel catalog. */
|
|
70
|
+
listChannels(): Promise<ApiResult>;
|
|
71
|
+
/** GET /channels/:id → ChannelView. */
|
|
72
|
+
getChannel(id: string): Promise<ApiResult>;
|
|
73
|
+
/** POST /channels/:id/auth/start → PublicAuthChallenge. */
|
|
74
|
+
startAuth(id: string): Promise<ApiResult>;
|
|
75
|
+
/** Resolve a stored challenge or a structured error result. */
|
|
76
|
+
private storedChallenge;
|
|
77
|
+
/** POST /channels/:id/auth/poll → PublicAuthPoll. */
|
|
78
|
+
pollAuth(id: string, challengeId: unknown): Promise<ApiResult>;
|
|
79
|
+
/** POST /channels/:id/auth/input → PublicAuthPoll. */
|
|
80
|
+
inputAuth(id: string, challengeId: unknown, input: AuthInput | undefined): Promise<ApiResult>;
|
|
81
|
+
/**
|
|
82
|
+
* Match a path under `/dsh-channels/api/v1` (the prefix is stripped by the
|
|
83
|
+
* caller) and dispatch to the appropriate handler. Returns 404 for unknown routes.
|
|
84
|
+
*/
|
|
85
|
+
handle(method: string, pathname: string, body: unknown): Promise<ApiResult>;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=routes.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/host/routes.ts"],"names":[],"mappings":"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"}
|