better-auth-lead 0.0.1-dev.1 → 0.0.1-dev.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/README.md +111 -1
- package/dist/client.d.mts +7 -1
- package/dist/client.mjs +4 -1
- package/dist/client.mjs.map +1 -1
- package/dist/error-codes-d1rquQlA.mjs +12 -0
- package/dist/error-codes-d1rquQlA.mjs.map +1 -0
- package/dist/index.d.mts +18 -8
- package/dist/index.mjs +83 -64
- package/dist/index.mjs.map +1 -1
- package/dist/{type-CedeOvZ6.d.mts → type-KTJzD7Eo.d.mts} +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ pnpm add better-auth-lead
|
|
|
13
13
|
yarn add better-auth-lead
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Add the plugin to your auth config
|
|
17
17
|
|
|
18
18
|
```ts
|
|
19
19
|
// server/auth.ts
|
|
@@ -42,3 +42,113 @@ const authClient = createAuthClient({
|
|
|
42
42
|
plugins: [leadClient()],
|
|
43
43
|
});
|
|
44
44
|
```
|
|
45
|
+
|
|
46
|
+
## Usage
|
|
47
|
+
|
|
48
|
+
### Subscribe
|
|
49
|
+
|
|
50
|
+
```ts
|
|
51
|
+
// POST /lead/subscribe
|
|
52
|
+
const { data, error } = await authClient.lead.subscribe({
|
|
53
|
+
email: 'user@example.com',
|
|
54
|
+
// json object
|
|
55
|
+
metadata: {
|
|
56
|
+
preferences: 'engineering',
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Verify
|
|
62
|
+
|
|
63
|
+
```ts
|
|
64
|
+
// GET /lead/verify
|
|
65
|
+
await authClient.lead.verify({
|
|
66
|
+
query: {
|
|
67
|
+
token,
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Unsubscribe
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
// POST /lead/unsubscribe
|
|
76
|
+
const { data, error } = await authClient.lead.unsubscribe({ id: 'lead-id' });
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Resend
|
|
80
|
+
|
|
81
|
+
```ts
|
|
82
|
+
// POST /lead/resend
|
|
83
|
+
const { data, error } = await authClient.lead.resend({
|
|
84
|
+
email: 'user@example.com',
|
|
85
|
+
});
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Update
|
|
89
|
+
|
|
90
|
+
```ts
|
|
91
|
+
// POST /lead/update
|
|
92
|
+
const { data, error } = await authClient.lead.update({
|
|
93
|
+
id: 'lead-id',
|
|
94
|
+
metadata: {
|
|
95
|
+
preferences: 'ai',
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### Email Verification
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
// server/auth.ts
|
|
104
|
+
import { betterAuth } from 'better-auth';
|
|
105
|
+
import { lead } from 'better-auth-lead';
|
|
106
|
+
import { sendEmail } from './email'; // your email sending function
|
|
107
|
+
|
|
108
|
+
export const auth = betterAuth({
|
|
109
|
+
plugins: [
|
|
110
|
+
lead({
|
|
111
|
+
sendVerificationEmail: async ({ email, url, token }) => {
|
|
112
|
+
void sendEmail({
|
|
113
|
+
to: email,
|
|
114
|
+
subject: 'Newsletter: Verify your email address',
|
|
115
|
+
text: `Click the link to verify your email: ${url}`,
|
|
116
|
+
});
|
|
117
|
+
},
|
|
118
|
+
}),
|
|
119
|
+
],
|
|
120
|
+
});
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
> Avoid awaiting the email sending to prevent timing attacks.
|
|
124
|
+
|
|
125
|
+
## Schema
|
|
126
|
+
|
|
127
|
+
### Lead
|
|
128
|
+
|
|
129
|
+
Table name: `lead`
|
|
130
|
+
|
|
131
|
+
| Field | Type | Key | Description |
|
|
132
|
+
| -------------- | ------- | ------ | ------------------------------- |
|
|
133
|
+
| id | string | pk | Unique identifier for each lead |
|
|
134
|
+
| email | string | unique | Email address of the lead |
|
|
135
|
+
| emailVerified | boolean | | Whether the email is verified |
|
|
136
|
+
| metadata | json | ? | Additional data about the lead |
|
|
137
|
+
| createdAt | date | | Timestamp of lead creation |
|
|
138
|
+
| updatedAt | date | | Timestamp of last update |
|
|
139
|
+
|
|
140
|
+
#### Prisma
|
|
141
|
+
|
|
142
|
+
```prisma
|
|
143
|
+
model Lead {
|
|
144
|
+
id String @id
|
|
145
|
+
createdAt DateTime @default(now())
|
|
146
|
+
updatedAt DateTime @updatedAt
|
|
147
|
+
email String
|
|
148
|
+
emailVerified Boolean @default(false)
|
|
149
|
+
metadata String?
|
|
150
|
+
|
|
151
|
+
@@unique([email])
|
|
152
|
+
@@map("lead")
|
|
153
|
+
}
|
|
154
|
+
```
|
package/dist/client.d.mts
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import "./type-
|
|
1
|
+
import "./type-KTJzD7Eo.mjs";
|
|
2
2
|
import { lead } from "./index.mjs";
|
|
3
|
+
import * as better_auth0 from "better-auth";
|
|
3
4
|
|
|
4
5
|
//#region src/client.d.ts
|
|
5
6
|
declare const leadClient: () => {
|
|
6
7
|
id: "lead";
|
|
7
8
|
$InferServerPlugin: ReturnType<typeof lead>;
|
|
9
|
+
$ERROR_CODES: {
|
|
10
|
+
INVALID_EMAIL: better_auth0.RawError<"INVALID_EMAIL">;
|
|
11
|
+
INVALID_TOKEN: better_auth0.RawError<"INVALID_TOKEN">;
|
|
12
|
+
TOKEN_EXPIRED: better_auth0.RawError<"TOKEN_EXPIRED">;
|
|
13
|
+
};
|
|
8
14
|
};
|
|
9
15
|
//#endregion
|
|
10
16
|
export { leadClient };
|
package/dist/client.mjs
CHANGED
package/dist/client.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from 'better-auth/client';\nimport type { lead } from './index';\n\nexport const leadClient = () => {\n return {\n id: 'lead',\n $InferServerPlugin: {} as ReturnType<typeof lead>,\n } satisfies BetterAuthClientPlugin;\n};\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"client.mjs","names":[],"sources":["../src/client.ts"],"sourcesContent":["import type { BetterAuthClientPlugin } from 'better-auth/client';\n\nimport { LEAD_ERROR_CODES } from './error-codes';\nimport type { lead } from './index';\n\nexport const leadClient = () => {\n return {\n id: 'lead',\n $InferServerPlugin: {} as ReturnType<typeof lead>,\n $ERROR_CODES: LEAD_ERROR_CODES,\n } satisfies BetterAuthClientPlugin;\n};\n"],"mappings":";;;AAKA,MAAa,mBAAmB;AAC9B,QAAO;EACL,IAAI;EACJ,oBAAoB,EAAE;EACtB,cAAc;EACf"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { defineErrorCodes } from "better-auth";
|
|
2
|
+
|
|
3
|
+
//#region src/error-codes.ts
|
|
4
|
+
const LEAD_ERROR_CODES = defineErrorCodes({
|
|
5
|
+
INVALID_EMAIL: "Invalid email",
|
|
6
|
+
INVALID_TOKEN: "Invalid token",
|
|
7
|
+
TOKEN_EXPIRED: "Token expired"
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
//#endregion
|
|
11
|
+
export { LEAD_ERROR_CODES as t };
|
|
12
|
+
//# sourceMappingURL=error-codes-d1rquQlA.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-codes-d1rquQlA.mjs","names":[],"sources":["../src/error-codes.ts"],"sourcesContent":["import { defineErrorCodes } from 'better-auth';\n\nexport const LEAD_ERROR_CODES = defineErrorCodes({\n INVALID_EMAIL: 'Invalid email',\n INVALID_TOKEN: 'Invalid token',\n TOKEN_EXPIRED: 'Token expired',\n});\n"],"mappings":";;;AAEA,MAAa,mBAAmB,iBAAiB;CAC/C,eAAe;CACf,eAAe;CACf,eAAe;CAChB,CAAC"}
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { n as LeadOptions, r as LeadPayload, t as Lead } from "./type-
|
|
1
|
+
import { n as LeadOptions, r as LeadPayload, t as Lead } from "./type-KTJzD7Eo.mjs";
|
|
2
2
|
import * as better_auth0 from "better-auth";
|
|
3
3
|
import * as zod from "zod";
|
|
4
4
|
|
|
5
5
|
//#region src/index.d.ts
|
|
6
|
-
declare const lead: <O extends LeadOptions>(options
|
|
6
|
+
declare const lead: <O extends LeadOptions>(options?: O) => {
|
|
7
7
|
id: "lead";
|
|
8
8
|
schema: {
|
|
9
9
|
lead: {
|
|
@@ -43,10 +43,9 @@ declare const lead: <O extends LeadOptions>(options: O) => {
|
|
|
43
43
|
subscribe: better_auth0.StrictEndpoint<"/lead/subscribe", {
|
|
44
44
|
method: "POST";
|
|
45
45
|
body: zod.ZodObject<{
|
|
46
|
-
email: zod.
|
|
46
|
+
email: zod.ZodString;
|
|
47
47
|
metadata: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
48
48
|
}, better_auth0.$strip>;
|
|
49
|
-
metadata: {};
|
|
50
49
|
}, {
|
|
51
50
|
status: boolean;
|
|
52
51
|
}>;
|
|
@@ -55,7 +54,6 @@ declare const lead: <O extends LeadOptions>(options: O) => {
|
|
|
55
54
|
query: zod.ZodObject<{
|
|
56
55
|
token: zod.ZodString;
|
|
57
56
|
}, better_auth0.$strip>;
|
|
58
|
-
metadata: {};
|
|
59
57
|
}, {
|
|
60
58
|
status: boolean;
|
|
61
59
|
}>;
|
|
@@ -64,16 +62,23 @@ declare const lead: <O extends LeadOptions>(options: O) => {
|
|
|
64
62
|
body: zod.ZodObject<{
|
|
65
63
|
id: zod.ZodString;
|
|
66
64
|
}, better_auth0.$strip>;
|
|
67
|
-
metadata: {};
|
|
68
65
|
}, {
|
|
69
66
|
status: boolean;
|
|
70
67
|
}>;
|
|
71
68
|
resend: better_auth0.StrictEndpoint<"/lead/resend", {
|
|
72
69
|
method: "POST";
|
|
73
70
|
body: zod.ZodObject<{
|
|
74
|
-
email: zod.
|
|
71
|
+
email: zod.ZodString;
|
|
72
|
+
}, better_auth0.$strip>;
|
|
73
|
+
}, {
|
|
74
|
+
status: boolean;
|
|
75
|
+
}>;
|
|
76
|
+
update: better_auth0.StrictEndpoint<"/lead/update", {
|
|
77
|
+
method: "POST";
|
|
78
|
+
body: zod.ZodObject<{
|
|
79
|
+
id: zod.ZodString;
|
|
80
|
+
metadata: zod.ZodOptional<zod.ZodRecord<zod.ZodString, zod.ZodAny>>;
|
|
75
81
|
}, better_auth0.$strip>;
|
|
76
|
-
metadata: {};
|
|
77
82
|
}, {
|
|
78
83
|
status: boolean;
|
|
79
84
|
}>;
|
|
@@ -84,6 +89,11 @@ declare const lead: <O extends LeadOptions>(options: O) => {
|
|
|
84
89
|
window: number;
|
|
85
90
|
max: number;
|
|
86
91
|
}[];
|
|
92
|
+
$ERROR_CODES: {
|
|
93
|
+
INVALID_EMAIL: better_auth0.RawError<"INVALID_EMAIL">;
|
|
94
|
+
INVALID_TOKEN: better_auth0.RawError<"INVALID_TOKEN">;
|
|
95
|
+
TOKEN_EXPIRED: better_auth0.RawError<"TOKEN_EXPIRED">;
|
|
96
|
+
};
|
|
87
97
|
};
|
|
88
98
|
//#endregion
|
|
89
99
|
export { Lead, LeadOptions, LeadPayload, lead };
|
package/dist/index.mjs
CHANGED
|
@@ -1,57 +1,22 @@
|
|
|
1
|
+
import { t as LEAD_ERROR_CODES } from "./error-codes-d1rquQlA.mjs";
|
|
1
2
|
import "better-auth";
|
|
2
|
-
import { mergeSchema } from "better-auth/db";
|
|
3
3
|
import { APIError, createAuthEndpoint, createEmailVerificationToken } from "better-auth/api";
|
|
4
|
-
import * as z from "zod";
|
|
5
4
|
import { jwtVerify } from "jose";
|
|
6
5
|
import { JWTExpired } from "jose/errors";
|
|
6
|
+
import * as z from "zod";
|
|
7
|
+
import { mergeSchema } from "better-auth/db";
|
|
7
8
|
|
|
8
|
-
//#region src/schema.ts
|
|
9
|
-
const lead$1 = { lead: { fields: {
|
|
10
|
-
createdAt: {
|
|
11
|
-
type: "date",
|
|
12
|
-
defaultValue: () => /* @__PURE__ */ new Date(),
|
|
13
|
-
required: true,
|
|
14
|
-
input: false
|
|
15
|
-
},
|
|
16
|
-
updatedAt: {
|
|
17
|
-
type: "date",
|
|
18
|
-
defaultValue: () => /* @__PURE__ */ new Date(),
|
|
19
|
-
onUpdate: () => /* @__PURE__ */ new Date(),
|
|
20
|
-
required: true,
|
|
21
|
-
input: false
|
|
22
|
-
},
|
|
23
|
-
email: {
|
|
24
|
-
type: "string",
|
|
25
|
-
required: true,
|
|
26
|
-
unique: true
|
|
27
|
-
},
|
|
28
|
-
emailVerified: {
|
|
29
|
-
type: "boolean",
|
|
30
|
-
defaultValue: false,
|
|
31
|
-
required: true,
|
|
32
|
-
input: false
|
|
33
|
-
},
|
|
34
|
-
metadata: {
|
|
35
|
-
type: "string",
|
|
36
|
-
required: false
|
|
37
|
-
}
|
|
38
|
-
} } };
|
|
39
|
-
const getSchema = (options) => {
|
|
40
|
-
return mergeSchema(lead$1, options.schema);
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
//#endregion
|
|
44
9
|
//#region src/routes.ts
|
|
45
10
|
const subscribeSchema = z.object({
|
|
46
|
-
email: z.
|
|
47
|
-
metadata: z.record(z.string(), z.any()).optional()
|
|
11
|
+
email: z.string().meta({ description: "Email address of the lead" }),
|
|
12
|
+
metadata: z.record(z.string(), z.any()).optional().meta({ description: "Additional metadata to store with the lead" })
|
|
48
13
|
});
|
|
49
14
|
const subscribe = (options) => createAuthEndpoint("/lead/subscribe", {
|
|
50
15
|
method: "POST",
|
|
51
|
-
body: subscribeSchema
|
|
52
|
-
metadata: {}
|
|
16
|
+
body: subscribeSchema
|
|
53
17
|
}, async (ctx) => {
|
|
54
18
|
const { email, metadata } = ctx.body;
|
|
19
|
+
if (!z.email().safeParse(email).success) throw APIError.from("BAD_REQUEST", LEAD_ERROR_CODES.INVALID_EMAIL);
|
|
55
20
|
const normalizedEmail = email.toLowerCase();
|
|
56
21
|
let lead = await ctx.context.adapter.findOne({
|
|
57
22
|
model: "lead",
|
|
@@ -78,14 +43,6 @@ const subscribe = (options) => createAuthEndpoint("/lead/subscribe", {
|
|
|
78
43
|
}]
|
|
79
44
|
});
|
|
80
45
|
}
|
|
81
|
-
else if (!lead.emailVerified) lead = await ctx.context.adapter.update({
|
|
82
|
-
model: "lead",
|
|
83
|
-
where: [{
|
|
84
|
-
field: "email",
|
|
85
|
-
value: normalizedEmail
|
|
86
|
-
}],
|
|
87
|
-
update: { metadata: metadata ? JSON.stringify(metadata) : lead.metadata }
|
|
88
|
-
});
|
|
89
46
|
if (options.sendVerificationEmail && lead && !lead.emailVerified) {
|
|
90
47
|
const token = await createEmailVerificationToken(ctx.context.secret, normalizedEmail, void 0, options.expiresIn ?? 3600);
|
|
91
48
|
const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;
|
|
@@ -97,19 +54,18 @@ const subscribe = (options) => createAuthEndpoint("/lead/subscribe", {
|
|
|
97
54
|
}
|
|
98
55
|
return ctx.json({ status: true });
|
|
99
56
|
});
|
|
100
|
-
const verifySchema = z.object({ token: z.string() });
|
|
57
|
+
const verifySchema = z.object({ token: z.string().meta({ description: "The token to verify the email" }) });
|
|
101
58
|
const verify = (options) => createAuthEndpoint("/lead/verify", {
|
|
102
59
|
method: "GET",
|
|
103
|
-
query: verifySchema
|
|
104
|
-
metadata: {}
|
|
60
|
+
query: verifySchema
|
|
105
61
|
}, async (ctx) => {
|
|
106
62
|
const { token } = ctx.query;
|
|
107
63
|
let jwt;
|
|
108
64
|
try {
|
|
109
65
|
jwt = await jwtVerify(token, new TextEncoder().encode(ctx.context.secret), { algorithms: ["HS256"] });
|
|
110
66
|
} catch (e) {
|
|
111
|
-
if (e instanceof JWTExpired) throw
|
|
112
|
-
throw
|
|
67
|
+
if (e instanceof JWTExpired) throw APIError.from("UNAUTHORIZED", LEAD_ERROR_CODES.TOKEN_EXPIRED);
|
|
68
|
+
throw APIError.from("UNAUTHORIZED", LEAD_ERROR_CODES.INVALID_TOKEN);
|
|
113
69
|
}
|
|
114
70
|
const parsed = subscribeSchema.parse(jwt.payload);
|
|
115
71
|
const lead = await ctx.context.adapter.findOne({
|
|
@@ -131,11 +87,10 @@ const verify = (options) => createAuthEndpoint("/lead/verify", {
|
|
|
131
87
|
});
|
|
132
88
|
return ctx.json({ status: true });
|
|
133
89
|
});
|
|
134
|
-
const unsubscribeSchema = z.object({ id: z.string() });
|
|
90
|
+
const unsubscribeSchema = z.object({ id: z.string().meta({ description: "The id of the lead to unsubscribe" }) });
|
|
135
91
|
const unsubscribe = (options) => createAuthEndpoint("/lead/unsubscribe", {
|
|
136
92
|
method: "POST",
|
|
137
|
-
body: unsubscribeSchema
|
|
138
|
-
metadata: {}
|
|
93
|
+
body: unsubscribeSchema
|
|
139
94
|
}, async (ctx) => {
|
|
140
95
|
const { id } = ctx.body;
|
|
141
96
|
if (!await ctx.context.adapter.findOne({
|
|
@@ -154,13 +109,13 @@ const unsubscribe = (options) => createAuthEndpoint("/lead/unsubscribe", {
|
|
|
154
109
|
});
|
|
155
110
|
return ctx.json({ status: true });
|
|
156
111
|
});
|
|
157
|
-
const resendSchema = z.object({ email: z.
|
|
112
|
+
const resendSchema = z.object({ email: z.string().meta({ description: "Email address to resend the verification email to" }) });
|
|
158
113
|
const resend = (options) => createAuthEndpoint("/lead/resend", {
|
|
159
114
|
method: "POST",
|
|
160
|
-
body: resendSchema
|
|
161
|
-
metadata: {}
|
|
115
|
+
body: resendSchema
|
|
162
116
|
}, async (ctx) => {
|
|
163
117
|
const { email } = ctx.body;
|
|
118
|
+
if (!z.email().safeParse(email).success) throw APIError.from("BAD_REQUEST", LEAD_ERROR_CODES.INVALID_EMAIL);
|
|
164
119
|
const normalizedEmail = email.toLowerCase();
|
|
165
120
|
const lead = await ctx.context.adapter.findOne({
|
|
166
121
|
model: "lead",
|
|
@@ -181,10 +136,72 @@ const resend = (options) => createAuthEndpoint("/lead/resend", {
|
|
|
181
136
|
}
|
|
182
137
|
return ctx.json({ status: true });
|
|
183
138
|
});
|
|
139
|
+
const updateSchema = z.object({
|
|
140
|
+
id: z.string().meta({ description: "The id of the lead to update" }),
|
|
141
|
+
metadata: z.record(z.string(), z.any()).optional().meta({ description: "Additional metadata to store with the lead" })
|
|
142
|
+
});
|
|
143
|
+
const update = (options) => createAuthEndpoint("/lead/update", {
|
|
144
|
+
method: "POST",
|
|
145
|
+
body: updateSchema
|
|
146
|
+
}, async (ctx) => {
|
|
147
|
+
const { id, metadata } = ctx.body;
|
|
148
|
+
if (!await ctx.context.adapter.findOne({
|
|
149
|
+
model: "lead",
|
|
150
|
+
where: [{
|
|
151
|
+
field: "id",
|
|
152
|
+
value: id
|
|
153
|
+
}]
|
|
154
|
+
})) return ctx.json({ status: true });
|
|
155
|
+
await ctx.context.adapter.update({
|
|
156
|
+
model: "lead",
|
|
157
|
+
where: [{
|
|
158
|
+
field: "id",
|
|
159
|
+
value: id
|
|
160
|
+
}],
|
|
161
|
+
update: { metadata: metadata ? JSON.stringify(metadata) : void 0 }
|
|
162
|
+
});
|
|
163
|
+
return ctx.json({ status: true });
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
//#endregion
|
|
167
|
+
//#region src/schema.ts
|
|
168
|
+
const lead$1 = { lead: { fields: {
|
|
169
|
+
createdAt: {
|
|
170
|
+
type: "date",
|
|
171
|
+
defaultValue: () => /* @__PURE__ */ new Date(),
|
|
172
|
+
required: true,
|
|
173
|
+
input: false
|
|
174
|
+
},
|
|
175
|
+
updatedAt: {
|
|
176
|
+
type: "date",
|
|
177
|
+
defaultValue: () => /* @__PURE__ */ new Date(),
|
|
178
|
+
onUpdate: () => /* @__PURE__ */ new Date(),
|
|
179
|
+
required: true,
|
|
180
|
+
input: false
|
|
181
|
+
},
|
|
182
|
+
email: {
|
|
183
|
+
type: "string",
|
|
184
|
+
required: true,
|
|
185
|
+
unique: true
|
|
186
|
+
},
|
|
187
|
+
emailVerified: {
|
|
188
|
+
type: "boolean",
|
|
189
|
+
defaultValue: false,
|
|
190
|
+
required: true,
|
|
191
|
+
input: false
|
|
192
|
+
},
|
|
193
|
+
metadata: {
|
|
194
|
+
type: "string",
|
|
195
|
+
required: false
|
|
196
|
+
}
|
|
197
|
+
} } };
|
|
198
|
+
const getSchema = (options) => {
|
|
199
|
+
return mergeSchema(lead$1, options.schema);
|
|
200
|
+
};
|
|
184
201
|
|
|
185
202
|
//#endregion
|
|
186
203
|
//#region src/index.ts
|
|
187
|
-
const lead = (options) => {
|
|
204
|
+
const lead = (options = {}) => {
|
|
188
205
|
return {
|
|
189
206
|
id: "lead",
|
|
190
207
|
schema: getSchema(options),
|
|
@@ -192,14 +209,16 @@ const lead = (options) => {
|
|
|
192
209
|
subscribe: subscribe(options),
|
|
193
210
|
verify: verify(options),
|
|
194
211
|
unsubscribe: unsubscribe(options),
|
|
195
|
-
resend: resend(options)
|
|
212
|
+
resend: resend(options),
|
|
213
|
+
update: update(options)
|
|
196
214
|
},
|
|
197
215
|
options,
|
|
198
216
|
rateLimit: [{
|
|
199
217
|
pathMatcher: (path) => ["/lead/subscribe", "/lead/resend"].includes(path),
|
|
200
218
|
window: options.rateLimit?.window ?? 10,
|
|
201
219
|
max: options.rateLimit?.max ?? 3
|
|
202
|
-
}]
|
|
220
|
+
}],
|
|
221
|
+
$ERROR_CODES: LEAD_ERROR_CODES
|
|
203
222
|
};
|
|
204
223
|
};
|
|
205
224
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["lead"],"sources":["../src/schema.ts","../src/routes.ts","../src/index.ts"],"sourcesContent":["import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport type { LeadOptions } from './type';\nimport { mergeSchema } from 'better-auth/db';\n\nexport const lead = {\n lead: {\n fields: {\n createdAt: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n input: false,\n },\n updatedAt: {\n type: 'date',\n defaultValue: () => new Date(),\n onUpdate: () => new Date(),\n required: true,\n input: false,\n },\n email: {\n type: 'string',\n required: true,\n unique: true,\n },\n emailVerified: {\n type: 'boolean',\n defaultValue: false,\n required: true,\n input: false,\n },\n metadata: {\n type: 'string',\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends LeadOptions>(options: O) => {\n return mergeSchema(lead, options.schema);\n};\n","import { APIError, createAuthEndpoint, createEmailVerificationToken } from 'better-auth/api';\nimport * as z from 'zod';\nimport type { Lead, LeadOptions, LeadPayload } from './type';\nimport { jwtVerify } from 'jose';\nimport type { JWTPayload, JWTVerifyResult } from 'jose';\nimport { JWTExpired } from 'jose/errors';\n\nconst subscribeSchema = z.object({\n email: z.email(),\n metadata: z.record(z.string(), z.any()).optional(),\n});\n\nexport const subscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/subscribe',\n {\n method: 'POST',\n body: subscribeSchema,\n metadata: {\n // TODO add openapi\n },\n },\n async (ctx) => {\n const { email, metadata } = ctx.body;\n\n const normalizedEmail = email.toLowerCase();\n\n let lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n try {\n lead = await ctx.context.adapter.create<LeadPayload, Lead>({\n model: 'lead',\n data: {\n email: normalizedEmail,\n metadata: metadata ? JSON.stringify(metadata) : undefined,\n },\n });\n } catch (e) {\n ctx.context.logger.info('Error creating lead');\n lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n }\n } else if (!lead.emailVerified) {\n lead = await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n update: {\n metadata: metadata ? JSON.stringify(metadata) : lead.metadata,\n },\n });\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n\n await ctx.context.runInBackgroundOrAwait(\n options.sendVerificationEmail({ email: normalizedEmail, url, token }, ctx.request),\n );\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst verifySchema = z.object({\n token: z.string(),\n});\n\nexport const verify = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/verify',\n {\n method: 'GET',\n query: verifySchema,\n metadata: {\n // TODO add openapi\n },\n },\n async (ctx) => {\n const { token } = ctx.query;\n\n let jwt: JWTVerifyResult<JWTPayload>;\n try {\n jwt = await jwtVerify(token, new TextEncoder().encode(ctx.context.secret), {\n algorithms: ['HS256'],\n });\n } catch (e) {\n if (e instanceof JWTExpired) {\n throw new APIError('UNAUTHORIZED', {\n message: 'Token expired',\n });\n }\n throw new APIError('UNAUTHORIZED', {\n message: 'Invalid token',\n });\n }\n\n const parsed = subscribeSchema.parse(jwt.payload);\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (lead.emailVerified) {\n return ctx.json({\n status: true,\n });\n }\n\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n update: {\n emailVerified: true,\n },\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst unsubscribeSchema = z.object({\n id: z.string(),\n});\n\nexport const unsubscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/unsubscribe',\n {\n method: 'POST',\n body: unsubscribeSchema,\n metadata: {\n // TODO add openapi\n },\n },\n async (ctx) => {\n const { id } = ctx.body;\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n await ctx.context.adapter.delete({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst resendSchema = z.object({\n email: z.email(),\n});\n\nexport const resend = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/resend',\n {\n method: 'POST',\n body: resendSchema,\n metadata: {\n // TODO add openapi\n },\n },\n async (ctx) => {\n const { email } = ctx.body;\n\n const normalizedEmail = email.toLowerCase();\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n\n await ctx.context.runInBackgroundOrAwait(\n options.sendVerificationEmail({ email: normalizedEmail, url, token }, ctx.request),\n );\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n","import type { BetterAuthPlugin } from 'better-auth';\nimport type { LeadOptions } from './type';\nimport { getSchema } from './schema';\nimport { resend, subscribe, unsubscribe, verify } from './routes';\n\nexport const lead = <O extends LeadOptions>(options: O) => {\n return {\n id: 'lead',\n schema: getSchema(options),\n endpoints: {\n subscribe: subscribe(options),\n verify: verify(options),\n unsubscribe: unsubscribe(options),\n resend: resend(options),\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/lead/subscribe', '/lead/resend'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 3,\n },\n ],\n } satisfies BetterAuthPlugin;\n};\n\nexport type * from './type';\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["lead"],"sources":["../src/routes.ts","../src/schema.ts","../src/index.ts"],"sourcesContent":["import { APIError, createAuthEndpoint, createEmailVerificationToken } from 'better-auth/api';\nimport { jwtVerify } from 'jose';\nimport type { JWTPayload, JWTVerifyResult } from 'jose';\nimport { JWTExpired } from 'jose/errors';\nimport * as z from 'zod';\n\nimport { LEAD_ERROR_CODES } from './error-codes';\nimport type { Lead, LeadOptions, LeadPayload } from './type';\n\nconst subscribeSchema = z.object({\n email: z.string().meta({\n description: 'Email address of the lead',\n }),\n metadata: z.record(z.string(), z.any()).optional().meta({\n description: 'Additional metadata to store with the lead',\n }),\n});\n\nexport const subscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/subscribe',\n {\n method: 'POST',\n body: subscribeSchema,\n },\n async (ctx) => {\n const { email, metadata } = ctx.body;\n\n const isValidEmail = z.email().safeParse(email);\n if (!isValidEmail.success) {\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_EMAIL);\n }\n\n const normalizedEmail = email.toLowerCase();\n\n let lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n try {\n lead = await ctx.context.adapter.create<LeadPayload, Lead>({\n model: 'lead',\n data: {\n email: normalizedEmail,\n metadata: metadata ? JSON.stringify(metadata) : undefined,\n },\n });\n } catch (e) {\n ctx.context.logger.info('Error creating lead');\n lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n }\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n\n await ctx.context.runInBackgroundOrAwait(\n options.sendVerificationEmail({ email: normalizedEmail, url, token }, ctx.request),\n );\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst verifySchema = z.object({\n token: z.string().meta({\n description: 'The token to verify the email',\n }),\n});\n\nexport const verify = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/verify',\n {\n method: 'GET',\n query: verifySchema,\n },\n async (ctx) => {\n const { token } = ctx.query;\n\n let jwt: JWTVerifyResult<JWTPayload>;\n try {\n jwt = await jwtVerify(token, new TextEncoder().encode(ctx.context.secret), {\n algorithms: ['HS256'],\n });\n } catch (e) {\n if (e instanceof JWTExpired) {\n throw APIError.from('UNAUTHORIZED', LEAD_ERROR_CODES.TOKEN_EXPIRED);\n }\n throw APIError.from('UNAUTHORIZED', LEAD_ERROR_CODES.INVALID_TOKEN);\n }\n\n const parsed = subscribeSchema.parse(jwt.payload);\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (lead.emailVerified) {\n return ctx.json({\n status: true,\n });\n }\n\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: parsed.email,\n },\n ],\n update: {\n emailVerified: true,\n },\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst unsubscribeSchema = z.object({\n id: z.string().meta({\n description: 'The id of the lead to unsubscribe',\n }),\n});\n\nexport const unsubscribe = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/unsubscribe',\n {\n method: 'POST',\n body: unsubscribeSchema,\n },\n async (ctx) => {\n const { id } = ctx.body;\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n await ctx.context.adapter.delete({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst resendSchema = z.object({\n email: z.string().meta({\n description: 'Email address to resend the verification email to',\n }),\n});\n\nexport const resend = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/resend',\n {\n method: 'POST',\n body: resendSchema,\n },\n async (ctx) => {\n const { email } = ctx.body;\n\n const isValidEmail = z.email().safeParse(email);\n if (!isValidEmail.success) {\n throw APIError.from('BAD_REQUEST', LEAD_ERROR_CODES.INVALID_EMAIL);\n }\n\n const normalizedEmail = email.toLowerCase();\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'email',\n value: normalizedEmail,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n if (options.sendVerificationEmail && lead && !lead.emailVerified) {\n const token = await createEmailVerificationToken(\n ctx.context.secret,\n normalizedEmail,\n undefined,\n options.expiresIn ?? 3600,\n );\n const url = `${ctx.context.baseURL}/lead/verify?token=${token}`;\n\n await ctx.context.runInBackgroundOrAwait(\n options.sendVerificationEmail({ email: normalizedEmail, url, token }, ctx.request),\n );\n }\n\n return ctx.json({\n status: true,\n });\n },\n );\n\nconst updateSchema = z.object({\n id: z.string().meta({\n description: 'The id of the lead to update',\n }),\n metadata: z.record(z.string(), z.any()).optional().meta({\n description: 'Additional metadata to store with the lead',\n }),\n});\n\nexport const update = <O extends LeadOptions>(options: O) =>\n createAuthEndpoint(\n '/lead/update',\n {\n method: 'POST',\n body: updateSchema,\n },\n async (ctx) => {\n const { id, metadata } = ctx.body;\n\n const lead = await ctx.context.adapter.findOne<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n });\n\n if (!lead) {\n return ctx.json({\n status: true,\n });\n }\n\n await ctx.context.adapter.update<Lead>({\n model: 'lead',\n where: [\n {\n field: 'id',\n value: id,\n },\n ],\n update: {\n metadata: metadata ? JSON.stringify(metadata) : undefined,\n },\n });\n\n return ctx.json({\n status: true,\n });\n },\n );\n","import { type BetterAuthPluginDBSchema } from 'better-auth';\nimport { mergeSchema } from 'better-auth/db';\n\nimport type { LeadOptions } from './type';\n\nexport const lead = {\n lead: {\n fields: {\n createdAt: {\n type: 'date',\n defaultValue: () => new Date(),\n required: true,\n input: false,\n },\n updatedAt: {\n type: 'date',\n defaultValue: () => new Date(),\n onUpdate: () => new Date(),\n required: true,\n input: false,\n },\n email: {\n type: 'string',\n required: true,\n unique: true,\n },\n emailVerified: {\n type: 'boolean',\n defaultValue: false,\n required: true,\n input: false,\n },\n metadata: {\n type: 'string',\n required: false,\n },\n },\n },\n} satisfies BetterAuthPluginDBSchema;\n\nexport const getSchema = <O extends LeadOptions>(options: O) => {\n return mergeSchema(lead, options.schema);\n};\n","import type { BetterAuthPlugin } from 'better-auth';\n\nimport { LEAD_ERROR_CODES } from './error-codes';\nimport { resend, subscribe, unsubscribe, update, verify } from './routes';\nimport { getSchema } from './schema';\nimport type { LeadOptions } from './type';\n\nexport const lead = <O extends LeadOptions>(options: O = {} as O) => {\n return {\n id: 'lead',\n schema: getSchema(options),\n endpoints: {\n subscribe: subscribe(options),\n verify: verify(options),\n unsubscribe: unsubscribe(options),\n resend: resend(options),\n update: update(options),\n },\n options: options as NoInfer<O>,\n rateLimit: [\n {\n pathMatcher: (path) => ['/lead/subscribe', '/lead/resend'].includes(path),\n window: options.rateLimit?.window ?? 10,\n max: options.rateLimit?.max ?? 3,\n },\n ],\n $ERROR_CODES: LEAD_ERROR_CODES,\n } satisfies BetterAuthPlugin;\n};\n\nexport type * from './type';\n"],"mappings":"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"}
|
|
@@ -95,4 +95,4 @@ interface Lead {
|
|
|
95
95
|
type LeadPayload = Omit<Lead, 'id' | 'createdAt' | 'updatedAt' | 'emailVerified'>;
|
|
96
96
|
//#endregion
|
|
97
97
|
export { LeadOptions as n, LeadPayload as r, Lead as t };
|
|
98
|
-
//# sourceMappingURL=type-
|
|
98
|
+
//# sourceMappingURL=type-KTJzD7Eo.d.mts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "better-auth-lead",
|
|
3
|
-
"version": "0.0.1-dev.
|
|
3
|
+
"version": "0.0.1-dev.2",
|
|
4
4
|
"description": "Better Auth Lead plugin",
|
|
5
5
|
"homepage": "https://github.com/marcjulian/better-auth-plugins#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"vitest": "^4.0.18"
|
|
37
37
|
},
|
|
38
38
|
"peerDependencies": {
|
|
39
|
-
"better-auth": "^1.5.
|
|
39
|
+
"better-auth": "^1.5.2"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsdown",
|