hazo_notify 1.0.0 → 1.0.1

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 (108) hide show
  1. package/dist/lib/emailer/emailer.d.ts +19 -0
  2. package/dist/lib/emailer/emailer.d.ts.map +1 -0
  3. package/dist/lib/emailer/emailer.js +220 -0
  4. package/dist/lib/emailer/emailer.js.map +1 -0
  5. package/{src/lib/emailer/index.ts → dist/lib/emailer/index.d.ts} +1 -1
  6. package/dist/lib/emailer/index.d.ts.map +1 -0
  7. package/dist/lib/emailer/index.js +34 -0
  8. package/dist/lib/emailer/index.js.map +1 -0
  9. package/dist/lib/emailer/providers/index.d.ts +15 -0
  10. package/dist/lib/emailer/providers/index.d.ts.map +1 -0
  11. package/dist/lib/emailer/providers/index.js +36 -0
  12. package/dist/lib/emailer/providers/index.js.map +1 -0
  13. package/dist/lib/emailer/providers/pop3_provider.d.ts +15 -0
  14. package/dist/lib/emailer/providers/pop3_provider.d.ts.map +1 -0
  15. package/dist/lib/emailer/providers/pop3_provider.js +29 -0
  16. package/dist/lib/emailer/providers/pop3_provider.js.map +1 -0
  17. package/dist/lib/emailer/providers/smtp_provider.d.ts +15 -0
  18. package/dist/lib/emailer/providers/smtp_provider.d.ts.map +1 -0
  19. package/dist/lib/emailer/providers/smtp_provider.js +29 -0
  20. package/dist/lib/emailer/providers/smtp_provider.js.map +1 -0
  21. package/dist/lib/emailer/providers/zeptomail_provider.d.ts +15 -0
  22. package/dist/lib/emailer/providers/zeptomail_provider.d.ts.map +1 -0
  23. package/dist/lib/emailer/providers/zeptomail_provider.js +250 -0
  24. package/dist/lib/emailer/providers/zeptomail_provider.js.map +1 -0
  25. package/dist/lib/emailer/types.d.ts +94 -0
  26. package/dist/lib/emailer/types.d.ts.map +1 -0
  27. package/dist/lib/emailer/types.js +6 -0
  28. package/dist/lib/emailer/types.js.map +1 -0
  29. package/dist/lib/emailer/utils/constants.d.ts +14 -0
  30. package/dist/lib/emailer/utils/constants.d.ts.map +1 -0
  31. package/dist/lib/emailer/utils/constants.js +22 -0
  32. package/dist/lib/emailer/utils/constants.js.map +1 -0
  33. package/{src/lib/emailer/utils/index.ts → dist/lib/emailer/utils/index.d.ts} +1 -2
  34. package/dist/lib/emailer/utils/index.d.ts.map +1 -0
  35. package/dist/lib/emailer/utils/index.js +24 -0
  36. package/dist/lib/emailer/utils/index.js.map +1 -0
  37. package/dist/lib/emailer/utils/logger.d.ts +37 -0
  38. package/dist/lib/emailer/utils/logger.d.ts.map +1 -0
  39. package/dist/lib/emailer/utils/logger.js +60 -0
  40. package/dist/lib/emailer/utils/logger.js.map +1 -0
  41. package/dist/lib/emailer/utils/validation.d.ts +37 -0
  42. package/dist/lib/emailer/utils/validation.d.ts.map +1 -0
  43. package/dist/lib/emailer/utils/validation.js +81 -0
  44. package/dist/lib/emailer/utils/validation.js.map +1 -0
  45. package/{src/lib/index.ts → dist/lib/index.d.ts} +1 -1
  46. package/dist/lib/index.d.ts.map +1 -0
  47. package/dist/lib/index.js +22 -0
  48. package/dist/lib/index.js.map +1 -0
  49. package/dist/lib/utils.d.ts +3 -0
  50. package/dist/lib/utils.d.ts.map +1 -0
  51. package/dist/lib/utils.js +9 -0
  52. package/dist/lib/utils.js.map +1 -0
  53. package/package.json +21 -7
  54. package/.cursor/rules/db_schema.mdc +0 -0
  55. package/.cursor/rules/design.mdc +0 -16
  56. package/.cursor/rules/general.mdc +0 -49
  57. package/components/emailer-html-editor.tsx +0 -94
  58. package/components/ui/button.tsx +0 -53
  59. package/components/ui/card.tsx +0 -78
  60. package/components/ui/input.tsx +0 -24
  61. package/components/ui/label.tsx +0 -21
  62. package/components/ui/sidebar.tsx +0 -121
  63. package/components/ui/spinner.tsx +0 -54
  64. package/components/ui/textarea.tsx +0 -23
  65. package/components/ui/tooltip.tsx +0 -30
  66. package/components.json +0 -20
  67. package/jest.config.js +0 -27
  68. package/jest.setup.js +0 -1
  69. package/next.config.js +0 -22
  70. package/postcss.config.js +0 -6
  71. package/src/app/api/hazo_notify/emailer/send/__tests__/route.test.ts +0 -227
  72. package/src/app/api/hazo_notify/emailer/send/route.ts +0 -537
  73. package/src/app/editor-00/page.tsx +0 -47
  74. package/src/app/globals.css +0 -69
  75. package/src/app/hazo_notify/emailer_test/layout.tsx +0 -53
  76. package/src/app/hazo_notify/emailer_test/page.tsx +0 -369
  77. package/src/app/hazo_notify/layout.tsx +0 -77
  78. package/src/app/hazo_notify/page.tsx +0 -12
  79. package/src/app/layout.tsx +0 -26
  80. package/src/app/page.tsx +0 -14
  81. package/src/components/blocks/editor-00/editor.tsx +0 -61
  82. package/src/components/blocks/editor-00/nodes.ts +0 -11
  83. package/src/components/blocks/editor-00/plugins.tsx +0 -36
  84. package/src/components/editor/editor-ui/content-editable.tsx +0 -34
  85. package/src/components/editor/themes/editor-theme.css +0 -91
  86. package/src/components/editor/themes/editor-theme.ts +0 -130
  87. package/src/components/ui/button.tsx +0 -53
  88. package/src/components/ui/card.tsx +0 -78
  89. package/src/components/ui/input.tsx +0 -24
  90. package/src/components/ui/label.tsx +0 -21
  91. package/src/components/ui/sidebar.tsx +0 -121
  92. package/src/components/ui/spinner.tsx +0 -54
  93. package/src/components/ui/textarea.tsx +0 -23
  94. package/src/components/ui/tooltip.tsx +0 -30
  95. package/src/lib/emailer/__tests__/emailer.test.ts +0 -200
  96. package/src/lib/emailer/emailer.ts +0 -263
  97. package/src/lib/emailer/providers/__tests__/zeptomail_provider.test.ts +0 -196
  98. package/src/lib/emailer/providers/index.ts +0 -33
  99. package/src/lib/emailer/providers/pop3_provider.ts +0 -30
  100. package/src/lib/emailer/providers/smtp_provider.ts +0 -30
  101. package/src/lib/emailer/providers/zeptomail_provider.ts +0 -299
  102. package/src/lib/emailer/types.ts +0 -119
  103. package/src/lib/emailer/utils/constants.ts +0 -24
  104. package/src/lib/emailer/utils/logger.ts +0 -71
  105. package/src/lib/emailer/utils/validation.ts +0 -84
  106. package/src/lib/utils.ts +0 -6
  107. package/tailwind.config.ts +0 -65
  108. package/tsconfig.json +0 -27
@@ -1,537 +0,0 @@
1
- /**
2
- * API route for sending emails
3
- * Handles POST requests to send emails via the emailer service
4
- */
5
-
6
- import { NextRequest, NextResponse } from 'next/server';
7
- import { send_email, SendEmailOptions, load_emailer_config } from '@/lib/emailer';
8
- import { HazoConfig } from 'hazo_config';
9
- import { join } from 'path';
10
- import { log_error, log_info, create_log_entry } from '@/lib/emailer/utils/logger';
11
- import { validate_email_address, validate_subject_length, validate_body_length, validate_attachment_size } from '@/lib/emailer/utils/validation';
12
- import { DEFAULT_RATE_LIMIT_REQUESTS, DEFAULT_RATE_LIMIT_WINDOW, DEFAULT_MAX_ATTACHMENT_SIZE, DEFAULT_MAX_ATTACHMENTS } from '@/lib/emailer/utils/constants';
13
-
14
- // In-memory rate limiting store
15
- // In production, consider using Redis or a proper rate limiting service
16
- interface RateLimitEntry {
17
- count: number;
18
- reset_time: number;
19
- }
20
-
21
- const rate_limit_store = new Map<string, RateLimitEntry>();
22
-
23
- /**
24
- * Check rate limit for an IP address
25
- * @param ip - IP address to check
26
- * @param max_requests - Maximum requests allowed
27
- * @param window_seconds - Time window in seconds
28
- * @returns true if within limit, false if rate limited
29
- */
30
- function check_rate_limit(ip: string, max_requests: number, window_seconds: number): boolean {
31
- const now = Date.now();
32
- const entry = rate_limit_store.get(ip);
33
-
34
- if (!entry || now > entry.reset_time) {
35
- // Create new entry or reset expired entry
36
- rate_limit_store.set(ip, {
37
- count: 1,
38
- reset_time: now + (window_seconds * 1000),
39
- });
40
- return true;
41
- }
42
-
43
- if (entry.count >= max_requests) {
44
- return false;
45
- }
46
-
47
- entry.count++;
48
- return true;
49
- }
50
-
51
- /**
52
- * Get client IP address from request
53
- * @param request - Next.js request object
54
- * @returns IP address string
55
- */
56
- function get_client_ip(request: NextRequest): string {
57
- // Try various headers for IP address
58
- const forwarded = request.headers.get('x-forwarded-for');
59
- if (forwarded) {
60
- return forwarded.split(',')[0].trim();
61
- }
62
-
63
- const real_ip = request.headers.get('x-real-ip');
64
- if (real_ip) {
65
- return real_ip;
66
- }
67
-
68
- // Fallback to a default if IP cannot be determined
69
- return 'unknown';
70
- }
71
-
72
- /**
73
- * Get CORS headers based on configuration
74
- * @param allowed_origins - Comma-separated list of allowed origins
75
- * @param request_origin - Origin from request header
76
- * @returns CORS headers object
77
- */
78
- function get_cors_headers(allowed_origins: string, request_origin: string | null): Record<string, string> {
79
- const headers: Record<string, string> = {};
80
-
81
- if (!allowed_origins || allowed_origins.trim() === '') {
82
- // Default: same-origin only (no CORS headers)
83
- return headers;
84
- }
85
-
86
- const origins = allowed_origins.split(',').map(o => o.trim());
87
-
88
- if (origins.includes('*')) {
89
- // Allow all origins (not recommended for production)
90
- headers['Access-Control-Allow-Origin'] = '*';
91
- } else if (request_origin && origins.includes(request_origin)) {
92
- // Allow specific origin
93
- headers['Access-Control-Allow-Origin'] = request_origin;
94
- }
95
-
96
- if (headers['Access-Control-Allow-Origin']) {
97
- headers['Access-Control-Allow-Methods'] = 'POST, OPTIONS';
98
- headers['Access-Control-Allow-Headers'] = 'Content-Type';
99
- headers['Access-Control-Max-Age'] = '86400'; // 24 hours
100
- }
101
-
102
- return headers;
103
- }
104
-
105
- /**
106
- * Check if UI component is enabled
107
- * @returns boolean indicating if UI is enabled
108
- */
109
- function is_ui_enabled(): boolean {
110
- try {
111
- const config_file_path = join(process.cwd(), 'hazo_notify_config.ini');
112
- const hazo_config = new HazoConfig({ filePath: config_file_path });
113
- const ui_section = hazo_config.getSection('ui') || {};
114
- const value: unknown = ui_section?.enable_ui;
115
-
116
- // Check for various truthy values
117
- return value === 'true' || value === true || value === '1' || value === 1;
118
- } catch (error) {
119
- // If config cannot be read, default to disabled for security
120
- console.error('Error checking UI enabled status:', error);
121
- return false;
122
- }
123
- }
124
-
125
- /**
126
- * OPTIONS handler for CORS preflight requests
127
- */
128
- export async function OPTIONS(request: NextRequest) {
129
- try {
130
- const config = load_emailer_config();
131
- const request_origin = request.headers.get('origin');
132
- const cors_headers = get_cors_headers(config.cors_allowed_origins || '', request_origin);
133
-
134
- return new NextResponse(null, {
135
- status: 204,
136
- headers: cors_headers,
137
- });
138
- } catch {
139
- return new NextResponse(null, { status: 204 });
140
- }
141
- }
142
-
143
- /**
144
- * POST handler for sending emails
145
- * @param request - Next.js request object
146
- * @returns Next.js response with email send result
147
- */
148
- export async function POST(request: NextRequest) {
149
- const filename = 'route.ts';
150
- const function_name = 'POST';
151
- const route_path = '/api/hazo_notify/emailer/send';
152
- const is_production = process.env.NODE_ENV === 'production';
153
- const request_origin = request.headers.get('origin');
154
-
155
- // Check if UI is enabled - if not, disable API route too
156
- if (!is_ui_enabled()) {
157
- // Try to get CORS headers even for disabled API
158
- let cors_headers: Record<string, string> = {};
159
- try {
160
- const config = load_emailer_config();
161
- cors_headers = get_cors_headers(config.cors_allowed_origins || '', request_origin);
162
- } catch {
163
- // Ignore config errors for disabled API
164
- }
165
-
166
- return NextResponse.json(
167
- {
168
- success: false,
169
- error: 'Emailer API is disabled',
170
- message: 'Emailer API is disabled. Set enable_ui=true in hazo_notify_config.ini to enable.',
171
- },
172
- {
173
- status: 403,
174
- headers: cors_headers,
175
- }
176
- );
177
- }
178
-
179
- // Load configuration for rate limiting and CORS
180
- let config;
181
- try {
182
- config = load_emailer_config();
183
- } catch (error: unknown) {
184
- const error_message = error instanceof Error ? error.message : 'Failed to load configuration';
185
- log_error(create_log_entry(filename, function_name, error_message, { route_path }));
186
-
187
- return NextResponse.json(
188
- {
189
- success: false,
190
- error: is_production ? 'Internal server error' : error_message,
191
- message: is_production ? 'Internal server error' : error_message,
192
- },
193
- { status: 500 }
194
- );
195
- }
196
-
197
- // Check rate limiting
198
- const client_ip = get_client_ip(request);
199
- const max_requests = config.rate_limit_requests || DEFAULT_RATE_LIMIT_REQUESTS;
200
- const window_seconds = config.rate_limit_window || DEFAULT_RATE_LIMIT_WINDOW;
201
-
202
- if (!check_rate_limit(client_ip, max_requests, window_seconds)) {
203
- log_error(create_log_entry(
204
- filename,
205
- function_name,
206
- 'Rate limit exceeded',
207
- { route_path, client_ip, max_requests, window_seconds }
208
- ));
209
-
210
- // Get CORS headers for rate limit response
211
- const cors_headers = get_cors_headers(config.cors_allowed_origins || '', request_origin);
212
-
213
- return NextResponse.json(
214
- {
215
- success: false,
216
- error: 'Rate limit exceeded',
217
- message: `Too many requests. Maximum ${max_requests} requests per ${window_seconds} seconds.`,
218
- },
219
- {
220
- status: 429,
221
- headers: cors_headers,
222
- }
223
- );
224
- }
225
-
226
- // Get CORS headers
227
- const cors_headers = get_cors_headers(config.cors_allowed_origins || '', request_origin);
228
-
229
- try {
230
- // Parse request body with better error handling
231
- let body: Record<string, unknown>;
232
- try {
233
- body = await request.json() as Record<string, unknown>;
234
- } catch (json_error: unknown) {
235
- const error_message = json_error instanceof Error ? json_error.message : 'Invalid JSON in request body';
236
- log_error(create_log_entry(filename, function_name, error_message, { route_path }));
237
-
238
- return NextResponse.json(
239
- {
240
- success: false,
241
- error: 'Invalid JSON in request body',
242
- message: 'Invalid JSON in request body',
243
- },
244
- {
245
- status: 400,
246
- headers: cors_headers,
247
- }
248
- );
249
- }
250
-
251
- // Validate request body
252
- if (!body.to) {
253
- return NextResponse.json(
254
- {
255
- success: false,
256
- error: 'Recipient email address(es) are required',
257
- message: 'Recipient email address(es) are required',
258
- },
259
- {
260
- status: 400,
261
- headers: cors_headers,
262
- }
263
- );
264
- }
265
-
266
- // Validate email addresses
267
- const to_emails = Array.isArray(body.to) ? body.to : [body.to];
268
- for (const email of to_emails) {
269
- if (typeof email !== 'string' || !validate_email_address(email)) {
270
- return NextResponse.json(
271
- {
272
- success: false,
273
- error: `Invalid recipient email address: ${email}`,
274
- message: `Invalid recipient email address: ${email}`,
275
- },
276
- {
277
- status: 400,
278
- headers: cors_headers,
279
- }
280
- );
281
- }
282
- }
283
-
284
- if (!body.subject || typeof body.subject !== 'string') {
285
- return NextResponse.json(
286
- {
287
- success: false,
288
- error: 'Email subject is required',
289
- message: 'Email subject is required',
290
- },
291
- {
292
- status: 400,
293
- headers: cors_headers,
294
- }
295
- );
296
- }
297
-
298
- // Validate subject length
299
- if (!validate_subject_length(body.subject)) {
300
- const max_length = config.max_subject_length || 255;
301
- return NextResponse.json(
302
- {
303
- success: false,
304
- error: `Email subject exceeds maximum length of ${max_length} characters`,
305
- message: `Email subject exceeds maximum length of ${max_length} characters`,
306
- },
307
- {
308
- status: 400,
309
- headers: cors_headers,
310
- }
311
- );
312
- }
313
-
314
- if (!body.content || (typeof body.content !== 'object')) {
315
- return NextResponse.json(
316
- {
317
- success: false,
318
- error: 'Email content (text or html) is required',
319
- message: 'Email content (text or html) is required',
320
- },
321
- {
322
- status: 400,
323
- headers: cors_headers,
324
- }
325
- );
326
- }
327
-
328
- const content = body.content as { text?: string; html?: string };
329
-
330
- if (!content.text && !content.html) {
331
- return NextResponse.json(
332
- {
333
- success: false,
334
- error: 'Email content (text or html) is required',
335
- message: 'Email content (text or html) is required',
336
- },
337
- {
338
- status: 400,
339
- headers: cors_headers,
340
- }
341
- );
342
- }
343
-
344
- // Validate body length
345
- if (content.text && typeof content.text === 'string' && !validate_body_length(content.text)) {
346
- const max_length = config.max_body_length || 1048576;
347
- return NextResponse.json(
348
- {
349
- success: false,
350
- error: `Email text body exceeds maximum size of ${max_length} bytes`,
351
- message: `Email text body exceeds maximum size of ${max_length} bytes`,
352
- },
353
- {
354
- status: 400,
355
- headers: cors_headers,
356
- }
357
- );
358
- }
359
-
360
- if (content.html && typeof content.html === 'string' && !validate_body_length(content.html)) {
361
- const max_length = config.max_body_length || 1048576;
362
- return NextResponse.json(
363
- {
364
- success: false,
365
- error: `Email HTML body exceeds maximum size of ${max_length} bytes`,
366
- message: `Email HTML body exceeds maximum size of ${max_length} bytes`,
367
- },
368
- {
369
- status: 400,
370
- headers: cors_headers,
371
- }
372
- );
373
- }
374
-
375
- // Validate attachments
376
- if (body.attachments) {
377
- if (!Array.isArray(body.attachments)) {
378
- return NextResponse.json(
379
- {
380
- success: false,
381
- error: 'Attachments must be an array',
382
- message: 'Attachments must be an array',
383
- },
384
- {
385
- status: 400,
386
- headers: cors_headers,
387
- }
388
- );
389
- }
390
-
391
- const max_attachments = config.max_attachments || DEFAULT_MAX_ATTACHMENTS;
392
- if (body.attachments.length > max_attachments) {
393
- return NextResponse.json(
394
- {
395
- success: false,
396
- error: `Maximum ${max_attachments} attachments allowed`,
397
- message: `Maximum ${max_attachments} attachments allowed`,
398
- },
399
- {
400
- status: 400,
401
- headers: cors_headers,
402
- }
403
- );
404
- }
405
-
406
- const max_size = config.max_attachment_size || DEFAULT_MAX_ATTACHMENT_SIZE;
407
- for (const attachment of body.attachments) {
408
- if (typeof attachment !== 'object' || !attachment || !('content' in attachment)) {
409
- return NextResponse.json(
410
- {
411
- success: false,
412
- error: 'Invalid attachment format',
413
- message: 'Invalid attachment format',
414
- },
415
- {
416
- status: 400,
417
- headers: cors_headers,
418
- }
419
- );
420
- }
421
-
422
- const att = attachment as { content: string; filename?: string };
423
- if (typeof att.content !== 'string' || !validate_attachment_size(att.content, max_size)) {
424
- return NextResponse.json(
425
- {
426
- success: false,
427
- error: `Attachment "${att.filename || 'unknown'}" exceeds maximum size of ${max_size} bytes`,
428
- message: `Attachment "${att.filename || 'unknown'}" exceeds maximum size of ${max_size} bytes`,
429
- },
430
- {
431
- status: 400,
432
- headers: cors_headers,
433
- }
434
- );
435
- }
436
- }
437
- }
438
-
439
- // Prepare email options
440
- const email_options: SendEmailOptions = {
441
- to: body.to as string | string[],
442
- subject: body.subject as string,
443
- content: {
444
- text: typeof content.text === 'string' ? content.text : undefined,
445
- html: typeof content.html === 'string' ? content.html : undefined,
446
- },
447
- attachments: body.attachments as SendEmailOptions['attachments'],
448
- from: typeof body.from === 'string' ? body.from : undefined,
449
- from_name: typeof body.from_name === 'string' ? body.from_name : undefined,
450
- reply_to: typeof body.reply_to === 'string' ? body.reply_to : undefined,
451
- cc: body.cc as string | string[] | undefined,
452
- bcc: body.bcc as string | string[] | undefined,
453
- };
454
-
455
- // Send email
456
- const result = await send_email(email_options);
457
-
458
- // Return response
459
- if (result.success) {
460
- return NextResponse.json(
461
- {
462
- success: true,
463
- message_id: result.message_id,
464
- message: result.message,
465
- raw_response: result.raw_response,
466
- },
467
- {
468
- status: 200,
469
- headers: cors_headers,
470
- }
471
- );
472
- } else {
473
- // Log error
474
- log_error(create_log_entry(
475
- filename,
476
- function_name,
477
- result.error || 'Failed to send email',
478
- {
479
- route_path,
480
- email_options: {
481
- to: email_options.to,
482
- subject: email_options.subject,
483
- has_text: !!email_options.content.text,
484
- has_html: !!email_options.content.html,
485
- attachments_count: email_options.attachments?.length || 0,
486
- },
487
- response: result.raw_response,
488
- }
489
- ));
490
-
491
- return NextResponse.json(
492
- {
493
- success: false,
494
- error: result.error || 'Failed to send email',
495
- message: result.message,
496
- raw_response: is_production ? undefined : result.raw_response,
497
- },
498
- {
499
- status: 500,
500
- headers: cors_headers,
501
- }
502
- );
503
- }
504
- } catch (error: unknown) {
505
- // Log error
506
- const error_message = error instanceof Error ? error.message : 'Internal server error';
507
- const error_string = error instanceof Error ? error.toString() : String(error);
508
- const stack = error instanceof Error ? error.stack : undefined;
509
-
510
- log_error(create_log_entry(
511
- filename,
512
- function_name,
513
- error_message,
514
- {
515
- line_number: stack || 'unknown',
516
- route_path,
517
- error: error_string,
518
- }
519
- ));
520
-
521
- return NextResponse.json(
522
- {
523
- success: false,
524
- error: is_production ? 'Internal server error' : error_message,
525
- message: is_production ? 'Internal server error' : error_message,
526
- raw_response: is_production ? undefined : {
527
- error: error_string,
528
- stack: stack,
529
- },
530
- },
531
- {
532
- status: 500,
533
- headers: cors_headers,
534
- }
535
- );
536
- }
537
- }
@@ -1,47 +0,0 @@
1
- "use client"
2
-
3
- import { useState } from "react"
4
- import { SerializedEditorState } from "lexical"
5
-
6
- import { Editor } from "@/components/blocks/editor-00/editor"
7
-
8
- const initialValue = {
9
- root: {
10
- children: [
11
- {
12
- children: [
13
- {
14
- detail: 0,
15
- format: 0,
16
- mode: "normal",
17
- style: "",
18
- text: "Hello World 🚀",
19
- type: "text",
20
- version: 1,
21
- },
22
- ],
23
- direction: "ltr",
24
- format: "",
25
- indent: 0,
26
- type: "paragraph",
27
- version: 1,
28
- },
29
- ],
30
- direction: "ltr",
31
- format: "",
32
- indent: 0,
33
- type: "root",
34
- version: 1,
35
- },
36
- } as unknown as SerializedEditorState
37
-
38
- export default function EditorPage() {
39
- const [editorState, setEditorState] =
40
- useState<SerializedEditorState>(initialValue)
41
- return (
42
- <Editor
43
- editorSerializedState={editorState}
44
- onSerializedChange={(value) => setEditorState(value)}
45
- />
46
- )
47
- }
@@ -1,69 +0,0 @@
1
- @tailwind base;
2
- @tailwind components;
3
- @tailwind utilities;
4
-
5
- @layer base {
6
- :root {
7
- --background: 0 0% 100%;
8
- --foreground: 222.2 84% 4.9%;
9
- --card: 0 0% 100%;
10
- --card-foreground: 222.2 84% 4.9%;
11
- --popover: 0 0% 100%;
12
- --popover-foreground: 222.2 84% 4.9%;
13
- --primary: 222.2 47.4% 11.2%;
14
- --primary-foreground: 210 40% 98%;
15
- --secondary: 210 40% 96.1%;
16
- --secondary-foreground: 222.2 47.4% 11.2%;
17
- --muted: 210 40% 96.1%;
18
- --muted-foreground: 215.4 16.3% 46.9%;
19
- --accent: 210 40% 96.1%;
20
- --accent-foreground: 222.2 47.4% 11.2%;
21
- --destructive: 0 84.2% 60.2%;
22
- --destructive-foreground: 210 40% 98%;
23
- --border: 214.3 31.8% 91.4%;
24
- --input: 214.3 31.8% 91.4%;
25
- --ring: 222.2 84% 4.9%;
26
- --chart-1: 12 76% 61%;
27
- --chart-2: 173 58% 39%;
28
- --chart-3: 197 37% 24%;
29
- --chart-4: 43 74% 66%;
30
- --chart-5: 27 87% 67%;
31
- --radius: 0.5rem;
32
- }
33
-
34
- .dark {
35
- --background: 222.2 84% 4.9%;
36
- --foreground: 210 40% 98%;
37
- --card: 222.2 84% 4.9%;
38
- --card-foreground: 210 40% 98%;
39
- --popover: 222.2 84% 4.9%;
40
- --popover-foreground: 210 40% 98%;
41
- --primary: 210 40% 98%;
42
- --primary-foreground: 222.2 47.4% 11.2%;
43
- --secondary: 217.2 32.6% 17.5%;
44
- --secondary-foreground: 210 40% 98%;
45
- --muted: 217.2 32.6% 17.5%;
46
- --muted-foreground: 215 20.2% 65.1%;
47
- --accent: 217.2 32.6% 17.5%;
48
- --accent-foreground: 210 40% 98%;
49
- --destructive: 0 62.8% 30.6%;
50
- --destructive-foreground: 210 40% 98%;
51
- --border: 217.2 32.6% 17.5%;
52
- --input: 217.2 32.6% 17.5%;
53
- --ring: 212.7 26.8% 83.9%;
54
- --chart-1: 220 70% 50%;
55
- --chart-2: 160 60% 45%;
56
- --chart-3: 30 80% 55%;
57
- --chart-4: 280 65% 60%;
58
- --chart-5: 340 75% 55%;
59
- }
60
- }
61
-
62
- @layer base {
63
- * {
64
- @apply border-border;
65
- }
66
- body {
67
- @apply bg-background text-foreground;
68
- }
69
- }
@@ -1,53 +0,0 @@
1
- /**
2
- * Layout for emailer test page
3
- * Checks if UI component is enabled in config
4
- * If disabled, shows a message instead of the page content
5
- */
6
-
7
- import { HazoConfig } from 'hazo_config';
8
- import { join } from 'path';
9
-
10
- /**
11
- * Check if UI component is enabled
12
- * @returns boolean indicating if UI is enabled
13
- */
14
- function is_ui_enabled(): boolean {
15
- try {
16
- const config_file_path = join(process.cwd(), 'hazo_notify_config.ini');
17
- const hazo_config = new HazoConfig({ filePath: config_file_path });
18
- const ui_section = hazo_config.getSection('ui') || {};
19
- const value: unknown = ui_section?.enable_ui;
20
-
21
- // Check for various truthy values
22
- return value === 'true' || value === true || value === '1' || value === 1;
23
- } catch (error) {
24
- // If config cannot be read, default to disabled for security
25
- console.error('Error checking UI enabled status:', error);
26
- return false;
27
- }
28
- }
29
-
30
- export default function EmailerTestLayout({
31
- children,
32
- }: {
33
- children: React.ReactNode;
34
- }) {
35
- const is_enabled = is_ui_enabled();
36
-
37
- if (!is_enabled) {
38
- return (
39
- <div className="cls_emailer_test_disabled flex items-center justify-center h-full p-6">
40
- <div className="cls_emailer_test_disabled_message text-center max-w-2xl">
41
- <h1 className="cls_emailer_test_disabled_title text-2xl font-bold mb-4">
42
- UI Component is Disabled
43
- </h1>
44
- <p className="cls_emailer_test_disabled_description text-muted-foreground">
45
- To enable the UI component and all routes, set <code className="cls_emailer_test_disabled_code bg-muted px-2 py-1 rounded">enable_ui=true</code> in the <code className="cls_emailer_test_disabled_code bg-muted px-2 py-1 rounded">[ui]</code> section of <code className="cls_emailer_test_disabled_code bg-muted px-2 py-1 rounded">hazo_notify_config.ini</code>.
46
- </p>
47
- </div>
48
- </div>
49
- );
50
- }
51
-
52
- return <>{children}</>;
53
- }