mirror of
https://github.com/LukeHagar/better-auth.git
synced 2025-12-09 20:27:44 +00:00
fix: mysql returning failing on update
This commit is contained in:
@@ -13,6 +13,8 @@ import { reactInvitationEmail } from "./email/invitation";
|
||||
import { LibsqlDialect } from "@libsql/kysely-libsql";
|
||||
import { reactResetPasswordEmail } from "./email/rest-password";
|
||||
import { resend } from "./email/resend";
|
||||
import { MysqlDialect } from "kysely";
|
||||
import { createPool } from "mysql2/promise";
|
||||
|
||||
const from = process.env.BETTER_AUTH_EMAIL || "delivered@resend.dev";
|
||||
const to = process.env.TEST_EMAIL || "";
|
||||
@@ -22,11 +24,15 @@ const libsql = new LibsqlDialect({
|
||||
authToken: process.env.TURSO_AUTH_TOKEN || "",
|
||||
});
|
||||
|
||||
const mysql = new MysqlDialect(
|
||||
createPool("mysql://user:password@localhost:3306/better_auth"),
|
||||
);
|
||||
|
||||
export const auth = betterAuth({
|
||||
appName: "Better Auth Demo",
|
||||
database: {
|
||||
dialect: libsql,
|
||||
type: "sqlite",
|
||||
dialect: mysql,
|
||||
type: "mysql",
|
||||
},
|
||||
session: {
|
||||
cookieCache: {
|
||||
|
||||
@@ -64,11 +64,12 @@
|
||||
"kysely": "^0.27.4",
|
||||
"lucide-react": "^0.439.0",
|
||||
"mini-svg-data-uri": "^1.4.4",
|
||||
"mysql2": "^3.11.0",
|
||||
"next": "15.0.0-rc.1",
|
||||
"next-themes": "^0.3.0",
|
||||
"prisma": "^5.19.1",
|
||||
"react-day-picker": "8.10.1",
|
||||
"react": "^18.3.1",
|
||||
"react-day-picker": "8.10.1",
|
||||
"react-dom": "^18.3.1",
|
||||
"react-hook-form": "^7.53.0",
|
||||
"react-qr-code": "^2.0.15",
|
||||
|
||||
@@ -19,6 +19,23 @@ interface KyselyAdapterConfig {
|
||||
generateId?: ((size?: number) => string) | false;
|
||||
}
|
||||
|
||||
function formatDateForMySQL(date: Date): string {
|
||||
const pad = (n: number) => (n < 10 ? "0" + n : n);
|
||||
return (
|
||||
date.getFullYear() +
|
||||
"-" +
|
||||
pad(date.getMonth() + 1) +
|
||||
"-" +
|
||||
pad(date.getDate()) +
|
||||
" " +
|
||||
pad(date.getHours()) +
|
||||
":" +
|
||||
pad(date.getMinutes()) +
|
||||
":" +
|
||||
pad(date.getSeconds())
|
||||
);
|
||||
}
|
||||
|
||||
const createTransform = (
|
||||
db: Kysely<any>,
|
||||
options: BetterAuthOptions,
|
||||
@@ -43,7 +60,10 @@ const createTransform = (
|
||||
if (f.type === "boolean" && type === "sqlite") {
|
||||
return value ? 1 : 0;
|
||||
}
|
||||
if (f.type === "date" && value) {
|
||||
if (f.type === "date" && value && value instanceof Date) {
|
||||
if (type === "mysql") {
|
||||
return formatDateForMySQL(value);
|
||||
}
|
||||
return value.toISOString();
|
||||
}
|
||||
return value;
|
||||
@@ -62,6 +82,10 @@ const createTransform = (
|
||||
return value;
|
||||
}
|
||||
|
||||
function getModelName(model: string) {
|
||||
return schema[model].tableName;
|
||||
}
|
||||
|
||||
const shouldGenerateId = config?.generateId !== false;
|
||||
return {
|
||||
transformInput(data: Record<string, any>, model: string) {
|
||||
@@ -194,25 +218,25 @@ const createTransform = (
|
||||
| InsertQueryBuilder<any, any, any>
|
||||
| UpdateQueryBuilder<any, string, string, any>,
|
||||
model: string,
|
||||
where: Where[],
|
||||
) {
|
||||
let res: any;
|
||||
if (config?.type !== "mysql") {
|
||||
res = await builder.returningAll().executeTakeFirst();
|
||||
} else {
|
||||
//this isn't good, but kysely doesn't support returning in mysql and it doesn't return the inserted id. Change this if there is a better way.
|
||||
await builder.execute();
|
||||
const primaryKey = "id";
|
||||
const insertedId = values[primaryKey];
|
||||
const field = values.id ? "id" : where[0].field ? where[0].field : "id";
|
||||
const value = values[field] || where[0].value;
|
||||
res = await db
|
||||
.selectFrom(this.getModelName(model))
|
||||
.selectFrom(getModelName(model))
|
||||
.selectAll()
|
||||
.where(primaryKey, "=", insertedId)
|
||||
.where(getField(model, field), "=", value)
|
||||
.executeTakeFirst();
|
||||
}
|
||||
return res;
|
||||
},
|
||||
getModelName(model: string) {
|
||||
return schema[model].tableName;
|
||||
},
|
||||
getModelName,
|
||||
getField,
|
||||
};
|
||||
};
|
||||
@@ -235,7 +259,7 @@ export const kyselyAdapter =
|
||||
const transformed = transformInput(values, model);
|
||||
const builder = db.insertInto(getModelName(model)).values(transformed);
|
||||
return transformOutput(
|
||||
await withReturning(transformed, builder, model),
|
||||
await withReturning(transformed, builder, model, []),
|
||||
model,
|
||||
select,
|
||||
);
|
||||
@@ -257,7 +281,7 @@ export const kyselyAdapter =
|
||||
async findMany(data) {
|
||||
const { model, where, limit, offset, sortBy } = data;
|
||||
const { and, or } = convertWhereClause(model, where);
|
||||
let query = db.selectFrom(getModelName(model)).selectAll();
|
||||
let query = db.selectFrom(getModelName(model));
|
||||
if (and) {
|
||||
query = query.where((eb) => eb.and(and.map((expr) => expr(eb))));
|
||||
}
|
||||
@@ -265,7 +289,9 @@ export const kyselyAdapter =
|
||||
query = query.where((eb) => eb.or(or.map((expr) => expr(eb))));
|
||||
}
|
||||
query = query.limit(limit || 100);
|
||||
if (offset) query = query.offset(offset);
|
||||
if (offset) {
|
||||
query = query.offset(offset);
|
||||
}
|
||||
if (sortBy) {
|
||||
query = query.orderBy(
|
||||
getField(model, sortBy.field),
|
||||
@@ -288,7 +314,7 @@ export const kyselyAdapter =
|
||||
query = query.where((eb) => eb.or(or.map((expr) => expr(eb))));
|
||||
}
|
||||
return transformOutput(
|
||||
await withReturning(transformedData, query, model),
|
||||
await withReturning(transformedData, query, model, where),
|
||||
model,
|
||||
);
|
||||
},
|
||||
|
||||
@@ -1,17 +1,28 @@
|
||||
import fs from "fs/promises";
|
||||
import { afterAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { afterAll, beforeAll, beforeEach, describe, expect, it } from "vitest";
|
||||
import { runAdapterTest } from "../../test";
|
||||
import { getMigrations } from "../../../db/get-migration";
|
||||
import path from "path";
|
||||
import Database from "better-sqlite3";
|
||||
import { kyselyAdapter } from "..";
|
||||
import { Kysely, SqliteDialect } from "kysely";
|
||||
import { Kysely, MysqlDialect, SqliteDialect } from "kysely";
|
||||
import type { BetterAuthOptions } from "../../../types";
|
||||
import { createPool } from "mysql2/promise";
|
||||
|
||||
describe("adapter test", async () => {
|
||||
const database = new Database(path.join(__dirname, "test.db"));
|
||||
const opts = {
|
||||
database,
|
||||
const sqlite = new Database(path.join(__dirname, "test.db"));
|
||||
const mysql = createPool("mysql://user:password@localhost:3306/better_auth");
|
||||
const sqliteKy = new Kysely({
|
||||
dialect: new SqliteDialect({
|
||||
database: sqlite,
|
||||
}),
|
||||
});
|
||||
const mysqlKy = new Kysely({
|
||||
dialect: new MysqlDialect(mysql),
|
||||
});
|
||||
const opts = (database: BetterAuthOptions["database"]) =>
|
||||
({
|
||||
database: database,
|
||||
user: {
|
||||
fields: {
|
||||
email: "email_address",
|
||||
@@ -20,23 +31,41 @@ describe("adapter test", async () => {
|
||||
session: {
|
||||
modelName: "sessions",
|
||||
},
|
||||
} satisfies BetterAuthOptions;
|
||||
beforeEach(async () => {
|
||||
const { runMigrations } = await getMigrations(opts);
|
||||
}) satisfies BetterAuthOptions;
|
||||
const mysqlOptions = opts({
|
||||
db: mysqlKy,
|
||||
type: "mysql",
|
||||
});
|
||||
const sqliteOptions = opts({
|
||||
db: sqliteKy,
|
||||
type: "sqlite",
|
||||
});
|
||||
beforeAll(async () => {
|
||||
const { runMigrations } = await getMigrations(mysqlOptions);
|
||||
await runMigrations();
|
||||
const { runMigrations: runMigrationsSqlite } =
|
||||
await getMigrations(sqliteOptions);
|
||||
await runMigrationsSqlite();
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await mysql.query("DROP DATABASE IF EXISTS better_auth");
|
||||
await mysql.query("CREATE DATABASE better_auth");
|
||||
await mysql.end();
|
||||
await fs.unlink(path.join(__dirname, "test.db"));
|
||||
});
|
||||
const sqlite = new Database(path.join(__dirname, "test.db"));
|
||||
const db = new Kysely({
|
||||
dialect: new SqliteDialect({
|
||||
database: sqlite,
|
||||
}),
|
||||
});
|
||||
const adapter = kyselyAdapter(db)(opts);
|
||||
|
||||
const mysqlAdapter = kyselyAdapter(mysqlKy, {
|
||||
type: "mysql",
|
||||
})(mysqlOptions);
|
||||
await runAdapterTest({
|
||||
adapter,
|
||||
adapter: mysqlAdapter,
|
||||
});
|
||||
|
||||
const sqliteAdapter = kyselyAdapter(sqliteKy, {
|
||||
type: "sqlite",
|
||||
})(sqliteOptions);
|
||||
await runAdapterTest({
|
||||
adapter: sqliteAdapter,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -59,7 +59,9 @@ export async function generateState(
|
||||
|
||||
export async function parseState(c: GenericEndpointContext) {
|
||||
const state = c.query.state || c.body.state;
|
||||
console.log("finding verification value", state);
|
||||
const data = await c.context.internalAdapter.findVerificationValue(state);
|
||||
console.log("data", data);
|
||||
if (!data) {
|
||||
logger.error("State Mismatch. Verification not found", {
|
||||
state,
|
||||
|
||||
3
pnpm-lock.yaml
generated
3
pnpm-lock.yaml
generated
@@ -203,6 +203,9 @@ importers:
|
||||
mini-svg-data-uri:
|
||||
specifier: ^1.4.4
|
||||
version: 1.4.4
|
||||
mysql2:
|
||||
specifier: ^3.11.0
|
||||
version: 3.11.3
|
||||
next:
|
||||
specifier: 15.0.0-rc.1
|
||||
version: 15.0.0-rc.1(@babel/core@7.26.0)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
|
||||
Reference in New Issue
Block a user