Add Usesend but havent tested it just yet

This commit is contained in:
2025-09-23 07:41:39 -05:00
parent 4fe32474df
commit 70924f84a9
11 changed files with 510 additions and 187 deletions

View File

@@ -19,11 +19,13 @@ export const Entra: AuthProviderMaterializedConfig = {
token:
'https://login.microsoftonline.com/16200986-86f1-44d2-974c-cfa99352722c/oauth2/v2.0/token',
userinfo: 'https://graph.microsoft.com/oidc/userinfo',
allowDangerousEmailAccountLinking: true,
profile(profile) {
return {
id: profile.sub,
name: profile.name,
email: profile.email,
//image: profile.picture,
};
},
};

View File

@@ -2,6 +2,7 @@ import { ConvexError } from 'convex/values';
import { Password as DefaultPassword } from '@convex-dev/auth/providers/Password';
import { validatePassword } from '../password/validate';
import type { DataModel } from '../../../_generated/dataModel';
import { UsesendOTPPasswordReset } from '../password/reset';
export const Password = DefaultPassword<DataModel>({
profile(params, ctx) {
@@ -15,4 +16,5 @@ export const Password = DefaultPassword<DataModel>({
throw new ConvexError('Invalid password.');
}
},
reset: UsesendOTPPasswordReset,
});

View File

@@ -0,0 +1,89 @@
import type { EmailConfig, EmailUserConfig } from '@auth/core/providers/email';
import { UseSend } from 'usesend-js';
/** @todo Document this */
export const Usesend = (config: EmailUserConfig): EmailConfig => {
return {
id: "usesend",
type: "email",
name: "Usesend",
from: "TechTracker Admin <admin@mail.techtracker.gbrown.org>",
maxAge: 24 * 60 * 60,
async sendVerificationRequest(params) {
const { identifier: to, provider, url, theme } = params
const { host } = new URL(url)
const useSend = new UseSend(provider.apiKey, 'https://usesend.gbrown.org')
const { error } = await useSend.emails.send({
to,
from: provider.from ?? 'TechTracker Admin <admin@mail.techtracker.gbrown.org>',
subject: `Sign in to ${host}`,
html: html({ url, host, theme }),
text: text({ url, host })
});
if (error) throw new Error("Usesend error: " + error.message)
},
options: config,
};
};
type Theme = {
colorScheme?: "auto" | "dark" | "light"
logo?: string
brandColor?: string
buttonText?: string
};
const text = ({ url, host }: { url: string; host: string }) => {
return `Sign in to ${host}\n${url}\n\n`
};
const html = (params: { url: string; host: string; theme: Theme }) => {
const { url, host, theme } = params;
const escapedHost = host.replace(/\./g, "&#8203;.");
const brandColor = theme.brandColor || "#346df1";
const buttonText = theme.buttonText || "#fff";
const color = {
background: "#f9f9f9",
text: "#444",
mainBackground: "#fff",
buttonBackground: brandColor,
buttonBorder: brandColor,
buttonText,
};
return `
<body style="background: ${color.background};">
<table width="100%" border="0" cellspacing="20" cellpadding="0"
style="background: ${color.mainBackground}; max-width: 600px; margin: auto; border-radius: 10px;">
<tr>
<td align="center"
style="padding: 10px 0px; font-size: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
Sign in to <strong>${escapedHost}</strong>
</td>
</tr>
<tr>
<td align="center" style="padding: 20px 0;">
<table border="0" cellspacing="0" cellpadding="0">
<tr>
<td align="center" style="border-radius: 5px;" bgcolor="${color.buttonBackground}"><a href="${url}"
target="_blank"
style="font-size: 18px; font-family: Helvetica, Arial, sans-serif; color: ${color.buttonText}; text-decoration: none; border-radius: 5px; padding: 10px 20px; border: 1px solid ${color.buttonBorder}; display: inline-block; font-weight: bold;">Sign
in</a></td>
</tr>
</table>
</td>
</tr>
<tr>
<td align="center"
style="padding: 0px 0px 10px 0px; font-size: 16px; line-height: 22px; font-family: Helvetica, Arial, sans-serif; color: ${color.text};">
If you did not request this email you can safely ignore it.
</td>
</tr>
</table>
</body>
`
};