diff --git a/src/lib/server/api/application.controller.ts b/src/lib/server/api/application.controller.ts index dc1d811..15d8330 100644 --- a/src/lib/server/api/application.controller.ts +++ b/src/lib/server/api/application.controller.ts @@ -11,11 +11,13 @@ import { browserSessions } from './common/middleware/browser-session.middleware' import { IamController } from './iam/iam.controller'; import configureOpenAPI from './configure-open-api'; import { pinoLogger } from './common/middleware/pino-logger.middleware'; +import { SignupController } from './signup/signup.controller'; @injectable() export class ApplicationController extends RootController { constructor( private iamController = inject(IamController), + private signupController = inject(SignupController), private usersController = inject(UsersController) ) { super(); @@ -48,7 +50,8 @@ export class ApplicationController extends RootController { .use(pinoLogger()) .route('/', this.routes()) .route('/iam', this.iamController.routes()) - .route('/users', this.usersController.routes()); + .route('/users', this.usersController.routes()) + .route('/signup', this.signupController.routes()); configureOpenAPI(app); return app; diff --git a/src/lib/server/api/common/utils/hono.ts b/src/lib/server/api/common/utils/hono.ts index 1b13fc0..8eb6ace 100644 --- a/src/lib/server/api/common/utils/hono.ts +++ b/src/lib/server/api/common/utils/hono.ts @@ -1,8 +1,10 @@ import { Hono } from 'hono'; import type { SessionDto } from '../../iam/sessions/dtos/session.dto'; +import type { PinoLogger } from 'hono-pino'; export type HonoEnv = { Variables: { + logger: PinoLogger; session: SessionDto | null; browserSessionId: string; requestId: string; diff --git a/src/lib/server/api/signup/signup.controller.ts b/src/lib/server/api/signup/signup.controller.ts index 08c7b0d..d7fe008 100644 --- a/src/lib/server/api/signup/signup.controller.ts +++ b/src/lib/server/api/signup/signup.controller.ts @@ -1,19 +1,16 @@ -import { limiter } from '$lib/server/api/common/middleware/rate-limit.middleware'; -import { cookieExpiresAt, createSessionTokenCookie, setSessionCookie } from '$lib/server/api/common/utils/cookies'; -import { signupUsernameEmailDto } from '$lib/server/api/dtos/signup-username-email.dto'; +import { rateLimit } from '$lib/server/api/common/middleware/rate-limit.middleware'; import { SessionsService } from '$lib/server/api/iam/sessions/sessions.service'; -import { LoginRequestsService } from '$lib/server/api/login/loginrequest.service'; import { UsersService } from '$lib/server/api/users/users.service'; import { zValidator } from '@hono/zod-validator'; import { inject, injectable } from '@needle-di/core'; import { authState } from '../common/middleware/auth.middleware'; import { Controller } from '../common/factories/controllers.factory'; +import { signupUsernameEmailDto } from './dtos/signup-username-email.dto'; @injectable() export class SignupController extends Controller { constructor( private usersService = inject(UsersService), - private loginRequestService = inject(LoginRequestsService), private sessionsService = inject(SessionsService), ) { super(); @@ -25,25 +22,19 @@ export class SignupController extends Controller { '/', authState('none'), zValidator('json', signupUsernameEmailDto), - limiter({ limit: 10, minutes: 60 }), + rateLimit({ limit: 10, minutes: 60 }), async (c) => { - const { email, username, password, confirm_password } = await c.req.valid('json'); - const existingUser = await this.usersService.findOneByUsername(username); - - if (existingUser) { - return c.body('User already exists', 400); - } - - const user = await this.usersService.createWithPassword({ email, username, password, confirm_password }); + const { email, username, password } = await c.req.valid('json'); + c.var.logger.info(`Signup with email: ${email} username: ${username}`); + const user = await this.usersService.createWithPassword(username, password, email); + c.var.logger.info(`Created user: ${user?.id}`); if (!user) { return c.body('Failed to create user', 500); } - const session = await this.loginRequestService.createUserSession(user.id, c.req, false, false); - const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt); - console.log('set cookie', sessionCookie); - setSessionCookie(c, sessionCookie); + const session = await this.sessionsService.createSession(user.id); + await this.sessionsService.setSessionCookie(session); return c.json({ message: 'ok' }); } ); diff --git a/src/lib/server/api/users/users.repository.ts b/src/lib/server/api/users/users.repository.ts index 71881a2..869929b 100644 --- a/src/lib/server/api/users/users.repository.ts +++ b/src/lib/server/api/users/users.repository.ts @@ -24,6 +24,10 @@ export class UsersRepository extends DrizzleRepository { return db.select().from(users_table).where(eq(users_table.email, email)).then(takeFirst); } + async findOneByUsername(username: string, db = this.drizzle.db) { + return db.select().from(users_table).where(eq(users_table.username, username)).then(takeFirst); + } + async findOneByIdOrThrow(id: string, db = this.drizzle.db) { const user = await this.findOneById(id, db); if (!user) throw NotFound('User not found'); diff --git a/src/lib/server/api/users/users.service.ts b/src/lib/server/api/users/users.service.ts index e5272d3..d3e5a96 100644 --- a/src/lib/server/api/users/users.service.ts +++ b/src/lib/server/api/users/users.service.ts @@ -8,6 +8,7 @@ import { CredentialsType } from './tables/credentials.table'; import { UsersRepository } from './users.repository'; import { UserRolesService } from './user_roles.service'; import { RoleName } from '../roles/tables/roles.table'; +import { BadRequest } from '../common/utils/exceptions'; @injectable() export class UsersService { @@ -31,8 +32,18 @@ export class UsersService { return this.usersRepository.create({ avatar: null, email, username: email }); } - async createWithPassword(username: string, password: string, email?: string) { - const hashedPassword = await this.tokenService.createHashedToken(password); + async createWithPassword(username: string, password: string, email?: string | undefined) { + const existingUsername = await this.usersRepository.findOneByUsername(username); + if (existingUsername) { + throw BadRequest('Could not create user'); + } + if (email) { + const existingEmail = await this.usersRepository.findOneByEmail(email); + if (existingUsername || existingEmail) { + throw BadRequest('Could not create user'); + } + } + return await this.drizzleService.db.transaction(async (trx) => { const createdUser = await this.usersRepository.create( { username, email: email || '', avatar: null }, @@ -43,6 +54,8 @@ export class UsersService { return null; } + const hashedPassword = await this.tokenService.createHashedToken(password); + const credentials = await this.credentialsRepository.create( { user_id: createdUser.id, diff --git a/src/lib/utils/api.ts b/src/lib/utils/api.ts index 7f5d4ec..a0f5879 100644 --- a/src/lib/utils/api.ts +++ b/src/lib/utils/api.ts @@ -1,7 +1,7 @@ import type { ApiRoutes } from "../server/api"; import { hc, type ClientRequestOptions, type ClientResponse } from "hono/client"; -export const honoClient = (options?: ClientRequestOptions) => hc('/', options).api; +export const honoClient = (options?: ClientRequestOptions) => hc('/', options); export async function parseClientResponse(response: ClientResponse) { if (response.ok) { diff --git a/src/routes/(auth)/signup/+page.server.ts b/src/routes/(auth)/signup/+page.server.ts index 80b434c..eca263c 100644 --- a/src/routes/(auth)/signup/+page.server.ts +++ b/src/routes/(auth)/signup/+page.server.ts @@ -42,12 +42,16 @@ export const actions: Actions = { throw redirect('/', message, event); } - const form = await superValidate(event, zod(signupUsernameEmailDto)); + const form = await superValidate(event, zod(signupUsernameEmailDto)); + + console.log('form data', form.data); const { error } = await locals.api.signup.$post({ json: form.data }).then(locals.parseApiResponse); - if (error) { + if (error) { + console.log('error', error); form.data.password = ''; - return setError(form, 'username', 'Unable to log in.'); + form.data.confirm_password = ''; + return setError(form, 'username', 'Unable to sign up.'); } if (!form.valid) {