diff --git a/.run/AdelieStack.xml b/.run/AdelieStack.xml
new file mode 100644
index 0000000..4982401
--- /dev/null
+++ b/.run/AdelieStack.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/lib/dtos/update-email.dto.ts b/src/lib/dtos/settings/update-email.dto.ts
similarity index 100%
rename from src/lib/dtos/update-email.dto.ts
rename to src/lib/dtos/settings/update-email.dto.ts
diff --git a/src/lib/dtos/verify-email.dto.ts b/src/lib/dtos/settings/verify-email.dto.ts
similarity index 82%
rename from src/lib/dtos/verify-email.dto.ts
rename to src/lib/dtos/settings/verify-email.dto.ts
index bd1a395..672372d 100644
--- a/src/lib/dtos/verify-email.dto.ts
+++ b/src/lib/dtos/settings/verify-email.dto.ts
@@ -1,7 +1,7 @@
import { z } from 'zod';
export const verifyEmailDto = z.object({
- token: z.string()
+ code: z.string().length(6),
});
export type VerifyEmailDto = z.infer;
diff --git a/src/lib/server/api/common/middleware/auth.middleware.ts b/src/lib/server/api/common/middleware/auth.middleware.ts
index a6ddf74..42c356b 100644
--- a/src/lib/server/api/common/middleware/auth.middleware.ts
+++ b/src/lib/server/api/common/middleware/auth.middleware.ts
@@ -13,7 +13,9 @@ type UnauthedReturnType = typeof unauthed;
export function authState(state: 'session'): AuthedReturnType;
export function authState(state: 'none'): UnauthedReturnType;
export function authState(state: AuthStates): AuthedReturnType | UnauthedReturnType {
- if (state === 'session') return authed;
+ if (state === 'session') {
+ return authed;
+ }
return unauthed;
}
diff --git a/src/lib/server/api/iam/iam.controller.ts b/src/lib/server/api/iam/iam.controller.ts
index 5a699d8..c3b58ed 100644
--- a/src/lib/server/api/iam/iam.controller.ts
+++ b/src/lib/server/api/iam/iam.controller.ts
@@ -30,6 +30,7 @@ export class IamController extends Controller {
routes() {
return this.controller
.post('/login', openApi(signInEmail), authState('none'), zValidator('json', signinDto), rateLimit({ limit: 3, minutes: 1 }), async (c) => {
+ this.loggerService.log.info(`Login with identifier: ${c.req.valid('json').identifier}`);
const session = await this.loginRequestsService.login(c.req.valid('json'));
await this.sessionsService.setSessionCookie(session);
return c.json({ message: 'welcome' });
diff --git a/src/lib/server/api/users/dtos/update-email.dto.ts b/src/lib/server/api/users/dtos/update-email.dto.ts
deleted file mode 100644
index fd8728a..0000000
--- a/src/lib/server/api/users/dtos/update-email.dto.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { z } from 'zod';
-import { userDto } from './user.dto';
-
-export const updateEmailDto = userDto.pick({ email: true });
-
-export type UpdateEmailDto = z.infer;
diff --git a/src/lib/server/api/users/dtos/verify-email.dto.ts b/src/lib/server/api/users/dtos/verify-email.dto.ts
deleted file mode 100644
index e062f54..0000000
--- a/src/lib/server/api/users/dtos/verify-email.dto.ts
+++ /dev/null
@@ -1,7 +0,0 @@
-import { z } from 'zod';
-
-export const verifyEmailDto = z.object({
- code: z.string().length(6)
-});
-
-export type VerifyEmailDto = z.infer;
diff --git a/src/lib/server/api/users/email-change-requests/email-change-requests.service.ts b/src/lib/server/api/users/email-change-requests/email-change-requests.service.ts
index 3208783..b2e3950 100644
--- a/src/lib/server/api/users/email-change-requests/email-change-requests.service.ts
+++ b/src/lib/server/api/users/email-change-requests/email-change-requests.service.ts
@@ -6,11 +6,13 @@ import { EmailChangeNoticeEmail } from '../../mail/templates/email-change-notice
import { BadRequest } from '../../common/utils/exceptions';
import { UsersRepository } from '../users.repository';
import { VerificationCodesService } from '../../common/services/verification-codes.service';
+import { LoggerService } from '../../common/services/logger.service';
@injectable()
export class EmailChangeRequestsService {
constructor(
private emailChangeRequetsRepository = inject(EmailChangeRequestsRepository),
+ private loggerService = inject(LoggerService),
private verificationCodesService = inject(VerificationCodesService),
private mailerService = inject(MailerService),
private usersRepository = inject(UsersRepository)
@@ -44,19 +46,24 @@ export class EmailChangeRequestsService {
to: requestedEmail,
template: new EmailChangeRequestEmail(verificationCode)
});
+ this.loggerService.log.info(`Email change request sent to ${requestedEmail}`);
}
async verifyEmailChange(userId: string, verificationCode: string) {
// Get the email change request
const emailChangeRequest = await this.emailChangeRequetsRepository.get(userId);
- if (!emailChangeRequest) throw BadRequest('Bad Request');
+ if (!emailChangeRequest) {
+ throw BadRequest('Bad Request');
+ }
// Verify the verification code
const isValid = await this.verificationCodesService.verify({
verificationCode,
hashedVerificationCode: emailChangeRequest.hashedCode
});
- if (!isValid) throw BadRequest('Bad Request');
+ if (!isValid) {
+ throw BadRequest('Bad Request');
+ }
// Update the account's email
await this.usersRepository.update(userId, {
diff --git a/src/lib/server/api/users/users.controller.ts b/src/lib/server/api/users/users.controller.ts
index cf47d8e..2bbc3c3 100644
--- a/src/lib/server/api/users/users.controller.ts
+++ b/src/lib/server/api/users/users.controller.ts
@@ -5,9 +5,9 @@ import { authState } from '../common/middleware/auth.middleware';
import { zValidator } from '@hono/zod-validator';
import { updateUserDto } from './dtos/update-user.dto';
import { EmailChangeRequestsService } from './email-change-requests/email-change-requests.service';
-import { updateEmailDto } from './dtos/update-email.dto';
+import { updateEmailDto } from '$lib/dtos/settings/update-email.dto';
import { UsersRepository } from './users.repository';
-import { verifyEmailDto } from './dtos/verify-email.dto';
+import { verifyEmailDto } from '$lib/dtos/settings/verify-email.dto';
import { rateLimit } from '../common/middleware/rate-limit.middleware';
@injectable()
@@ -39,6 +39,7 @@ export class UsersController extends Controller {
zValidator('json', updateEmailDto),
rateLimit({ limit: 5, minutes: 15 }),
async (c) => {
+ c.var.logger.info(`Request email change: ${c.req.valid('json').email}`);
await this.emailChangeRequestsService.requestEmailChange(
c.var.session.userId,
c.req.valid('json').email
diff --git a/src/routes/(app)/(protected)/settings/account/+page.server.ts b/src/routes/(app)/(protected)/settings/account/+page.server.ts
index b57c05f..3d7bdc2 100644
--- a/src/routes/(app)/(protected)/settings/account/+page.server.ts
+++ b/src/routes/(app)/(protected)/settings/account/+page.server.ts
@@ -1,32 +1,47 @@
import { zod } from "sveltekit-superforms/adapters";
import { fail, setError, superValidate } from "sveltekit-superforms";
import { StatusCodes } from "@/constants/status-codes.js";
-import { updateEmailFormSchema, verifyEmailFormSchema } from "./schemas.js";
+import { updateEmailDto } from "$lib/dtos/settings/update-email.dto.js";
+import { verifyEmailDto } from "$lib/dtos/settings/verify-email.dto.js";
+import { redirect } from "sveltekit-flash-message/server";
+import { notSignedInMessage } from "$lib/utils/flashMessages.js";
-export let load = async (event) => {
- const authedUser = await event.locals.getAuthedUserOrThrow()
+export const load = async (event) => {
+ const { parent } = event;
+ const { authedUser } = await parent();
+
+ if (!authedUser) {
+ throw redirect('/login', notSignedInMessage, event);
+ }
return {
- authedUser,
- updateEmailForm: await superValidate(authedUser, zod(updateEmailFormSchema)),
- verifyEmailForm: await superValidate(zod(verifyEmailFormSchema))
+ updateEmailForm: await superValidate(zod(updateEmailDto), {
+ defaults: {
+ email: authedUser.email
+ }
+ }),
+ verifyEmailForm: await superValidate(zod(verifyEmailDto))
};
};
export const actions = {
updateEmail: async ({ request, locals }) => {
- const updateEmailForm = await superValidate(request, zod(updateEmailFormSchema));
+ const updateEmailForm = await superValidate(request, zod(updateEmailDto));
if (!updateEmailForm.valid) return fail(StatusCodes.BAD_REQUEST, { updateEmailForm })
- const { error } = await locals.api.iam.email.$patch({ json: updateEmailForm.data }).then(locals.parseApiResponse);
+ const { error } = await locals.api.users.me.email.request.$post({ json: updateEmailForm.data }).then(locals.parseApiResponse);
if (error) return setError(updateEmailForm, 'email', error);
return { updateEmailForm }
},
verifyEmail: async ({ request, locals }) => {
- const verifyEmailForm = await superValidate(request, zod(verifyEmailFormSchema));
+ const verifyEmailForm = await superValidate(request, zod(verifyEmailDto));
console.log(verifyEmailForm)
- if (!verifyEmailForm.valid) return fail(StatusCodes.BAD_REQUEST, { verifyEmailForm })
- const { error } = await locals.api.iam.email.verify.$post({ json: verifyEmailForm.data }).then(locals.parseApiResponse);
- if (error) return setError(verifyEmailForm, 'token', error);
+ if (!verifyEmailForm.valid) {
+ return fail(StatusCodes.BAD_REQUEST, { verifyEmailForm });
+ }
+ const { error } = await locals.api.users.me.email.verify.$post({ json: verifyEmailForm.data }).then(locals.parseApiResponse);
+ if (error) {
+ return setError(verifyEmailForm, 'code', error);
+ }
return { verifyEmailForm }
}
};
diff --git a/src/routes/(app)/(protected)/settings/account/schemas.ts b/src/routes/(app)/(protected)/settings/account/schemas.ts
deleted file mode 100644
index 793dabd..0000000
--- a/src/routes/(app)/(protected)/settings/account/schemas.ts
+++ /dev/null
@@ -1,9 +0,0 @@
-import { z } from "zod";
-
-export const verifyEmailFormSchema = z.object({
- token: z.string()
-});
-
-export const updateEmailFormSchema = z.object({
- email: z.string().email()
-});
\ No newline at end of file
diff --git a/src/routes/(app)/(protected)/settings/account/update-email-card.svelte b/src/routes/(app)/(protected)/settings/account/update-email-card.svelte
index 866d889..c3b65be 100644
--- a/src/routes/(app)/(protected)/settings/account/update-email-card.svelte
+++ b/src/routes/(app)/(protected)/settings/account/update-email-card.svelte
@@ -1,12 +1,3 @@
-
-