@iskra-bun/web-kit 0.1.0 → 0.2.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.
- package/CHANGELOG.md +41 -0
- package/dist/index.d.ts +38 -192
- package/dist/index.js +178 -446
- package/dist/index.js.map +1 -1
- package/package.json +6 -7
- package/src/features/api-key.ts +44 -14
- package/src/features/auth/index.ts +53 -6
- package/src/features/csrf.ts +50 -4
- package/src/features/db.ts +26 -11
- package/src/features/email/index.ts +50 -68
- package/src/features/health.ts +42 -6
- package/src/features/storage/index.ts +5 -14
- package/src/features/upload/helper.ts +13 -2
- package/src/server.ts +17 -1
- package/src/types.ts +3 -0
- package/dist/chunk-POXNRNTC.js +0 -51
- package/dist/chunk-POXNRNTC.js.map +0 -1
- package/dist/mailgun-Z46GZJNI.js +0 -83
- package/dist/mailgun-Z46GZJNI.js.map +0 -1
- package/dist/s3-7IG4ESFW.js +0 -171
- package/dist/s3-7IG4ESFW.js.map +0 -1
- package/dist/sendgrid-UK2GSBEF.js +0 -43
- package/dist/sendgrid-UK2GSBEF.js.map +0 -1
- package/dist/smtp-WJDLYKD5.js +0 -50
- package/dist/smtp-WJDLYKD5.js.map +0 -1
- package/src/features/auth/better-auth-config.ts +0 -160
- package/src/features/auth/schema.ts +0 -174
- package/src/features/auth/types.ts +0 -114
- package/src/features/email/providers/mailgun.ts +0 -99
- package/src/features/email/providers/sendgrid.ts +0 -42
- package/src/features/email/providers/smtp.ts +0 -51
- package/src/features/storage/adapters/local.ts +0 -133
- package/src/features/storage/adapters/s3.ts +0 -193
- package/src/features/storage/base.ts +0 -112
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/features/email/providers/smtp.ts"],"sourcesContent":["import type { EmailAdapter, EmailConfig, EmailMessage, TemplateData } from \"../index\";\nimport * as nodemailer from \"nodemailer\";\n\nexport class SmtpEmailAdapter implements EmailAdapter {\n private transporter: nodemailer.Transporter;\n\n constructor(private config: EmailConfig) {\n if (!config.smtp) throw new Error(\"SMTP config required\");\n this.transporter = nodemailer.createTransport({\n host: config.smtp.host,\n port: config.smtp.port,\n secure: config.smtp.secure ?? false,\n auth: {\n user: config.smtp.username,\n pass: config.smtp.password\n }\n });\n }\n\n async send(message: EmailMessage) {\n const from = message.from || this.config.from;\n if (!from) throw new Error(\"From address required\");\n\n const info = await this.transporter.sendMail({\n from: from.name ? `\"${from.name}\" <${from.email}>` : from.email,\n to: Array.isArray(message.to) ? message.to.join(\", \") : message.to,\n subject: message.subject,\n text: message.text,\n html: message.html,\n cc: message.cc ? (Array.isArray(message.cc) ? message.cc.join(\", \") : message.cc) : undefined,\n bcc: message.bcc ? (Array.isArray(message.bcc) ? message.bcc.join(\", \") : message.bcc) : undefined,\n replyTo: message.replyTo,\n attachments: message.attachments?.map(a => ({\n filename: a.filename,\n content: typeof a.content === 'string' ? a.content : Buffer.from(a.content),\n contentType: a.contentType\n }))\n });\n\n return { messageId: (info as any).messageId, success: true };\n }\n\n async sendTemplate(templateName: string, to: string | string[], data: TemplateData) {\n // Simple mock template engine\n return this.send({\n to,\n subject: `Template: ${templateName}`,\n html: `<p>Template ${templateName} rendered with ${JSON.stringify(data)}</p>`\n });\n }\n}\n"],"mappings":";AACA,YAAY,gBAAgB;AAErB,IAAM,mBAAN,MAA+C;AAAA,EAGlD,YAAoB,QAAqB;AAArB;AAChB,QAAI,CAAC,OAAO,KAAM,OAAM,IAAI,MAAM,sBAAsB;AACxD,SAAK,cAAyB,2BAAgB;AAAA,MAC1C,MAAM,OAAO,KAAK;AAAA,MAClB,MAAM,OAAO,KAAK;AAAA,MAClB,QAAQ,OAAO,KAAK,UAAU;AAAA,MAC9B,MAAM;AAAA,QACF,MAAM,OAAO,KAAK;AAAA,QAClB,MAAM,OAAO,KAAK;AAAA,MACtB;AAAA,IACJ,CAAC;AAAA,EACL;AAAA,EAXoB;AAAA,EAFZ;AAAA,EAeR,MAAM,KAAK,SAAuB;AAC9B,UAAM,OAAO,QAAQ,QAAQ,KAAK,OAAO;AACzC,QAAI,CAAC,KAAM,OAAM,IAAI,MAAM,uBAAuB;AAElD,UAAM,OAAO,MAAM,KAAK,YAAY,SAAS;AAAA,MACzC,MAAM,KAAK,OAAO,IAAI,KAAK,IAAI,MAAM,KAAK,KAAK,MAAM,KAAK;AAAA,MAC1D,IAAI,MAAM,QAAQ,QAAQ,EAAE,IAAI,QAAQ,GAAG,KAAK,IAAI,IAAI,QAAQ;AAAA,MAChE,SAAS,QAAQ;AAAA,MACjB,MAAM,QAAQ;AAAA,MACd,MAAM,QAAQ;AAAA,MACd,IAAI,QAAQ,KAAM,MAAM,QAAQ,QAAQ,EAAE,IAAI,QAAQ,GAAG,KAAK,IAAI,IAAI,QAAQ,KAAM;AAAA,MACpF,KAAK,QAAQ,MAAO,MAAM,QAAQ,QAAQ,GAAG,IAAI,QAAQ,IAAI,KAAK,IAAI,IAAI,QAAQ,MAAO;AAAA,MACzF,SAAS,QAAQ;AAAA,MACjB,aAAa,QAAQ,aAAa,IAAI,QAAM;AAAA,QACxC,UAAU,EAAE;AAAA,QACZ,SAAS,OAAO,EAAE,YAAY,WAAW,EAAE,UAAU,OAAO,KAAK,EAAE,OAAO;AAAA,QAC1E,aAAa,EAAE;AAAA,MACnB,EAAE;AAAA,IACN,CAAC;AAED,WAAO,EAAE,WAAY,KAAa,WAAW,SAAS,KAAK;AAAA,EAC/D;AAAA,EAEA,MAAM,aAAa,cAAsB,IAAuB,MAAoB;AAEhF,WAAO,KAAK,KAAK;AAAA,MACb;AAAA,MACA,SAAS,aAAa,YAAY;AAAA,MAClC,MAAM,eAAe,YAAY,kBAAkB,KAAK,UAAU,IAAI,CAAC;AAAA,IAC3E,CAAC;AAAA,EACL;AACJ;","names":[]}
|
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
import { betterAuth, type Auth as BetterAuthInstance } from "better-auth";
|
|
2
|
-
import { drizzleAdapter } from "better-auth/adapters/drizzle";
|
|
3
|
-
import { genericOAuth } from "better-auth/plugins/generic-oauth";
|
|
4
|
-
import { pgSchema, mysqlSchema, sqliteSchema } from "./schema";
|
|
5
|
-
|
|
6
|
-
export interface BetterAuthConfigOptions {
|
|
7
|
-
db: any; // Drizzle instance
|
|
8
|
-
adapterType: "postgres" | "mysql" | "sqlite";
|
|
9
|
-
secret: string;
|
|
10
|
-
baseURL?: string;
|
|
11
|
-
basePath?: string;
|
|
12
|
-
trustedOrigins?: string[];
|
|
13
|
-
enableEmailPassword?: boolean;
|
|
14
|
-
disableCSRFCheck?: boolean;
|
|
15
|
-
// deno-lint-ignore no-explicit-any
|
|
16
|
-
socialProviders?: Record<string, any>;
|
|
17
|
-
oidcConfig?: {
|
|
18
|
-
clientId: string;
|
|
19
|
-
clientSecret: string;
|
|
20
|
-
issuer: string;
|
|
21
|
-
providerId?: string;
|
|
22
|
-
authorizationEndpoint?: string;
|
|
23
|
-
tokenEndpoint?: string;
|
|
24
|
-
userinfoEndpoint?: string;
|
|
25
|
-
jwksEndpoint?: string;
|
|
26
|
-
discoveryEndpoint?: string;
|
|
27
|
-
scopes?: string[];
|
|
28
|
-
pkce?: boolean;
|
|
29
|
-
mapping?: {
|
|
30
|
-
id?: string;
|
|
31
|
-
email?: string;
|
|
32
|
-
emailVerified?: string;
|
|
33
|
-
name?: string;
|
|
34
|
-
image?: string;
|
|
35
|
-
extraFields?: Record<string, string>;
|
|
36
|
-
};
|
|
37
|
-
};
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export function createBetterAuth(options: BetterAuthConfigOptions): BetterAuthInstance {
|
|
41
|
-
const {
|
|
42
|
-
db,
|
|
43
|
-
adapterType,
|
|
44
|
-
secret,
|
|
45
|
-
baseURL = "http://localhost:3000",
|
|
46
|
-
basePath = "/api/auth",
|
|
47
|
-
trustedOrigins = [],
|
|
48
|
-
enableEmailPassword = true,
|
|
49
|
-
disableCSRFCheck = false,
|
|
50
|
-
socialProviders,
|
|
51
|
-
oidcConfig,
|
|
52
|
-
} = options;
|
|
53
|
-
|
|
54
|
-
const baseOrigin = new URL(baseURL).origin;
|
|
55
|
-
const allTrustedOrigins = trustedOrigins.includes(baseOrigin)
|
|
56
|
-
? trustedOrigins
|
|
57
|
-
: [baseOrigin, ...trustedOrigins];
|
|
58
|
-
|
|
59
|
-
console.log("[BetterAuth Config] Creating Drizzle adapter...");
|
|
60
|
-
|
|
61
|
-
let schema;
|
|
62
|
-
let provider: "pg" | "mysql" | "sqlite";
|
|
63
|
-
|
|
64
|
-
switch (adapterType) {
|
|
65
|
-
case "postgres":
|
|
66
|
-
schema = pgSchema;
|
|
67
|
-
provider = "pg";
|
|
68
|
-
break;
|
|
69
|
-
case "mysql":
|
|
70
|
-
schema = mysqlSchema;
|
|
71
|
-
provider = "mysql";
|
|
72
|
-
break;
|
|
73
|
-
case "sqlite":
|
|
74
|
-
schema = sqliteSchema;
|
|
75
|
-
provider = "sqlite";
|
|
76
|
-
break;
|
|
77
|
-
default:
|
|
78
|
-
throw new Error(`Unsupported adapter type: ${adapterType}`);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
const database = drizzleAdapter(db, {
|
|
82
|
-
provider,
|
|
83
|
-
schema
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
console.log("[BetterAuth Config] Initializing betterAuth with config:", {
|
|
87
|
-
baseURL,
|
|
88
|
-
basePath,
|
|
89
|
-
provider,
|
|
90
|
-
enableEmailPassword
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
const plugins = [];
|
|
94
|
-
if (oidcConfig) {
|
|
95
|
-
const authorizationUrl = oidcConfig.authorizationEndpoint ||
|
|
96
|
-
`${oidcConfig.issuer}/protocol/openid-connect/auth`;
|
|
97
|
-
const tokenUrl = oidcConfig.tokenEndpoint ||
|
|
98
|
-
`${oidcConfig.issuer}/protocol/openid-connect/token`;
|
|
99
|
-
const userInfoUrl = oidcConfig.userinfoEndpoint ||
|
|
100
|
-
`${oidcConfig.issuer}/protocol/openid-connect/userinfo`;
|
|
101
|
-
|
|
102
|
-
plugins.push(
|
|
103
|
-
genericOAuth({
|
|
104
|
-
config: [
|
|
105
|
-
{
|
|
106
|
-
providerId: oidcConfig.providerId || "oidc",
|
|
107
|
-
clientId: oidcConfig.clientId,
|
|
108
|
-
clientSecret: oidcConfig.clientSecret,
|
|
109
|
-
authorizationUrl,
|
|
110
|
-
tokenUrl,
|
|
111
|
-
userInfoUrl,
|
|
112
|
-
discoveryUrl: oidcConfig.discoveryEndpoint ||
|
|
113
|
-
`${oidcConfig.issuer}/.well-known/openid-configuration`,
|
|
114
|
-
scopes: oidcConfig.scopes || ["openid", "email", "profile"],
|
|
115
|
-
pkce: oidcConfig.pkce !== undefined ? oidcConfig.pkce : false,
|
|
116
|
-
mapProfileToUser: (profile: any) => {
|
|
117
|
-
return {
|
|
118
|
-
id: profile.sub || profile.id,
|
|
119
|
-
email: profile.email,
|
|
120
|
-
name: profile.name || profile.preferred_username,
|
|
121
|
-
image: profile.picture || profile.image,
|
|
122
|
-
emailVerified: profile.email_verified || false,
|
|
123
|
-
};
|
|
124
|
-
},
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
}),
|
|
128
|
-
);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
return betterAuth({
|
|
132
|
-
database,
|
|
133
|
-
secret,
|
|
134
|
-
baseURL,
|
|
135
|
-
basePath,
|
|
136
|
-
trustedOrigins: allTrustedOrigins,
|
|
137
|
-
emailAndPassword: enableEmailPassword
|
|
138
|
-
? {
|
|
139
|
-
enabled: true,
|
|
140
|
-
autoSignIn: true,
|
|
141
|
-
}
|
|
142
|
-
: undefined,
|
|
143
|
-
socialProviders: Object.keys(socialProviders || {}).length > 0 ? socialProviders : undefined,
|
|
144
|
-
plugins,
|
|
145
|
-
session: {
|
|
146
|
-
expiresIn: 60 * 60 * 24 * 7,
|
|
147
|
-
updateAge: 60 * 60 * 24,
|
|
148
|
-
cookieCache: {
|
|
149
|
-
enabled: true,
|
|
150
|
-
maxAge: 5 * 60,
|
|
151
|
-
},
|
|
152
|
-
},
|
|
153
|
-
advanced: {
|
|
154
|
-
disableCSRFCheck,
|
|
155
|
-
generateId: () => crypto.randomUUID().replace(/-/g, ""),
|
|
156
|
-
},
|
|
157
|
-
}) as unknown as BetterAuthInstance;
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
export type Auth = ReturnType<typeof createBetterAuth>;
|
|
@@ -1,174 +0,0 @@
|
|
|
1
|
-
import { pgTable, text, timestamp, boolean } from "drizzle-orm/pg-core";
|
|
2
|
-
import { mysqlTable, varchar, timestamp as mysqlTimestamp, boolean as mysqlBoolean, text as mysqlText } from "drizzle-orm/mysql-core";
|
|
3
|
-
import { sqliteTable, text as sqliteText, integer } from "drizzle-orm/sqlite-core";
|
|
4
|
-
|
|
5
|
-
// ==========================================
|
|
6
|
-
// PostgreSQL Schema
|
|
7
|
-
// ==========================================
|
|
8
|
-
|
|
9
|
-
export const pgUser = pgTable("user", {
|
|
10
|
-
id: text("id").primaryKey(),
|
|
11
|
-
name: text("name"),
|
|
12
|
-
email: text("email").notNull().unique(),
|
|
13
|
-
emailVerified: boolean("emailVerified").notNull(),
|
|
14
|
-
image: text("image"),
|
|
15
|
-
createdAt: timestamp("createdAt").notNull(),
|
|
16
|
-
updatedAt: timestamp("updatedAt").notNull()
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
export const pgSession = pgTable("session", {
|
|
20
|
-
id: text("id").primaryKey(),
|
|
21
|
-
expiresAt: timestamp("expiresAt").notNull(),
|
|
22
|
-
token: text("token").notNull().unique(),
|
|
23
|
-
createdAt: timestamp("createdAt").notNull(),
|
|
24
|
-
updatedAt: timestamp("updatedAt").notNull(),
|
|
25
|
-
ipAddress: text("ipAddress"),
|
|
26
|
-
userAgent: text("userAgent"),
|
|
27
|
-
userId: text("userId").notNull().references(() => pgUser.id)
|
|
28
|
-
});
|
|
29
|
-
|
|
30
|
-
export const pgAccount = pgTable("account", {
|
|
31
|
-
id: text("id").primaryKey(),
|
|
32
|
-
accountId: text("accountId").notNull(),
|
|
33
|
-
providerId: text("providerId").notNull(),
|
|
34
|
-
userId: text("userId").notNull().references(() => pgUser.id),
|
|
35
|
-
accessToken: text("accessToken"),
|
|
36
|
-
refreshToken: text("refreshToken"),
|
|
37
|
-
idToken: text("idToken"),
|
|
38
|
-
accessTokenExpiresAt: timestamp("accessTokenExpiresAt"),
|
|
39
|
-
refreshTokenExpiresAt: timestamp("refreshTokenExpiresAt"),
|
|
40
|
-
scope: text("scope"),
|
|
41
|
-
password: text("password"),
|
|
42
|
-
createdAt: timestamp("createdAt").notNull(),
|
|
43
|
-
updatedAt: timestamp("updatedAt").notNull()
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export const pgVerification = pgTable("verification", {
|
|
47
|
-
id: text("id").primaryKey(),
|
|
48
|
-
identifier: text("identifier").notNull(),
|
|
49
|
-
value: text("value").notNull(),
|
|
50
|
-
expiresAt: timestamp("expiresAt").notNull(),
|
|
51
|
-
createdAt: timestamp("createdAt"),
|
|
52
|
-
updatedAt: timestamp("updatedAt")
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
export const pgSchema = {
|
|
56
|
-
user: pgUser,
|
|
57
|
-
session: pgSession,
|
|
58
|
-
account: pgAccount,
|
|
59
|
-
verification: pgVerification
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
// ==========================================
|
|
63
|
-
// MySQL Schema
|
|
64
|
-
// ==========================================
|
|
65
|
-
|
|
66
|
-
export const mysqlUser = mysqlTable("user", {
|
|
67
|
-
id: varchar("id", { length: 36 }).primaryKey(),
|
|
68
|
-
name: varchar("name", { length: 255 }),
|
|
69
|
-
email: varchar("email", { length: 255 }).notNull().unique(),
|
|
70
|
-
emailVerified: mysqlBoolean("emailVerified").notNull(),
|
|
71
|
-
image: varchar("image", { length: 255 }),
|
|
72
|
-
createdAt: mysqlTimestamp("createdAt").notNull(),
|
|
73
|
-
updatedAt: mysqlTimestamp("updatedAt").notNull()
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
export const mysqlSession = mysqlTable("session", {
|
|
77
|
-
id: varchar("id", { length: 36 }).primaryKey(),
|
|
78
|
-
expiresAt: mysqlTimestamp("expiresAt").notNull(),
|
|
79
|
-
token: varchar("token", { length: 255 }).notNull().unique(),
|
|
80
|
-
createdAt: mysqlTimestamp("createdAt").notNull(),
|
|
81
|
-
updatedAt: mysqlTimestamp("updatedAt").notNull(),
|
|
82
|
-
ipAddress: varchar("ipAddress", { length: 45 }),
|
|
83
|
-
userAgent: mysqlText("userAgent"),
|
|
84
|
-
userId: varchar("userId", { length: 36 }).notNull().references(() => mysqlUser.id)
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
export const mysqlAccount = mysqlTable("account", {
|
|
88
|
-
id: varchar("id", { length: 36 }).primaryKey(),
|
|
89
|
-
accountId: varchar("accountId", { length: 255 }).notNull(),
|
|
90
|
-
providerId: varchar("providerId", { length: 255 }).notNull(),
|
|
91
|
-
userId: varchar("userId", { length: 36 }).notNull().references(() => mysqlUser.id),
|
|
92
|
-
accessToken: mysqlText("accessToken"),
|
|
93
|
-
refreshToken: mysqlText("refreshToken"),
|
|
94
|
-
idToken: mysqlText("idToken"),
|
|
95
|
-
accessTokenExpiresAt: mysqlTimestamp("accessTokenExpiresAt"),
|
|
96
|
-
refreshTokenExpiresAt: mysqlTimestamp("refreshTokenExpiresAt"),
|
|
97
|
-
scope: mysqlText("scope"),
|
|
98
|
-
password: varchar("password", { length: 255 }),
|
|
99
|
-
createdAt: mysqlTimestamp("createdAt").notNull(),
|
|
100
|
-
updatedAt: mysqlTimestamp("updatedAt").notNull()
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
export const mysqlVerification = mysqlTable("verification", {
|
|
104
|
-
id: varchar("id", { length: 36 }).primaryKey(),
|
|
105
|
-
identifier: varchar("identifier", { length: 255 }).notNull(),
|
|
106
|
-
value: varchar("value", { length: 255 }).notNull(),
|
|
107
|
-
expiresAt: mysqlTimestamp("expiresAt").notNull(),
|
|
108
|
-
createdAt: mysqlTimestamp("createdAt"),
|
|
109
|
-
updatedAt: mysqlTimestamp("updatedAt")
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
export const mysqlSchema = {
|
|
113
|
-
user: mysqlUser,
|
|
114
|
-
session: mysqlSession,
|
|
115
|
-
account: mysqlAccount,
|
|
116
|
-
verification: mysqlVerification
|
|
117
|
-
};
|
|
118
|
-
|
|
119
|
-
// ==========================================
|
|
120
|
-
// SQLite Schema
|
|
121
|
-
// ==========================================
|
|
122
|
-
|
|
123
|
-
export const sqliteUser = sqliteTable("user", {
|
|
124
|
-
id: sqliteText("id").primaryKey(),
|
|
125
|
-
name: sqliteText("name"),
|
|
126
|
-
email: sqliteText("email").notNull().unique(),
|
|
127
|
-
emailVerified: integer("emailVerified", { mode: "boolean" }).notNull(),
|
|
128
|
-
image: sqliteText("image"),
|
|
129
|
-
createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
|
|
130
|
-
updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull()
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
export const sqliteSession = sqliteTable("session", {
|
|
134
|
-
id: sqliteText("id").primaryKey(),
|
|
135
|
-
expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
|
|
136
|
-
token: sqliteText("token").notNull().unique(),
|
|
137
|
-
createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
|
|
138
|
-
updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull(),
|
|
139
|
-
ipAddress: sqliteText("ipAddress"),
|
|
140
|
-
userAgent: sqliteText("userAgent"),
|
|
141
|
-
userId: sqliteText("userId").notNull().references(() => sqliteUser.id)
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
export const sqliteAccount = sqliteTable("account", {
|
|
145
|
-
id: sqliteText("id").primaryKey(),
|
|
146
|
-
accountId: sqliteText("accountId").notNull(),
|
|
147
|
-
providerId: sqliteText("providerId").notNull(),
|
|
148
|
-
userId: sqliteText("userId").notNull().references(() => sqliteUser.id),
|
|
149
|
-
accessToken: sqliteText("accessToken"),
|
|
150
|
-
refreshToken: sqliteText("refreshToken"),
|
|
151
|
-
idToken: sqliteText("idToken"),
|
|
152
|
-
accessTokenExpiresAt: integer("accessTokenExpiresAt", { mode: "timestamp" }),
|
|
153
|
-
refreshTokenExpiresAt: integer("refreshTokenExpiresAt", { mode: "timestamp" }),
|
|
154
|
-
scope: sqliteText("scope"),
|
|
155
|
-
password: sqliteText("password"),
|
|
156
|
-
createdAt: integer("createdAt", { mode: "timestamp" }).notNull(),
|
|
157
|
-
updatedAt: integer("updatedAt", { mode: "timestamp" }).notNull()
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
export const sqliteVerification = sqliteTable("verification", {
|
|
161
|
-
id: sqliteText("id").primaryKey(),
|
|
162
|
-
identifier: sqliteText("identifier").notNull(),
|
|
163
|
-
value: sqliteText("value").notNull(),
|
|
164
|
-
expiresAt: integer("expiresAt", { mode: "timestamp" }).notNull(),
|
|
165
|
-
createdAt: integer("createdAt", { mode: "timestamp" }),
|
|
166
|
-
updatedAt: integer("updatedAt", { mode: "timestamp" })
|
|
167
|
-
});
|
|
168
|
-
|
|
169
|
-
export const sqliteSchema = {
|
|
170
|
-
user: sqliteUser,
|
|
171
|
-
session: sqliteSession,
|
|
172
|
-
account: sqliteAccount,
|
|
173
|
-
verification: sqliteVerification
|
|
174
|
-
};
|
|
@@ -1,114 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Auth feature types and interfaces
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface User {
|
|
6
|
-
id: string;
|
|
7
|
-
email: string;
|
|
8
|
-
emailVerified: boolean;
|
|
9
|
-
name?: string | null;
|
|
10
|
-
image?: string | null;
|
|
11
|
-
createdAt: Date;
|
|
12
|
-
updatedAt: Date;
|
|
13
|
-
// deno-lint-ignore no-explicit-any
|
|
14
|
-
[key: string]: any; // Allow custom fields
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export interface Account {
|
|
18
|
-
id: string;
|
|
19
|
-
userId: string;
|
|
20
|
-
accountId: string;
|
|
21
|
-
providerId: string;
|
|
22
|
-
accessToken?: string;
|
|
23
|
-
refreshToken?: string;
|
|
24
|
-
idToken?: string;
|
|
25
|
-
expiresAt?: Date;
|
|
26
|
-
scope?: string;
|
|
27
|
-
password?: string; // For credentials provider
|
|
28
|
-
createdAt: Date;
|
|
29
|
-
updatedAt: Date;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface Verification {
|
|
33
|
-
id: string;
|
|
34
|
-
identifier: string; // email or phone
|
|
35
|
-
value: string; // verification token
|
|
36
|
-
expiresAt: Date;
|
|
37
|
-
createdAt: Date;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
export interface AuthSession {
|
|
41
|
-
id: string;
|
|
42
|
-
userId: string;
|
|
43
|
-
expiresAt: Date;
|
|
44
|
-
ipAddress?: string;
|
|
45
|
-
userAgent?: string;
|
|
46
|
-
createdAt: Date;
|
|
47
|
-
updatedAt: Date;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export interface PasswordResetToken {
|
|
51
|
-
id: string;
|
|
52
|
-
userId: string;
|
|
53
|
-
token: string;
|
|
54
|
-
expiresAt: Date;
|
|
55
|
-
createdAt: Date;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export interface EmailVerificationToken {
|
|
59
|
-
id: string;
|
|
60
|
-
userId: string;
|
|
61
|
-
token: string;
|
|
62
|
-
email: string;
|
|
63
|
-
expiresAt: Date;
|
|
64
|
-
createdAt: Date;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Auth context available in route handlers
|
|
69
|
-
*/
|
|
70
|
-
export interface AuthContext {
|
|
71
|
-
user: User | null;
|
|
72
|
-
session: AuthSession | null;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Registration input
|
|
77
|
-
*/
|
|
78
|
-
export interface SignUpInput {
|
|
79
|
-
email: string;
|
|
80
|
-
password: string;
|
|
81
|
-
name?: string;
|
|
82
|
-
// deno-lint-ignore no-explicit-any
|
|
83
|
-
[key: string]: any; // Allow custom fields
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Login input
|
|
88
|
-
*/
|
|
89
|
-
export interface SignInInput {
|
|
90
|
-
email: string;
|
|
91
|
-
password: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Password reset request
|
|
96
|
-
*/
|
|
97
|
-
export interface PasswordResetRequest {
|
|
98
|
-
email: string;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Password reset confirmation
|
|
103
|
-
*/
|
|
104
|
-
export interface PasswordResetConfirm {
|
|
105
|
-
token: string;
|
|
106
|
-
newPassword: string;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
/**
|
|
110
|
-
* Email verification
|
|
111
|
-
*/
|
|
112
|
-
export interface EmailVerificationRequest {
|
|
113
|
-
token: string;
|
|
114
|
-
}
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import type { EmailAdapter, EmailMessage, EmailConfig, TemplateData } from "../index";
|
|
2
|
-
|
|
3
|
-
export class MailgunEmailAdapter implements EmailAdapter {
|
|
4
|
-
private apiKey: string;
|
|
5
|
-
private domain: string;
|
|
6
|
-
private baseUrl: string;
|
|
7
|
-
private defaultFrom?: { name?: string; email: string };
|
|
8
|
-
|
|
9
|
-
constructor(config: EmailConfig) {
|
|
10
|
-
if (!config.apiKey) throw new Error("Mailgun requires apiKey");
|
|
11
|
-
if (!config.domain) throw new Error("Mailgun requires domain");
|
|
12
|
-
|
|
13
|
-
this.apiKey = config.apiKey;
|
|
14
|
-
this.domain = config.domain;
|
|
15
|
-
this.baseUrl = config.baseUrl || "https://api.mailgun.net/v3";
|
|
16
|
-
this.defaultFrom = config.from;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
async send(message: EmailMessage): Promise<{ messageId: string; success: boolean }> {
|
|
20
|
-
const form = new FormData();
|
|
21
|
-
|
|
22
|
-
const from = message.from || this.defaultFrom;
|
|
23
|
-
if (from) {
|
|
24
|
-
form.append("from", from.name ? `${from.name} <${from.email}>` : from.email);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
const to = Array.isArray(message.to) ? message.to.join(",") : message.to;
|
|
28
|
-
form.append("to", to);
|
|
29
|
-
form.append("subject", message.subject);
|
|
30
|
-
|
|
31
|
-
if (message.text) form.append("text", message.text);
|
|
32
|
-
if (message.html) form.append("html", message.html);
|
|
33
|
-
if (message.cc) form.append("cc", Array.isArray(message.cc) ? message.cc.join(",") : message.cc);
|
|
34
|
-
if (message.bcc) form.append("bcc", Array.isArray(message.bcc) ? message.bcc.join(",") : message.bcc);
|
|
35
|
-
if (message.replyTo) form.append("h:Reply-To", message.replyTo);
|
|
36
|
-
|
|
37
|
-
if (message.headers) {
|
|
38
|
-
for (const [key, value] of Object.entries(message.headers)) {
|
|
39
|
-
form.append(`h:${key}`, value);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
if (message.attachments) {
|
|
44
|
-
for (const att of message.attachments) {
|
|
45
|
-
const content = typeof att.content === "string"
|
|
46
|
-
? new TextEncoder().encode(att.content)
|
|
47
|
-
: att.content;
|
|
48
|
-
const bytes = new Uint8Array(content);
|
|
49
|
-
const blob = new Blob([bytes], { type: att.contentType || "application/octet-stream" });
|
|
50
|
-
form.append("attachment", blob, att.filename);
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const response = await fetch(`${this.baseUrl}/${this.domain}/messages`, {
|
|
55
|
-
method: "POST",
|
|
56
|
-
headers: {
|
|
57
|
-
Authorization: "Basic " + btoa(`api:${this.apiKey}`),
|
|
58
|
-
},
|
|
59
|
-
body: form,
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
if (!response.ok) {
|
|
63
|
-
const errorText = await response.text();
|
|
64
|
-
throw new Error(`Mailgun API error (${response.status}): ${errorText}`);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const result = await response.json() as { id: string; message: string };
|
|
68
|
-
return { messageId: result.id, success: true };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
async sendTemplate(templateName: string, to: string | string[], data: TemplateData): Promise<{ messageId: string; success: boolean }> {
|
|
72
|
-
const form = new FormData();
|
|
73
|
-
|
|
74
|
-
if (this.defaultFrom) {
|
|
75
|
-
const from = this.defaultFrom;
|
|
76
|
-
form.append("from", from.name ? `${from.name} <${from.email}>` : from.email);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
form.append("to", Array.isArray(to) ? to.join(",") : to);
|
|
80
|
-
form.append("template", templateName);
|
|
81
|
-
form.append("h:X-Mailgun-Variables", JSON.stringify(data));
|
|
82
|
-
|
|
83
|
-
const response = await fetch(`${this.baseUrl}/${this.domain}/messages`, {
|
|
84
|
-
method: "POST",
|
|
85
|
-
headers: {
|
|
86
|
-
Authorization: "Basic " + btoa(`api:${this.apiKey}`),
|
|
87
|
-
},
|
|
88
|
-
body: form,
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
if (!response.ok) {
|
|
92
|
-
const errorText = await response.text();
|
|
93
|
-
throw new Error(`Mailgun API error (${response.status}): ${errorText}`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const result = await response.json() as { id: string; message: string };
|
|
97
|
-
return { messageId: result.id, success: true };
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import type { EmailAdapter, EmailConfig, EmailMessage, TemplateData } from "../index";
|
|
2
|
-
import sgMail from "@sendgrid/mail";
|
|
3
|
-
|
|
4
|
-
export class SendGridEmailAdapter implements EmailAdapter {
|
|
5
|
-
constructor(private config: EmailConfig) {
|
|
6
|
-
if (!config.apiKey) throw new Error("SendGrid API Key required");
|
|
7
|
-
sgMail.setApiKey(config.apiKey);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
async send(message: EmailMessage) {
|
|
11
|
-
const from = message.from || this.config.from;
|
|
12
|
-
if (!from) throw new Error("From address required");
|
|
13
|
-
|
|
14
|
-
const msg = {
|
|
15
|
-
to: message.to,
|
|
16
|
-
from: from.name ? { email: from.email, name: from.name } : from.email,
|
|
17
|
-
subject: message.subject,
|
|
18
|
-
text: message.text,
|
|
19
|
-
html: message.html,
|
|
20
|
-
cc: message.cc as any,
|
|
21
|
-
bcc: message.bcc as any,
|
|
22
|
-
replyTo: message.replyTo,
|
|
23
|
-
attachments: message.attachments?.map(a => ({
|
|
24
|
-
filename: a.filename,
|
|
25
|
-
content: typeof a.content === 'string' ? a.content : Buffer.from(a.content).toString("base64"),
|
|
26
|
-
type: a.contentType,
|
|
27
|
-
disposition: "attachment"
|
|
28
|
-
}))
|
|
29
|
-
} as any;
|
|
30
|
-
|
|
31
|
-
const [response] = await sgMail.send(msg);
|
|
32
|
-
return { messageId: response.headers["x-message-id"] as string, success: true };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
async sendTemplate(templateName: string, to: string | string[], data: TemplateData) {
|
|
36
|
-
return this.send({
|
|
37
|
-
to,
|
|
38
|
-
subject: `Template: ${templateName}`,
|
|
39
|
-
html: `<p>Template ${templateName} rendered with ${JSON.stringify(data)}</p>`
|
|
40
|
-
});
|
|
41
|
-
}
|
|
42
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { EmailAdapter, EmailConfig, EmailMessage, TemplateData } from "../index";
|
|
2
|
-
import * as nodemailer from "nodemailer";
|
|
3
|
-
|
|
4
|
-
export class SmtpEmailAdapter implements EmailAdapter {
|
|
5
|
-
private transporter: nodemailer.Transporter;
|
|
6
|
-
|
|
7
|
-
constructor(private config: EmailConfig) {
|
|
8
|
-
if (!config.smtp) throw new Error("SMTP config required");
|
|
9
|
-
this.transporter = nodemailer.createTransport({
|
|
10
|
-
host: config.smtp.host,
|
|
11
|
-
port: config.smtp.port,
|
|
12
|
-
secure: config.smtp.secure ?? false,
|
|
13
|
-
auth: {
|
|
14
|
-
user: config.smtp.username,
|
|
15
|
-
pass: config.smtp.password
|
|
16
|
-
}
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async send(message: EmailMessage) {
|
|
21
|
-
const from = message.from || this.config.from;
|
|
22
|
-
if (!from) throw new Error("From address required");
|
|
23
|
-
|
|
24
|
-
const info = await this.transporter.sendMail({
|
|
25
|
-
from: from.name ? `"${from.name}" <${from.email}>` : from.email,
|
|
26
|
-
to: Array.isArray(message.to) ? message.to.join(", ") : message.to,
|
|
27
|
-
subject: message.subject,
|
|
28
|
-
text: message.text,
|
|
29
|
-
html: message.html,
|
|
30
|
-
cc: message.cc ? (Array.isArray(message.cc) ? message.cc.join(", ") : message.cc) : undefined,
|
|
31
|
-
bcc: message.bcc ? (Array.isArray(message.bcc) ? message.bcc.join(", ") : message.bcc) : undefined,
|
|
32
|
-
replyTo: message.replyTo,
|
|
33
|
-
attachments: message.attachments?.map(a => ({
|
|
34
|
-
filename: a.filename,
|
|
35
|
-
content: typeof a.content === 'string' ? a.content : Buffer.from(a.content),
|
|
36
|
-
contentType: a.contentType
|
|
37
|
-
}))
|
|
38
|
-
});
|
|
39
|
-
|
|
40
|
-
return { messageId: (info as any).messageId, success: true };
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
async sendTemplate(templateName: string, to: string | string[], data: TemplateData) {
|
|
44
|
-
// Simple mock template engine
|
|
45
|
-
return this.send({
|
|
46
|
-
to,
|
|
47
|
-
subject: `Template: ${templateName}`,
|
|
48
|
-
html: `<p>Template ${templateName} rendered with ${JSON.stringify(data)}</p>`
|
|
49
|
-
});
|
|
50
|
-
}
|
|
51
|
-
}
|