v3: added onboarding email on account creation
This commit is contained in:
@@ -46,7 +46,7 @@ if (userCount.count === 0) {
|
||||
const hash = scryptSync("admin", salt, 64).toString("hex");
|
||||
const passwordHash = `${hash}.${salt}`;
|
||||
db.prepare(
|
||||
`INSERT INTO users (id, username, password_hash, is_admin, created_at) VALUES (?, 'admin', ?, 1, datetime('now'))`,
|
||||
`INSERT INTO users (id, username, password_hash, is_admin, created_at, email) VALUES (?, 'admin', ?, 1, datetime('now'), 'admin@localhost')`,
|
||||
).run(crypto.randomUUID(), passwordHash);
|
||||
console.log("Created default admin user (username: admin, password: admin)");
|
||||
}
|
||||
@@ -87,6 +87,7 @@ export interface UserRow {
|
||||
invited_by: string | null;
|
||||
// Present only when joined: LEFT JOIN users i ON i.id = u.invited_by
|
||||
invited_by_username: string | null;
|
||||
email: string;
|
||||
[key: string]: SQLOutputValue; // Index signature
|
||||
}
|
||||
|
||||
@@ -136,7 +137,8 @@ export function isUserRow(obj: unknown): obj is UserRow {
|
||||
"description" in obj &&
|
||||
(typeof obj.description === "string" || obj.description === null) &&
|
||||
"invited_by" in obj &&
|
||||
(typeof obj.invited_by === "string" || obj.invited_by === null);
|
||||
(typeof obj.invited_by === "string" || obj.invited_by === null) &&
|
||||
"email" in obj && typeof obj.email === "string";
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,6 +202,7 @@ export function userRowToApi(row: UserRow): User {
|
||||
invitedByUsername: typeof row.invited_by_username === "string"
|
||||
? row.invited_by_username
|
||||
: undefined,
|
||||
email: row.email,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -215,6 +218,7 @@ export function userApiToRow(user: User): UserRow {
|
||||
description: user.description ?? null,
|
||||
invited_by: null,
|
||||
invited_by_username: null,
|
||||
email: user.email,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ export interface User {
|
||||
avatarMime?: string;
|
||||
description?: string;
|
||||
invitedByUsername?: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface LoginUserRequest {
|
||||
@@ -59,6 +60,7 @@ export interface RegisterUserRequest {
|
||||
username: string;
|
||||
password: string;
|
||||
inviteToken: string;
|
||||
email: string;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
@@ -66,6 +68,7 @@ export interface UpdateUserRequest {
|
||||
password?: string;
|
||||
isAdmin?: boolean;
|
||||
description?: string | null;
|
||||
email?: string;
|
||||
}
|
||||
|
||||
export function isLoginUserRequest(obj: unknown): obj is LoginUserRequest {
|
||||
@@ -86,9 +89,13 @@ export function validateRegisterUserRequest(obj: unknown): string | null {
|
||||
!obj || typeof obj !== "object" ||
|
||||
!("username" in obj) || typeof obj.username !== "string" ||
|
||||
!("password" in obj) || typeof obj.password !== "string" ||
|
||||
!("inviteToken" in obj) || typeof obj.inviteToken !== "string"
|
||||
!("inviteToken" in obj) || typeof obj.inviteToken !== "string" ||
|
||||
!("email" in obj) || typeof obj.email !== "string"
|
||||
) return "Invalid request";
|
||||
const { username, password } = obj as RegisterUserRequest;
|
||||
const { username, password, email } = obj as RegisterUserRequest;
|
||||
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
||||
return "Invalid email address";
|
||||
}
|
||||
if (
|
||||
!new RegExp(
|
||||
`^[a-zA-Z0-9_]{${VALIDATION.USERNAME_MIN},${VALIDATION.USERNAME_MAX}}$`,
|
||||
@@ -125,6 +132,10 @@ export function isUpdateUserRequest(obj: unknown): obj is UpdateUserRequest {
|
||||
"description" in o && typeof o.description !== "string" &&
|
||||
o.description !== null
|
||||
) return false;
|
||||
if ("email" in o) {
|
||||
if (typeof o.email !== "string") return false;
|
||||
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(o.email as string)) return false;
|
||||
}
|
||||
if (
|
||||
typeof o.description === "string" &&
|
||||
(o.description as string).length > VALIDATION.USER_DESCRIPTION_MAX
|
||||
@@ -517,7 +528,7 @@ export interface PlaylistDumpsUpdatedMessage {
|
||||
|
||||
export interface UserUpdatedMessage {
|
||||
type: "user_updated";
|
||||
user: Omit<User, "passwordHash">;
|
||||
user: Omit<User, "passwordHash" | "email">;
|
||||
}
|
||||
|
||||
export interface CommentCreatedMessage {
|
||||
|
||||
Reference in New Issue
Block a user