v3: added onboarding email on account creation

This commit is contained in:
khannurien
2026-03-30 14:55:30 +00:00
parent cbb3505139
commit 378b3ffa46
27 changed files with 404 additions and 59 deletions

View File

@@ -226,7 +226,10 @@ export function searchDumps(
const totalRow = db.prepare(
`SELECT COUNT(*) as count FROM dumps WHERE (is_private = 0 OR user_id = ?) AND ${searchClause};`,
).get(requestingUserId, ...searchParams) as { count: number } | undefined;
return { items: rows.filter(isDumpRow).map(dumpRowToApi), total: totalRow?.count ?? 0 };
return {
items: rows.filter(isDumpRow).map(dumpRowToApi),
total: totalRow?.count ?? 0,
};
} else {
const rows = db.prepare(
`SELECT ${SELECT_COLS} FROM dumps WHERE is_private = 0 AND ${searchClause} ORDER BY created_at DESC LIMIT ? OFFSET ?;`,
@@ -234,7 +237,10 @@ export function searchDumps(
const totalRow = db.prepare(
`SELECT COUNT(*) as count FROM dumps WHERE is_private = 0 AND ${searchClause};`,
).get(...searchParams) as { count: number } | undefined;
return { items: rows.filter(isDumpRow).map(dumpRowToApi), total: totalRow?.count ?? 0 };
return {
items: rows.filter(isDumpRow).map(dumpRowToApi),
total: totalRow?.count ?? 0,
};
}
}

View File

@@ -59,7 +59,9 @@ export async function verifyEmailTransport(): Promise<boolean> {
return true;
}
export async function sendEmail(message: EmailMessage): Promise<EmailSendResult> {
export async function sendEmail(
message: EmailMessage,
): Promise<EmailSendResult> {
if (normalizeRecipients(message.to).length === 0) {
throw new Error("Email recipient is required.");
}

View File

@@ -56,8 +56,9 @@ async function fetchOEmbed(
url: string,
): Promise<{ title?: string; thumbnailUrl?: string }> {
try {
const oembedUrl =
`https://www.youtube.com/oembed?url=${encodeURIComponent(url)}&format=json`;
const oembedUrl = `https://www.youtube.com/oembed?url=${
encodeURIComponent(url)
}&format=json`;
const res = await fetchWithTimeout(oembedUrl);
if (res.ok) {
const data = await res.json() as {
@@ -122,7 +123,8 @@ export const youtubeProvider: RichContentProvider = {
url,
title,
thumbnailUrl,
embedUrl: `https://www.youtube.com/embed/videoseries?list=${listId}&rel=0`,
embedUrl:
`https://www.youtube.com/embed/videoseries?list=${listId}&rel=0`,
};
}

View File

@@ -12,7 +12,7 @@ import { hashPassword } from "../lib/jwt.ts";
import { linkAttachments } from "./attachment-service.ts";
const USER_SELECT =
`SELECT u.id, u.username, u.password_hash, u.is_admin, u.created_at, u.updated_at, u.avatar_mime, u.description, u.invited_by,
`SELECT u.id, u.username, u.password_hash, u.is_admin, u.created_at, u.updated_at, u.avatar_mime, u.description, u.invited_by, u.email,
i.username as invited_by_username
FROM users u
LEFT JOIN users i ON i.id = u.invited_by`;
@@ -39,8 +39,8 @@ export async function createUser(
const passwordHash = await hashPassword(request.password);
db.prepare(
`INSERT INTO users (id, username, password_hash, is_admin, created_at, invited_by)
VALUES (?, ?, ?, ?, ?, ?);`,
`INSERT INTO users (id, username, password_hash, is_admin, created_at, invited_by, email)
VALUES (?, ?, ?, ?, ?, ?, ?);`,
).run(
userId,
request.username,
@@ -48,6 +48,7 @@ export async function createUser(
0,
createdAt.toISOString(),
inviterId,
request.email,
);
return {
@@ -56,6 +57,7 @@ export async function createUser(
passwordHash,
isAdmin: false,
createdAt,
email: request.email,
};
}
@@ -129,7 +131,7 @@ export async function updateUser(
): Promise<User> {
const user = getUserById(userId);
const { password, description, ...requestFields } = request;
const { password, description, email, ...requestFields } = request;
const now = new Date();
const updatedUser: User = {
@@ -137,18 +139,20 @@ export async function updateUser(
passwordHash: password ? await hashPassword(password) : user.passwordHash,
...requestFields,
description: description ?? user.description,
email: email ?? user.email,
updatedAt: now,
};
const updatedUserRow = userApiToRow(updatedUser);
const userResult = db.prepare(
`UPDATE users SET username = ?, password_hash = ?, is_admin = ?, description = ?, updated_at = ? WHERE id = ?`,
`UPDATE users SET username = ?, password_hash = ?, is_admin = ?, description = ?, email = ?, updated_at = ? WHERE id = ?`,
).run(
updatedUserRow.username,
updatedUserRow.password_hash,
updatedUserRow.is_admin,
updatedUserRow.description,
updatedUserRow.email,
now.toISOString(),
updatedUserRow.id,
);

View File

@@ -163,7 +163,9 @@ export function broadcastPlaylistDumpsUpdated(
});
}
export function broadcastUserUpdated(user: Omit<User, "passwordHash">): void {
export function broadcastUserUpdated(
user: Omit<User, "passwordHash" | "email">,
): void {
for (const client of clients) {
send(client.socket, { type: "user_updated", user });
}