224 remove sensitive information from logs (#225)

* Logging adapted

* Fix log mocks

* Lint fixes
This commit is contained in:
Hendrik
2026-03-10 19:08:03 +01:00
committed by GitHub
parent fc385fc9cc
commit bd6c10e160
46 changed files with 77 additions and 161 deletions
+1 -5
View File
@@ -208,11 +208,7 @@ export class UnifiedAppointmentCrypto {
// Note: clientKeyShare will be used during actual appointment creation
await this.encryptTunnelKeyForClient();
console.log("✅ New client initialized", {
tunnelId: this.tunnelId,
emailHashPrefix: this.emailHash.slice(0, 8),
staffCount: staffPublicKeys.length,
});
console.log("✅ New client initialized", {});
this.clientAuthenticated = true;
} catch (error) {
+5 -7
View File
@@ -13,9 +13,7 @@ export class AuthorizationService {
}
if (user.role !== requiredRole) {
logger.warn(
`Access denied: User ${user.email} has role ${user.role}, required ${requiredRole}`,
);
logger.warn(`Access denied: User ${user.id} has role ${user.role}, required ${requiredRole}`);
throw new AuthorizationError();
}
@@ -29,7 +27,7 @@ export class AuthorizationService {
if (!allowedRoles.includes(user.role as UserRole)) {
logger.warn(
`Access denied: User ${user.email} has role ${user.role}, allowed roles: ${allowedRoles.join(", ")}`,
`Access denied: User ${user.id} has role ${user.role}, allowed roles: ${allowedRoles.join(", ")}`,
);
throw new AuthorizationError();
}
@@ -51,13 +49,13 @@ export class AuthorizationService {
if (user.role === "TENANT_ADMIN" || user.role === "STAFF") {
if (!user.tenantId) {
logger.warn(`Access denied: User ${user.email} has no tenant assigned`);
logger.warn(`Access denied: User ${user.id} has no tenant assigned`);
throw new AuthorizationError("No tenant access");
}
if (user.tenantId !== tenantId) {
logger.warn(
`Access denied: User ${user.email} trying to access tenant ${tenantId}, but belongs to ${user.tenantId}`,
`Access denied: User ${user.id} trying to access tenant ${tenantId}, but belongs to ${user.tenantId}`,
);
throw new AuthorizationError("Tenant access denied");
}
@@ -66,7 +64,7 @@ export class AuthorizationService {
return;
}
logger.warn(`Access denied: User ${user.email} has invalid role ${user.role}`);
logger.warn(`Access denied: User ${user.id} has invalid role ${user.role}`);
throw new AuthorizationError("Invalid role");
}
+11 -11
View File
@@ -43,7 +43,7 @@ export class SessionService {
userAgent?: string,
passkeyId?: string,
): Promise<SessionData> {
logger.info(`Creating session for user: ${userId}`);
logger.debug(`Creating session for user: ${userId}`);
const existingUser = await db.select().from(user).where(eq(user.id, userId)).limit(1);
@@ -88,7 +88,7 @@ export class SessionService {
await db.update(user).set({ lastLoginAt: new Date() }).where(eq(user.id, userId));
logger.info(`Session created successfully for user: ${userId}`, { passkeyId });
logger.debug(`Session created successfully for user: ${userId}`, { passkeyId });
return {
sessionToken: updatedSession.sessionToken,
@@ -246,7 +246,7 @@ export class SessionService {
})
.where(eq(userSession.id, session.user_session.id));
logger.info("Session tokens refreshed successfully");
logger.debug("Session tokens refreshed successfully");
return {
accessToken: newTokens.accessToken,
@@ -256,27 +256,27 @@ export class SessionService {
}
static async logout(sessionToken: string): Promise<void> {
logger.info(`Logging out session: ${sessionToken}`);
logger.debug(`Logging out session: ${sessionToken}`);
await db.delete(userSession).where(eq(userSession.sessionToken, sessionToken));
logger.info(`Session logged out successfully: ${sessionToken}`);
logger.debug(`Session logged out successfully: ${sessionToken}`);
}
static async logoutAllSessions(userId: string): Promise<void> {
logger.info(`Logging out all sessions for user: ${userId}`);
logger.debug(`Logging out all sessions for user: ${userId}`);
await db.delete(userSession).where(eq(userSession.userId, userId));
logger.info(`All sessions logged out for user: ${userId}`);
logger.debug(`All sessions logged out for user: ${userId}`);
}
static async cleanupExpiredSessions(): Promise<void> {
logger.info("Cleaning up expired sessions");
logger.debug("Cleaning up expired sessions");
await db.delete(userSession).where(lt(userSession.expiresAt, new Date()));
logger.info("Expired sessions cleaned up");
logger.debug("Expired sessions cleaned up");
}
static async getUserSession(sessionId: string): Promise<SelectUserSession | null> {
@@ -309,10 +309,10 @@ export class SessionService {
}
static async revokeSession(sessionId: string): Promise<void> {
logger.info(`Revoking session: ${sessionId}`);
logger.debug(`Revoking session: ${sessionId}`);
await db.delete(userSession).where(eq(userSession.id, sessionId));
logger.info(`Session revoked: ${sessionId}`);
logger.debug(`Session revoked: ${sessionId}`);
}
}
+2 -6
View File
@@ -155,8 +155,7 @@ export class WebAuthnService {
})
.where(eq(userPasskey.id, normalizedCredentialId));
logger.info("WebAuthn authentication successful", {
credentialId: normalizedCredentialId.substring(0, 20) + "...",
logger.debug("WebAuthn authentication successful", {
userId: passkey.userId,
counterUpdated: `${passkey.counter}${newCounter}`,
});
@@ -244,12 +243,9 @@ export class WebAuthnService {
"base64",
);
logger.info("Registration verified successfully", {
credentialId: normalizedCredentialId.substring(0, 20) + "...",
credentialIdLength: normalizedCredentialId.length,
logger.debug("Registration verified successfully", {
counter: registrationInfo.credential.counter,
publicKeyLength: registrationInfo.credential.publicKey.length,
publicKeyBytesFirst10: Array.from(registrationInfo.credential.publicKey.slice(0, 10)),
});
return {
+7 -4
View File
@@ -2,6 +2,7 @@ import nodemailer from "nodemailer";
import { env } from "$env/dynamic/private";
import type Mail from "nodemailer/lib/mailer";
import type { SelectUser } from "../db/central-schema";
import logger from "$lib/logger";
/**
* Simple client data type for email sending
@@ -92,10 +93,11 @@ export async function sendEmail(
textContent: string,
): Promise<void> {
const transporter = createTransporter();
logger.setContext("sendMail");
if (!recipient.email) {
// Recipient has not stored an email address, so no mail will be send
// TODO: Log this event
logger.info("Recipient has no email address, skipping email sending");
return;
}
@@ -121,9 +123,9 @@ export async function sendEmail(
try {
await transporter.sendMail(mailOptions);
console.log(`Email sent successfully to ${recipient.email}`);
logger.debug(`Email sent successfully to ${recipient.email}`);
} catch (error) {
console.error("Failed to send email:", error);
logger.error("Failed to send email:", { error });
throw new Error(`Failed to send email to ${recipient.email}`);
}
}
@@ -134,11 +136,12 @@ export async function sendEmail(
*/
export async function testEmailConnection(): Promise<boolean> {
try {
logger.setContext("testEmailConnection");
const transporter = createTransporter();
await transporter.verify();
return true;
} catch (error) {
console.error("SMTP connection test failed:", error);
logger.error("SMTP connection test failed:", { error });
return false;
}
}
+7 -10
View File
@@ -513,11 +513,10 @@ export class AppointmentService {
): Promise<AppointmentResponse> {
const log = logger.setContext("AppointmentService");
log.info("Adding appointment to existing tunnel", {
log.debug("Adding appointment to existing tunnel", {
tenantId: this.tenantId,
tunnelId: appointmentData.tunnelId,
appointmentDate: appointmentData.appointmentDate,
emailHashPrefix: appointmentData.emailHash.slice(0, 8),
});
const db = await this.getDb();
@@ -593,7 +592,7 @@ export class AppointmentService {
requiresConfirmation,
};
log.info("Successfully added appointment to tunnel", {
log.debug("Successfully added appointment to tunnel", {
tenantId: this.tenantId,
tunnelId: appointmentData.tunnelId,
appointmentId: result.id,
@@ -611,11 +610,10 @@ export class AppointmentService {
): Promise<AppointmentResponse> {
const log = logger.setContext("AppointmentService");
log.info("Creating new client appointment tunnel", {
log.debug("Creating new client appointment tunnel", {
tenantId: this.tenantId,
tunnelId: clientData.tunnelId,
appointmentDate: clientData.appointmentDate,
emailHashPrefix: clientData.emailHash.slice(0, 8),
});
// Check if there are any authorized users (ACCESS_GRANTED) in this tenant
@@ -764,7 +762,7 @@ export class AppointmentService {
requiresConfirmation: result.requiresConfirmation,
};
log.info("Successfully created new client appointment tunnel", {
log.debug("Successfully created new client appointment tunnel", {
tenantId: this.tenantId,
tunnelId: clientData.tunnelId,
appointmentId: result.appointment.id,
@@ -955,10 +953,9 @@ export class AppointmentService {
challengeResponse: string,
): Promise<void> {
const log = logger.setContext("AppointmentService");
log.info("Client deleting appointment with authentication", {
log.debug("Client deleting appointment with authentication", {
tenantId: this.tenantId,
appointmentId,
emailHashPrefix: emailHash.slice(0, 8),
});
// 1. Verify challenge-response
@@ -1058,7 +1055,7 @@ export class AppointmentService {
// 4. Delete the appointment
await db.delete(tenantSchema.appointment).where(eq(tenantSchema.appointment.id, appointmentId));
log.info("Appointment deleted successfully by client", {
log.debug("Appointment deleted successfully by client", {
tenantId: this.tenantId,
appointmentId,
emailHashPrefix: emailHash.slice(0, 8),
@@ -1073,7 +1070,7 @@ export class AppointmentService {
},
});
log.info("Staff notification sent for client-initiated deletion", {
log.debug("Staff notification sent for client-initiated deletion", {
tenantId: this.tenantId,
appointmentId,
channelId: appointment.channelId,
@@ -194,8 +194,6 @@ export class CentralDatabaseMigrationService {
logger.info("Central database created and initialized", {
database: config.database,
host: config.host,
port: config.port,
});
}
@@ -90,8 +90,6 @@ export class ClientPinResetService {
.returning({ token: tenantSchema.clientPinResetToken.token });
log.info("PIN reset token created", {
emailHash: emailHash.slice(0, 8),
tokenId: resetToken.token.slice(0, 8),
expiresAt,
});
@@ -226,9 +224,7 @@ export class ClientPinResetService {
.where(eq(tenantSchema.clientPinResetToken.token, token));
log.info("PIN reset completed successfully", {
tokenId: token.slice(0, 8),
tunnelId: tunnel.id,
emailHash: emailHash.slice(0, 8),
});
return tunnel.id;
+1 -5
View File
@@ -39,13 +39,10 @@ export class InviteService {
const [createdInvite] = await db.insert(userInvite).values(inviteData).returning();
logger.info("User invitation created", {
logger.debug("User invitation created", {
inviteId: createdInvite.id,
inviteCode: createdInvite.inviteCode,
email: createdInvite.email,
tenantId: createdInvite.tenantId,
role: createdInvite.role,
invitedBy: createdInvite.invitedBy,
});
return createdInvite;
@@ -124,7 +121,6 @@ export class InviteService {
logger.info("Invitation marked as used", {
inviteCode,
createdUserId,
email: updatedInvite.email,
});
return updatedInvite;
@@ -97,7 +97,6 @@ export class StaffCryptoService {
log.info("Staff keypair stored successfully", {
tenantId,
userId,
passkeyId,
hasPublicKey: !!publicKey,
hasPrivateKeyShare: !!privateKeyShare,
});
@@ -321,7 +320,6 @@ export class StaffCryptoService {
log.info("Staff crypto deletion completed", {
tenantId,
userId,
passkeyId,
deleted,
});
+1 -1
View File
@@ -250,7 +250,7 @@ export class StaffService {
deletedKeySharesCount,
};
logger.info("Staff member deleted successfully", {
logger.debug("Staff member deleted successfully", {
staffId,
tenantId,
deletedUser: userDeletionResult.deletedUser,
-1
View File
@@ -660,7 +660,6 @@ export class UserService {
log.info("User deleted successfully", {
userId,
deletedUser: deletedUsers[0],
deletedPasskeysCount,
tenantId: user.tenantId,
});
+2 -2
View File
@@ -36,8 +36,8 @@ const createPinThrottleStore = () => {
// Throttle expired, clear storage
localStorage.removeItem(STORAGE_KEY);
}
} catch (e) {
console.error("Failed to parse throttle state:", e);
} catch (error) {
console.error("Failed to parse throttle state:", error);
localStorage.removeItem(STORAGE_KEY);
}
}
@@ -1,5 +1,5 @@
import AppointmentBooked from "$lib/emails/AppointmentBooked.svelte";
import { renderOutputToHtml, htmlToText } from "$lib/emails/utils";
import { renderOutputToHtml } from "$lib/emails/utils";
import type { SelectTenant } from "$lib/server/db/central-schema";
import type { SelectAppointment } from "$lib/server/db/tenant-schema";
import type { RequestHandler } from "@sveltejs/kit";
@@ -30,10 +30,6 @@ export const GET: RequestHandler = async () => {
},
});
const html = renderOutputToHtml(emailRender);
const text = htmlToText(html);
// Output for testing purposes
console.log(text);
return new Response(html, {
headers: {
"Content-Type": "text/html",
@@ -1,5 +1,5 @@
import AppointmentReminder from "$lib/emails/AppointmentReminder.svelte";
import { renderOutputToHtml, htmlToText } from "$lib/emails/utils";
import { renderOutputToHtml } from "$lib/emails/utils";
import type { SelectTenant } from "$lib/server/db/central-schema";
import type { SelectAppointment } from "$lib/server/db/tenant-schema";
import type { RequestHandler } from "@sveltejs/kit";
@@ -30,10 +30,6 @@ export const GET: RequestHandler = async () => {
},
});
const html = renderOutputToHtml(emailRender);
const text = htmlToText(html);
// Output for testing purposes
console.log(text);
return new Response(html, {
headers: {
"Content-Type": "text/html",
@@ -1,5 +1,5 @@
import AppointmentRequested from "$lib/emails/AppointmentRequest.svelte";
import { renderOutputToHtml, htmlToText } from "$lib/emails/utils";
import { renderOutputToHtml } from "$lib/emails/utils";
import type { SelectTenant } from "$lib/server/db/central-schema";
import type { SelectAppointment } from "$lib/server/db/tenant-schema";
import type { RequestHandler } from "@sveltejs/kit";
@@ -29,10 +29,6 @@ export const GET: RequestHandler = async () => {
},
});
const html = renderOutputToHtml(emailRender);
const text = htmlToText(html);
// Output for testing purposes
console.log(text);
return new Response(html, {
headers: {
"Content-Type": "text/html",
@@ -1,5 +1,5 @@
import Confirmation from "$lib/emails/Confirmation.svelte";
import { htmlToText, renderOutputToHtml } from "$lib/emails/utils";
import { renderOutputToHtml } from "$lib/emails/utils";
import type { RequestHandler } from "@sveltejs/kit";
import { render } from "svelte/server";
@@ -17,10 +17,6 @@ export const GET: RequestHandler = async () => {
},
});
const html = renderOutputToHtml(emailRender);
const text = htmlToText(html);
// Output for testing purposes
console.log(text);
return new Response(html, {
headers: {
"Content-Type": "text/html",
@@ -1,5 +1,5 @@
import PinReset from "$lib/emails/PinReset.svelte";
import { renderOutputToHtml, htmlToText } from "$lib/emails/utils";
import { renderOutputToHtml } from "$lib/emails/utils";
import type { SelectTenant } from "$lib/server/db/central-schema";
import type { RequestHandler } from "@sveltejs/kit";
import { render } from "svelte/server";
@@ -17,10 +17,7 @@ export const GET: RequestHandler = async () => {
},
});
const html = renderOutputToHtml(emailRender);
const text = htmlToText(html);
// Output for testing purposes
console.log(text);
return new Response(html, {
headers: {
"Content-Type": "text/html",
@@ -1,5 +1,5 @@
import UserInvite from "$lib/emails/UserInvite.svelte";
import { htmlToText, renderOutputToHtml } from "$lib/emails/utils";
import { renderOutputToHtml } from "$lib/emails/utils";
import type { SelectTenant } from "$lib/server/db/central-schema";
import type { RequestHandler } from "@sveltejs/kit";
import { render } from "svelte/server";
@@ -19,10 +19,6 @@ export const GET: RequestHandler = async () => {
},
});
const html = renderOutputToHtml(emailRender);
const text = htmlToText(html);
// Output for testing purposes
console.log(text);
return new Response(html, {
headers: {
"Content-Type": "text/html",
@@ -148,14 +148,12 @@
prfOutput = prfOutputResp;
logger.info("PRF output retrieved successfully", {
passkeyId: passkeyResp.id,
prfOutputLength: prfOutputResp.byteLength,
});
} catch (error) {
$passkeyLoading = "error";
logger.error("Failed to get PRF output", {
email: $formData.email,
passkeyId: passkeyResp.id,
error,
});
toast.error(m["setupPasskey.errorGettingPrfOutput"]());
@@ -74,7 +74,7 @@
staffKeyShare: item.appointment.staffKeyShare,
});
} catch (err) {
console.error("Error decrypting appointment:", err);
console.error("Error decrypting appointment:", item.id);
error = err instanceof Error ? err.message : "Decryption failed";
}
};
@@ -137,13 +137,11 @@
if (credentialResp.prfOutput) {
prfOutputBase64 = arrayBufferToBase64(credentialResp.prfOutput);
logger.info("PRF output retrieved from login", {
passkeyId,
prfOutputLength: credentialResp.prfOutput.byteLength,
});
} else {
logger.warn("No PRF output in login response - crypto features may not work", {
email: $formData.email,
passkeyId,
});
}
+1 -2
View File
@@ -206,8 +206,7 @@ export const POST: RequestHandler = async ({ request, locals }) => {
// Send invitation email
await sendUserInviteEmail(email, name, tenant, role, registrationUrl, language);
logger.info("User invitation sent successfully", {
invitedBy: locals.user.id,
logger.debug("User invitation sent successfully", {
invitedEmail: email,
tenantId,
role,
-1
View File
@@ -284,7 +284,6 @@ export const POST: RequestHandler = async ({ request, cookies, getClientAddress,
logger.info("Login successful", {
userId: sessionData.user.id,
email: sessionData.user.email,
role: sessionData.user.role,
authMethod: body.passphrase ? "passphrase" : "webauthn",
});
@@ -149,7 +149,6 @@ export async function DELETE({ params, locals }: RequestEvent) {
log.info("Passkey deleted successfully", {
passkeyId,
userId,
deviceName: deletedPasskey.deviceName,
});
// If user has a tenant (STAFF or TENANT_ADMIN), also delete associated crypto data (CASCADE)
@@ -165,7 +164,6 @@ export async function DELETE({ params, locals }: RequestEvent) {
);
log.info("Staff crypto data cascaded deletion completed", {
passkeyId,
userId,
tenantId,
deleted,
@@ -262,11 +262,9 @@ export async function POST({ request, params, locals }: RequestEvent) {
privateKeyShare,
);
log.info("Crypto keys stored successfully", {
passkeyId,
log.debug("Crypto keys stored successfully", {
userId,
tenantId,
prfHash: prfHash.substring(0, 16) + "...",
});
return json({
-2
View File
@@ -11,8 +11,6 @@ export async function GET() {
envOkay = false;
if (!process.env.DATABASE_URL?.startsWith("postgres:")) envOkay = false;
console.warn("Environment is okay", envOkay);
return json({
envOkay,
});
+4 -1
View File
@@ -1,4 +1,5 @@
import { dev } from "$app/environment";
import logger from "$lib/logger";
import { db } from "$lib/server/db";
import { tenant } from "$lib/server/db/central-schema";
import { eq } from "drizzle-orm";
@@ -30,7 +31,9 @@ export const getTenantIdByDomain = async (
.limit(1);
return tenants[0]?.id || null;
} catch (error) {
console.log("error", error);
logger
.setContext("API.Public.Utils")
.error("Failed to get tenant ID by domain", { domain, error });
throw error;
}
}
+1 -1
View File
@@ -497,7 +497,7 @@ export const DELETE: RequestHandler = async ({ params, locals }) => {
throw new ValidationError(ERRORS.TENANTS.NO_TENANT_ID);
}
log.info("Attempting tenant deletion", {
log.debug("Attempting tenant deletion", {
tenantId,
requestedBy: locals.user?.id,
userRole: locals.user?.role,
@@ -133,7 +133,7 @@ export const DELETE: RequestHandler = async ({ request, params }) => {
const body = await request.json();
const { emailHash, challengeId, challengeResponse } = requestSchema.parse(body);
log.info("Client deleting appointment", {
log.debug("Client deleting appointment", {
tenantId,
appointmentId,
emailHashPrefix: emailHash.slice(0, 8),
@@ -148,10 +148,9 @@ export const DELETE: RequestHandler = async ({ request, params }) => {
challengeResponse,
);
log.info("Appointment deleted successfully by client", {
log.debug("Appointment deleted successfully by client", {
tenantId,
appointmentId,
emailHashPrefix: emailHash.slice(0, 8),
});
return json({
@@ -123,14 +123,13 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
const body = await request.json();
const { clientEmail, clientLanguage } = requestSchema.parse(body);
log.info("Denying appointment", {
log.debug("Denying appointment", {
tenantId,
appointmentId,
clientEmailPrefix: clientEmail ? clientEmail.slice(0, 3) : undefined,
});
await appointmentService.denyAppointment(appointmentId, clientEmail, clientLanguage);
log.info("Appointment denied successfully", {
log.debug("Appointment denied successfully", {
tenantId,
appointmentId,
});
@@ -16,6 +16,7 @@ vi.mock("$lib/server/utils/permissions", () => ({
vi.mock("$lib/logger", () => ({
default: {
setContext: vi.fn(() => ({
debug: vi.fn(),
info: vi.fn(),
error: vi.fn(),
})),
@@ -208,11 +208,10 @@ export const POST: RequestHandler = async ({ request, params }) => {
throw new ValidationError("Bees incoming");
}
logger.info("Adding appointment to existing tunnel", {
logger.debug("Adding appointment to existing tunnel", {
tenantId,
tunnelId: validatedData.tunnelId,
appointmentDate: validatedData.appointmentDate,
emailHashPrefix: validatedData.emailHash.slice(0, 8),
});
const db = await getTenantDb(tenantId);
@@ -230,7 +229,6 @@ export const POST: RequestHandler = async ({ request, params }) => {
logger.warn("Client tunnel not found", {
tenantId,
tunnelId: validatedData.tunnelId,
emailHashPrefix: validatedData.emailHash.slice(0, 8),
});
throw new NotFoundError("Tunnel not found or access denied");
}
@@ -335,14 +333,14 @@ export const POST: RequestHandler = async ({ request, params }) => {
if (requiresConfirmation) {
await sendAppointmentRequestEmail(clientData, tenant, result, channelTitle);
logger.info("Appointment request email sent", {
logger.debug("Appointment request email sent", {
tunnelId: validatedData.tunnelId,
appointmentId: result.id,
tenantId,
});
} else {
await sendAppointmentCreatedEmail(clientData, tenant, result, channelTitle);
logger.info("Appointment confirmation email sent", {
logger.debug("Appointment confirmation email sent", {
tunnelId: validatedData.tunnelId,
appointmentId: result.id,
tenantId,
@@ -352,7 +350,6 @@ export const POST: RequestHandler = async ({ request, params }) => {
}
} catch (emailError) {
logger.error("Failed to send appointment notification email", {
tunnelId: validatedData.tunnelId,
appointmentId: result.id,
tenantId,
error: String(emailError),
@@ -144,7 +144,6 @@ export const POST: RequestHandler = async ({ request, params }) => {
if (!throttleResult.allowed) {
logger.warn("PIN challenge throttled", {
tenantId,
emailHashPrefix: emailHash.slice(0, 8),
retryAfterMs: throttleResult.retryAfterMs,
failedAttempts: throttleResult.failedAttempts,
});
@@ -163,7 +162,7 @@ export const POST: RequestHandler = async ({ request, params }) => {
);
}
logger.info("Creating challenge for existing client", {
logger.debug("Creating challenge for existing client", {
tenantId,
emailHashPrefix: emailHash.slice(0, 8),
});
@@ -184,7 +183,6 @@ export const POST: RequestHandler = async ({ request, params }) => {
if (tunnelResult.length === 0) {
logger.warn("Client tunnel not found for challenge", {
tenantId,
emailHashPrefix: emailHash.slice(0, 8),
});
return json({ error: "Client not found" }, { status: 404 });
}
@@ -223,7 +221,7 @@ export const POST: RequestHandler = async ({ request, params }) => {
privateKeyShare: tunnel.privateKeyShare,
};
logger.info("Successfully created challenge", {
logger.debug("Successfully created challenge", {
tenantId,
tunnelId: tunnel.id,
challengeLength: challenge.length,
@@ -233,12 +233,9 @@ export const POST: RequestHandler = async ({ request, params }) => {
throw new ValidationError("Bees incoming");
}
logger.info("Creating new client appointment tunnel", {
logger.debug("Creating new client appointment tunnel", {
tenantId,
tunnelId: validatedData.tunnelId,
appointmentDate: validatedData.appointmentDate,
duration: validatedData.duration,
emailHashPrefix: validatedData.emailHash.slice(0, 8),
});
const appointmentService = await AppointmentService.forTenant(tenantId);
@@ -13,6 +13,7 @@ vi.mock("$lib/server/services/appointment-service", () => ({
vi.mock("$lib/logger", () => ({
logger: {
debug: vi.fn(),
info: vi.fn(),
warn: vi.fn(),
error: vi.fn(),
@@ -96,12 +97,9 @@ describe("Create New Client API Route", () => {
expect(response.status).toBe(200);
expect(data).toEqual(mockAppointment);
expect(mockService.createNewClientWithAppointment).toHaveBeenCalledWith(validRequestBody);
expect(logger.info).toHaveBeenCalledWith("Creating new client appointment tunnel", {
expect(logger.debug).toHaveBeenCalledWith("Creating new client appointment tunnel", {
tenantId: mockTenantId,
tunnelId: mockTunnelId,
appointmentDate: validRequestBody.appointmentDate,
duration: validRequestBody.duration,
emailHashPrefix: "test-ema",
});
});
@@ -195,12 +193,9 @@ describe("Create New Client API Route", () => {
expect(data.error).toBe(
"Cannot create client appointments: No authorized users found in tenant",
);
expect(logger.info).toHaveBeenCalledWith("Creating new client appointment tunnel", {
expect(logger.debug).toHaveBeenCalledWith("Creating new client appointment tunnel", {
tenantId: mockTenantId,
tunnelId: mockTunnelId,
appointmentDate: validRequestBody.appointmentDate,
duration: validRequestBody.duration,
emailHashPrefix: "test-ema",
});
});
@@ -256,12 +251,9 @@ describe("Create New Client API Route", () => {
const event = createMockRequestEvent();
await POST(event);
expect(logger.info).toHaveBeenCalledWith("Creating new client appointment tunnel", {
expect(logger.debug).toHaveBeenCalledWith("Creating new client appointment tunnel", {
tenantId: mockTenantId,
tunnelId: mockTunnelId,
appointmentDate: validRequestBody.appointmentDate,
duration: 10,
emailHashPrefix: "test-ema", // First 8 chars of "test-email-hash"
});
});
@@ -376,7 +376,6 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
logger.info("PIN reset token created for new client", {
tenantId,
tokenId: pinResetToken.slice(0, 8),
});
} catch (error) {
logger.error("Failed to create PIN reset token for new client", {
@@ -246,8 +246,6 @@ export const POST: RequestHandler = async ({ params, locals, request }) => {
log.info("No new key shares to add - all already exist", {
tenantId,
staffUserId,
totalRequested: keyShares.length,
previousConfirmationState: staffUser[0].confirmationState,
});
await ensureAccessGranted();
@@ -284,12 +282,11 @@ export const POST: RequestHandler = async ({ params, locals, request }) => {
await ensureAccessGranted();
log.info("Staff key shares added successfully", {
log.debug("Staff key shares added successfully", {
tenantId,
staffUserId,
addedCount: result.length,
skippedCount: duplicateKeyShares.length,
requesterId: locals.user?.id,
});
return json({
@@ -133,7 +133,7 @@ export const POST: RequestHandler = async ({ request, params }) => {
const body = await request.json();
const { challengeId, challengeResponse } = requestSchema.parse(body);
logger.info("Verifying challenge for existing client", {
logger.debug("Verifying challenge for existing client", {
tenantId,
challengeId,
});
@@ -160,7 +160,6 @@ export const POST: RequestHandler = async ({ request, params }) => {
logger.warn("Challenge response mismatch", {
tenantId,
challengeId,
emailHashPrefix: storedChallenge.emailHash.slice(0, 8),
});
// Record failed attempt for throttling
@@ -185,14 +184,13 @@ export const POST: RequestHandler = async ({ request, params }) => {
logger.warn("Client tunnel not found for verification", {
tenantId,
challengeId,
emailHashPrefix: storedChallenge.emailHash.slice(0, 8),
});
throw new NotFoundError("Client not found");
}
const tunnel = tunnelResult[0];
logger.info("Challenge response validated successfully", {
logger.debug("Challenge response validated successfully", {
tenantId,
challengeId,
tunnelId: tunnel.id,
@@ -207,7 +205,7 @@ export const POST: RequestHandler = async ({ request, params }) => {
tunnelId: tunnel.id,
};
logger.info("Successfully verified challenge", {
logger.debug("Successfully verified challenge", {
tenantId,
tunnelId: tunnel.id,
});
@@ -157,10 +157,9 @@ export const POST: RequestHandler = async ({ params, request }) => {
validatedData.newClientEncryptedTunnelKey,
);
logger.info("PIN reset completed successfully", {
logger.debug("PIN reset completed successfully", {
tenantId,
tunnelId,
tokenId: validatedData.token.slice(0, 8),
});
return json({
@@ -152,10 +152,9 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
const expiresAt = new Date(Date.now() + expirationMinutes * 60 * 1000);
logger.info("PIN reset token created for QR code", {
logger.debug("PIN reset token created for QR code", {
tenantId,
emailHash: validatedData.emailHash.slice(0, 8),
tokenId: token.slice(0, 8),
initiatedBy: locals.user?.id,
});
@@ -166,12 +166,10 @@ export const POST: RequestHandler = async ({ params, request, locals }) => {
const baseUrl = env.PUBLIC_APP_URL || "http://localhost:5173";
const resetUrl = `${baseUrl}/reset-pin/${token}`;
logger.info("PIN reset token created for email", {
logger.debug("PIN reset token created for email", {
tenantId,
emailHash: validatedData.emailHash.slice(0, 8),
tokenId: token.slice(0, 8),
initiatedBy: locals.user?.id,
resetUrl,
});
// TODO: Send actual email once we have a way to get the client's email
@@ -243,7 +243,7 @@ export const DELETE: RequestHandler = async ({ params, url, locals }) => {
const notificationService = await NotificationService.forTenant(tenantId);
const deletedCount = await notificationService.deleteAllNotifications(locals.user.id, readOnly);
log.info("Notifications deleted successfully", {
log.debug("Notifications deleted successfully", {
tenantId,
staffId: locals.user.id,
deletedCount,
@@ -182,7 +182,7 @@ export const DELETE: RequestHandler = async ({ params, locals }) => {
const notificationService = await NotificationService.forTenant(tenantId);
await notificationService.deleteNotification(notificationId, locals.user.id);
log.info("Notification deleted successfully", {
log.debug("Notification deleted successfully", {
tenantId,
staffId: locals.user.id,
notificationId,
@@ -233,7 +233,7 @@ export const PUT: RequestHandler = async ({ params, locals }) => {
const notificationService = await NotificationService.forTenant(tenantId);
await notificationService.markAsRead(notificationId, locals.user.id);
log.info("Notification marked as read successfully", {
log.debug("Notification marked as read successfully", {
tenantId,
staffId: locals.user.id,
notificationId,
@@ -131,7 +131,6 @@ export const POST: RequestHandler = async ({ params, locals, request, cookies })
const validation = requestSchema.safeParse(body);
if (!validation.success) {
console.error("Validation error:", validation.error.issues);
throw new ValidationError(
"Invalid request data: " + validation.error.issues.map((e) => e.message).join(", "),
);
@@ -174,7 +173,6 @@ export const POST: RequestHandler = async ({ params, locals, request, cookies })
log.info("Staff crypto keys stored successfully", {
tenantId,
staffId,
passkeyId,
});
return json({
+1 -1
View File
@@ -91,7 +91,7 @@ export const apiAuthHandle: Handle = async ({ event, resolve }) => {
// Protected auth paths require any authenticated user (no specific role required)
// The authentication check above is sufficient
logger.debug(`User authenticated: ${sessionData?.user.email ?? "unauthenticated"} for ${path}`);
logger.debug(`User authenticated: ${sessionData?.user.id ?? "unauthenticated"} for ${path}`);
return resolve(event);
};