mirror of
https://github.com/fosrl/pangolin.git
synced 2025-08-03 09:34:48 +02:00
fix issues from test deploy
This commit is contained in:
parent
3fb3be1f1e
commit
ce5df3b0b9
92 changed files with 1410 additions and 1019 deletions
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue