@develit-services/notification 0.0.2
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/@types/consts/audit-log.consts.ts +7 -0
- package/@types/consts/index.ts +1 -0
- package/@types/database/audit-log.types.ts +7 -0
- package/@types/database/index.ts +1 -0
- package/@types/email/IEmail.connector.ts +21 -0
- package/@types/email/IEmail.types.ts +25 -0
- package/@types/email/ecomail/ecomail.connector.ts +140 -0
- package/@types/email/ecomail/ecomail.types.ts +27 -0
- package/@types/email/ecomail/index.ts +2 -0
- package/@types/email/index.ts +3 -0
- package/@types/index.ts +9 -0
- package/@types/io/index.ts +3 -0
- package/@types/io/sendEmail.ts +18 -0
- package/@types/io/sendSlack.ts +19 -0
- package/@types/io/sendSms.ts +20 -0
- package/@types/pushNotification/IPushNotification.ts +1 -0
- package/@types/pushNotification/index.ts +1 -0
- package/@types/queue.ts +24 -0
- package/@types/service.ts +30 -0
- package/@types/slack/ISlack.types.ts +3 -0
- package/@types/slack/index.ts +1 -0
- package/@types/slack/slack.connector.ts +27 -0
- package/@types/sms/ISms.connector.ts +22 -0
- package/@types/sms/ISms.types.ts +4 -0
- package/@types/sms/index.ts +3 -0
- package/@types/sms/twilio/index.ts +1 -0
- package/@types/sms/twilio/twilio.connector.ts +36 -0
- package/CHANGELOG.md +28 -0
- package/build.config.ts +28 -0
- package/drizzle.config.ts +3 -0
- package/package.json +40 -0
- package/scripts/generate-wrangler.ts +13 -0
- package/src/database/drizzle.ts +6 -0
- package/src/database/migrations/0000_funny_beast.sql +12 -0
- package/src/database/migrations/meta/0000_snapshot.json +101 -0
- package/src/database/migrations/meta/_journal.json +13 -0
- package/src/database/schema/audit-log.schema.ts +13 -0
- package/src/database/schema/index.ts +1 -0
- package/src/defineNotificationService.ts +321 -0
- package/src/defineNotificationWrangler.ts +78 -0
- package/src/index.ts +9 -0
- package/src/utils/connectors.ts +1 -0
- package/src/utils/database/command/create-audit-log.command.ts +34 -0
- package/src/utils/database/command/index.ts +1 -0
- package/src/utils/database/index.ts +1 -0
- package/src/utils/email.ts +25 -0
- package/src/utils/index.ts +3 -0
- package/src/utils/sms.ts +28 -0
- package/test/env.d.ts +7 -0
- package/test/integration/sendEmail.test.ts +88 -0
- package/test/setup/migrations.ts +3 -0
- package/test/unit/connectors/ecomail.connector.ts +715 -0
- package/test/unit/email.test.ts +55 -0
- package/tsconfig.json +3 -0
- package/vitest.config.ts +31 -0
- package/worker-configuration.d.ts +25 -0
- package/wrangler.jsonc +199 -0
- package/wrangler.ts +106 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './audit-log.consts'
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { tables } from '@services/notification/src/database/drizzle'
|
|
2
|
+
import type { InferInsertModel, InferSelectModel } from 'drizzle-orm'
|
|
3
|
+
|
|
4
|
+
export interface AuditLogSelectType
|
|
5
|
+
extends InferSelectModel<typeof tables.auditLog> {}
|
|
6
|
+
export interface AuditLogInsertType
|
|
7
|
+
extends InferInsertModel<typeof tables.auditLog> {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './audit-log.types'
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { IContact, IEmail } from '@develit-services/notification/@types'
|
|
2
|
+
|
|
3
|
+
export abstract class IEmailConnector {
|
|
4
|
+
static providerName: string
|
|
5
|
+
|
|
6
|
+
public API_KEY: string
|
|
7
|
+
public SMTP_HOST: string
|
|
8
|
+
public SENDER: IContact
|
|
9
|
+
|
|
10
|
+
protected constructor({
|
|
11
|
+
API_KEY,
|
|
12
|
+
SMTP_HOST,
|
|
13
|
+
SENDER,
|
|
14
|
+
}: { API_KEY: string; SMTP_HOST: string; SENDER: IContact }) {
|
|
15
|
+
this.API_KEY = API_KEY
|
|
16
|
+
this.SMTP_HOST = SMTP_HOST
|
|
17
|
+
this.SENDER = SENDER
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
abstract sendEmail(email: IEmail): Promise<Response | null>
|
|
21
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const iContactSchema = z.union([
|
|
4
|
+
z.string(),
|
|
5
|
+
z.object({
|
|
6
|
+
email: z.string(),
|
|
7
|
+
name: z.union([z.string(), z.undefined()]),
|
|
8
|
+
}),
|
|
9
|
+
])
|
|
10
|
+
|
|
11
|
+
export const iEmailSchema = z.object({
|
|
12
|
+
to: z.union([iContactSchema, z.array(iContactSchema)]),
|
|
13
|
+
replyTo: z.union([iContactSchema, z.array(iContactSchema)]).optional(),
|
|
14
|
+
cc: z.union([iContactSchema, z.array(iContactSchema)]).optional(),
|
|
15
|
+
bcc: z.union([iContactSchema, z.array(iContactSchema)]).optional(),
|
|
16
|
+
from: iContactSchema.optional(),
|
|
17
|
+
subject: z.string(),
|
|
18
|
+
text: z.string().optional(),
|
|
19
|
+
html: z.string().optional(),
|
|
20
|
+
templateId: z.number().optional(),
|
|
21
|
+
templateVariables: z.record(z.string(), z.string()).optional(),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
export type IContact = z.infer<typeof iContactSchema>
|
|
25
|
+
export type IEmail = z.infer<typeof iEmailSchema>
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { useResult } from '@develit-io/backend-sdk'
|
|
2
|
+
// TODO: Tests fail when imported as from '@services/notification/@types'`
|
|
3
|
+
import {
|
|
4
|
+
type EMAttachment,
|
|
5
|
+
type EMContact,
|
|
6
|
+
type EMEmail,
|
|
7
|
+
type IContact,
|
|
8
|
+
type IEmail,
|
|
9
|
+
IEmailConnector,
|
|
10
|
+
} from '@services/notification/@types/email'
|
|
11
|
+
|
|
12
|
+
export class EcomailConnector extends IEmailConnector {
|
|
13
|
+
static providerName = 'ecomail'
|
|
14
|
+
|
|
15
|
+
constructor({
|
|
16
|
+
API_KEY,
|
|
17
|
+
SMTP_HOST,
|
|
18
|
+
SENDER,
|
|
19
|
+
}: { API_KEY: string; SMTP_HOST: string; SENDER: IContact }) {
|
|
20
|
+
super({ API_KEY, SMTP_HOST, SENDER })
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async sendEmail(email: IEmail): Promise<Response | null> {
|
|
24
|
+
// This is needed only for ecomail connector because when template variables includes word 'localhost' it does not send email
|
|
25
|
+
if (email.templateVariables) {
|
|
26
|
+
for (const [key, value] of Object.entries(email.templateVariables)) {
|
|
27
|
+
if (
|
|
28
|
+
typeof value === 'string' &&
|
|
29
|
+
String(value).includes('localhost') &&
|
|
30
|
+
key in email.templateVariables
|
|
31
|
+
) {
|
|
32
|
+
email.templateVariables[key as keyof typeof email.templateVariables] =
|
|
33
|
+
String(value).replace('localhost', 'origin')
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const emEmail: EMEmail = this.convertEmail(email)
|
|
39
|
+
const uri = emEmail.message.template_id
|
|
40
|
+
? 'https://api2.ecomailapp.cz/transactional/send-template'
|
|
41
|
+
: 'https://api2.ecomailapp.cz/transactional/send-message'
|
|
42
|
+
|
|
43
|
+
const [data, error] = await useResult(
|
|
44
|
+
fetch(uri, {
|
|
45
|
+
method: 'POST',
|
|
46
|
+
headers: {
|
|
47
|
+
'Content-Type': 'application/json',
|
|
48
|
+
key: this.API_KEY,
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify(emEmail),
|
|
51
|
+
}),
|
|
52
|
+
)
|
|
53
|
+
if (error) throw error
|
|
54
|
+
|
|
55
|
+
return data
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
protected convertEmail(email: IEmail): EMEmail {
|
|
59
|
+
const toContacts: EMContact[] = this.convertContacts(email.to)
|
|
60
|
+
const ccContacts: EMContact[] = email.cc
|
|
61
|
+
? this.convertContacts(email.cc)
|
|
62
|
+
: []
|
|
63
|
+
const bccContacts: EMContact[] = email.bcc
|
|
64
|
+
? this.convertContacts(email.bcc)
|
|
65
|
+
: []
|
|
66
|
+
|
|
67
|
+
const replyTo: EMContact | undefined = email.replyTo
|
|
68
|
+
? this.convertContacts(email.replyTo)[0]
|
|
69
|
+
: undefined
|
|
70
|
+
|
|
71
|
+
const from: EMContact = this.convertContact(email.from || this.SENDER)
|
|
72
|
+
const subject: string = email.subject
|
|
73
|
+
|
|
74
|
+
const textAttachments: EMAttachment[] = email.text
|
|
75
|
+
? [{ name: 'plain', type: 'text/plain', content: email.text }]
|
|
76
|
+
: []
|
|
77
|
+
|
|
78
|
+
const htmlAttachments: EMAttachment[] = email.html
|
|
79
|
+
? [{ type: 'text/html', content: email.html, name: 'email_body.html' }]
|
|
80
|
+
: []
|
|
81
|
+
|
|
82
|
+
const attachments: EMAttachment[] = [...textAttachments, ...htmlAttachments]
|
|
83
|
+
|
|
84
|
+
const contacts: EMContact[] = toContacts.flatMap((to) => {
|
|
85
|
+
const entries: EMContact[] = []
|
|
86
|
+
if (ccContacts.length > 0) {
|
|
87
|
+
ccContacts.forEach((cc) => {
|
|
88
|
+
entries.push({ email: cc?.email, name: cc?.name, cc: cc?.email })
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
if (bccContacts.length > 0) {
|
|
92
|
+
bccContacts.forEach((bcc) => {
|
|
93
|
+
entries.push({ email: bcc?.email, name: bcc?.name, bcc: bcc?.email })
|
|
94
|
+
})
|
|
95
|
+
}
|
|
96
|
+
return entries.length > 0 ? entries : [to]
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
return {
|
|
100
|
+
message: {
|
|
101
|
+
from_email: from?.email,
|
|
102
|
+
from_name: from.name || '',
|
|
103
|
+
subject,
|
|
104
|
+
attachments,
|
|
105
|
+
to: contacts,
|
|
106
|
+
text: email.text,
|
|
107
|
+
html: email.html,
|
|
108
|
+
reply_to: replyTo?.email,
|
|
109
|
+
template_id: email.templateId,
|
|
110
|
+
global_merge_vars: email.templateVariables
|
|
111
|
+
? Object.keys(email.templateVariables).map((key) => ({
|
|
112
|
+
name: key,
|
|
113
|
+
content:
|
|
114
|
+
email.templateVariables![
|
|
115
|
+
key as keyof typeof email.templateVariables
|
|
116
|
+
],
|
|
117
|
+
}))
|
|
118
|
+
: null,
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
protected convertContacts(contacts: IContact | IContact[]): EMContact[] {
|
|
124
|
+
if (!contacts) {
|
|
125
|
+
return []
|
|
126
|
+
}
|
|
127
|
+
const contactArray: IContact[] = Array.isArray(contacts)
|
|
128
|
+
? contacts
|
|
129
|
+
: [contacts]
|
|
130
|
+
return contactArray.map(this.convertContact)
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
protected convertContact(contact: IContact): EMContact {
|
|
134
|
+
if (typeof contact === 'string') {
|
|
135
|
+
return { email: contact, name: undefined }
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return { email: contact?.email, name: contact?.name }
|
|
139
|
+
}
|
|
140
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export interface EMContact {
|
|
2
|
+
email: string
|
|
3
|
+
name?: string
|
|
4
|
+
cc?: string
|
|
5
|
+
bcc?: string
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface EMAttachment {
|
|
9
|
+
type: string
|
|
10
|
+
name: string
|
|
11
|
+
content: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface EMEmail {
|
|
15
|
+
message: {
|
|
16
|
+
template_id?: number
|
|
17
|
+
subject: string
|
|
18
|
+
from_name: string
|
|
19
|
+
from_email: string
|
|
20
|
+
reply_to?: string
|
|
21
|
+
to: EMContact[]
|
|
22
|
+
attachments: EMAttachment[]
|
|
23
|
+
text?: string
|
|
24
|
+
html?: string
|
|
25
|
+
global_merge_vars: object | null
|
|
26
|
+
}
|
|
27
|
+
}
|
package/@types/index.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { iEmailSchema } from '@services/notification/@types'
|
|
2
|
+
import z from 'zod'
|
|
3
|
+
|
|
4
|
+
export const sendEmailInputSchema = z.object({
|
|
5
|
+
email: iEmailSchema,
|
|
6
|
+
metadata: z.object({
|
|
7
|
+
userAgent: z.string().optional(),
|
|
8
|
+
ip: z.ipv4().or(z.ipv6()).optional(),
|
|
9
|
+
initiator: z.object({
|
|
10
|
+
service: z.string(),
|
|
11
|
+
userId: z.string().optional(),
|
|
12
|
+
}),
|
|
13
|
+
}),
|
|
14
|
+
})
|
|
15
|
+
|
|
16
|
+
export interface SendEmailInput extends z.infer<typeof sendEmailInputSchema> {}
|
|
17
|
+
|
|
18
|
+
export interface SendEmailOutput {}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod/v4'
|
|
2
|
+
|
|
3
|
+
export const sendSlackInputSchema = z.object({
|
|
4
|
+
slack: z.object({
|
|
5
|
+
message: z.string(),
|
|
6
|
+
}),
|
|
7
|
+
metadata: z.object({
|
|
8
|
+
userAgent: z.string().optional(),
|
|
9
|
+
ip: z.ipv4().or(z.ipv6()).optional(),
|
|
10
|
+
initiator: z.object({
|
|
11
|
+
service: z.string(),
|
|
12
|
+
userId: z.string().optional(),
|
|
13
|
+
}),
|
|
14
|
+
}),
|
|
15
|
+
})
|
|
16
|
+
|
|
17
|
+
export interface SendSlackInput extends z.infer<typeof sendSlackInputSchema> {}
|
|
18
|
+
|
|
19
|
+
export interface SendSlackOutput {}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
export const sendSmsInputSchema = z.object({
|
|
4
|
+
sms: z.object({
|
|
5
|
+
message: z.string(),
|
|
6
|
+
to: z.string(),
|
|
7
|
+
}),
|
|
8
|
+
metadata: z.object({
|
|
9
|
+
userAgent: z.string().optional(),
|
|
10
|
+
ip: z.ipv4().or(z.ipv6()).optional(),
|
|
11
|
+
initiator: z.object({
|
|
12
|
+
service: z.string(),
|
|
13
|
+
userId: z.string().optional(),
|
|
14
|
+
}),
|
|
15
|
+
}),
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
export interface SendSmsInput extends z.infer<typeof sendSmsInputSchema> {}
|
|
19
|
+
|
|
20
|
+
export interface SendSmsOutput {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export interface IPushNotification {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './IPushNotification'
|
package/@types/queue.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
IEmail,
|
|
3
|
+
IPushNotification,
|
|
4
|
+
ISlack,
|
|
5
|
+
ISms,
|
|
6
|
+
} from '@services/notification/@types'
|
|
7
|
+
|
|
8
|
+
export interface NotificationQueueMessage {
|
|
9
|
+
type: 'email' | 'sms' | 'pushNotification' | 'slack'
|
|
10
|
+
metadata: {
|
|
11
|
+
userAgent?: string
|
|
12
|
+
ip?: string
|
|
13
|
+
initiator: {
|
|
14
|
+
service: string
|
|
15
|
+
userId?: string
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
payload: {
|
|
19
|
+
email?: IEmail
|
|
20
|
+
sms?: ISms
|
|
21
|
+
pushNotification?: IPushNotification
|
|
22
|
+
slack?: ISlack
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface NotificationServiceEnvironmentConfig {
|
|
2
|
+
queue?: {
|
|
3
|
+
max_batch_size?: number
|
|
4
|
+
max_batch_timeout?: number
|
|
5
|
+
}
|
|
6
|
+
d1: {
|
|
7
|
+
id: string
|
|
8
|
+
}
|
|
9
|
+
vars: {
|
|
10
|
+
EMAIL_PROVIDER: 'ecomail'
|
|
11
|
+
EMAIL_SENDER: { name: string; email: string }
|
|
12
|
+
EMAIL_SMTP_HOST: string
|
|
13
|
+
EMAIL_API_KEY: string
|
|
14
|
+
SMS_PROVIDER: 'twilio'
|
|
15
|
+
SMS_ACCOUNT_ID: string
|
|
16
|
+
SMS_AUTH_TOKEN: string
|
|
17
|
+
SMS_SERVICE_ID: string | number
|
|
18
|
+
SLACK_WEBHOOKS: [string]
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface NotificationServiceWranglerConfig {
|
|
23
|
+
name: string
|
|
24
|
+
envs: {
|
|
25
|
+
local: NotificationServiceEnvironmentConfig
|
|
26
|
+
[key: string]: NotificationServiceEnvironmentConfig
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface NotificationServiceEnv extends NotificationEnv {}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './ISlack.types'
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export class SlackConnector {
|
|
2
|
+
private webhooks: string[]
|
|
3
|
+
|
|
4
|
+
constructor(webhooks: string[]) {
|
|
5
|
+
this.webhooks = webhooks
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async sendNotificationToAllSlack(message: string) {
|
|
9
|
+
const controller = new AbortController()
|
|
10
|
+
const timeoutId = setTimeout(() => controller.abort(), 3000)
|
|
11
|
+
|
|
12
|
+
for (const webhook of this.webhooks) {
|
|
13
|
+
let response = await fetch(webhook, {
|
|
14
|
+
method: 'POST',
|
|
15
|
+
body: JSON.stringify({ text: message }),
|
|
16
|
+
headers: { 'Content-Type': 'application/json' },
|
|
17
|
+
signal: controller.signal,
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
clearTimeout(timeoutId)
|
|
21
|
+
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
throw new Error('Failed sending Slack notification to ' + message)
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { InternalError } from '@develit-io/backend-sdk'
|
|
2
|
+
import type { ISms } from '@services/notification/@types'
|
|
3
|
+
|
|
4
|
+
export abstract class ISmsConnector {
|
|
5
|
+
static providerName: string
|
|
6
|
+
|
|
7
|
+
public ACCOUNT_ID: string
|
|
8
|
+
public AUTH_TOKEN: string
|
|
9
|
+
public SERVICE_ID: string
|
|
10
|
+
|
|
11
|
+
protected constructor({
|
|
12
|
+
ACCOUNT_ID,
|
|
13
|
+
AUTH_TOKEN,
|
|
14
|
+
SERVICE_ID,
|
|
15
|
+
}: { ACCOUNT_ID: string; AUTH_TOKEN: string; SERVICE_ID: string }) {
|
|
16
|
+
this.ACCOUNT_ID = ACCOUNT_ID
|
|
17
|
+
this.AUTH_TOKEN = AUTH_TOKEN
|
|
18
|
+
this.SERVICE_ID = SERVICE_ID
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
abstract sendSms(sms: ISms): Promise<InternalError | void>
|
|
22
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './twilio.connector'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
// TODO: Tests fail when imported as from '@services/notification/@types'`
|
|
2
|
+
import {
|
|
3
|
+
type InternalError,
|
|
4
|
+
createInternalError,
|
|
5
|
+
} from '@develit-io/backend-sdk'
|
|
6
|
+
import { type ISms, ISmsConnector } from '@services/notification/@types/sms'
|
|
7
|
+
import twilio from 'twilio'
|
|
8
|
+
|
|
9
|
+
export class TwilioConnector extends ISmsConnector {
|
|
10
|
+
static providerName = 'twilio'
|
|
11
|
+
|
|
12
|
+
readonly twilioClient: twilio.Twilio
|
|
13
|
+
|
|
14
|
+
constructor({
|
|
15
|
+
ACCOUNT_ID,
|
|
16
|
+
AUTH_TOKEN,
|
|
17
|
+
SERVICE_ID,
|
|
18
|
+
}: { ACCOUNT_ID: string; AUTH_TOKEN: string; SERVICE_ID: string }) {
|
|
19
|
+
super({ ACCOUNT_ID, AUTH_TOKEN, SERVICE_ID })
|
|
20
|
+
this.twilioClient = twilio(ACCOUNT_ID, AUTH_TOKEN)
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async sendSms(sms: ISms): Promise<InternalError | void> {
|
|
24
|
+
const message = await this.twilioClient.messages.create({
|
|
25
|
+
body: sms.message,
|
|
26
|
+
messagingServiceSid: this.SERVICE_ID,
|
|
27
|
+
to: sms.to,
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
if (message.errorMessage)
|
|
31
|
+
return createInternalError(null, {
|
|
32
|
+
message: message.errorMessage,
|
|
33
|
+
status: message.errorCode,
|
|
34
|
+
})
|
|
35
|
+
}
|
|
36
|
+
}
|
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
## v0.0.2
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
### 🚀 Enhancements
|
|
8
|
+
|
|
9
|
+
- **notification:** Init codebase + notification service ([3fa7306](https://github.com/develit-io/develit-services/commit/3fa7306))
|
|
10
|
+
|
|
11
|
+
### 📦 Build
|
|
12
|
+
|
|
13
|
+
- **notification:** Setup package build ([79fb543](https://github.com/develit-io/develit-services/commit/79fb543))
|
|
14
|
+
|
|
15
|
+
### ❤️ Contributors
|
|
16
|
+
|
|
17
|
+
- Klein Petr ([@kleinpetr](https://github.com/kleinpetr))
|
|
18
|
+
|
|
19
|
+
## v0.0.1
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
### 🚀 Enhancements
|
|
23
|
+
|
|
24
|
+
- **notification:** Init codebase + notification service ([3fa7306](https://github.com/develit-io/develit-services/commit/3fa7306))
|
|
25
|
+
|
|
26
|
+
### ❤️ Contributors
|
|
27
|
+
|
|
28
|
+
- Klein Petr ([@kleinpetr](https://github.com/kleinpetr))
|
package/build.config.ts
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { defineBuildConfig } from 'unbuild'
|
|
2
|
+
|
|
3
|
+
export default defineBuildConfig({
|
|
4
|
+
entries: ['./src'],
|
|
5
|
+
outDir: 'dist',
|
|
6
|
+
declaration: true,
|
|
7
|
+
rollup: {
|
|
8
|
+
emitCJS: true,
|
|
9
|
+
},
|
|
10
|
+
externals: [
|
|
11
|
+
'cloudflare:workers',
|
|
12
|
+
'@cloudflare/workers-types',
|
|
13
|
+
'@develit-io/backend-sdk',
|
|
14
|
+
'@cloudflare/vitest-pool-workers',
|
|
15
|
+
'cloudflare',
|
|
16
|
+
'drizzle-kit',
|
|
17
|
+
'drizzle-orm',
|
|
18
|
+
'drizzle-seed',
|
|
19
|
+
'wrangler',
|
|
20
|
+
'zod',
|
|
21
|
+
'@types/node',
|
|
22
|
+
'cloudflare:workers',
|
|
23
|
+
'@std/text',
|
|
24
|
+
'@develit-services/notification/@types',
|
|
25
|
+
'@develit-services/notification/src/database/drizzle',
|
|
26
|
+
'@develit-services/notification/src/utils',
|
|
27
|
+
],
|
|
28
|
+
})
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@develit-services/notification",
|
|
3
|
+
"version": "0.0.2",
|
|
4
|
+
"author": "Develit.io s.r.o.",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"postinstall": "bun cf:typegen",
|
|
8
|
+
"wrangler:generate": "bun ./scripts/generate-wrangler.ts",
|
|
9
|
+
"cf:typegen": "bun wrangler:generate && wrangler types --include-runtime false --env-interface NotificationEnv",
|
|
10
|
+
"dev": "bun cf:typegen && wrangler dev --port 9234",
|
|
11
|
+
"lint": "biome check",
|
|
12
|
+
"lint:fix": "biome check --fix",
|
|
13
|
+
"typecheck": "tsc",
|
|
14
|
+
"db:init": "wrangler d1 execute notification-db --local --persist-to ../../.wrangler/state --command=\"SELECT 'creating sqlite...' AS status;\"",
|
|
15
|
+
"db:generate": "drizzle-kit generate",
|
|
16
|
+
"db:migrate": "drizzle-kit migrate",
|
|
17
|
+
"db:explore": "drizzle-kit studio",
|
|
18
|
+
"test": "vitest",
|
|
19
|
+
"test:cov": "vitest run --coverage",
|
|
20
|
+
"test:unit": "vitest unit fixtures",
|
|
21
|
+
"test:int": "vitest integration",
|
|
22
|
+
"changelogen": "bunx changelogen@latest --bump",
|
|
23
|
+
"build": "unbuild",
|
|
24
|
+
"release": "bun run build && bunx changelogen@latest --release --push && npm publish --access public"
|
|
25
|
+
},
|
|
26
|
+
"peerDependencies": {
|
|
27
|
+
"@develit-io/backend-sdk": "5.14.1",
|
|
28
|
+
"@cloudflare/vitest-pool-workers": "^0.8.70",
|
|
29
|
+
"@cloudflare/workers-types": "^4.20250906.0",
|
|
30
|
+
"cloudflare": "^5.0.0",
|
|
31
|
+
"drizzle-kit": "^0.31.4",
|
|
32
|
+
"drizzle-orm": "^0.44.4",
|
|
33
|
+
"drizzle-seed": "^0.3.1",
|
|
34
|
+
"wrangler": "^4.34.0",
|
|
35
|
+
"zod": "^4.1.5"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"twilio": "^5.9.0"
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import fs from 'fs'
|
|
2
|
+
import path from 'path'
|
|
3
|
+
import wrangler from '../wrangler'
|
|
4
|
+
|
|
5
|
+
const outputPath = path.resolve(__dirname, '../wrangler.jsonc')
|
|
6
|
+
|
|
7
|
+
const header = `// ⚠️ AUTO-GENERATED FILE. DO NOT EDIT.
|
|
8
|
+
// To make changes, update wrangler.ts and re-run the generation script.
|
|
9
|
+
\n`
|
|
10
|
+
const body = JSON.stringify(wrangler, null, 2)
|
|
11
|
+
|
|
12
|
+
fs.writeFileSync(outputPath, header + body)
|
|
13
|
+
console.log(`✅ Generated wrangler.jsonc at ${outputPath}`)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
CREATE TABLE `audit_log` (
|
|
2
|
+
`id` text PRIMARY KEY NOT NULL,
|
|
3
|
+
`created_at` integer DEFAULT (unixepoch('subsec') * 1000),
|
|
4
|
+
`updated_at` integer DEFAULT (unixepoch('subsec') * 1000),
|
|
5
|
+
`deleted_at` integer DEFAULT null,
|
|
6
|
+
`event` text NOT NULL,
|
|
7
|
+
`ip` text,
|
|
8
|
+
`user_agent` text,
|
|
9
|
+
`description` text,
|
|
10
|
+
`initiator_service` text NOT NULL,
|
|
11
|
+
`initiator_user_id` text
|
|
12
|
+
);
|