Fixing api client path, signup api on server and kit server side.

This commit is contained in:
Bradley Shellnut 2025-01-01 20:43:19 -08:00
parent 64f0e10715
commit ae21011b3f
7 changed files with 42 additions and 25 deletions

View file

@ -11,11 +11,13 @@ import { browserSessions } from './common/middleware/browser-session.middleware'
import { IamController } from './iam/iam.controller'; import { IamController } from './iam/iam.controller';
import configureOpenAPI from './configure-open-api'; import configureOpenAPI from './configure-open-api';
import { pinoLogger } from './common/middleware/pino-logger.middleware'; import { pinoLogger } from './common/middleware/pino-logger.middleware';
import { SignupController } from './signup/signup.controller';
@injectable() @injectable()
export class ApplicationController extends RootController { export class ApplicationController extends RootController {
constructor( constructor(
private iamController = inject(IamController), private iamController = inject(IamController),
private signupController = inject(SignupController),
private usersController = inject(UsersController) private usersController = inject(UsersController)
) { ) {
super(); super();
@ -48,7 +50,8 @@ export class ApplicationController extends RootController {
.use(pinoLogger()) .use(pinoLogger())
.route('/', this.routes()) .route('/', this.routes())
.route('/iam', this.iamController.routes()) .route('/iam', this.iamController.routes())
.route('/users', this.usersController.routes()); .route('/users', this.usersController.routes())
.route('/signup', this.signupController.routes());
configureOpenAPI(app); configureOpenAPI(app);
return app; return app;

View file

@ -1,8 +1,10 @@
import { Hono } from 'hono'; import { Hono } from 'hono';
import type { SessionDto } from '../../iam/sessions/dtos/session.dto'; import type { SessionDto } from '../../iam/sessions/dtos/session.dto';
import type { PinoLogger } from 'hono-pino';
export type HonoEnv = { export type HonoEnv = {
Variables: { Variables: {
logger: PinoLogger;
session: SessionDto | null; session: SessionDto | null;
browserSessionId: string; browserSessionId: string;
requestId: string; requestId: string;

View file

@ -1,19 +1,16 @@
import { limiter } from '$lib/server/api/common/middleware/rate-limit.middleware'; import { rateLimit } 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 { SessionsService } from '$lib/server/api/iam/sessions/sessions.service'; 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 { UsersService } from '$lib/server/api/users/users.service';
import { zValidator } from '@hono/zod-validator'; import { zValidator } from '@hono/zod-validator';
import { inject, injectable } from '@needle-di/core'; import { inject, injectable } from '@needle-di/core';
import { authState } from '../common/middleware/auth.middleware'; import { authState } from '../common/middleware/auth.middleware';
import { Controller } from '../common/factories/controllers.factory'; import { Controller } from '../common/factories/controllers.factory';
import { signupUsernameEmailDto } from './dtos/signup-username-email.dto';
@injectable() @injectable()
export class SignupController extends Controller { export class SignupController extends Controller {
constructor( constructor(
private usersService = inject(UsersService), private usersService = inject(UsersService),
private loginRequestService = inject(LoginRequestsService),
private sessionsService = inject(SessionsService), private sessionsService = inject(SessionsService),
) { ) {
super(); super();
@ -25,25 +22,19 @@ export class SignupController extends Controller {
'/', '/',
authState('none'), authState('none'),
zValidator('json', signupUsernameEmailDto), zValidator('json', signupUsernameEmailDto),
limiter({ limit: 10, minutes: 60 }), rateLimit({ limit: 10, minutes: 60 }),
async (c) => { async (c) => {
const { email, username, password, confirm_password } = await c.req.valid('json'); const { email, username, password } = await c.req.valid('json');
const existingUser = await this.usersService.findOneByUsername(username); c.var.logger.info(`Signup with email: ${email} username: ${username}`);
const user = await this.usersService.createWithPassword(username, password, email);
if (existingUser) {
return c.body('User already exists', 400);
}
const user = await this.usersService.createWithPassword({ email, username, password, confirm_password });
c.var.logger.info(`Created user: ${user?.id}`);
if (!user) { if (!user) {
return c.body('Failed to create user', 500); return c.body('Failed to create user', 500);
} }
const session = await this.loginRequestService.createUserSession(user.id, c.req, false, false); const session = await this.sessionsService.createSession(user.id);
const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt); await this.sessionsService.setSessionCookie(session);
console.log('set cookie', sessionCookie);
setSessionCookie(c, sessionCookie);
return c.json({ message: 'ok' }); return c.json({ message: 'ok' });
} }
); );

View file

@ -24,6 +24,10 @@ export class UsersRepository extends DrizzleRepository {
return db.select().from(users_table).where(eq(users_table.email, email)).then(takeFirst); 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) { async findOneByIdOrThrow(id: string, db = this.drizzle.db) {
const user = await this.findOneById(id, db); const user = await this.findOneById(id, db);
if (!user) throw NotFound('User not found'); if (!user) throw NotFound('User not found');

View file

@ -8,6 +8,7 @@ import { CredentialsType } from './tables/credentials.table';
import { UsersRepository } from './users.repository'; import { UsersRepository } from './users.repository';
import { UserRolesService } from './user_roles.service'; import { UserRolesService } from './user_roles.service';
import { RoleName } from '../roles/tables/roles.table'; import { RoleName } from '../roles/tables/roles.table';
import { BadRequest } from '../common/utils/exceptions';
@injectable() @injectable()
export class UsersService { export class UsersService {
@ -31,8 +32,18 @@ export class UsersService {
return this.usersRepository.create({ avatar: null, email, username: email }); return this.usersRepository.create({ avatar: null, email, username: email });
} }
async createWithPassword(username: string, password: string, email?: string) { async createWithPassword(username: string, password: string, email?: string | undefined) {
const hashedPassword = await this.tokenService.createHashedToken(password); 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) => { return await this.drizzleService.db.transaction(async (trx) => {
const createdUser = await this.usersRepository.create( const createdUser = await this.usersRepository.create(
{ username, email: email || '', avatar: null }, { username, email: email || '', avatar: null },
@ -43,6 +54,8 @@ export class UsersService {
return null; return null;
} }
const hashedPassword = await this.tokenService.createHashedToken(password);
const credentials = await this.credentialsRepository.create( const credentials = await this.credentialsRepository.create(
{ {
user_id: createdUser.id, user_id: createdUser.id,

View file

@ -1,7 +1,7 @@
import type { ApiRoutes } from "../server/api"; import type { ApiRoutes } from "../server/api";
import { hc, type ClientRequestOptions, type ClientResponse } from "hono/client"; import { hc, type ClientRequestOptions, type ClientResponse } from "hono/client";
export const honoClient = (options?: ClientRequestOptions) => hc<ApiRoutes>('/', options).api; export const honoClient = (options?: ClientRequestOptions) => hc<ApiRoutes>('/', options);
export async function parseClientResponse<T>(response: ClientResponse<T>) { export async function parseClientResponse<T>(response: ClientResponse<T>) {
if (response.ok) { if (response.ok) {

View file

@ -44,10 +44,14 @@ export const actions: Actions = {
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); const { error } = await locals.api.signup.$post({ json: form.data }).then(locals.parseApiResponse);
if (error) { if (error) {
console.log('error', error);
form.data.password = ''; 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) { if (!form.valid) {