mirror of
https://github.com/Dvorinka/Primora.git
synced 2026-10-29 00:01:20 +00:00
initiall commit
This commit is contained in:
commit
7ddfb1f52b
276 files changed
+37629
No files matched your search
@@ -0,0 +1,105 @@
|
||||
import { betterAuth } from "better-auth";
|
||||
import { jwt } from "better-auth/plugins";
|
||||
import { getMigrations } from "better-auth/db/migration";
|
||||
import { Pool } from "pg";
|
||||
|
||||
import { sendTransactionalEmail } from "./mail.js";
|
||||
import { env } from "./env.js";
|
||||
|
||||
export const authPool = new Pool({
|
||||
connectionString: env.DATABASE_URL,
|
||||
});
|
||||
|
||||
const socialProviders = {
|
||||
...(env.GITHUB_CLIENT_ID && env.GITHUB_CLIENT_SECRET
|
||||
? {
|
||||
github: {
|
||||
clientId: env.GITHUB_CLIENT_ID,
|
||||
clientSecret: env.GITHUB_CLIENT_SECRET,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(env.GOOGLE_CLIENT_ID && env.GOOGLE_CLIENT_SECRET
|
||||
? {
|
||||
google: {
|
||||
clientId: env.GOOGLE_CLIENT_ID,
|
||||
clientSecret: env.GOOGLE_CLIENT_SECRET,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(env.DISCORD_CLIENT_ID && env.DISCORD_CLIENT_SECRET
|
||||
? {
|
||||
discord: {
|
||||
clientId: env.DISCORD_CLIENT_ID,
|
||||
clientSecret: env.DISCORD_CLIENT_SECRET,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
...(env.MICROSOFT_CLIENT_ID && env.MICROSOFT_CLIENT_SECRET
|
||||
? {
|
||||
microsoft: {
|
||||
clientId: env.MICROSOFT_CLIENT_ID,
|
||||
clientSecret: env.MICROSOFT_CLIENT_SECRET,
|
||||
tenantId: env.MICROSOFT_TENANT_ID,
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
};
|
||||
|
||||
export const auth = betterAuth({
|
||||
appName: "Primora",
|
||||
secret: env.BETTER_AUTH_SECRET,
|
||||
baseURL: env.BETTER_AUTH_URL,
|
||||
trustedOrigins: [env.VITE_APP_URL, env.AUTH_BASE_URL],
|
||||
database: authPool,
|
||||
emailAndPassword: {
|
||||
enabled: true,
|
||||
async sendResetPassword({ user, url }) {
|
||||
await sendTransactionalEmail({
|
||||
to: user.email,
|
||||
subject: "Reset your Primora password",
|
||||
text: `Reset your Primora password: ${url}`,
|
||||
html: `<p>Reset your Primora password.</p><p><a href="${url}">Reset password</a></p>`,
|
||||
});
|
||||
},
|
||||
},
|
||||
emailVerification: {
|
||||
sendOnSignUp: true,
|
||||
autoSignInAfterVerification: true,
|
||||
async sendVerificationEmail({ user, url }) {
|
||||
await sendTransactionalEmail({
|
||||
to: user.email,
|
||||
subject: "Verify your Primora email",
|
||||
text: `Verify your Primora email: ${url}`,
|
||||
html: `<p>Verify your Primora email.</p><p><a href="${url}">Verify email</a></p>`,
|
||||
});
|
||||
},
|
||||
},
|
||||
socialProviders,
|
||||
plugins: [
|
||||
jwt({
|
||||
jwt: {
|
||||
issuer: env.JWT_ISSUER,
|
||||
audience: env.JWT_AUDIENCE,
|
||||
expirationTime: `${env.JWT_TTL_SECONDS} seconds`,
|
||||
getSubject({ user }) {
|
||||
return user.id;
|
||||
},
|
||||
definePayload({ user, session }) {
|
||||
return {
|
||||
sid: session.id,
|
||||
email: user.email,
|
||||
email_verified: user.emailVerified,
|
||||
name: user.name,
|
||||
};
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export async function runAuthMigrations() {
|
||||
const migrations = await getMigrations(auth.options);
|
||||
await migrations.runMigrations();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { z } from "zod";
|
||||
|
||||
const envSchema = z.object({
|
||||
NODE_ENV: z.string().default("development"),
|
||||
AUTH_PORT: z.string().default("3001"),
|
||||
DATABASE_URL: z.string().min(1),
|
||||
DRAGONFLY_URL: z.string().default("redis://localhost:6379/0"),
|
||||
BETTER_AUTH_SECRET: z.string().min(16),
|
||||
BETTER_AUTH_URL: z.string().url(),
|
||||
AUTH_BASE_URL: z.string().url().optional(),
|
||||
VITE_APP_URL: z.string().url(),
|
||||
JWT_ISSUER: z.string().min(1),
|
||||
JWT_AUDIENCE: z.string().min(1),
|
||||
JWT_TTL_SECONDS: z.string().default("900"),
|
||||
MAIL_FROM: z.string().min(1),
|
||||
RESEND_API_KEY: z.string().optional(),
|
||||
SMTP_HOST: z.string().optional(),
|
||||
SMTP_PORT: z.string().default("1025"),
|
||||
SMTP_USER: z.string().optional(),
|
||||
SMTP_PASSWORD: z.string().optional(),
|
||||
GITHUB_CLIENT_ID: z.string().optional(),
|
||||
GITHUB_CLIENT_SECRET: z.string().optional(),
|
||||
GOOGLE_CLIENT_ID: z.string().optional(),
|
||||
GOOGLE_CLIENT_SECRET: z.string().optional(),
|
||||
DISCORD_CLIENT_ID: z.string().optional(),
|
||||
DISCORD_CLIENT_SECRET: z.string().optional(),
|
||||
MICROSOFT_CLIENT_ID: z.string().optional(),
|
||||
MICROSOFT_CLIENT_SECRET: z.string().optional(),
|
||||
MICROSOFT_TENANT_ID: z.string().optional(),
|
||||
});
|
||||
|
||||
const parsed = envSchema.safeParse(process.env);
|
||||
|
||||
if (!parsed.success) {
|
||||
throw new Error(
|
||||
"Invalid auth environment:\n" + JSON.stringify(parsed.error.flatten().fieldErrors, null, 2),
|
||||
);
|
||||
}
|
||||
|
||||
export const env = {
|
||||
...parsed.data,
|
||||
AUTH_PORT: Number(parsed.data.AUTH_PORT),
|
||||
JWT_TTL_SECONDS: Number(parsed.data.JWT_TTL_SECONDS),
|
||||
SMTP_PORT: Number(parsed.data.SMTP_PORT),
|
||||
AUTH_BASE_URL: parsed.data.AUTH_BASE_URL ?? parsed.data.BETTER_AUTH_URL,
|
||||
};
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import nodemailer from "nodemailer";
|
||||
import { Resend } from "resend";
|
||||
|
||||
import { env } from "./env.js";
|
||||
|
||||
const resend = env.RESEND_API_KEY ? new Resend(env.RESEND_API_KEY) : null;
|
||||
|
||||
const transporter =
|
||||
!resend && env.SMTP_HOST
|
||||
? nodemailer.createTransport({
|
||||
host: env.SMTP_HOST,
|
||||
port: env.SMTP_PORT,
|
||||
secure: false,
|
||||
auth: env.SMTP_USER
|
||||
? {
|
||||
user: env.SMTP_USER,
|
||||
pass: env.SMTP_PASSWORD,
|
||||
}
|
||||
: undefined,
|
||||
})
|
||||
: null;
|
||||
|
||||
export async function sendTransactionalEmail(input: {
|
||||
to: string;
|
||||
subject: string;
|
||||
text: string;
|
||||
html: string;
|
||||
}) {
|
||||
if (resend) {
|
||||
await resend.emails.send({
|
||||
from: env.MAIL_FROM,
|
||||
to: [input.to],
|
||||
subject: input.subject,
|
||||
text: input.text,
|
||||
html: input.html,
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!transporter) {
|
||||
throw new Error("No mail transport configured for auth service.");
|
||||
}
|
||||
|
||||
await transporter.sendMail({
|
||||
from: env.MAIL_FROM,
|
||||
to: input.to,
|
||||
subject: input.subject,
|
||||
text: input.text,
|
||||
html: input.html,
|
||||
});
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user