hazo_notify 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/.cursor/rules/db_schema.mdc +0 -0
  2. package/.cursor/rules/design.mdc +16 -0
  3. package/.cursor/rules/general.mdc +49 -0
  4. package/README.md +765 -0
  5. package/components/emailer-html-editor.tsx +94 -0
  6. package/components/ui/button.tsx +53 -0
  7. package/components/ui/card.tsx +78 -0
  8. package/components/ui/input.tsx +24 -0
  9. package/components/ui/label.tsx +21 -0
  10. package/components/ui/sidebar.tsx +121 -0
  11. package/components/ui/spinner.tsx +54 -0
  12. package/components/ui/textarea.tsx +23 -0
  13. package/components/ui/tooltip.tsx +30 -0
  14. package/components.json +20 -0
  15. package/hazo_notify_config.ini +153 -0
  16. package/jest.config.js +27 -0
  17. package/jest.setup.js +1 -0
  18. package/next.config.js +22 -0
  19. package/package.json +72 -0
  20. package/postcss.config.js +6 -0
  21. package/src/app/api/hazo_notify/emailer/send/__tests__/route.test.ts +227 -0
  22. package/src/app/api/hazo_notify/emailer/send/route.ts +537 -0
  23. package/src/app/editor-00/page.tsx +47 -0
  24. package/src/app/globals.css +69 -0
  25. package/src/app/hazo_notify/emailer_test/layout.tsx +53 -0
  26. package/src/app/hazo_notify/emailer_test/page.tsx +369 -0
  27. package/src/app/hazo_notify/layout.tsx +77 -0
  28. package/src/app/hazo_notify/page.tsx +12 -0
  29. package/src/app/layout.tsx +26 -0
  30. package/src/app/page.tsx +14 -0
  31. package/src/components/blocks/editor-00/editor.tsx +61 -0
  32. package/src/components/blocks/editor-00/nodes.ts +11 -0
  33. package/src/components/blocks/editor-00/plugins.tsx +36 -0
  34. package/src/components/editor/editor-ui/content-editable.tsx +34 -0
  35. package/src/components/editor/themes/editor-theme.css +91 -0
  36. package/src/components/editor/themes/editor-theme.ts +130 -0
  37. package/src/components/ui/button.tsx +53 -0
  38. package/src/components/ui/card.tsx +78 -0
  39. package/src/components/ui/input.tsx +24 -0
  40. package/src/components/ui/label.tsx +21 -0
  41. package/src/components/ui/sidebar.tsx +121 -0
  42. package/src/components/ui/spinner.tsx +54 -0
  43. package/src/components/ui/textarea.tsx +23 -0
  44. package/src/components/ui/tooltip.tsx +30 -0
  45. package/src/lib/emailer/__tests__/emailer.test.ts +200 -0
  46. package/src/lib/emailer/emailer.ts +263 -0
  47. package/src/lib/emailer/index.ts +11 -0
  48. package/src/lib/emailer/providers/__tests__/zeptomail_provider.test.ts +196 -0
  49. package/src/lib/emailer/providers/index.ts +33 -0
  50. package/src/lib/emailer/providers/pop3_provider.ts +30 -0
  51. package/src/lib/emailer/providers/smtp_provider.ts +30 -0
  52. package/src/lib/emailer/providers/zeptomail_provider.ts +299 -0
  53. package/src/lib/emailer/types.ts +119 -0
  54. package/src/lib/emailer/utils/constants.ts +24 -0
  55. package/src/lib/emailer/utils/index.ts +9 -0
  56. package/src/lib/emailer/utils/logger.ts +71 -0
  57. package/src/lib/emailer/utils/validation.ts +84 -0
  58. package/src/lib/index.ts +6 -0
  59. package/src/lib/utils.ts +6 -0
  60. package/tailwind.config.ts +65 -0
  61. package/tsconfig.json +27 -0
package/jest.config.js ADDED
@@ -0,0 +1,27 @@
1
+ const nextJest = require('next/jest');
2
+
3
+ const createJestConfig = nextJest({
4
+ dir: './',
5
+ });
6
+
7
+ const customJestConfig = {
8
+ setupFilesAfterEnv: ['<rootDir>/jest.setup.js'],
9
+ testEnvironment: 'jest-environment-jsdom',
10
+ moduleNameMapper: {
11
+ '^@/(.*)$': '<rootDir>/src/$1',
12
+ '^@/components/(.*)$': '<rootDir>/components/$1',
13
+ },
14
+ collectCoverageFrom: [
15
+ 'src/**/*.{js,jsx,ts,tsx}',
16
+ '!src/**/*.d.ts',
17
+ '!src/**/*.stories.{js,jsx,ts,tsx}',
18
+ '!src/**/__tests__/**',
19
+ '!src/app/**',
20
+ ],
21
+ testMatch: [
22
+ '**/__tests__/**/*.test.[jt]s?(x)',
23
+ '**/?(*.)+(spec|test).[jt]s?(x)',
24
+ ],
25
+ };
26
+
27
+ module.exports = createJestConfig(customJestConfig);
package/jest.setup.js ADDED
@@ -0,0 +1 @@
1
+ import '@testing-library/jest-dom';
package/next.config.js ADDED
@@ -0,0 +1,22 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ reactStrictMode: true,
4
+ typescript: {
5
+ ignoreBuildErrors: false,
6
+ },
7
+ eslint: {
8
+ ignoreDuringBuilds: false,
9
+ },
10
+ webpack: (config, { isServer }) => {
11
+ if (isServer) {
12
+ // Externalize isomorphic-dompurify for server-side rendering
13
+ config.externals = config.externals || [];
14
+ config.externals.push({
15
+ 'isomorphic-dompurify': 'commonjs isomorphic-dompurify',
16
+ });
17
+ }
18
+ return config;
19
+ },
20
+ };
21
+
22
+ module.exports = nextConfig;
package/package.json ADDED
@@ -0,0 +1,72 @@
1
+ {
2
+ "name": "hazo_notify",
3
+ "version": "1.0.0",
4
+ "description": "Email that acts as a notification center as well for schedules too",
5
+ "main": "./src/lib/index.ts",
6
+ "types": "./src/lib/index.ts",
7
+ "exports": {
8
+ ".": "./src/lib/index.ts",
9
+ "./emailer": "./src/lib/emailer/index.ts"
10
+ },
11
+ "scripts": {
12
+ "dev": "next dev",
13
+ "build": "next build",
14
+ "start": "next start",
15
+ "lint": "next lint",
16
+ "test": "jest",
17
+ "test:watch": "jest --watch",
18
+ "test:coverage": "jest --coverage"
19
+ },
20
+ "repository": {
21
+ "type": "git",
22
+ "url": "git+https://github.com/pub12/hazo_notify.git"
23
+ },
24
+ "keywords": [
25
+ "emailer",
26
+ "notify",
27
+ "zeptomail",
28
+ "email",
29
+ "notification"
30
+ ],
31
+ "author": "Pubs Abayasiri",
32
+ "license": "MIT",
33
+ "bugs": {
34
+ "url": "https://github.com/pub12/hazo_notify/issues"
35
+ },
36
+ "homepage": "https://github.com/pub12/hazo_notify#readme",
37
+ "dependencies": {
38
+ "@lexical/react": "^0.38.2",
39
+ "@lexical/rich-text": "^0.38.2",
40
+ "@lexical/selection": "^0.38.2",
41
+ "@lexical/utils": "^0.38.2",
42
+ "@radix-ui/react-tooltip": "^1.2.8",
43
+ "class-variance-authority": "^0.7.0",
44
+ "clsx": "^2.1.0",
45
+ "hazo_config": "^1.3.0",
46
+ "isomorphic-dompurify": "^2.9.0",
47
+ "lexical": "^0.38.2",
48
+ "lucide-react": "^0.344.0",
49
+ "next": "^14.2.0",
50
+ "react": "^18.3.0",
51
+ "react-dom": "^18.3.0",
52
+ "sonner": "^1.4.0",
53
+ "tailwind-merge": "^2.2.0"
54
+ },
55
+ "devDependencies": {
56
+ "@testing-library/jest-dom": "^6.2.0",
57
+ "@testing-library/react": "^14.1.0",
58
+ "@testing-library/user-event": "^14.5.0",
59
+ "@types/jest": "^29.5.0",
60
+ "@types/node": "^20.11.0",
61
+ "@types/react": "^18.3.0",
62
+ "@types/react-dom": "^18.3.0",
63
+ "autoprefixer": "^10.4.0",
64
+ "jest": "^29.7.0",
65
+ "jest-environment-jsdom": "^29.7.0",
66
+ "postcss": "^8.4.0",
67
+ "tailwindcss": "^3.4.0",
68
+ "tailwindcss-animate": "^1.0.7",
69
+ "ts-jest": "^29.1.0",
70
+ "typescript": "^5.3.0"
71
+ }
72
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
@@ -0,0 +1,227 @@
1
+ /**
2
+ * Integration tests for emailer API route
3
+ */
4
+
5
+ import { POST } from '../../route';
6
+ import { NextRequest } from 'next/server';
7
+
8
+ // Mock the emailer service
9
+ jest.mock('@/lib/emailer', () => ({
10
+ send_email: jest.fn(),
11
+ }));
12
+
13
+ import { send_email } from '@/lib/emailer';
14
+
15
+ describe('POST /api/hazo_notify/emailer/send', () => {
16
+ beforeEach(() => {
17
+ jest.clearAllMocks();
18
+ });
19
+
20
+ it('should send email successfully', async () => {
21
+ (send_email as jest.Mock).mockResolvedValueOnce({
22
+ success: true,
23
+ message_id: 'test_message_id',
24
+ message: 'Email sent successfully',
25
+ raw_response: {
26
+ status: 200,
27
+ body: {
28
+ data: {
29
+ message_id: 'test_message_id',
30
+ },
31
+ },
32
+ },
33
+ });
34
+
35
+ const request_body = {
36
+ to: 'recipient@example.com',
37
+ subject: 'Test Subject',
38
+ content: {
39
+ text: 'Test email content',
40
+ },
41
+ };
42
+
43
+ // Create a mock Request object
44
+ const request = new Request('http://localhost/api/hazo_notify/emailer/send', {
45
+ method: 'POST',
46
+ body: JSON.stringify(request_body),
47
+ headers: {
48
+ 'Content-Type': 'application/json',
49
+ },
50
+ }) as NextRequest;
51
+
52
+ const response = await POST(request);
53
+ const data = await response.json();
54
+
55
+ expect(response.status).toBe(200);
56
+ expect(data.success).toBe(true);
57
+ expect(data.message_id).toBe('test_message_id');
58
+ expect(send_email).toHaveBeenCalledTimes(1);
59
+ });
60
+
61
+ it('should validate required fields', async () => {
62
+ const request_body = {
63
+ to: '',
64
+ subject: 'Test Subject',
65
+ content: {
66
+ text: 'Test email content',
67
+ },
68
+ };
69
+
70
+ // Create a mock Request object
71
+ const request = new Request('http://localhost/api/hazo_notify/emailer/send', {
72
+ method: 'POST',
73
+ body: JSON.stringify(request_body),
74
+ headers: {
75
+ 'Content-Type': 'application/json',
76
+ },
77
+ }) as NextRequest;
78
+
79
+ const response = await POST(request);
80
+ const data = await response.json();
81
+
82
+ expect(response.status).toBe(400);
83
+ expect(data.success).toBe(false);
84
+ expect(data.error).toContain('Recipient email address');
85
+ });
86
+
87
+ it('should handle email sending failure', async () => {
88
+ (send_email as jest.Mock).mockResolvedValueOnce({
89
+ success: false,
90
+ error: 'Failed to send email',
91
+ message: 'Failed to send email',
92
+ raw_response: {
93
+ status: 500,
94
+ body: {
95
+ error: 'Failed to send email',
96
+ },
97
+ },
98
+ });
99
+
100
+ const request_body = {
101
+ to: 'recipient@example.com',
102
+ subject: 'Test Subject',
103
+ content: {
104
+ text: 'Test email content',
105
+ },
106
+ };
107
+
108
+ // Create a mock Request object
109
+ const request = new Request('http://localhost/api/hazo_notify/emailer/send', {
110
+ method: 'POST',
111
+ body: JSON.stringify(request_body),
112
+ headers: {
113
+ 'Content-Type': 'application/json',
114
+ },
115
+ }) as NextRequest;
116
+
117
+ const response = await POST(request);
118
+ const data = await response.json();
119
+
120
+ expect(response.status).toBe(500);
121
+ expect(data.success).toBe(false);
122
+ expect(data.error).toBe('Failed to send email');
123
+ });
124
+
125
+ it('should support HTML email content', async () => {
126
+ (send_email as jest.Mock).mockResolvedValueOnce({
127
+ success: true,
128
+ message_id: 'test_message_id',
129
+ message: 'Email sent successfully',
130
+ raw_response: {
131
+ status: 200,
132
+ body: {
133
+ data: {
134
+ message_id: 'test_message_id',
135
+ },
136
+ },
137
+ },
138
+ });
139
+
140
+ const request_body = {
141
+ to: 'recipient@example.com',
142
+ subject: 'Test Subject',
143
+ content: {
144
+ html: '<h1>Test HTML Email</h1>',
145
+ },
146
+ };
147
+
148
+ // Create a mock Request object
149
+ const request = new Request('http://localhost/api/hazo_notify/emailer/send', {
150
+ method: 'POST',
151
+ body: JSON.stringify(request_body),
152
+ headers: {
153
+ 'Content-Type': 'application/json',
154
+ },
155
+ }) as NextRequest;
156
+
157
+ const response = await POST(request);
158
+ const data = await response.json();
159
+
160
+ expect(response.status).toBe(200);
161
+ expect(data.success).toBe(true);
162
+ expect(send_email).toHaveBeenCalledWith(
163
+ expect.objectContaining({
164
+ content: {
165
+ html: '<h1>Test HTML Email</h1>',
166
+ },
167
+ })
168
+ );
169
+ });
170
+
171
+ it('should support attachments', async () => {
172
+ (send_email as jest.Mock).mockResolvedValueOnce({
173
+ success: true,
174
+ message_id: 'test_message_id',
175
+ message: 'Email sent successfully',
176
+ raw_response: {
177
+ status: 200,
178
+ body: {
179
+ data: {
180
+ message_id: 'test_message_id',
181
+ },
182
+ },
183
+ },
184
+ });
185
+
186
+ const request_body = {
187
+ to: 'recipient@example.com',
188
+ subject: 'Test Subject',
189
+ content: {
190
+ text: 'Test email content',
191
+ },
192
+ attachments: [
193
+ {
194
+ filename: 'test.txt',
195
+ content: 'dGVzdCBjb250ZW50',
196
+ mime_type: 'text/plain',
197
+ },
198
+ ],
199
+ };
200
+
201
+ // Create a mock Request object
202
+ const request = new Request('http://localhost/api/hazo_notify/emailer/send', {
203
+ method: 'POST',
204
+ body: JSON.stringify(request_body),
205
+ headers: {
206
+ 'Content-Type': 'application/json',
207
+ },
208
+ }) as NextRequest;
209
+
210
+ const response = await POST(request);
211
+ const data = await response.json();
212
+
213
+ expect(response.status).toBe(200);
214
+ expect(data.success).toBe(true);
215
+ expect(send_email).toHaveBeenCalledWith(
216
+ expect.objectContaining({
217
+ attachments: [
218
+ {
219
+ filename: 'test.txt',
220
+ content: 'dGVzdCBjb250ZW50',
221
+ mime_type: 'text/plain',
222
+ },
223
+ ],
224
+ })
225
+ );
226
+ });
227
+ });