v3: added onboarding email on account creation
This commit is contained in:
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.");
|
||||
}
|
||||
|
||||
@@ -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`,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
);
|
||||
|
||||
@@ -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 });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user