@authrim/setup 0.1.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.
Files changed (78) hide show
  1. package/README.md +303 -0
  2. package/dist/__tests__/config.test.d.ts +5 -0
  3. package/dist/__tests__/config.test.d.ts.map +1 -0
  4. package/dist/__tests__/config.test.js +115 -0
  5. package/dist/__tests__/config.test.js.map +1 -0
  6. package/dist/__tests__/keys.test.d.ts +5 -0
  7. package/dist/__tests__/keys.test.d.ts.map +1 -0
  8. package/dist/__tests__/keys.test.js +87 -0
  9. package/dist/__tests__/keys.test.js.map +1 -0
  10. package/dist/__tests__/naming.test.d.ts +5 -0
  11. package/dist/__tests__/naming.test.d.ts.map +1 -0
  12. package/dist/__tests__/naming.test.js +84 -0
  13. package/dist/__tests__/naming.test.js.map +1 -0
  14. package/dist/cli/commands/config.d.ts +13 -0
  15. package/dist/cli/commands/config.d.ts.map +1 -0
  16. package/dist/cli/commands/config.js +231 -0
  17. package/dist/cli/commands/config.js.map +1 -0
  18. package/dist/cli/commands/deploy.d.ts +21 -0
  19. package/dist/cli/commands/deploy.d.ts.map +1 -0
  20. package/dist/cli/commands/deploy.js +304 -0
  21. package/dist/cli/commands/deploy.js.map +1 -0
  22. package/dist/cli/commands/init.d.ts +14 -0
  23. package/dist/cli/commands/init.d.ts.map +1 -0
  24. package/dist/cli/commands/init.js +1248 -0
  25. package/dist/cli/commands/init.js.map +1 -0
  26. package/dist/core/admin.d.ts +64 -0
  27. package/dist/core/admin.d.ts.map +1 -0
  28. package/dist/core/admin.js +247 -0
  29. package/dist/core/admin.js.map +1 -0
  30. package/dist/core/cloudflare.d.ts +157 -0
  31. package/dist/core/cloudflare.d.ts.map +1 -0
  32. package/dist/core/cloudflare.js +452 -0
  33. package/dist/core/cloudflare.js.map +1 -0
  34. package/dist/core/config.d.ts +891 -0
  35. package/dist/core/config.d.ts.map +1 -0
  36. package/dist/core/config.js +208 -0
  37. package/dist/core/config.js.map +1 -0
  38. package/dist/core/deploy.d.ts +81 -0
  39. package/dist/core/deploy.d.ts.map +1 -0
  40. package/dist/core/deploy.js +389 -0
  41. package/dist/core/deploy.js.map +1 -0
  42. package/dist/core/keys.d.ts +111 -0
  43. package/dist/core/keys.d.ts.map +1 -0
  44. package/dist/core/keys.js +287 -0
  45. package/dist/core/keys.js.map +1 -0
  46. package/dist/core/lock.d.ts +220 -0
  47. package/dist/core/lock.d.ts.map +1 -0
  48. package/dist/core/lock.js +230 -0
  49. package/dist/core/lock.js.map +1 -0
  50. package/dist/core/naming.d.ts +151 -0
  51. package/dist/core/naming.d.ts.map +1 -0
  52. package/dist/core/naming.js +209 -0
  53. package/dist/core/naming.js.map +1 -0
  54. package/dist/core/source.d.ts +68 -0
  55. package/dist/core/source.d.ts.map +1 -0
  56. package/dist/core/source.js +285 -0
  57. package/dist/core/source.js.map +1 -0
  58. package/dist/core/wrangler.d.ts +87 -0
  59. package/dist/core/wrangler.d.ts.map +1 -0
  60. package/dist/core/wrangler.js +398 -0
  61. package/dist/core/wrangler.js.map +1 -0
  62. package/dist/index.d.ts +11 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +117 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/web/api.d.ts +21 -0
  67. package/dist/web/api.d.ts.map +1 -0
  68. package/dist/web/api.js +423 -0
  69. package/dist/web/api.js.map +1 -0
  70. package/dist/web/server.d.ts +12 -0
  71. package/dist/web/server.d.ts.map +1 -0
  72. package/dist/web/server.js +112 -0
  73. package/dist/web/server.js.map +1 -0
  74. package/dist/web/ui.d.ts +7 -0
  75. package/dist/web/ui.d.ts.map +1 -0
  76. package/dist/web/ui.js +765 -0
  77. package/dist/web/ui.js.map +1 -0
  78. package/package.json +61 -0
@@ -0,0 +1,423 @@
1
+ /**
2
+ * API Routes for Authrim Setup Web UI
3
+ *
4
+ * Provides REST API endpoints for the setup wizard.
5
+ *
6
+ * Security Notes:
7
+ * - This API is designed to be accessed from localhost only
8
+ * - A session token is generated on server start to prevent unauthorized access
9
+ * - Operations are serialized to prevent race conditions
10
+ */
11
+ import { Hono } from 'hono';
12
+ import { z } from 'zod';
13
+ import { existsSync } from 'node:fs';
14
+ import { readFile } from 'node:fs/promises';
15
+ import { resolve } from 'node:path';
16
+ import { randomBytes } from 'node:crypto';
17
+ import { isWranglerInstalled, checkAuth, provisionResources, } from '../core/cloudflare.js';
18
+ import { AuthrimConfigSchema, createDefaultConfig } from '../core/config.js';
19
+ import { generateAllSecrets, saveKeysToDirectory } from '../core/keys.js';
20
+ import { createLockFile, saveLockFile, loadLockFile } from '../core/lock.js';
21
+ import { generateWranglerConfig, toToml } from '../core/wrangler.js';
22
+ import { deployAll, uploadSecrets } from '../core/deploy.js';
23
+ import { CORE_WORKER_COMPONENTS } from '../core/naming.js';
24
+ import { completeInitialSetup } from '../core/admin.js';
25
+ import { writeFile } from 'node:fs/promises';
26
+ import { join } from 'node:path';
27
+ // =============================================================================
28
+ // Session & Security
29
+ // =============================================================================
30
+ /**
31
+ * Session token for API authentication (generated on server start)
32
+ * This is embedded in the HTML page served to the browser
33
+ */
34
+ let sessionToken = '';
35
+ /**
36
+ * Generate a new session token
37
+ */
38
+ export function generateSessionToken() {
39
+ sessionToken = randomBytes(32).toString('hex');
40
+ return sessionToken;
41
+ }
42
+ /**
43
+ * Get current session token (for embedding in HTML)
44
+ */
45
+ export function getSessionToken() {
46
+ return sessionToken;
47
+ }
48
+ // =============================================================================
49
+ // Operation Lock (prevents concurrent state mutations)
50
+ // =============================================================================
51
+ let operationLock = Promise.resolve();
52
+ /**
53
+ * Acquire operation lock to serialize state mutations
54
+ */
55
+ async function withLock(operation) {
56
+ // Wait for previous operation to complete
57
+ await operationLock;
58
+ // Create new lock for this operation
59
+ let releaseLock;
60
+ operationLock = new Promise((resolve) => {
61
+ releaseLock = resolve;
62
+ });
63
+ try {
64
+ return await operation();
65
+ }
66
+ finally {
67
+ releaseLock();
68
+ }
69
+ }
70
+ const state = {
71
+ status: 'idle',
72
+ config: null,
73
+ auth: null,
74
+ progress: [],
75
+ error: null,
76
+ deployResults: [],
77
+ };
78
+ function addProgress(message) {
79
+ state.progress.push(message);
80
+ }
81
+ function clearProgress() {
82
+ state.progress = [];
83
+ }
84
+ /**
85
+ * Sanitize error messages to prevent information leakage
86
+ */
87
+ function sanitizeError(error) {
88
+ const message = error instanceof Error ? error.message : String(error);
89
+ // Remove potential file paths and secrets
90
+ return message
91
+ .replace(/\/[^\s:]+/g, '[path]')
92
+ .replace(/\\[^\s:]+/g, '[path]')
93
+ .replace(/[a-f0-9]{32,}/gi, '[redacted]');
94
+ }
95
+ // =============================================================================
96
+ // API Routes
97
+ // =============================================================================
98
+ export function createApiRoutes() {
99
+ const api = new Hono();
100
+ // Session token validation middleware for mutating operations
101
+ const validateSession = async (c, next) => {
102
+ const token = c.req.header('X-Session-Token');
103
+ if (!sessionToken || token !== sessionToken) {
104
+ return c.json({ error: 'Invalid or missing session token' }, 401);
105
+ }
106
+ await next();
107
+ };
108
+ // Apply session validation to all POST/PUT/DELETE routes
109
+ api.use('/config', validateSession);
110
+ api.use('/config/*', validateSession);
111
+ api.use('/keys/*', validateSession);
112
+ api.use('/provision', validateSession);
113
+ api.use('/wrangler/*', validateSession);
114
+ api.use('/deploy', validateSession);
115
+ api.use('/reset', validateSession);
116
+ api.use('/admin/*', validateSession);
117
+ // Get current state (no auth required - read-only)
118
+ api.get('/state', (c) => {
119
+ return c.json(state);
120
+ });
121
+ // Check prerequisites (no auth required - read-only)
122
+ api.get('/prerequisites', async (c) => {
123
+ const wranglerInstalled = await isWranglerInstalled();
124
+ const auth = await checkAuth();
125
+ state.auth = auth;
126
+ return c.json({
127
+ wranglerInstalled,
128
+ auth,
129
+ });
130
+ });
131
+ // Load existing config (no auth required - read-only)
132
+ api.get('/config', async (c) => {
133
+ const configPath = 'authrim-config.json';
134
+ if (!existsSync(configPath)) {
135
+ return c.json({ exists: false, config: null });
136
+ }
137
+ try {
138
+ const content = await readFile(configPath, 'utf-8');
139
+ const config = JSON.parse(content);
140
+ state.config = config;
141
+ return c.json({ exists: true, config });
142
+ }
143
+ catch (error) {
144
+ return c.json({ exists: false, error: sanitizeError(error) }, 500);
145
+ }
146
+ });
147
+ // Save config (with lock to prevent race conditions)
148
+ api.post('/config', async (c) => {
149
+ return withLock(async () => {
150
+ try {
151
+ const body = await c.req.json();
152
+ const config = AuthrimConfigSchema.parse(body);
153
+ await writeFile('authrim-config.json', JSON.stringify(config, null, 2));
154
+ state.config = config;
155
+ return c.json({ success: true });
156
+ }
157
+ catch (error) {
158
+ if (error instanceof z.ZodError) {
159
+ return c.json({ success: false, errors: error.errors }, 400);
160
+ }
161
+ return c.json({ success: false, error: sanitizeError(error) }, 500);
162
+ }
163
+ });
164
+ });
165
+ // Create default config (with lock)
166
+ api.post('/config/default', async (c) => {
167
+ return withLock(async () => {
168
+ try {
169
+ const body = await c.req.json();
170
+ const { env = 'prod', domain } = body;
171
+ const config = createDefaultConfig(env);
172
+ // Update URLs if domain is provided
173
+ if (domain) {
174
+ config.urls = {
175
+ api: { custom: domain, auto: `https://${env}-ar-router.workers.dev` },
176
+ loginUi: { custom: null, auto: `https://${env}-ar-ui.pages.dev` },
177
+ adminUi: { custom: null, auto: `https://${env}-ar-ui.pages.dev/admin` },
178
+ };
179
+ }
180
+ state.config = config;
181
+ return c.json({ success: true, config });
182
+ }
183
+ catch (error) {
184
+ return c.json({ success: false, error: sanitizeError(error) }, 500);
185
+ }
186
+ });
187
+ });
188
+ // Generate keys (with lock)
189
+ api.post('/keys/generate', async (c) => {
190
+ return withLock(async () => {
191
+ try {
192
+ const body = await c.req.json();
193
+ const { keyId, keysDir = '.keys' } = body;
194
+ addProgress('Generating cryptographic keys...');
195
+ const secrets = generateAllSecrets(keyId);
196
+ addProgress('Saving keys to directory...');
197
+ await saveKeysToDirectory(secrets, keysDir);
198
+ addProgress('Keys generated successfully');
199
+ // Only return public information
200
+ return c.json({
201
+ success: true,
202
+ keyId: secrets.keyPair.keyId,
203
+ publicKeyJwk: secrets.keyPair.publicKeyJwk,
204
+ });
205
+ }
206
+ catch (error) {
207
+ state.error = sanitizeError(error);
208
+ return c.json({ success: false, error: sanitizeError(error) }, 500);
209
+ }
210
+ });
211
+ });
212
+ // Provision Cloudflare resources (with lock)
213
+ api.post('/provision', async (c) => {
214
+ return withLock(async () => {
215
+ try {
216
+ const body = await c.req.json();
217
+ const { env } = body;
218
+ state.status = 'provisioning';
219
+ clearProgress();
220
+ addProgress(`Provisioning Cloudflare resources for ${env}...`);
221
+ const resources = await provisionResources({
222
+ env,
223
+ createD1: true,
224
+ createKV: true,
225
+ createQueues: body.createQueues || false,
226
+ createR2: body.createR2 || false,
227
+ onProgress: addProgress,
228
+ });
229
+ addProgress('Creating lock file...');
230
+ const lock = createLockFile(env, resources);
231
+ await saveLockFile(lock);
232
+ state.status = 'configuring';
233
+ addProgress('Provisioning complete!');
234
+ return c.json({
235
+ success: true,
236
+ resources,
237
+ lock,
238
+ });
239
+ }
240
+ catch (error) {
241
+ state.status = 'error';
242
+ state.error = sanitizeError(error);
243
+ return c.json({ success: false, error: sanitizeError(error) }, 500);
244
+ }
245
+ });
246
+ });
247
+ // Generate wrangler configs (with lock)
248
+ api.post('/wrangler/generate', async (c) => {
249
+ return withLock(async () => {
250
+ try {
251
+ const body = await c.req.json();
252
+ const { env, rootDir = '.' } = body;
253
+ // Load lock file
254
+ const lock = await loadLockFile();
255
+ if (!lock) {
256
+ return c.json({ success: false, error: 'Lock file not found' }, 400);
257
+ }
258
+ // Load config
259
+ let config;
260
+ if (state.config) {
261
+ config = AuthrimConfigSchema.parse(state.config);
262
+ }
263
+ else {
264
+ config = createDefaultConfig(env);
265
+ }
266
+ addProgress('Generating wrangler.toml files...');
267
+ // Build resource IDs from lock file
268
+ const resourceIds = {
269
+ d1: lock.d1,
270
+ kv: Object.fromEntries(Object.entries(lock.kv).map(([k, v]) => [k, { id: v.id, name: v.name }])),
271
+ queues: lock.queues,
272
+ r2: lock.r2,
273
+ };
274
+ // Generate and save wrangler configs for each component
275
+ const generatedComponents = [];
276
+ for (const component of CORE_WORKER_COMPONENTS) {
277
+ const componentDir = join(rootDir, 'packages', component);
278
+ if (!existsSync(componentDir)) {
279
+ continue;
280
+ }
281
+ const wranglerConfig = generateWranglerConfig(component, config, resourceIds);
282
+ const tomlContent = toToml(wranglerConfig);
283
+ const tomlPath = join(componentDir, `wrangler.${env}.toml`);
284
+ await writeFile(tomlPath, tomlContent, 'utf-8');
285
+ generatedComponents.push(component);
286
+ }
287
+ addProgress('Wrangler configs generated!');
288
+ return c.json({
289
+ success: true,
290
+ components: generatedComponents,
291
+ });
292
+ }
293
+ catch (error) {
294
+ return c.json({ success: false, error: sanitizeError(error) }, 500);
295
+ }
296
+ });
297
+ });
298
+ // Deploy (with lock - long-running operation)
299
+ api.post('/deploy', async (c) => {
300
+ return withLock(async () => {
301
+ try {
302
+ const body = await c.req.json();
303
+ const { env, rootDir = '.', dryRun = false, components } = body;
304
+ state.status = 'deploying';
305
+ clearProgress();
306
+ // Upload secrets first (secrets are read but not stored in state)
307
+ if (!dryRun && existsSync('.keys')) {
308
+ addProgress('Uploading secrets...');
309
+ const secrets = {};
310
+ const secretFiles = [
311
+ { file: '.keys/private.pem', name: 'PRIVATE_KEY_PEM' },
312
+ { file: '.keys/rp_token_encryption_key.txt', name: 'RP_TOKEN_ENCRYPTION_KEY' },
313
+ { file: '.keys/admin_api_secret.txt', name: 'ADMIN_API_SECRET' },
314
+ { file: '.keys/key_manager_secret.txt', name: 'KEY_MANAGER_SECRET' },
315
+ ];
316
+ for (const { file, name } of secretFiles) {
317
+ if (existsSync(file)) {
318
+ secrets[name] = await readFile(file, 'utf-8');
319
+ }
320
+ }
321
+ if (Object.keys(secrets).length > 0) {
322
+ await uploadSecrets(secrets, {
323
+ env,
324
+ rootDir: resolve(rootDir),
325
+ onProgress: addProgress,
326
+ });
327
+ // Note: secrets object goes out of scope here and will be garbage collected
328
+ }
329
+ }
330
+ addProgress('Deploying workers...');
331
+ const enabledComponents = components;
332
+ const summary = await deployAll({
333
+ env,
334
+ rootDir: resolve(rootDir),
335
+ dryRun,
336
+ onProgress: addProgress,
337
+ onError: (comp, error) => {
338
+ addProgress(`Error in ${comp}: ${sanitizeError(error)}`);
339
+ },
340
+ }, enabledComponents);
341
+ state.deployResults = summary.results;
342
+ if (summary.failedCount === 0) {
343
+ state.status = 'complete';
344
+ addProgress('Deployment complete!');
345
+ }
346
+ else {
347
+ state.status = 'error';
348
+ state.error = `${summary.failedCount} components failed to deploy`;
349
+ }
350
+ return c.json({
351
+ success: summary.failedCount === 0,
352
+ summary,
353
+ });
354
+ }
355
+ catch (error) {
356
+ state.status = 'error';
357
+ state.error = sanitizeError(error);
358
+ return c.json({ success: false, error: sanitizeError(error) }, 500);
359
+ }
360
+ });
361
+ });
362
+ // Get deployment status (no auth required - read-only)
363
+ api.get('/deploy/status', (c) => {
364
+ return c.json({
365
+ status: state.status,
366
+ progress: state.progress,
367
+ error: state.error,
368
+ results: state.deployResults,
369
+ });
370
+ });
371
+ // Reset state (with lock)
372
+ api.post('/reset', async (c) => {
373
+ return withLock(async () => {
374
+ state.status = 'idle';
375
+ state.config = null;
376
+ state.progress = [];
377
+ state.error = null;
378
+ state.deployResults = [];
379
+ return c.json({ success: true });
380
+ });
381
+ });
382
+ // Complete initial admin setup (store setup token in KV)
383
+ api.post('/admin/setup', async (c) => {
384
+ return withLock(async () => {
385
+ try {
386
+ const body = await c.req.json();
387
+ const { env, baseUrl, keysDir = '.keys' } = body;
388
+ if (!env || !baseUrl) {
389
+ return c.json({ success: false, error: 'env and baseUrl are required' }, 400);
390
+ }
391
+ addProgress('Setting up initial admin...');
392
+ const result = await completeInitialSetup({
393
+ env,
394
+ baseUrl,
395
+ keysDir,
396
+ onProgress: addProgress,
397
+ });
398
+ if (result.alreadyCompleted) {
399
+ addProgress('Initial admin setup already completed');
400
+ return c.json({
401
+ success: true,
402
+ alreadyCompleted: true,
403
+ message: 'Initial admin setup was already completed',
404
+ });
405
+ }
406
+ if (result.success && result.setupUrl) {
407
+ addProgress('Setup token stored successfully');
408
+ return c.json({
409
+ success: true,
410
+ setupUrl: result.setupUrl,
411
+ message: 'Visit the setup URL to create the initial administrator',
412
+ });
413
+ }
414
+ return c.json({ success: false, error: result.error }, 500);
415
+ }
416
+ catch (error) {
417
+ return c.json({ success: false, error: sanitizeError(error) }, 500);
418
+ }
419
+ });
420
+ });
421
+ return api;
422
+ }
423
+ //# sourceMappingURL=api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/web/api.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EACL,mBAAmB,EACnB,SAAS,EACT,kBAAkB,GAEnB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAsB,MAAM,mBAAmB,CAAC;AACjG,OAAO,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAC1E,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC7E,OAAO,EAAE,sBAAsB,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,aAAa,EAAqB,MAAM,mBAAmB,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAwB,MAAM,mBAAmB,CAAC;AACjF,OAAO,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,gFAAgF;AAChF,qBAAqB;AACrB,gFAAgF;AAEhF;;;GAGG;AACH,IAAI,YAAY,GAAW,EAAE,CAAC;AAE9B;;GAEG;AACH,MAAM,UAAU,oBAAoB;IAClC,YAAY,GAAG,WAAW,CAAC,EAAE,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC/C,OAAO,YAAY,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe;IAC7B,OAAO,YAAY,CAAC;AACtB,CAAC;AAED,gFAAgF;AAChF,uDAAuD;AACvD,gFAAgF;AAEhF,IAAI,aAAa,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAC;AAErD;;GAEG;AACH,KAAK,UAAU,QAAQ,CAAI,SAA2B;IACpD,0CAA0C;IAC1C,MAAM,aAAa,CAAC;IAEpB,qCAAqC;IACrC,IAAI,WAAuB,CAAC;IAC5B,aAAa,GAAG,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QACtC,WAAW,GAAG,OAAO,CAAC;IACxB,CAAC,CAAC,CAAC;IAEH,IAAI,CAAC;QACH,OAAO,MAAM,SAAS,EAAE,CAAC;IAC3B,CAAC;YAAS,CAAC;QACT,WAAY,EAAE,CAAC;IACjB,CAAC;AACH,CAAC;AAeD,MAAM,KAAK,GAAe;IACxB,MAAM,EAAE,MAAM;IACd,MAAM,EAAE,IAAI;IACZ,IAAI,EAAE,IAAI;IACV,QAAQ,EAAE,EAAE;IACZ,KAAK,EAAE,IAAI;IACX,aAAa,EAAE,EAAE;CAClB,CAAC;AAEF,SAAS,WAAW,CAAC,OAAe;IAClC,KAAK,CAAC,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAED,SAAS,aAAa;IACpB,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC;AACtB,CAAC;AAED;;GAEG;AACH,SAAS,aAAa,CAAC,KAAc;IACnC,MAAM,OAAO,GAAG,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IACvE,0CAA0C;IAC1C,OAAO,OAAO;SACX,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC;SAC/B,OAAO,CAAC,YAAY,EAAE,QAAQ,CAAC;SAC/B,OAAO,CAAC,iBAAiB,EAAE,YAAY,CAAC,CAAC;AAC9C,CAAC;AAED,gFAAgF;AAChF,aAAa;AACb,gFAAgF;AAEhF,MAAM,UAAU,eAAe;IAC7B,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,8DAA8D;IAC9D,MAAM,eAAe,GAAG,KAAK,EAC3B,CAA+C,EAC/C,IAAyB,EACzB,EAAE;QACF,MAAM,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,iBAAiB,CAAC,CAAC;QAC9C,IAAI,CAAC,YAAY,IAAI,KAAK,KAAK,YAAY,EAAE,CAAC;YAC5C,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,kCAAkC,EAAE,EAAE,GAAG,CAAC,CAAC;QACpE,CAAC;QACD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;IAEF,yDAAyD;IACzD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACpC,GAAG,CAAC,GAAG,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IACtC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACpC,GAAG,CAAC,GAAG,CAAC,YAAY,EAAE,eAAe,CAAC,CAAC;IACvC,GAAG,CAAC,GAAG,CAAC,aAAa,EAAE,eAAe,CAAC,CAAC;IACxC,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;IACpC,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,eAAe,CAAC,CAAC;IACnC,GAAG,CAAC,GAAG,CAAC,UAAU,EAAE,eAAe,CAAC,CAAC;IAErC,mDAAmD;IACnD,GAAG,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE;QACtB,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC,CAAC,CAAC;IAEH,qDAAqD;IACrD,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACpC,MAAM,iBAAiB,GAAG,MAAM,mBAAmB,EAAE,CAAC;QACtD,MAAM,IAAI,GAAG,MAAM,SAAS,EAAE,CAAC;QAE/B,KAAK,CAAC,IAAI,GAAG,IAAI,CAAC;QAElB,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,iBAAiB;YACjB,IAAI;SACL,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,sDAAsD;IACtD,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC7B,MAAM,UAAU,GAAG,qBAAqB,CAAC;QAEzC,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;YAC5B,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACjD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YACpD,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACnC,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;YACtB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;QAC1C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;QACrE,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,qDAAqD;IACrD,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;gBAE/C,MAAM,SAAS,CAAC,qBAAqB,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;gBACxE,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;gBAEtB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACnC,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,IAAI,KAAK,YAAY,CAAC,CAAC,QAAQ,EAAE,CAAC;oBAChC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,MAAM,EAAE,EAAE,GAAG,CAAC,CAAC;gBAC/D,CAAC;gBACD,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,oCAAoC;IACpC,GAAG,CAAC,IAAI,CAAC,iBAAiB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACtC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,EAAE,GAAG,GAAG,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;gBAEtC,MAAM,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;gBAExC,oCAAoC;gBACpC,IAAI,MAAM,EAAE,CAAC;oBACX,MAAM,CAAC,IAAI,GAAG;wBACZ,GAAG,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,wBAAwB,EAAE;wBACrE,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,GAAG,kBAAkB,EAAE;wBACjE,OAAO,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,GAAG,wBAAwB,EAAE;qBACxE,CAAC;gBACJ,CAAC;gBAED,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;gBAEtB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC,CAAC;YAC3C,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,4BAA4B;IAC5B,GAAG,CAAC,IAAI,CAAC,gBAAgB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACrC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;gBAE1C,WAAW,CAAC,kCAAkC,CAAC,CAAC;gBAChD,MAAM,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;gBAE1C,WAAW,CAAC,6BAA6B,CAAC,CAAC;gBAC3C,MAAM,mBAAmB,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;gBAE5C,WAAW,CAAC,6BAA6B,CAAC,CAAC;gBAE3C,iCAAiC;gBACjC,OAAO,CAAC,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,IAAI;oBACb,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK;oBAC5B,YAAY,EAAE,OAAO,CAAC,OAAO,CAAC,YAAY;iBAC3C,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;gBACnC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,6CAA6C;IAC7C,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACjC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,EAAE,GAAG,EAAE,GAAG,IAAI,CAAC;gBAErB,KAAK,CAAC,MAAM,GAAG,cAAc,CAAC;gBAC9B,aAAa,EAAE,CAAC;gBAEhB,WAAW,CAAC,yCAAyC,GAAG,KAAK,CAAC,CAAC;gBAE/D,MAAM,SAAS,GAAG,MAAM,kBAAkB,CAAC;oBACzC,GAAG;oBACH,QAAQ,EAAE,IAAI;oBACd,QAAQ,EAAE,IAAI;oBACd,YAAY,EAAE,IAAI,CAAC,YAAY,IAAI,KAAK;oBACxC,QAAQ,EAAE,IAAI,CAAC,QAAQ,IAAI,KAAK;oBAChC,UAAU,EAAE,WAAW;iBACxB,CAAC,CAAC;gBAEH,WAAW,CAAC,uBAAuB,CAAC,CAAC;gBACrC,MAAM,IAAI,GAAG,cAAc,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBAC5C,MAAM,YAAY,CAAC,IAAI,CAAC,CAAC;gBAEzB,KAAK,CAAC,MAAM,GAAG,aAAa,CAAC;gBAC7B,WAAW,CAAC,wBAAwB,CAAC,CAAC;gBAEtC,OAAO,CAAC,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,IAAI;oBACb,SAAS;oBACT,IAAI;iBACL,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;gBACvB,KAAK,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;gBACnC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,wCAAwC;IACxC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACzC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,GAAG,EAAE,GAAG,IAAI,CAAC;gBAEpC,iBAAiB;gBACjB,MAAM,IAAI,GAAG,MAAM,YAAY,EAAE,CAAC;gBAClC,IAAI,CAAC,IAAI,EAAE,CAAC;oBACV,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,qBAAqB,EAAE,EAAE,GAAG,CAAC,CAAC;gBACvE,CAAC;gBAED,cAAc;gBACd,IAAI,MAAqB,CAAC;gBAC1B,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;oBACjB,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;gBACnD,CAAC;qBAAM,CAAC;oBACN,MAAM,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC;gBACpC,CAAC;gBAED,WAAW,CAAC,mCAAmC,CAAC,CAAC;gBAEjD,oCAAoC;gBACpC,MAAM,WAAW,GAAG;oBAClB,EAAE,EAAE,IAAI,CAAC,EAAE;oBACX,EAAE,EAAE,MAAM,CAAC,WAAW,CACpB,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC,EAAE,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CACzE;oBACD,MAAM,EAAE,IAAI,CAAC,MAAM;oBACnB,EAAE,EAAE,IAAI,CAAC,EAAE;iBACZ,CAAC;gBAEF,wDAAwD;gBACxD,MAAM,mBAAmB,GAAa,EAAE,CAAC;gBACzC,KAAK,MAAM,SAAS,IAAI,sBAAsB,EAAE,CAAC;oBAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;oBAC1D,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;wBAC9B,SAAS;oBACX,CAAC;oBAED,MAAM,cAAc,GAAG,sBAAsB,CAAC,SAAS,EAAE,MAAM,EAAE,WAAW,CAAC,CAAC;oBAC9E,MAAM,WAAW,GAAG,MAAM,CAAC,cAAc,CAAC,CAAC;oBAC3C,MAAM,QAAQ,GAAG,IAAI,CAAC,YAAY,EAAE,YAAY,GAAG,OAAO,CAAC,CAAC;oBAC5D,MAAM,SAAS,CAAC,QAAQ,EAAE,WAAW,EAAE,OAAO,CAAC,CAAC;oBAChD,mBAAmB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;gBACtC,CAAC;gBAED,WAAW,CAAC,6BAA6B,CAAC,CAAC;gBAE3C,OAAO,CAAC,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,IAAI;oBACb,UAAU,EAAE,mBAAmB;iBAChC,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,8CAA8C;IAC9C,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC9B,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,EAAE,GAAG,EAAE,OAAO,GAAG,GAAG,EAAE,MAAM,GAAG,KAAK,EAAE,UAAU,EAAE,GAAG,IAAI,CAAC;gBAEhE,KAAK,CAAC,MAAM,GAAG,WAAW,CAAC;gBAC3B,aAAa,EAAE,CAAC;gBAEhB,kEAAkE;gBAClE,IAAI,CAAC,MAAM,IAAI,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;oBACnC,WAAW,CAAC,sBAAsB,CAAC,CAAC;oBAEpC,MAAM,OAAO,GAA2B,EAAE,CAAC;oBAC3C,MAAM,WAAW,GAAG;wBAClB,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,iBAAiB,EAAE;wBACtD,EAAE,IAAI,EAAE,mCAAmC,EAAE,IAAI,EAAE,yBAAyB,EAAE;wBAC9E,EAAE,IAAI,EAAE,4BAA4B,EAAE,IAAI,EAAE,kBAAkB,EAAE;wBAChE,EAAE,IAAI,EAAE,8BAA8B,EAAE,IAAI,EAAE,oBAAoB,EAAE;qBACrE,CAAC;oBAEF,KAAK,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,WAAW,EAAE,CAAC;wBACzC,IAAI,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC;4BACrB,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;wBAChD,CAAC;oBACH,CAAC;oBAED,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBACpC,MAAM,aAAa,CAAC,OAAO,EAAE;4BAC3B,GAAG;4BACH,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;4BACzB,UAAU,EAAE,WAAW;yBACxB,CAAC,CAAC;wBACH,4EAA4E;oBAC9E,CAAC;gBACH,CAAC;gBAED,WAAW,CAAC,sBAAsB,CAAC,CAAC;gBAEpC,MAAM,iBAAiB,GAAkC,UAAU,CAAC;gBAEpE,MAAM,OAAO,GAAG,MAAM,SAAS,CAC7B;oBACE,GAAG;oBACH,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC;oBACzB,MAAM;oBACN,UAAU,EAAE,WAAW;oBACvB,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE;wBACvB,WAAW,CAAC,YAAY,IAAI,KAAK,aAAa,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;oBAC3D,CAAC;iBACF,EACD,iBAAiB,CAClB,CAAC;gBAEF,KAAK,CAAC,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC;gBAEtC,IAAI,OAAO,CAAC,WAAW,KAAK,CAAC,EAAE,CAAC;oBAC9B,KAAK,CAAC,MAAM,GAAG,UAAU,CAAC;oBAC1B,WAAW,CAAC,sBAAsB,CAAC,CAAC;gBACtC,CAAC;qBAAM,CAAC;oBACN,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;oBACvB,KAAK,CAAC,KAAK,GAAG,GAAG,OAAO,CAAC,WAAW,8BAA8B,CAAC;gBACrE,CAAC;gBAED,OAAO,CAAC,CAAC,IAAI,CAAC;oBACZ,OAAO,EAAE,OAAO,CAAC,WAAW,KAAK,CAAC;oBAClC,OAAO;iBACR,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,KAAK,CAAC,MAAM,GAAG,OAAO,CAAC;gBACvB,KAAK,CAAC,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;gBACnC,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,uDAAuD;IACvD,GAAG,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,EAAE;QAC9B,OAAO,CAAC,CAAC,IAAI,CAAC;YACZ,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,KAAK,EAAE,KAAK,CAAC,KAAK;YAClB,OAAO,EAAE,KAAK,CAAC,aAAa;SAC7B,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,0BAA0B;IAC1B,GAAG,CAAC,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QAC7B,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,KAAK,CAAC,MAAM,GAAG,MAAM,CAAC;YACtB,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC;YACpB,KAAK,CAAC,QAAQ,GAAG,EAAE,CAAC;YACpB,KAAK,CAAC,KAAK,GAAG,IAAI,CAAC;YACnB,KAAK,CAAC,aAAa,GAAG,EAAE,CAAC;YAEzB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,yDAAyD;IACzD,GAAG,CAAC,IAAI,CAAC,cAAc,EAAE,KAAK,EAAE,CAAC,EAAE,EAAE;QACnC,OAAO,QAAQ,CAAC,KAAK,IAAI,EAAE;YACzB,IAAI,CAAC;gBACH,MAAM,IAAI,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC;gBAChC,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,GAAG,IAAI,CAAC;gBAEjD,IAAI,CAAC,GAAG,IAAI,CAAC,OAAO,EAAE,CAAC;oBACrB,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,8BAA8B,EAAE,EAAE,GAAG,CAAC,CAAC;gBAChF,CAAC;gBAED,WAAW,CAAC,6BAA6B,CAAC,CAAC;gBAE3C,MAAM,MAAM,GAAG,MAAM,oBAAoB,CAAC;oBACxC,GAAG;oBACH,OAAO;oBACP,OAAO;oBACP,UAAU,EAAE,WAAW;iBACxB,CAAC,CAAC;gBAEH,IAAI,MAAM,CAAC,gBAAgB,EAAE,CAAC;oBAC5B,WAAW,CAAC,uCAAuC,CAAC,CAAC;oBACrD,OAAO,CAAC,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,IAAI;wBACb,gBAAgB,EAAE,IAAI;wBACtB,OAAO,EAAE,2CAA2C;qBACrD,CAAC,CAAC;gBACL,CAAC;gBAED,IAAI,MAAM,CAAC,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;oBACtC,WAAW,CAAC,iCAAiC,CAAC,CAAC;oBAC/C,OAAO,CAAC,CAAC,IAAI,CAAC;wBACZ,OAAO,EAAE,IAAI;wBACb,QAAQ,EAAE,MAAM,CAAC,QAAQ;wBACzB,OAAO,EAAE,yDAAyD;qBACnE,CAAC,CAAC;gBACL,CAAC;gBAED,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,KAAK,EAAE,EAAE,GAAG,CAAC,CAAC;YAC9D,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO,CAAC,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,aAAa,CAAC,KAAK,CAAC,EAAE,EAAE,GAAG,CAAC,CAAC;YACtE,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,GAAG,CAAC;AACb,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Web UI Server for Authrim Setup
3
+ *
4
+ * Provides a web-based interface for configuring and deploying Authrim.
5
+ */
6
+ export interface WebServerOptions {
7
+ port?: number;
8
+ host?: string;
9
+ openBrowser?: boolean;
10
+ }
11
+ export declare function startWebServer(options?: WebServerOptions): Promise<void>;
12
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAaH,MAAM,WAAW,gBAAgB;IAC/B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAMD,wBAAsB,cAAc,CAAC,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,IAAI,CAAC,CA8ClF"}
@@ -0,0 +1,112 @@
1
+ /**
2
+ * Web UI Server for Authrim Setup
3
+ *
4
+ * Provides a web-based interface for configuring and deploying Authrim.
5
+ */
6
+ import { Hono } from 'hono';
7
+ import { serve } from '@hono/node-server';
8
+ import { cors } from 'hono/cors';
9
+ import chalk from 'chalk';
10
+ import { createApiRoutes, generateSessionToken, getSessionToken } from './api.js';
11
+ import { getHtmlTemplate } from './ui.js';
12
+ // =============================================================================
13
+ // Server
14
+ // =============================================================================
15
+ export async function startWebServer(options = {}) {
16
+ const { port = 3456, host = 'localhost' } = options;
17
+ // Generate session token for this server instance
18
+ generateSessionToken();
19
+ const app = new Hono();
20
+ // CORS for API requests (localhost only)
21
+ app.use('/api/*', cors({
22
+ origin: [`http://localhost:${port}`, `http://127.0.0.1:${port}`],
23
+ allowMethods: ['GET', 'POST', 'PUT', 'DELETE'],
24
+ allowHeaders: ['Content-Type', 'X-Session-Token'],
25
+ }));
26
+ // API routes
27
+ const apiRoutes = createApiRoutes();
28
+ app.route('/api', apiRoutes);
29
+ // Serve UI with embedded session token
30
+ app.get('/', (c) => {
31
+ return c.html(getHtmlTemplate(getSessionToken()));
32
+ });
33
+ // Static assets (if needed in the future)
34
+ app.get('/health', (c) => c.json({ status: 'ok' }));
35
+ // Start server
36
+ console.log(chalk.bold('\n🌐 Authrim Setup Web UI\n'));
37
+ console.log(`Server running at ${chalk.cyan(`http://${host}:${port}`)}`);
38
+ console.log(chalk.gray('\nPress Ctrl+C to stop\n'));
39
+ // Open browser if requested
40
+ if (options.openBrowser !== false) {
41
+ const url = `http://${host}:${port}`;
42
+ openBrowser(url);
43
+ }
44
+ serve({
45
+ fetch: app.fetch,
46
+ port,
47
+ hostname: host,
48
+ });
49
+ }
50
+ // =============================================================================
51
+ // Browser Opening
52
+ // =============================================================================
53
+ /**
54
+ * Validate that the URL is a safe localhost URL
55
+ * Only allows http://localhost:PORT or http://127.0.0.1:PORT
56
+ */
57
+ function validateLocalhostUrl(url) {
58
+ try {
59
+ const parsed = new URL(url);
60
+ // Only allow http protocol (not https for local dev server)
61
+ if (parsed.protocol !== 'http:') {
62
+ return false;
63
+ }
64
+ // Only allow localhost or 127.0.0.1
65
+ if (parsed.hostname !== 'localhost' && parsed.hostname !== '127.0.0.1') {
66
+ return false;
67
+ }
68
+ // Port must be a valid number
69
+ const port = parseInt(parsed.port || '80', 10);
70
+ if (isNaN(port) || port < 1 || port > 65535) {
71
+ return false;
72
+ }
73
+ // Path should only be simple (no shell metacharacters)
74
+ if (/[;&|`$(){}[\]<>!#*?'"]/.test(parsed.pathname)) {
75
+ return false;
76
+ }
77
+ return true;
78
+ }
79
+ catch {
80
+ return false;
81
+ }
82
+ }
83
+ async function openBrowser(url) {
84
+ // Security: Validate URL to prevent command injection
85
+ if (!validateLocalhostUrl(url)) {
86
+ console.log(chalk.yellow(`\nInvalid URL for browser opening: ${url}`));
87
+ console.log(chalk.gray('Only localhost URLs are allowed for automatic browser opening.'));
88
+ return;
89
+ }
90
+ const { platform } = process;
91
+ try {
92
+ const { execa } = await import('execa');
93
+ switch (platform) {
94
+ case 'darwin':
95
+ await execa('open', [url]);
96
+ break;
97
+ case 'win32':
98
+ // On Windows, use 'start' command with empty title to avoid shell expansion issues
99
+ await execa('cmd', ['/c', 'start', '""', url]);
100
+ break;
101
+ default:
102
+ // Linux and others
103
+ await execa('xdg-open', [url]);
104
+ break;
105
+ }
106
+ }
107
+ catch {
108
+ console.log(chalk.yellow(`\nCould not open browser automatically.`));
109
+ console.log(`Please open ${chalk.cyan(url)} in your browser.\n`);
110
+ }
111
+ }
112
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../../src/web/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAC1C,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,EAAE,eAAe,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAClF,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAY1C,gFAAgF;AAChF,SAAS;AACT,gFAAgF;AAEhF,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,UAA4B,EAAE;IACjE,MAAM,EAAE,IAAI,GAAG,IAAI,EAAE,IAAI,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC;IAEpD,kDAAkD;IAClD,oBAAoB,EAAE,CAAC;IAEvB,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC;IAEvB,yCAAyC;IACzC,GAAG,CAAC,GAAG,CACL,QAAQ,EACR,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,oBAAoB,IAAI,EAAE,EAAE,oBAAoB,IAAI,EAAE,CAAC;QAChE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ,CAAC;QAC9C,YAAY,EAAE,CAAC,cAAc,EAAE,iBAAiB,CAAC;KAClD,CAAC,CACH,CAAC;IAEF,aAAa;IACb,MAAM,SAAS,GAAG,eAAe,EAAE,CAAC;IACpC,GAAG,CAAC,KAAK,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAE7B,uCAAuC;IACvC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE;QACjB,OAAO,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,eAAe,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,0CAA0C;IAC1C,GAAG,CAAC,GAAG,CAAC,SAAS,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;IAEpD,eAAe;IACf,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,6BAA6B,CAAC,CAAC,CAAC;IACvD,OAAO,CAAC,GAAG,CAAC,qBAAqB,KAAK,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC,EAAE,CAAC,CAAC;IACzE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC,CAAC;IAEpD,4BAA4B;IAC5B,IAAI,OAAO,CAAC,WAAW,KAAK,KAAK,EAAE,CAAC;QAClC,MAAM,GAAG,GAAG,UAAU,IAAI,IAAI,IAAI,EAAE,CAAC;QACrC,WAAW,CAAC,GAAG,CAAC,CAAC;IACnB,CAAC;IAED,KAAK,CAAC;QACJ,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,IAAI;QACJ,QAAQ,EAAE,IAAI;KACf,CAAC,CAAC;AACL,CAAC;AAED,gFAAgF;AAChF,kBAAkB;AAClB,gFAAgF;AAEhF;;;GAGG;AACH,SAAS,oBAAoB,CAAC,GAAW;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC;QAC5B,4DAA4D;QAC5D,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,oCAAoC;QACpC,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,IAAI,MAAM,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;YACvE,OAAO,KAAK,CAAC;QACf,CAAC;QACD,8BAA8B;QAC9B,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,IAAI,IAAI,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/C,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;YAC5C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,uDAAuD;QACvD,IAAI,wBAAwB,CAAC,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;YACnD,OAAO,KAAK,CAAC;QACf,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,KAAK,UAAU,WAAW,CAAC,GAAW;IACpC,sDAAsD;IACtD,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,EAAE,CAAC;QAC/B,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,sCAAsC,GAAG,EAAE,CAAC,CAAC,CAAC;QACvE,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,gEAAgE,CAAC,CAAC,CAAC;QAC1F,OAAO;IACT,CAAC;IAED,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;IAE7B,IAAI,CAAC;QACH,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,CAAC;QAExC,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,QAAQ;gBACX,MAAM,KAAK,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC3B,MAAM;YACR,KAAK,OAAO;gBACV,mFAAmF;gBACnF,MAAM,KAAK,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;gBAC/C,MAAM;YACR;gBACE,mBAAmB;gBACnB,MAAM,KAAK,CAAC,UAAU,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;gBAC/B,MAAM;QACV,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,yCAAyC,CAAC,CAAC,CAAC;QACrE,OAAO,CAAC,GAAG,CAAC,eAAe,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,qBAAqB,CAAC,CAAC;IACnE,CAAC;AACH,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * HTML Template for Authrim Setup Web UI
3
+ *
4
+ * A simple, self-contained UI for the setup wizard.
5
+ */
6
+ export declare function getHtmlTemplate(sessionToken?: string): string;
7
+ //# sourceMappingURL=ui.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ui.d.ts","sourceRoot":"","sources":["../../src/web/ui.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,wBAAgB,eAAe,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAuvB7D"}