Add missing migrations
This commit is contained in:
1 parent
0201d21c38
commit
2d09a4a92c
3 files changed
+90
-3
No files matched your search
@@ -0,0 +1,4 @@
|
|||||||
|
-- Add terms/age consent columns expected by the users schema (register flow).
|
||||||
|
ALTER TABLE `users`
|
||||||
|
ADD COLUMN `terms_accepted` TINYINT(1) NOT NULL DEFAULT 0,
|
||||||
|
ADD COLUMN `age_verified` TINYINT(1) NOT NULL DEFAULT 0;
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import { randomBytes } from "node:crypto";
|
||||||
|
import { eq } from "drizzle-orm";
|
||||||
|
import { drizzle } from "drizzle-orm/mysql2";
|
||||||
|
import { bcrypt, bcryptVerify } from "hash-wasm";
|
||||||
|
import mysql from "mysql2/promise";
|
||||||
|
import * as schema from "@/db/schema";
|
||||||
|
|
||||||
|
const url = new URL(
|
||||||
|
"mysql://cms:HeelM%3Coeikgsdgsccusygcsugy@localhost:3306/habbo",
|
||||||
|
);
|
||||||
|
const pool = mysql.createPool({
|
||||||
|
host: url.hostname,
|
||||||
|
port: Number(url.port) || 3306,
|
||||||
|
user: decodeURIComponent(url.username),
|
||||||
|
password: decodeURIComponent(url.password),
|
||||||
|
database: "habbo",
|
||||||
|
charset: "utf8mb4",
|
||||||
|
});
|
||||||
|
|
||||||
|
const db = drizzle(pool, { schema, mode: "default" });
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const hash = await bcrypt({
|
||||||
|
password: "Test1234",
|
||||||
|
salt: randomBytes(16),
|
||||||
|
costFactor: 12,
|
||||||
|
outputType: "encoded",
|
||||||
|
});
|
||||||
|
const uname = "__e2e_test_" + Date.now();
|
||||||
|
try {
|
||||||
|
await db.insert(schema.User).values({
|
||||||
|
username: uname,
|
||||||
|
password: hash,
|
||||||
|
mail: null,
|
||||||
|
accountCreated: Math.floor(Date.now() / 1000),
|
||||||
|
ipRegister: "127.0.0.1",
|
||||||
|
ipCurrent: "127.0.0.1",
|
||||||
|
look: "hr-100-.hd-180-1.ch-255-66.lg-280-110.sh-305-62",
|
||||||
|
});
|
||||||
|
console.log("INSERT OK");
|
||||||
|
|
||||||
|
// Simuleer login-verificatie
|
||||||
|
const [user] = await db
|
||||||
|
.select()
|
||||||
|
.from(schema.User)
|
||||||
|
.where(eq(schema.User.username, uname))
|
||||||
|
.limit(1);
|
||||||
|
if (!user) throw new Error("User not found after insert");
|
||||||
|
const ok = await bcryptVerify({
|
||||||
|
password: "Test1234",
|
||||||
|
hash: user.password,
|
||||||
|
});
|
||||||
|
console.log("LOGIN VERIFY:", ok ? "OK" : "FAILED");
|
||||||
|
} catch (err) {
|
||||||
|
console.error("FAILED:", (err as Error).message);
|
||||||
|
if ((err as { cause?: Error }).cause)
|
||||||
|
console.error("CAUSE:", (err as { cause?: Error }).cause?.message);
|
||||||
|
process.exitCode = 1;
|
||||||
|
} finally {
|
||||||
|
await pool.execute(
|
||||||
|
"DELETE FROM users WHERE username LIKE '\\_\\_e2e\\_test\\_%'",
|
||||||
|
);
|
||||||
|
await pool.end();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
+19
-3
@@ -29,6 +29,7 @@ const registerSchema = z.object({
|
|||||||
.regex(/[A-Z]/, "Password must contain at least one uppercase letter")
|
.regex(/[A-Z]/, "Password must contain at least one uppercase letter")
|
||||||
.regex(/[a-z]/, "Password must contain at least one lowercase letter")
|
.regex(/[a-z]/, "Password must contain at least one lowercase letter")
|
||||||
.regex(/[0-9]/, "Password must contain at least one digit"),
|
.regex(/[0-9]/, "Password must contain at least one digit"),
|
||||||
|
passwordConfirmation: z.string(),
|
||||||
look: z.string().optional(),
|
look: z.string().optional(),
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -48,6 +49,9 @@ export async function register(
|
|||||||
.trim()
|
.trim()
|
||||||
.toLowerCase(),
|
.toLowerCase(),
|
||||||
password: String(formData.get("password") ?? "").normalize("NFC"),
|
password: String(formData.get("password") ?? "").normalize("NFC"),
|
||||||
|
passwordConfirmation: String(
|
||||||
|
formData.get("password_confirmation") ?? "",
|
||||||
|
).normalize("NFC"),
|
||||||
look:
|
look:
|
||||||
String(formData.get("look") ?? "")
|
String(formData.get("look") ?? "")
|
||||||
.normalize("NFC")
|
.normalize("NFC")
|
||||||
@@ -60,6 +64,10 @@ export async function register(
|
|||||||
return parsed.error.issues[0]?.message ?? "Invalid input";
|
return parsed.error.issues[0]?.message ?? "Invalid input";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parsed.data.password !== parsed.data.passwordConfirmation) {
|
||||||
|
return "Passwords do not match";
|
||||||
|
}
|
||||||
|
|
||||||
const { username, mail, password, look } = parsed.data;
|
const { username, mail, password, look } = parsed.data;
|
||||||
const hasEmail = !!mail;
|
const hasEmail = !!mail;
|
||||||
const ip = await clientIp();
|
const ip = await clientIp();
|
||||||
@@ -124,6 +132,7 @@ export async function register(
|
|||||||
ipRegister: ip,
|
ipRegister: ip,
|
||||||
ipCurrent: ip,
|
ipCurrent: ip,
|
||||||
look,
|
look,
|
||||||
|
termsAccepted: raw.termsAccepted,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (hasEmail) {
|
if (hasEmail) {
|
||||||
@@ -133,9 +142,16 @@ export async function register(
|
|||||||
logger.warn("Failed to send verification email after registration");
|
logger.warn("Failed to send verification email after registration");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch (err) {
|
||||||
logger.warn("Account creation failed");
|
const code = (err as { cause?: { code?: string } }).cause?.code;
|
||||||
return "Could not create the account (is the username unique?)";
|
if (code === "ER_DUP_ENTRY") {
|
||||||
|
return "That username is already taken";
|
||||||
|
}
|
||||||
|
logger.error("Account creation failed", {
|
||||||
|
code,
|
||||||
|
message: err instanceof Error ? err.message : String(err),
|
||||||
|
});
|
||||||
|
return "Could not create the account. Please try again or contact staff.";
|
||||||
}
|
}
|
||||||
|
|
||||||
redirect("/login?registered=1");
|
redirect("/login?registered=1");
|
||||||
|
|||||||
Reference in new issue
Block a user