mirror of
https://github.com/PretendoNetwork/website.git
synced 2026-09-13 19:37:37 -05:00
Merge pull request #456 from PretendoNetwork/http-errors
Handle HTTP Errors better in reset password endpoint
This commit is contained in:
@@ -21,11 +21,11 @@ const errors: Record<string, ApiErrorCodes> = {
|
||||
'INVALID_ARGUMENT: Two or more punctuation characters cannot be used in a row': 'USERNAME_INVALID_CHARS',
|
||||
'INVALID_ARGUMENT: PNID already in use': 'USERNAME_IN_USE',
|
||||
'INVALID_ARGUMENT: Mii name too long': 'MIINAME_TOO_LONG',
|
||||
'INVALID_ARGUMENT: Password must be between 6 and 16 characters long': 'INVALID_PASSWORD_INPUT',
|
||||
'INVALID_ARGUMENT: Password cannot be the same as username': 'INVALID_PASSWORD_INPUT',
|
||||
'INVALID_ARGUMENT: Password must have combination of letters, numbers, and/or punctuation characters': 'INVALID_PASSWORD_INPUT',
|
||||
'INVALID_ARGUMENT: Password may not have 3 repeating characters': 'INVALID_PASSWORD_INPUT',
|
||||
'INVALID_ARGUMENT: Passwords do not match': 'INVALID_PASSWORD_NO_MATCH'
|
||||
'INVALID_ARGUMENT: Password must be between 6 and 16 characters long': 'PASSWORD_INVALID_LENGTH',
|
||||
'INVALID_ARGUMENT: Password cannot be the same as username': 'PASSWORD_NOT_USERNAME',
|
||||
'INVALID_ARGUMENT: Password must have combination of letters, numbers, and/or punctuation characters': 'PASSWORD_NEEDS_CHARS',
|
||||
'INVALID_ARGUMENT: Password may not have 3 repeating characters': 'PASSWORD_REPEATED_CHARS',
|
||||
'INVALID_ARGUMENT: Passwords do not match': 'PASSWORDS_DO_NOT_MATCH'
|
||||
};
|
||||
|
||||
function getCutoffDateForAge(today: Date, age: number) {
|
||||
|
||||
@@ -1,19 +1,45 @@
|
||||
import { ResetPasswordSchema } from '~~/shared/api-types';
|
||||
import type { ApiErrorCodes } from '~~/shared/errors';
|
||||
|
||||
const errors: Record<string, ApiErrorCodes> = {
|
||||
'Missing token': 'INVALID_INPUT',
|
||||
'Invalid token': 'INVALID_INPUT',
|
||||
'Token expired': 'INVALID_INPUT',
|
||||
'Invalid token. No user found': 'INVALID_INPUT',
|
||||
'Must enter a password': 'PASSWORD_INVALID_LENGTH',
|
||||
'Password is too long': 'PASSWORD_INVALID_LENGTH',
|
||||
'Password is too short': 'PASSWORD_INVALID_LENGTH',
|
||||
'Password cannot be the same as username': 'PASSWORD_NOT_USERNAME',
|
||||
'Password must have combination of letters, numbers, and/or punctuation characters': 'PASSWORD_NEEDS_CHARS',
|
||||
'Password may not have 3 repeating characters': 'PASSWORD_REPEATED_CHARS',
|
||||
'Passwords do not match': 'PASSWORDS_DO_NOT_MATCH'
|
||||
};
|
||||
|
||||
export default defineEventHandler(async (event): Promise<void> => {
|
||||
const body = await readZodBody(event, ResetPasswordSchema);
|
||||
const apiFetch = useHttpApi(event);
|
||||
|
||||
// The GRPC version requires a login token, which the user doesnt have when resetting password, so we're using the HTTP api
|
||||
await apiFetch('/v1/reset-password', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
password: body.password,
|
||||
password_confirm: body.passwordConfirm,
|
||||
token: body.resetToken
|
||||
}),
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
try {
|
||||
await apiFetch('/v1/reset-password', {
|
||||
method: 'POST',
|
||||
body: JSON.stringify({
|
||||
password: body.password,
|
||||
password_confirm: body.passwordConfirm,
|
||||
token: body.resetToken
|
||||
}),
|
||||
headers: {
|
||||
'Content-type': 'application/json'
|
||||
}
|
||||
});
|
||||
} catch (err: any) {
|
||||
const data = err?.data;
|
||||
let errorCode: ApiErrorCodes = 'UNHANDLED_ERROR';
|
||||
|
||||
if (typeof data === 'object' && data?.error) {
|
||||
errorCode = errors[data.error] ?? 'UNHANDLED_ERROR';
|
||||
}
|
||||
});
|
||||
|
||||
throw createApiError(errorCode);
|
||||
}
|
||||
});
|
||||
|
||||
18
server/plugins/log-errors.ts
Normal file
18
server/plugins/log-errors.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import type { H3Error } from 'h3';
|
||||
|
||||
export default defineNitroPlugin((nitroApp) => {
|
||||
nitroApp.hooks.hook('error', (err, { event }) => {
|
||||
const error = err as Partial<H3Error>;
|
||||
const statusCode = error.statusCode ?? 500;
|
||||
|
||||
// Skip expected client errors (404, 401, 422, redirects, ...).
|
||||
if (statusCode < 500) {
|
||||
return;
|
||||
}
|
||||
|
||||
const where = event ? `${event.method} ${event.path}` : 'non-request';
|
||||
const tag = error.unhandled ? '[unhandled]' : '';
|
||||
|
||||
console.error(`[server error] ${tag} ${statusCode} ${where}:`, err);
|
||||
});
|
||||
});
|
||||
@@ -27,7 +27,11 @@ export function useHttpApi(event: H3Event, token?: string): HttpApiFetch {
|
||||
if (response.statusCode >= 400) {
|
||||
const err = new Error(`Request failed with ${response.statusCode}`);
|
||||
try {
|
||||
(err as any).data = await response.body.text();
|
||||
if (response.headers['content-type']?.includes('application/json')) {
|
||||
(err as any).data = await response.body.json();
|
||||
} else {
|
||||
(err as any).data = await response.body.text();
|
||||
}
|
||||
} catch {
|
||||
// It's already errored, we don't need to know the body
|
||||
}
|
||||
|
||||
@@ -16,8 +16,11 @@ const apiErrorCodes = {
|
||||
USERNAME_INVALID_CHARS: 'Username contains invalid characters',
|
||||
USERNAME_IN_USE: 'PNID already in use',
|
||||
MIINAME_TOO_LONG: 'Mii name too long',
|
||||
INVALID_PASSWORD_INPUT: 'Password must be between 6 and 16 characters long',
|
||||
INVALID_PASSWORD_NO_MATCH: 'Passwords do not match',
|
||||
PASSWORD_INVALID_LENGTH: 'Password must be between 6 and 16 characters long',
|
||||
PASSWORD_NOT_USERNAME: 'Password cannot be the same as your username',
|
||||
PASSWORD_NEEDS_CHARS: 'Password must have combination of letters, numbers, and/or punctuation characters',
|
||||
PASSWORDS_DO_NOT_MATCH: 'Passwords do not match',
|
||||
PASSWORD_REPEATED_CHARS: 'Password may not have 3 repeating characters',
|
||||
ACCOUNT_DELETED: 'Account has been deleted',
|
||||
INVALID_ACCESS_LEVEL: 'Invalid access level',
|
||||
BANNED: 'Account is banned',
|
||||
@@ -45,8 +48,11 @@ export const apiErrorCodeStatus: Record<ApiErrorCodes, number> = {
|
||||
UNDER_THIRTEEN: 400,
|
||||
ACCOUNT_DELETED: 400,
|
||||
INVALID_EMAIL: 400,
|
||||
INVALID_PASSWORD_INPUT: 400,
|
||||
INVALID_PASSWORD_NO_MATCH: 400,
|
||||
PASSWORD_INVALID_LENGTH: 400,
|
||||
PASSWORD_NOT_USERNAME: 400,
|
||||
PASSWORD_NEEDS_CHARS: 400,
|
||||
PASSWORDS_DO_NOT_MATCH: 400,
|
||||
PASSWORD_REPEATED_CHARS: 400,
|
||||
MIINAME_TOO_LONG: 400,
|
||||
USERNAME_IN_USE: 400,
|
||||
USERNAME_INVALID_CHARS: 400,
|
||||
|
||||
@@ -3,9 +3,11 @@ import { watchImmediate } from '@vueuse/core';
|
||||
import DefaultLayout from './layouts/default.vue';
|
||||
const { error } = defineProps<{ error: any }>();
|
||||
|
||||
watchImmediate([error], () => {
|
||||
console.error(error);
|
||||
});
|
||||
if (import.meta.client) {
|
||||
watchImmediate([() => error], () => {
|
||||
console.error(error);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
Reference in New Issue
Block a user