fix issues from test deploy

This commit is contained in:
Milo Schwartz 2024-12-21 21:01:12 -05:00
parent 3fb3be1f1e
commit ce5df3b0b9
No known key found for this signature in database
92 changed files with 1410 additions and 1019 deletions

View file

@ -11,12 +11,13 @@ import { response } from "@server/utils";
import { hashPassword, verifyPassword } from "@server/auth/password";
import { verifyTotpCode } from "@server/auth/2fa";
import { passwordSchema } from "@server/auth/passwordSchema";
import logger from "@server/logger";
export const changePasswordBody = z.object({
oldPassword: z.string(),
newPassword: passwordSchema,
code: z.string().optional(),
});
}).strict();
export type ChangePasswordBody = z.infer<typeof changePasswordBody>;
@ -108,6 +109,7 @@ export async function changePassword(
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -5,11 +5,12 @@ import { fromError } from "zod-validation-error";
import HttpCode from "@server/types/HttpCode";
import { response } from "@server/utils";
import { validateResourceSessionToken } from "@server/auth/resource";
import logger from "@server/logger";
export const params = z.object({
token: z.string(),
resourceId: z.string().transform(Number).pipe(z.number().int().positive()),
});
}).strict();
export type CheckResourceSessionParams = z.infer<typeof params>;
@ -54,6 +55,7 @@ export async function checkResourceSession(
status: HttpCode.OK,
});
} catch (e) {
logger.error(e);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -10,11 +10,12 @@ import { eq } from "drizzle-orm";
import { response } from "@server/utils";
import { verifyPassword } from "@server/auth/password";
import { verifyTotpCode } from "@server/auth/2fa";
import logger from "@server/logger";
export const disable2faBody = z.object({
password: z.string(),
code: z.string().optional(),
});
}).strict();
export type Disable2faBody = z.infer<typeof disable2faBody>;
@ -100,6 +101,7 @@ export async function disable2fa(
status: HttpCode.OK,
});
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -22,7 +22,7 @@ export const loginBodySchema = z.object({
email: z.string().email(),
password: z.string(),
code: z.string().optional(),
});
}).strict();
export type LoginBody = z.infer<typeof loginBodySchema>;
@ -151,6 +151,7 @@ export async function login(
status: HttpCode.OK,
});
} catch (e) {
logger.error(e);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -6,13 +6,13 @@ import logger from "@server/logger";
import {
createBlankSessionTokenCookie,
invalidateSession,
SESSION_COOKIE_NAME,
SESSION_COOKIE_NAME
} from "@server/auth";
export async function logout(
req: Request,
res: Response,
next: NextFunction,
next: NextFunction
): Promise<any> {
const sessionId = req.cookies[SESSION_COOKIE_NAME];
@ -20,8 +20,8 @@ export async function logout(
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"You must be logged in to sign out",
),
"You must be logged in to sign out"
)
);
}
@ -34,15 +34,12 @@ export async function logout(
success: true,
error: false,
message: "Logged out successfully",
status: HttpCode.OK,
status: HttpCode.OK
});
} catch (error) {
logger.error("Failed to log out", error);
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Failed to log out",
),
createHttpError(HttpCode.INTERNAL_SERVER_ERROR, "Failed to log out")
);
}
}

View file

@ -3,8 +3,9 @@ import createHttpError from "http-errors";
import HttpCode from "@server/types/HttpCode";
import { response } from "@server/utils";
import { User } from "@server/db/schema";
import { sendEmailVerificationCode } from "./sendEmailVerificationCode";
import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
import config from "@server/config";
import logger from "@server/logger";
export type RequestEmailVerificationCodeResponse = {
codeSent: boolean;
@ -40,14 +41,15 @@ export async function requestEmailVerificationCode(
return response<RequestEmailVerificationCodeResponse>(res, {
data: {
codeSent: true,
codeSent: true
},
status: HttpCode.OK,
success: true,
error: false,
message: `Email verification code sent to ${user.email}`,
message: `Email verification code sent to ${user.email}`
});
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -16,7 +16,7 @@ import { TimeSpan } from "oslo";
export const requestPasswordResetBody = z.object({
email: z.string().email(),
});
}).strict();
export type RequestPasswordResetBody = z.infer<typeof requestPasswordResetBody>;
@ -87,6 +87,7 @@ export async function requestPasswordReset(
status: HttpCode.OK,
});
} catch (e) {
logger.error(e);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -12,10 +12,13 @@ import { eq } from "drizzle-orm";
import { verify } from "@node-rs/argon2";
import { createTOTPKeyURI } from "oslo/otp";
import config from "@server/config";
import logger from "@server/logger";
export const requestTotpSecretBody = z.object({
password: z.string(),
});
export const requestTotpSecretBody = z
.object({
password: z.string()
})
.strict();
export type RequestTotpSecretBody = z.infer<typeof requestTotpSecretBody>;
@ -26,7 +29,7 @@ export type RequestTotpSecretResponse = {
export async function requestTotpSecret(
req: Request,
res: Response,
next: NextFunction,
next: NextFunction
): Promise<any> {
const parsedBody = requestTotpSecretBody.safeParse(req.body);
@ -34,8 +37,8 @@ export async function requestTotpSecret(
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString(),
),
fromError(parsedBody.error).toString()
)
);
}
@ -48,7 +51,7 @@ export async function requestTotpSecret(
memoryCost: 19456,
timeCost: 2,
outputLen: 32,
parallelism: 1,
parallelism: 1
});
if (!validPassword) {
return next(unauthorized());
@ -58,8 +61,8 @@ export async function requestTotpSecret(
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"User has already enabled two-factor authentication",
),
"User has already enabled two-factor authentication"
)
);
}
@ -70,25 +73,26 @@ export async function requestTotpSecret(
await db
.update(users)
.set({
twoFactorSecret: secret,
twoFactorSecret: secret
})
.where(eq(users.userId, user.userId));
return response<RequestTotpSecretResponse>(res, {
data: {
secret: uri,
secret: uri
},
success: true,
error: false,
message: "TOTP secret generated successfully",
status: HttpCode.OK,
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Failed to generate TOTP secret",
),
"Failed to generate TOTP secret"
)
);
}
}

View file

@ -14,12 +14,15 @@ import { passwordSchema } from "@server/auth/passwordSchema";
import { encodeHex } from "oslo/encoding";
import { isWithinExpirationDate } from "oslo";
import { invalidateAllSessions } from "@server/auth";
import logger from "@server/logger";
export const resetPasswordBody = z.object({
token: z.string(),
newPassword: passwordSchema,
code: z.string().optional(),
});
export const resetPasswordBody = z
.object({
token: z.string(),
newPassword: passwordSchema,
code: z.string().optional()
})
.strict();
export type ResetPasswordBody = z.infer<typeof resetPasswordBody>;
@ -30,7 +33,7 @@ export type ResetPasswordResponse = {
export async function resetPassword(
req: Request,
res: Response,
next: NextFunction,
next: NextFunction
): Promise<any> {
const parsedBody = resetPasswordBody.safeParse(req.body);
@ -38,8 +41,8 @@ export async function resetPassword(
return next(
createHttpError(
HttpCode.BAD_REQUEST,
fromError(parsedBody.error).toString(),
),
fromError(parsedBody.error).toString()
)
);
}
@ -47,7 +50,7 @@ export async function resetPassword(
try {
const tokenHash = encodeHex(
await sha256(new TextEncoder().encode(token)),
await sha256(new TextEncoder().encode(token))
);
const resetRequest = await db
@ -63,8 +66,8 @@ export async function resetPassword(
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Invalid or expired password reset token",
),
"Invalid or expired password reset token"
)
);
}
@ -77,8 +80,8 @@ export async function resetPassword(
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"User not found",
),
"User not found"
)
);
}
@ -89,22 +92,22 @@ export async function resetPassword(
success: true,
error: false,
message: "Two-factor authentication required",
status: HttpCode.ACCEPTED,
status: HttpCode.ACCEPTED
});
}
const validOTP = await verifyTotpCode(
code!,
user[0].twoFactorSecret!,
user[0].userId,
user[0].userId
);
if (!validOTP) {
return next(
createHttpError(
HttpCode.BAD_REQUEST,
"Invalid two-factor authentication code",
),
"Invalid two-factor authentication code"
)
);
}
}
@ -129,14 +132,15 @@ export async function resetPassword(
success: true,
error: false,
message: "Password reset successfully",
status: HttpCode.OK,
status: HttpCode.OK
});
} catch (e) {
logger.error(e);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,
"Failed to reset password",
),
"Failed to reset password"
)
);
}
}

View file

@ -1,48 +0,0 @@
import { TimeSpan, createDate } from "oslo";
import { generateRandomString, alphabet } from "oslo/crypto";
import db from "@server/db";
import { users, emailVerificationCodes } from "@server/db/schema";
import { eq } from "drizzle-orm";
import { sendEmail } from "@server/emails";
import config from "@server/config";
import { VerifyEmail } from "@server/emails/templates/VerifyEmailCode";
export async function sendEmailVerificationCode(
email: string,
userId: string
): Promise<void> {
const code = await generateEmailVerificationCode(userId, email);
await sendEmail(
VerifyEmail({
username: email,
verificationCode: code,
verifyLink: `${config.app.base_url}/auth/verify-email`
}),
{
to: email,
from: config.email?.no_reply,
subject: "Verify your email address"
}
);
}
async function generateEmailVerificationCode(
userId: string,
email: string
): Promise<string> {
await db
.delete(emailVerificationCodes)
.where(eq(emailVerificationCodes.userId, userId));
const code = generateRandomString(8, alphabet("0-9"));
await db.insert(emailVerificationCodes).values({
userId,
email,
code,
expiresAt: createDate(new TimeSpan(15, "m")).getTime()
});
return code;
}

View file

@ -8,7 +8,7 @@ import { fromError } from "zod-validation-error";
import createHttpError from "http-errors";
import response from "@server/utils/response";
import { SqliteError } from "better-sqlite3";
import { sendEmailVerificationCode } from "./sendEmailVerificationCode";
import { sendEmailVerificationCode } from "../../auth/sendEmailVerificationCode";
import { passwordSchema } from "@server/auth/passwordSchema";
import { eq } from "drizzle-orm";
import moment from "moment";
@ -20,6 +20,7 @@ import {
} from "@server/auth";
import { ActionsEnum } from "@server/auth/actions";
import config from "@server/config";
import logger from "@server/logger";
export const signupBodySchema = z.object({
email: z.string().email(),
@ -153,6 +154,7 @@ export async function signup(
)
);
} else {
logger.error(e);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -9,10 +9,13 @@ import { User, emailVerificationCodes, users } from "@server/db/schema";
import { eq } from "drizzle-orm";
import { isWithinExpirationDate } from "oslo";
import config from "@server/config";
import logger from "@server/logger";
export const verifyEmailBody = z.object({
code: z.string(),
});
export const verifyEmailBody = z
.object({
code: z.string()
})
.strict();
export type VerifyEmailBody = z.infer<typeof verifyEmailBody>;
@ -66,7 +69,7 @@ export async function verifyEmail(
await db
.update(users)
.set({
emailVerified: true,
emailVerified: true
})
.where(eq(users.userId, user.userId));
} else {
@ -84,10 +87,11 @@ export async function verifyEmail(
message: "Email verified",
status: HttpCode.OK,
data: {
valid,
},
valid
}
});
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,

View file

@ -10,10 +10,13 @@ import { eq } from "drizzle-orm";
import { alphabet, generateRandomString } from "oslo/crypto";
import { hashPassword } from "@server/auth/password";
import { verifyTotpCode } from "@server/auth/2fa";
import logger from "@server/logger";
export const verifyTotpBody = z.object({
code: z.string(),
});
export const verifyTotpBody = z
.object({
code: z.string()
})
.strict();
export type VerifyTotpBody = z.infer<typeof verifyTotpBody>;
@ -82,7 +85,7 @@ export async function verifyTotp(
await db.insert(twoFactorBackupCodes).values({
userId: user.userId,
codeHash: hash,
codeHash: hash
});
}
}
@ -92,16 +95,17 @@ export async function verifyTotp(
return response<VerifyTotpResponse>(res, {
data: {
valid,
...(valid && codes ? { backupCodes: codes } : {}),
...(valid && codes ? { backupCodes: codes } : {})
},
success: true,
error: false,
message: valid
? "Code is valid. Two-factor is now enabled"
: "Code is invalid",
status: HttpCode.OK,
status: HttpCode.OK
});
} catch (error) {
logger.error(error);
return next(
createHttpError(
HttpCode.INTERNAL_SERVER_ERROR,