Merge pull request #33 from BradNut/needle-di

Change from TSyringe to Needle-DI.
This commit is contained in:
Bradley Shellnut 2024-11-11 21:45:29 -08:00 committed by GitHub
commit 289775caf2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
46 changed files with 179 additions and 295 deletions

View file

@ -1,15 +1,14 @@
import 'reflect-metadata';
import {StatusCodes} from '$lib/constants/status-codes'; import {StatusCodes} from '$lib/constants/status-codes';
import {Controller} from '$lib/server/api/common/types/controller'; import {Controller} from '$lib/server/api/common/types/controller';
import {allCollections, getCollectionByCUID, numberOfCollections} from '$lib/server/api/controllers/collection.routes'; import {allCollections, getCollectionByCUID, numberOfCollections} from '$lib/server/api/controllers/collection.routes';
import {CollectionsService} from '$lib/server/api/services/collections.service'; import {CollectionsService} from '$lib/server/api/services/collections.service';
import {openApi} from 'hono-zod-openapi'; import {openApi} from 'hono-zod-openapi';
import {inject, injectable} from 'tsyringe'; import { injectable, inject } from '@needle-di/core';
import {requireAuth} from '../middleware/require-auth.middleware'; import {requireAuth} from '../middleware/require-auth.middleware';
@injectable() @injectable()
export class CollectionController extends Controller { export class CollectionController extends Controller {
constructor(@inject(CollectionsService) private readonly collectionsService: CollectionsService) { constructor(private collectionsService = inject(CollectionsService)) {
super(); super();
} }

View file

@ -11,16 +11,16 @@ import {LoginRequestsService} from '$lib/server/api/services/loginrequest.servic
import {SessionsService} from '$lib/server/api/services/sessions.service'; import {SessionsService} from '$lib/server/api/services/sessions.service';
import {zValidator} from '@hono/zod-validator'; import {zValidator} from '@hono/zod-validator';
import {openApi} from 'hono-zod-openapi'; import {openApi} from 'hono-zod-openapi';
import {inject, injectable} from 'tsyringe'; import { injectable, inject } from "@needle-di/core";
import {requireAuth} from '../middleware/require-auth.middleware'; import {requireAuth} from '../middleware/require-auth.middleware';
import {iam, logout, updateEmail, updatePassword, updateProfile, verifyPassword} from './iam.routes'; import {iam, logout, updateEmail, updatePassword, updateProfile, verifyPassword} from './iam.routes';
@injectable() @injectable()
export class IamController extends Controller { export class IamController extends Controller {
constructor( constructor(
@inject(IamService) private readonly iamService: IamService, private iamService = inject(IamService),
@inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService, private loginRequestService = inject(LoginRequestsService),
@inject(SessionsService) private sessionsService: SessionsService, private sessionsService = inject(SessionsService),
) { ) {
super(); super();
} }

View file

@ -1,11 +1,10 @@
import 'reflect-metadata';
import {Controller} from '$lib/server/api/common/types/controller'; import {Controller} from '$lib/server/api/common/types/controller';
import {cookieExpiresAt, createSessionTokenCookie, setSessionCookie} from '$lib/server/api/common/utils/cookies'; import {cookieExpiresAt, createSessionTokenCookie, setSessionCookie} from '$lib/server/api/common/utils/cookies';
import {signinUsernameDto} from '$lib/server/api/dtos/signin-username.dto'; import {signinUsernameDto} from '$lib/server/api/dtos/signin-username.dto';
import {SessionsService} from '$lib/server/api/services/sessions.service'; import {SessionsService} from '$lib/server/api/services/sessions.service';
import {zValidator} from '@hono/zod-validator'; import {zValidator} from '@hono/zod-validator';
import {openApi} from 'hono-zod-openapi'; import {openApi} from 'hono-zod-openapi';
import {inject, injectable} from 'tsyringe'; import { inject, injectable } from '@needle-di/core';
import {limiter} from '../middleware/rate-limiter.middleware'; import {limiter} from '../middleware/rate-limiter.middleware';
import {LoginRequestsService} from '../services/loginrequest.service'; import {LoginRequestsService} from '../services/loginrequest.service';
import {signinUsername} from './login.routes'; import {signinUsername} from './login.routes';
@ -13,8 +12,8 @@ import {signinUsername} from './login.routes';
@injectable() @injectable()
export class LoginController extends Controller { export class LoginController extends Controller {
constructor( constructor(
@inject(LoginRequestsService) private readonly loginRequestsService: LoginRequestsService, private loginRequestsService = inject(LoginRequestsService),
@inject(SessionsService) private luciaService: SessionsService, private sessionsService = inject(SessionsService),
) { ) {
super(); super();
} }

View file

@ -1,4 +1,3 @@
import 'reflect-metadata';
import {StatusCodes} from '$lib/constants/status-codes'; import {StatusCodes} from '$lib/constants/status-codes';
import {Controller} from '$lib/server/api/common/types/controller'; import {Controller} from '$lib/server/api/common/types/controller';
import {verifyTotpDto} from '$lib/server/api/dtos/verify-totp.dto'; import {verifyTotpDto} from '$lib/server/api/dtos/verify-totp.dto';
@ -6,16 +5,16 @@ import {RecoveryCodesService} from '$lib/server/api/services/recovery-codes.serv
import {TotpService} from '$lib/server/api/services/totp.service'; import {TotpService} from '$lib/server/api/services/totp.service';
import {UsersService} from '$lib/server/api/services/users.service'; import {UsersService} from '$lib/server/api/services/users.service';
import {zValidator} from '@hono/zod-validator'; import {zValidator} from '@hono/zod-validator';
import {inject, injectable} from 'tsyringe'; import { inject, injectable } from '@needle-di/core';
import {CredentialsType} from '../databases/postgres/tables'; import {CredentialsType} from '../databases/postgres/tables';
import {requireAuth} from '../middleware/require-auth.middleware'; import {requireAuth} from '../middleware/require-auth.middleware';
@injectable() @injectable()
export class MfaController extends Controller { export class MfaController extends Controller {
constructor( constructor(
@inject(RecoveryCodesService) private readonly recoveryCodesService: RecoveryCodesService, private recoveryCodesService = inject(RecoveryCodesService),
@inject(TotpService) private readonly totpService: TotpService, private totpService = inject(TotpService),
@inject(UsersService) private readonly usersService: UsersService, private usersService = inject(UsersService),
) { ) {
super(); super();
} }

View file

@ -1,4 +1,3 @@
import 'reflect-metadata';
import {Controller} from '$lib/server/api/common/types/controller'; import {Controller} from '$lib/server/api/common/types/controller';
import type {OAuthUser} from '$lib/server/api/common/types/oauth'; import type {OAuthUser} from '$lib/server/api/common/types/oauth';
import {cookieExpiresAt, createSessionTokenCookie, setSessionCookie} from '$lib/server/api/common/utils/cookies'; import {cookieExpiresAt, createSessionTokenCookie, setSessionCookie} from '$lib/server/api/common/utils/cookies';
@ -7,14 +6,13 @@ import {SessionsService} from '$lib/server/api/services/sessions.service';
import {github, google} from '$lib/server/auth'; import {github, google} from '$lib/server/auth';
import {OAuth2RequestError} from 'arctic'; import {OAuth2RequestError} from 'arctic';
import {getCookie} from 'hono/cookie'; import {getCookie} from 'hono/cookie';
import { injectable, inject } from "@needle-di/core";
import {inject, injectable} from 'tsyringe';
@injectable() @injectable()
export class OAuthController extends Controller { export class OAuthController extends Controller {
constructor( constructor(
@inject(SessionsService) private sessionsService: SessionsService, private oauthService = inject(OAuthService),
@inject(OAuthService) private oauthService: OAuthService, private sessionsService = inject(SessionsService),
) { ) {
super(); super();
} }

View file

@ -6,16 +6,15 @@ import {LoginRequestsService} from '$lib/server/api/services/loginrequest.servic
import {SessionsService} from '$lib/server/api/services/sessions.service'; import {SessionsService} from '$lib/server/api/services/sessions.service';
import {UsersService} from '$lib/server/api/services/users.service'; import {UsersService} from '$lib/server/api/services/users.service';
import {zValidator} from '@hono/zod-validator'; import {zValidator} from '@hono/zod-validator';
import {setCookie} from 'hono/cookie'; import {inject, injectable} from '@needle-di/core';
import {TimeSpan} from 'oslo'; import {cookieExpiresAt, createSessionTokenCookie, setSessionCookie} from "$lib/server/api/common/utils/cookies";
import {inject, injectable} from 'tsyringe';
@injectable() @injectable()
export class SignupController extends Controller { export class SignupController extends Controller {
constructor( constructor(
@inject(UsersService) private readonly usersService: UsersService, private usersService = inject(UsersService),
@inject(LoginRequestsService) private readonly loginRequestService: LoginRequestsService, private loginRequestService = inject(LoginRequestsService),
@inject(SessionsService) private luciaService: SessionsService, private sessionsService = inject(SessionsService),
) { ) {
super(); super();
} }
@ -36,20 +35,9 @@ export class SignupController extends Controller {
} }
const session = await this.loginRequestService.createUserSession(user.id, c.req, undefined); const session = await this.loginRequestService.createUserSession(user.id, c.req, undefined);
const sessionCookie = this.luciaService.lucia.createSessionCookie(session.id); const sessionCookie = createSessionTokenCookie(session.id, cookieExpiresAt);
console.log('set cookie', sessionCookie); console.log('set cookie', sessionCookie);
setCookie(c, sessionCookie.name, sessionCookie.value, { setSessionCookie(c, sessionCookie);
path: sessionCookie.attributes.path,
maxAge:
sessionCookie?.attributes?.maxAge && sessionCookie?.attributes?.maxAge < new TimeSpan(365, 'd').seconds()
? sessionCookie.attributes.maxAge
: new TimeSpan(2, 'w').seconds(),
domain: sessionCookie.attributes.domain,
sameSite: sessionCookie.attributes.sameSite as any,
secure: sessionCookie.attributes.secure,
httpOnly: sessionCookie.attributes.httpOnly,
expires: sessionCookie.attributes.expires,
});
return c.json({ message: 'ok' }); return c.json({ message: 'ok' });
}); });
} }

View file

@ -1,12 +1,11 @@
import 'reflect-metadata';
import {Controller} from '$lib/server/api/common/types/controller'; import {Controller} from '$lib/server/api/common/types/controller';
import {UsersService} from '$lib/server/api/services/users.service'; import {UsersService} from '$lib/server/api/services/users.service';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {requireAuth} from '../middleware/require-auth.middleware'; import {requireAuth} from '../middleware/require-auth.middleware';
@injectable() @injectable()
export class UserController extends Controller { export class UserController extends Controller {
constructor(@inject(UsersService) private readonly usersService: UsersService) { constructor(private usersService = inject(UsersService)) {
super(); super();
} }

View file

@ -1,12 +1,11 @@
import 'reflect-metadata'
import {Controller} from '$lib/server/api/common/types/controller' import {Controller} from '$lib/server/api/common/types/controller'
import {WishlistsService} from '$lib/server/api/services/wishlists.service' import {WishlistsService} from '$lib/server/api/services/wishlists.service'
import {inject, injectable} from 'tsyringe' import {inject, injectable} from '@needle-di/core'
import {requireAuth} from '../middleware/require-auth.middleware' import {requireAuth} from '../middleware/require-auth.middleware'
@injectable() @injectable()
export class WishlistController extends Controller { export class WishlistController extends Controller {
constructor(@inject(WishlistsService) private readonly wishlistsService: WishlistsService) { constructor(private wishlistsService = inject(WishlistsService)) {
super() super()
} }

View file

@ -9,7 +9,8 @@ import {WishlistController} from '$lib/server/api/controllers/wishlist.controlle
import {AuthCleanupJobs} from '$lib/server/api/jobs/auth-cleanup.job'; import {AuthCleanupJobs} from '$lib/server/api/jobs/auth-cleanup.job';
import {extendZodWithOpenApi} from 'hono-zod-openapi'; import {extendZodWithOpenApi} from 'hono-zod-openapi';
import {hc} from 'hono/client'; import {hc} from 'hono/client';
import {container} from 'tsyringe'; // import {container} from 'tsyringe';
import { Container } from '@needle-di/core';
import {z} from 'zod'; import {z} from 'zod';
import {config} from './common/config'; import {config} from './common/config';
import {IamController} from './controllers/iam.controller'; import {IamController} from './controllers/iam.controller';
@ -17,20 +18,22 @@ import {LoginController} from './controllers/login.controller';
extendZodWithOpenApi(z); extendZodWithOpenApi(z);
const container = new Container();
export const app = createApp(); export const app = createApp();
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* Routes */ /* Routes */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
const routes = app const routes = app
.route('/me', container.resolve(IamController).routes()) .route('/me', container.get(IamController).routes())
.route('/user', container.resolve(UserController).routes()) .route('/user', container.get(UserController).routes())
.route('/login', container.resolve(LoginController).routes()) .route('/login', container.get(LoginController).routes())
.route('/oauth', container.resolve(OAuthController).routes()) .route('/oauth', container.get(OAuthController).routes())
.route('/signup', container.resolve(SignupController).routes()) .route('/signup', container.get(SignupController).routes())
.route('/wishlists', container.resolve(WishlistController).routes()) .route('/wishlists', container.get(WishlistController).routes())
.route('/collections', container.resolve(CollectionController).routes()) .route('/collections', container.get(CollectionController).routes())
.route('/mfa', container.resolve(MfaController).routes()) .route('/mfa', container.get(MfaController).routes())
.get('/', (c) => c.json({ message: 'Server is healthy' })); .get('/', (c) => c.json({ message: 'Server is healthy' }));
configureOpenAPI(app); configureOpenAPI(app);
@ -38,8 +41,8 @@ configureOpenAPI(app);
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* Cron Jobs */ /* Cron Jobs */
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
container.resolve(AuthCleanupJobs).deleteStaleEmailVerificationRequests(); container.get(AuthCleanupJobs).deleteStaleEmailVerificationRequests();
container.resolve(AuthCleanupJobs).deleteStaleLoginRequests(); container.get(AuthCleanupJobs).deleteStaleLoginRequests();
/* -------------------------------------------------------------------------- */ /* -------------------------------------------------------------------------- */
/* Exports */ /* Exports */

View file

@ -1,11 +1,11 @@
import {inject, injectable} from 'tsyringe' import {inject, injectable} from '@needle-di/core'
import {JobsService} from '../services/jobs.service' import {JobsService} from '../services/jobs.service'
@injectable() @injectable()
export class AuthCleanupJobs { export class AuthCleanupJobs {
private queue private queue
constructor(@inject(JobsService) private jobsService: JobsService) { constructor(private jobsService = inject(JobsService)) {
/* ------------------------------ Create Queue ------------------------------ */ /* ------------------------------ Create Queue ------------------------------ */
this.queue = this.jobsService.createQueue('test') this.queue = this.jobsService.createQueue('test')

View file

@ -1,4 +1,3 @@
import 'reflect-metadata';
import { import {
cookieExpiresAt, cookieExpiresAt,
cookieName, cookieName,
@ -12,11 +11,12 @@ import type {MiddlewareHandler} from 'hono';
import {getCookie} from 'hono/cookie'; import {getCookie} from 'hono/cookie';
import {createMiddleware} from 'hono/factory'; import {createMiddleware} from 'hono/factory';
import {verifyRequestOrigin} from 'oslo/request'; import {verifyRequestOrigin} from 'oslo/request';
import {container} from 'tsyringe'; import { Container } from '@needle-di/core';
import type {AppBindings} from '../common/types/hono'; import type {AppBindings} from '../common/types/hono';
// resolve dependencies from the container // resolve dependencies from the container
const sessionService = container.resolve(SessionsService); const container = new Container();
const sessionService = container.get(SessionsService);
// CSRF protection middleware // CSRF protection middleware
export const verifyOrigin: MiddlewareHandler<AppBindings> = createMiddleware(async (c, next) => { export const verifyOrigin: MiddlewareHandler<AppBindings> = createMiddleware(async (c, next) => {

View file

@ -1,11 +1,12 @@
import {rateLimiter} from 'hono-rate-limiter'; import {rateLimiter} from 'hono-rate-limiter';
import {RedisStore} from 'rate-limit-redis'; import {RedisStore} from 'rate-limit-redis';
import {container} from 'tsyringe'; import { Container } from '@needle-di/core';
import type {AppBindings} from '../common/types/hono'; import type {AppBindings} from '../common/types/hono';
import {RedisService} from '../services/redis.service'; import {RedisService} from '../services/redis.service';
const container = new Container();
// resolve dependencies from the container // resolve dependencies from the container
const { client } = container.resolve(RedisService); const { client } = container.get(RedisService);
export function limiter({ export function limiter({
limit, limit,

View file

@ -1,11 +0,0 @@
import {container} from 'tsyringe'
import {db} from '../packages/drizzle'
// Symbol
export const DatabaseProvider = Symbol('DATABASE_TOKEN')
// Type
export type DatabaseProvider = typeof db
// Register
container.register<DatabaseProvider>(DatabaseProvider, { useValue: db })

View file

@ -1,11 +0,0 @@
import {container} from 'tsyringe'
import {lucia} from '../packages/lucia'
// Symbol
export const LuciaProvider = Symbol('LUCIA_PROVIDER')
// Type
export type LuciaProvider = typeof lucia
// Register
container.register<LuciaProvider>(LuciaProvider, { useValue: lucia })

View file

@ -1,12 +0,0 @@
import RedisClient from 'ioredis'
import {container} from 'tsyringe'
import {config} from '../common/config'
export const RedisProvider = Symbol('REDIS_TOKEN')
export type RedisProvider = RedisClient
container.register<RedisProvider>(RedisProvider, {
useValue: new RedisClient(config.redis.url, {
maxRetriesPerRequest: null,
}),
})

View file

@ -1,7 +1,7 @@
import {takeFirstOrThrow} from '$lib/server/api/common/utils/repository'; import {takeFirstOrThrow} from '$lib/server/api/common/utils/repository';
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {eq, type InferInsertModel} from 'drizzle-orm'; import {eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {collections} from '../databases/postgres/tables'; import {collections} from '../databases/postgres/tables';
export type CreateCollection = InferInsertModel<typeof collections>; export type CreateCollection = InferInsertModel<typeof collections>;
@ -9,7 +9,7 @@ export type UpdateCollection = Partial<CreateCollection>;
@injectable() @injectable()
export class CollectionsRepository { export class CollectionsRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async findAll(db = this.drizzle.db) { async findAll(db = this.drizzle.db) {
return db.query.collections.findMany(); return db.query.collections.findMany();

View file

@ -1,8 +1,7 @@
import 'reflect-metadata';
import {credentialsTable, CredentialsType} from '$lib/server/api/databases/postgres/tables/credentials.table'; import {credentialsTable, CredentialsType} from '$lib/server/api/databases/postgres/tables/credentials.table';
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {and, eq, type InferInsertModel} from 'drizzle-orm'; import {and, eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {takeFirstOrThrow} from '../common/utils/repository'; import {takeFirstOrThrow} from '../common/utils/repository';
export type CreateCredentials = InferInsertModel<typeof credentialsTable>; export type CreateCredentials = InferInsertModel<typeof credentialsTable>;
@ -11,7 +10,7 @@ export type DeleteCredentials = Pick<CreateCredentials, 'id'>;
@injectable() @injectable()
export class CredentialsRepository { export class CredentialsRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async findOneByUserId(userId: string, db = this.drizzle.db) { async findOneByUserId(userId: string, db = this.drizzle.db) {
return db.query.credentialsTable.findFirst({ return db.query.credentialsTable.findFirst({

View file

@ -1,5 +1,5 @@
import {and, eq, type InferInsertModel} from 'drizzle-orm'; import {and, eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {takeFirstOrThrow} from '../common/utils/repository'; import {takeFirstOrThrow} from '../common/utils/repository';
import {federatedIdentityTable} from '../databases/postgres/tables'; import {federatedIdentityTable} from '../databases/postgres/tables';
import {DrizzleService} from '../services/drizzle.service'; import {DrizzleService} from '../services/drizzle.service';
@ -8,7 +8,7 @@ export type CreateFederatedIdentity = InferInsertModel<typeof federatedIdentityT
@injectable() @injectable()
export class FederatedIdentityRepository { export class FederatedIdentityRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async findOneByUserIdAndProvider(userId: string, provider: string) { async findOneByUserIdAndProvider(userId: string, provider: string) {
return this.drizzle.db.query.federatedIdentityTable.findFirst({ return this.drizzle.db.query.federatedIdentityTable.findFirst({

View file

@ -1,15 +1,14 @@
import 'reflect-metadata';
import {takeFirstOrThrow} from '$lib/server/api/common/utils/repository'; import {takeFirstOrThrow} from '$lib/server/api/common/utils/repository';
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {eq, type InferInsertModel} from 'drizzle-orm'; import {eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {recoveryCodesTable} from '../databases/postgres/tables'; import {recoveryCodesTable} from '../databases/postgres/tables';
export type CreateRecoveryCodes = InferInsertModel<typeof recoveryCodesTable>; export type CreateRecoveryCodes = InferInsertModel<typeof recoveryCodesTable>;
@injectable() @injectable()
export class RecoveryCodesRepository { export class RecoveryCodesRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async create(data: CreateRecoveryCodes, db = this.drizzle.db) { async create(data: CreateRecoveryCodes, db = this.drizzle.db) {
return db.insert(recoveryCodesTable).values(data).returning().then(takeFirstOrThrow); return db.insert(recoveryCodesTable).values(data).returning().then(takeFirstOrThrow);

View file

@ -1,31 +1,15 @@
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {eq, type InferInsertModel} from 'drizzle-orm'; import {eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {takeFirstOrThrow} from '../common/utils/repository'; import {takeFirstOrThrow} from '../common/utils/repository';
import {rolesTable} from '../databases/postgres/tables'; import {rolesTable} from '../databases/postgres/tables';
/* -------------------------------------------------------------------------- */
/* Repository */
/* -------------------------------------------------------------------------- */
/* ---------------------------------- About --------------------------------- */
/*
Repositories are the layer that interacts with the database. They are responsible for retrieving and
storing data. They should not contain any business logic, only database queries.
*/
/* ---------------------------------- Notes --------------------------------- */
/*
Repositories should only contain methods for CRUD operations and any other database interactions.
Any complex logic should be delegated to a service. If a repository method requires a transaction,
it should be passed in as an argument or the class should have a method to set the transaction.
In our case the method 'trxHost' is used to set the transaction context.
*/
export type CreateRole = InferInsertModel<typeof rolesTable>; export type CreateRole = InferInsertModel<typeof rolesTable>;
export type UpdateRole = Partial<CreateRole>; export type UpdateRole = Partial<CreateRole>;
@injectable() @injectable()
export class RolesRepository { export class RolesRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async findOneById(id: string, db = this.drizzle.db) { async findOneById(id: string, db = this.drizzle.db) {
return db.query.rolesTable.findFirst({ return db.query.rolesTable.findFirst({

View file

@ -1,15 +1,14 @@
import 'reflect-metadata';
import {takeFirstOrThrow} from '$lib/server/api/common/utils/repository'; import {takeFirstOrThrow} from '$lib/server/api/common/utils/repository';
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {eq, type InferInsertModel} from 'drizzle-orm'; import {eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {sessionsTable, usersTable} from '../databases/postgres/tables'; import {sessionsTable, usersTable} from '../databases/postgres/tables';
export type CreateSession = InferInsertModel<typeof sessionsTable>; export type CreateSession = InferInsertModel<typeof sessionsTable>;
@injectable() @injectable()
export class SessionsRepository { export class SessionsRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async create(data: CreateSession, db = this.drizzle.db) { async create(data: CreateSession, db = this.drizzle.db) {
return db.insert(sessionsTable).values(data).returning().then(takeFirstOrThrow); return db.insert(sessionsTable).values(data).returning().then(takeFirstOrThrow);

View file

@ -1,31 +1,15 @@
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {eq, type InferInsertModel} from 'drizzle-orm'; import {eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {takeFirstOrThrow} from '../common/utils/repository'; import {takeFirstOrThrow} from '../common/utils/repository';
import {user_roles} from '../databases/postgres/tables'; import {user_roles} from '../databases/postgres/tables';
/* -------------------------------------------------------------------------- */
/* Repository */
/* -------------------------------------------------------------------------- */
/* ---------------------------------- About --------------------------------- */
/*
Repositories are the layer that interacts with the database. They are responsible for retrieving and
storing data. They should not contain any business logic, only database queries.
*/
/* ---------------------------------- Notes --------------------------------- */
/*
Repositories should only contain methods for CRUD operations and any other database interactions.
Any complex logic should be delegated to a service. If a repository method requires a transaction,
it should be passed in as an argument or the class should have a method to set the transaction.
In our case the method 'trxHost' is used to set the transaction context.
*/
export type CreateUserRole = InferInsertModel<typeof user_roles>; export type CreateUserRole = InferInsertModel<typeof user_roles>;
export type UpdateUserRole = Partial<CreateUserRole>; export type UpdateUserRole = Partial<CreateUserRole>;
@injectable() @injectable()
export class UserRolesRepository { export class UserRolesRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async findOneById(id: string, db = this.drizzle.db) { async findOneById(id: string, db = this.drizzle.db) {
return db.query.user_roles.findFirst({ return db.query.user_roles.findFirst({

View file

@ -1,31 +1,15 @@
import {usersTable} from '$lib/server/api/databases/postgres/tables/users.table'; import {usersTable} from '$lib/server/api/databases/postgres/tables/users.table';
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {eq, type InferInsertModel} from 'drizzle-orm'; import {eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {takeFirstOrThrow} from '../common/utils/repository'; import {takeFirstOrThrow} from '../common/utils/repository';
/* -------------------------------------------------------------------------- */
/* Repository */
/* -------------------------------------------------------------------------- */
/* ---------------------------------- About --------------------------------- */
/*
Repositories are the layer that interacts with the database. They are responsible for retrieving and
storing data. They should not contain any business logic, only database queries.
*/
/* ---------------------------------- Notes --------------------------------- */
/*
Repositories should only contain methods for CRUD operations and any other database interactions.
Any complex logic should be delegated to a service. If a repository method requires a transaction,
it should be passed in as an argument or the class should have a method to set the transaction.
In our case the method 'trxHost' is used to set the transaction context.
*/
export type CreateUser = InferInsertModel<typeof usersTable>; export type CreateUser = InferInsertModel<typeof usersTable>;
export type UpdateUser = Partial<CreateUser>; export type UpdateUser = Partial<CreateUser>;
@injectable() @injectable()
export class UsersRepository { export class UsersRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async findOneById(id: string, db = this.drizzle.db) { async findOneById(id: string, db = this.drizzle.db) {
return db.query.usersTable.findFirst({ return db.query.usersTable.findFirst({

View file

@ -1,6 +1,6 @@
import {DrizzleService} from '$lib/server/api/services/drizzle.service'; import {DrizzleService} from '$lib/server/api/services/drizzle.service';
import {eq, type InferInsertModel} from 'drizzle-orm'; import {eq, type InferInsertModel} from 'drizzle-orm';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {takeFirstOrThrow} from '../common/utils/repository'; import {takeFirstOrThrow} from '../common/utils/repository';
import {wishlistsTable} from '../databases/postgres/tables'; import {wishlistsTable} from '../databases/postgres/tables';
@ -9,7 +9,7 @@ export type UpdateWishlist = Partial<CreateWishlist>;
@injectable() @injectable()
export class WishlistsRepository { export class WishlistsRepository {
constructor(@inject(DrizzleService) private readonly drizzle: DrizzleService) {} constructor(private drizzle = inject(DrizzleService)) {}
async findAll(db = this.drizzle.db) { async findAll(db = this.drizzle.db) {
return db.query.wishlistsTable.findMany(); return db.query.wishlistsTable.findMany();

View file

@ -1,11 +1,11 @@
import type {db} from '$lib/server/api/packages/drizzle' import type {db} from '$lib/server/api/packages/drizzle'
import {generateRandomAnimalName} from '$lib/utils/randomDataUtil' import {generateRandomAnimalName} from '$lib/utils/randomDataUtil'
import {inject, injectable} from 'tsyringe' import {inject, injectable} from '@needle-di/core'
import {CollectionsRepository} from '../repositories/collections.repository' import {CollectionsRepository} from '../repositories/collections.repository'
@injectable() @injectable()
export class CollectionsService { export class CollectionsService {
constructor(@inject(CollectionsRepository) private readonly collectionsRepository: CollectionsRepository) {} constructor(private collectionsRepository = inject(CollectionsRepository)) {}
async findOneByUserId(userId: string) { async findOneByUserId(userId: string) {
return this.collectionsRepository.findOneByUserId(userId) return this.collectionsRepository.findOneByUserId(userId)

View file

@ -1,7 +1,6 @@
import 'reflect-metadata';
import {drizzle, type NodePgDatabase} from 'drizzle-orm/node-postgres'; import {drizzle, type NodePgDatabase} from 'drizzle-orm/node-postgres';
import pg from 'pg'; import pg from 'pg';
import {type Disposable, injectable} from 'tsyringe'; import {type Disposable, injectable} from '@needle-di/core';
import {config} from '../common/config'; import {config} from '../common/config';
import * as schema from '../databases/postgres/tables'; import * as schema from '../databases/postgres/tables';

View file

@ -1,7 +1,7 @@
import {scrypt} from 'node:crypto' import {scrypt} from 'node:crypto'
import {decodeHex, encodeHexLowerCase} from '@oslojs/encoding' import {decodeHex, encodeHexLowerCase} from '@oslojs/encoding'
import {constantTimeEqual} from '@oslojs/crypto/subtle' import {constantTimeEqual} from '@oslojs/crypto/subtle'
import {injectable} from 'tsyringe' import {injectable} from '@needle-di/core'
@injectable() @injectable()
export class HashingService { export class HashingService {

View file

@ -4,30 +4,13 @@ import type {UpdateProfileDto} from '$lib/server/api/dtos/update-profile.dto';
import type {VerifyPasswordDto} from '$lib/server/api/dtos/verify-password.dto'; import type {VerifyPasswordDto} from '$lib/server/api/dtos/verify-password.dto';
import {SessionsService} from '$lib/server/api/services/sessions.service'; import {SessionsService} from '$lib/server/api/services/sessions.service';
import {UsersService} from '$lib/server/api/services/users.service'; import {UsersService} from '$lib/server/api/services/users.service';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
/* -------------------------------------------------------------------------- */
/* Service */
/* -------------------------------------------------------------------------- */
/* -------------------------------------------------------------------------- */
/* ---------------------------------- About --------------------------------- */
/*
Services are responsible for handling business logic and data manipulation.
They generally call on repositories or other services to complete a use-case.
*/
/* ---------------------------------- Notes --------------------------------- */
/*
Services should be kept as clean and simple as possible.
Create private functions to handle complex logic and keep the public methods as
simple as possible. This makes the service easier to read, test and understand.
*/
/* -------------------------------------------------------------------------- */
@injectable() @injectable()
export class IamService { export class IamService {
constructor( constructor(
@inject(SessionsService) private sessionsService: SessionsService, private readonly sessionsService = inject(SessionsService),
@inject(UsersService) private readonly usersService: UsersService, private readonly usersService = inject(UsersService),
) {} ) {}
async logout(sessionId: string) { async logout(sessionId: string) {

View file

@ -1,16 +1,25 @@
import {RedisProvider} from '$lib/server/api/providers/redis.provider'
import {type Processor, Queue, Worker} from 'bullmq' import {type Processor, Queue, Worker} from 'bullmq'
import {inject, injectable} from 'tsyringe' import RedisClient from 'ioredis';
import { config } from "../common/config";
import { injectable } from '@needle-di/core';
@injectable() @injectable()
export class JobsService { export class JobsService {
constructor(@inject(RedisProvider) private readonly redis: RedisProvider) {} constructor() { }
createQueue(name: string) { createQueue(name: string) {
return new Queue(name, { connection: this.redis }) return new Queue(name, {
connection: new RedisClient(config.redis.url, {
maxRetriesPerRequest: null,
})
})
} }
createWorker(name: string, processor: Processor) { createWorker(name: string, processor: Processor) {
return new Worker(name, processor, { connection: this.redis }) return new Worker(name, processor, {
connection: new RedisClient(config.redis.url, {
maxRetriesPerRequest: null,
})
})
} }
} }

View file

@ -1,24 +1,24 @@
import type {SigninUsernameDto} from '$lib/server/api/dtos/signin-username.dto'; import type {SigninUsernameDto} from '$lib/server/api/dtos/signin-username.dto';
import {SessionsService} from '$lib/server/api/services/sessions.service'; import {SessionsService} from '$lib/server/api/services/sessions.service';
import type {HonoRequest} from 'hono'; import type {HonoRequest} from 'hono';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import {BadRequest} from '../common/exceptions'; import {BadRequest} from '../common/exceptions';
import type {Credentials} from '../databases/postgres/tables'; import type {Credentials} from '../databases/postgres/tables';
import {DatabaseProvider} from '../providers/database.provider';
import {CredentialsRepository} from '../repositories/credentials.repository'; import {CredentialsRepository} from '../repositories/credentials.repository';
import {UsersRepository} from '../repositories/users.repository'; import {UsersRepository} from '../repositories/users.repository';
import {MailerService} from './mailer.service'; import {MailerService} from './mailer.service';
import {TokensService} from './tokens.service'; import {TokensService} from './tokens.service';
import {DrizzleService} from "$lib/server/api/services/drizzle.service";
@injectable() @injectable()
export class LoginRequestsService { export class LoginRequestsService {
constructor( constructor(
@inject(SessionsService) private sessionsService: SessionsService, private sessionsService = inject(SessionsService),
@inject(DatabaseProvider) private readonly db: DatabaseProvider, private drizzleService = inject(DrizzleService) ,
@inject(TokensService) private readonly tokensService: TokensService, private tokensService = inject(TokensService) ,
@inject(MailerService) private readonly mailerService: MailerService, private mailerService = inject(MailerService) ,
@inject(UsersRepository) private readonly usersRepository: UsersRepository, private usersRepository = inject(UsersRepository) ,
@inject(CredentialsRepository) private readonly credentialsRepository: CredentialsRepository, private credentialsRepository = inject(CredentialsRepository) ,
) {} ) {}
// async create(data: RegisterEmailDto) { // async create(data: RegisterEmailDto) {
@ -73,7 +73,7 @@ export class LoginRequestsService {
// Create a new user and send a welcome email - or other onboarding process // Create a new user and send a welcome email - or other onboarding process
private async handleNewUserRegistration(email: string) { private async handleNewUserRegistration(email: string) {
const newUser = await this.usersRepository.create({ email, verified: true }); const newUser = await this.usersRepository.create({ email, verified: true });
this.mailerService.sendWelcome({ to: email, props: null }); // this.mailerService.sendWelcome({ to: email, props: null });
// TODO: add whatever onboarding process or extra data you need here // TODO: add whatever onboarding process or extra data you need here
return newUser; return newUser;
} }

View file

@ -1,6 +1,6 @@
import {injectable} from 'tsyringe' import {injectable} from '@needle-di/core'
import {config} from '../common/config' import {config} from '../common/config'
import type {Email} from '../common/inferfaces/email.interface' import type {Email} from "$lib/server/api/common/types/email";
type SendProps = { type SendProps = {
to: string | string[] to: string | string[]

View file

@ -1,4 +1,4 @@
import {inject, injectable} from 'tsyringe' import {inject, injectable} from '@needle-di/core'
import {FederatedIdentityRepository} from '../repositories/federated_identity.repository' import {FederatedIdentityRepository} from '../repositories/federated_identity.repository'
import {UsersService} from './users.service' import {UsersService} from './users.service'
import type {OAuthProviders, OAuthUser} from "$lib/server/api/common/types/oauth"; import type {OAuthProviders, OAuthUser} from "$lib/server/api/common/types/oauth";
@ -6,8 +6,8 @@ import type {OAuthProviders, OAuthUser} from "$lib/server/api/common/types/oauth
@injectable() @injectable()
export class OAuthService { export class OAuthService {
constructor( constructor(
@inject(FederatedIdentityRepository) private readonly federatedIdentityRepository: FederatedIdentityRepository, private federatedIdentityRepository = inject(FederatedIdentityRepository),
@inject(UsersService) private readonly usersService: UsersService, private usersService = inject(UsersService),
) {} ) {}
async handleOAuthUser(oAuthUser: OAuthUser, oauthProvider: OAuthProviders) { async handleOAuthUser(oAuthUser: OAuthUser, oauthProvider: OAuthProviders) {

View file

@ -1,14 +1,13 @@
import 'reflect-metadata'
import {RecoveryCodesRepository} from '$lib/server/api/repositories/recovery-codes.repository' import {RecoveryCodesRepository} from '$lib/server/api/repositories/recovery-codes.repository'
import {alphabet, generateRandomString} from 'oslo/crypto' import {alphabet, generateRandomString} from 'oslo/crypto'
import {inject, injectable} from 'tsyringe' import {inject, injectable} from '@needle-di/core'
import {HashingService} from './hashing.service' import {HashingService} from './hashing.service'
@injectable() @injectable()
export class RecoveryCodesService { export class RecoveryCodesService {
constructor( constructor(
@inject(HashingService) private readonly hashingService: HashingService, private hashingService = inject(HashingService),
@inject(RecoveryCodesRepository) private readonly recoveryCodesRepository: RecoveryCodesRepository private recoveryCodesRepository = inject(RecoveryCodesRepository),
) {} ) {}
async findAllRecoveryCodesByUserId(userId: string) { async findAllRecoveryCodesByUserId(userId: string) {

View file

@ -1,6 +1,7 @@
import {config} from '$lib/server/api/common/config' import {config} from '$lib/server/api/common/config'
import {Redis} from 'ioredis' import {Redis} from 'ioredis'
import {type Disposable, injectable} from 'tsyringe' import { injectable} from '@needle-di/core';
import type {Disposable} from 'tsyringe';
@injectable() @injectable()
export class RedisService implements Disposable { export class RedisService implements Disposable {

View file

@ -1,10 +1,10 @@
import {inject, injectable} from "tsyringe"; import {inject, injectable} from "@needle-di/core";
import {RolesRepository} from "$lib/server/api/repositories/roles.repository"; import {RolesRepository} from "$lib/server/api/repositories/roles.repository";
@injectable() @injectable()
export class RolesService { export class RolesService {
constructor( constructor(
@inject(RolesRepository) private readonly rolesRepository: RolesRepository private rolesRepository = inject(RolesRepository)
) { } ) { }

View file

@ -3,7 +3,7 @@ import { UsersRepository } from '$lib/server/api/repositories/users.repository';
import { RedisService } from '$lib/server/api/services/redis.service'; import { RedisService } from '$lib/server/api/services/redis.service';
import { sha256 } from '@oslojs/crypto/sha2'; import { sha256 } from '@oslojs/crypto/sha2';
import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding'; import { encodeBase32LowerCaseNoPadding, encodeHexLowerCase } from '@oslojs/encoding';
import { inject, injectable } from 'tsyringe'; import { inject, injectable } from '@needle-di/core';
import type { Users } from '../databases/postgres/tables'; import type { Users } from '../databases/postgres/tables';
export type RedisSession = { export type RedisSession = {
@ -31,8 +31,8 @@ export type SessionValidationResult = { session: Session; user: Users } | { sess
@injectable() @injectable()
export class SessionsService { export class SessionsService {
constructor( constructor(
@inject(RedisService) private readonly redisService: RedisService, private redisService = inject(RedisService),
@inject(UsersRepository) private readonly usersRepository: UsersRepository, private usersRepository = inject(UsersRepository),
) {} ) {}
generateSessionToken() { generateSessionToken() {

View file

@ -1,11 +1,11 @@
import {inject, injectable} from "tsyringe"; import {inject, injectable} from "@needle-di/core";
import {generateRandomString} from "oslo/crypto"; import {generateRandomString} from "oslo/crypto";
import {createDate, TimeSpan, type TimeSpanUnit} from 'oslo'; import {createDate, TimeSpan, type TimeSpanUnit} from 'oslo';
import {HashingService} from "./hashing.service"; import {HashingService} from "./hashing.service";
@injectable() @injectable()
export class TokensService { export class TokensService {
constructor(@inject(HashingService) private readonly hashingService: HashingService) { } constructor(private hashingService = inject(HashingService)) { }
generateToken() { generateToken() {
const alphabet = '23456789ACDEFGHJKLMNPQRSTUVWXYZ'; // alphabet with removed look-alike characters (0, 1, O, I) const alphabet = '23456789ACDEFGHJKLMNPQRSTUVWXYZ'; // alphabet with removed look-alike characters (0, 1, O, I)

View file

@ -1,12 +1,12 @@
import {CredentialsRepository} from '$lib/server/api/repositories/credentials.repository'; import {CredentialsRepository} from '$lib/server/api/repositories/credentials.repository';
import {decodeHex, encodeHexLowerCase} from '@oslojs/encoding'; import {decodeHex, encodeHexLowerCase} from '@oslojs/encoding';
import {verifyTOTP} from '@oslojs/otp'; import {verifyTOTP} from '@oslojs/otp';
import {inject, injectable} from 'tsyringe'; import {inject, injectable} from '@needle-di/core';
import type {CredentialsType} from '../databases/postgres/tables'; import type {CredentialsType} from '../databases/postgres/tables';
@injectable() @injectable()
export class TotpService { export class TotpService {
constructor(@inject(CredentialsRepository) private readonly credentialsRepository: CredentialsRepository) {} constructor(private credentialsRepository = inject(CredentialsRepository)) {}
async findOneByUserId(userId: string) { async findOneByUserId(userId: string) {
return this.credentialsRepository.findTOTPCredentialsByUserId(userId); return this.credentialsRepository.findTOTPCredentialsByUserId(userId);

View file

@ -1,13 +1,13 @@
import type {db} from '$lib/server/api/packages/drizzle' import type {db} from '$lib/server/api/packages/drizzle'
import {type CreateUserRole, UserRolesRepository} from '$lib/server/api/repositories/user_roles.repository' import {type CreateUserRole, UserRolesRepository} from '$lib/server/api/repositories/user_roles.repository'
import {RolesService} from '$lib/server/api/services/roles.service' import {RolesService} from '$lib/server/api/services/roles.service'
import {inject, injectable} from 'tsyringe' import {inject, injectable} from '@needle-di/core'
@injectable() @injectable()
export class UserRolesService { export class UserRolesService {
constructor( constructor(
@inject(UserRolesRepository) private readonly userRolesRepository: UserRolesRepository, private userRolesRepository = inject(UserRolesRepository),
@inject(RolesService) private readonly rolesService: RolesService, private rolesService = inject(RolesService),
) {} ) {}
async findOneById(id: string) { async findOneById(id: string) {

View file

@ -5,7 +5,7 @@ import {FederatedIdentityRepository} from '$lib/server/api/repositories/federate
import {WishlistsRepository} from '$lib/server/api/repositories/wishlists.repository'; import {WishlistsRepository} from '$lib/server/api/repositories/wishlists.repository';
import {TokensService} from '$lib/server/api/services/tokens.service'; import {TokensService} from '$lib/server/api/services/tokens.service';
import {UserRolesService} from '$lib/server/api/services/user_roles.service'; import {UserRolesService} from '$lib/server/api/services/user_roles.service';
import {inject, injectable} from 'tsyringe'; import { inject, injectable } from '@needle-di/core';
import {CredentialsType, RoleName} from '../databases/postgres/tables'; import {CredentialsType, RoleName} from '../databases/postgres/tables';
import {type UpdateUser, UsersRepository} from '../repositories/users.repository'; import {type UpdateUser, UsersRepository} from '../repositories/users.repository';
import {CollectionsService} from './collections.service'; import {CollectionsService} from './collections.service';
@ -15,15 +15,15 @@ import {WishlistsService} from './wishlists.service';
@injectable() @injectable()
export class UsersService { export class UsersService {
constructor( constructor(
@inject(CollectionsService) private readonly collectionsService: CollectionsService, private collectionsService = inject(CollectionsService),
@inject(CredentialsRepository) private readonly credentialsRepository: CredentialsRepository, private credentialsRepository = inject(CredentialsRepository),
@inject(DrizzleService) private readonly drizzleService: DrizzleService, private drizzleService = inject(DrizzleService),
@inject(FederatedIdentityRepository) private readonly federatedIdentityRepository: FederatedIdentityRepository, private federatedIdentityRepository = inject(FederatedIdentityRepository),
@inject(TokensService) private readonly tokenService: TokensService, private tokenService = inject(TokensService),
@inject(UsersRepository) private readonly usersRepository: UsersRepository, private usersRepository = inject(UsersRepository),
@inject(UserRolesService) private readonly userRolesService: UserRolesService, private userRolesService = inject(UserRolesService),
@inject(WishlistsRepository) private readonly wishlistsRepository: WishlistsRepository, private wishlistsRepository = inject(WishlistsRepository),
@inject(WishlistsService) private readonly wishlistsService: WishlistsService, private wishlistsService = inject(WishlistsService),
) {} ) {}
async create(data: SignupUsernameEmailDto) { async create(data: SignupUsernameEmailDto) {
@ -63,6 +63,7 @@ export class UsersService {
await this.wishlistsService.createEmptyNoName(createdUser.id, trx); await this.wishlistsService.createEmptyNoName(createdUser.id, trx);
await this.collectionsService.createEmptyNoName(createdUser.id, trx); await this.collectionsService.createEmptyNoName(createdUser.id, trx);
return createdUser;
}); });
} }

View file

@ -1,11 +1,11 @@
import type {db} from '$lib/server/api/packages/drizzle' import type {db} from '$lib/server/api/packages/drizzle'
import {generateRandomAnimalName} from '$lib/utils/randomDataUtil' import {generateRandomAnimalName} from '$lib/utils/randomDataUtil'
import {inject, injectable} from 'tsyringe' import {inject, injectable} from '@needle-di/core'
import {WishlistsRepository} from '../repositories/wishlists.repository' import {WishlistsRepository} from '../repositories/wishlists.repository'
@injectable() @injectable()
export class WishlistsService { export class WishlistsService {
constructor(@inject(WishlistsRepository) private readonly wishlistsRepository: WishlistsRepository) {} constructor(private wishlistsRepository = inject(WishlistsRepository)) {}
async findAllByUserId(userId: string) { async findAllByUserId(userId: string) {
return this.wishlistsRepository.findAllByUserId(userId) return this.wishlistsRepository.findAllByUserId(userId)

View file

@ -1,13 +1,13 @@
import 'reflect-metadata' import { Container } from '@needle-di/core';
import {container} from 'tsyringe'
import {afterAll, beforeAll, describe, expect, it, vi} from 'vitest' import {afterAll, beforeAll, describe, expect, it, vi} from 'vitest'
import {HashingService} from '../services/hashing.service' import {HashingService} from '../services/hashing.service'
describe('HashingService', () => { describe('HashingService', () => {
let service: HashingService let service: HashingService
const container = new Container()
beforeAll(() => { beforeAll(() => {
service = container.resolve(HashingService) service = container.get(HashingService)
}) })
afterAll(() => { afterAll(() => {

View file

@ -1,21 +1,22 @@
import 'reflect-metadata';
import {IamService} from '$lib/server/api/services/iam.service'; import {IamService} from '$lib/server/api/services/iam.service';
import {SessionsService} from '$lib/server/api/services/sessions.service'; import {SessionsService} from '$lib/server/api/services/sessions.service';
import {UsersService} from '$lib/server/api/services/users.service'; import {UsersService} from '$lib/server/api/services/users.service';
import {faker} from '@faker-js/faker'; import {faker} from '@faker-js/faker';
import {container} from 'tsyringe'; import { Container } from '@needle-di/core';
import {afterAll, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest'; import {afterAll, beforeAll, beforeEach, describe, expect, it, vi} from 'vitest';
describe('IamService', () => { describe('IamService', () => {
let service: IamService; let service: IamService;
const luciaService = vi.mocked(SessionsService.prototype); const container = new Container();
const sessionService = vi.mocked(SessionsService.prototype);
const userService = vi.mocked(UsersService.prototype); const userService = vi.mocked(UsersService.prototype);
beforeAll(() => { beforeAll(() => {
service = container container
.register<SessionsService>(SessionsService, { useValue: luciaService }) .bind<SessionsService>({ provide: SessionsService, useValue: sessionService })
.register<UsersService>(UsersService, { useValue: userService }) .bind<UsersService>({ provide: UsersService, useValue: userService });
.resolve(IamService);
service = container.get(IamService);
}); });
beforeEach(() => { beforeEach(() => {

View file

@ -1,15 +1,17 @@
import 'reflect-metadata' import 'reflect-metadata'
import {container} from 'tsyringe' import { Container } from '@needle-di/core'
import {afterAll, beforeAll, describe, expect, expectTypeOf, it, vi} from 'vitest' import {afterAll, beforeAll, describe, expect, expectTypeOf, it, vi} from 'vitest'
import {HashingService} from '../services/hashing.service' import {HashingService} from '../services/hashing.service'
import {TokensService} from '../services/tokens.service' import {TokensService} from '../services/tokens.service'
describe('TokensService', () => { describe('TokensService', () => {
const container = new Container()
let service: TokensService let service: TokensService
const hashingService = vi.mocked(HashingService.prototype) const hashingService = vi.mocked(HashingService.prototype)
beforeAll(() => { beforeAll(() => {
service = container.register<HashingService>(HashingService, { useValue: hashingService }).resolve(TokensService) container.bind<HashingService>({ provide: HashingService, useValue: hashingService });
service = container.get(TokensService);
}) })
afterAll(() => { afterAll(() => {

View file

@ -1,6 +1,6 @@
import 'reflect-metadata'; import 'reflect-metadata';
import {faker} from '@faker-js/faker'; import {faker} from '@faker-js/faker';
import {container} from 'tsyringe'; import { Container } from '@needle-di/core';
import {afterAll, beforeAll, describe, expect, it, vi} from 'vitest'; import {afterAll, beforeAll, describe, expect, it, vi} from 'vitest';
import {RoleName} from '../databases/postgres/tables'; import {RoleName} from '../databases/postgres/tables';
import {UserRolesRepository} from '../repositories/user_roles.repository'; import {UserRolesRepository} from '../repositories/user_roles.repository';
@ -8,15 +8,17 @@ import {RolesService} from '../services/roles.service';
import {UserRolesService} from '../services/user_roles.service'; import {UserRolesService} from '../services/user_roles.service';
describe('UserRolesService', () => { describe('UserRolesService', () => {
const container = new Container();
let service: UserRolesService; let service: UserRolesService;
const userRolesRepository = vi.mocked(UserRolesRepository.prototype); const userRolesRepository = vi.mocked(UserRolesRepository.prototype);
const rolesService = vi.mocked(RolesService.prototype); const rolesService = vi.mocked(RolesService.prototype);
beforeAll(() => { beforeAll(() => {
service = container container
.register<UserRolesRepository>(UserRolesRepository, { useValue: userRolesRepository }) .bind<UserRolesRepository>({ provide: UserRolesRepository, useValue: userRolesRepository })
.register<RolesService>(RolesService, { useValue: rolesService }) .bind<RolesService>({ provide: RolesService, useValue: rolesService });
.resolve(UserRolesService);
service = container.get(UserRolesService);
}); });
afterAll(() => { afterAll(() => {

View file

@ -1,6 +1,6 @@
import 'reflect-metadata'; import 'reflect-metadata';
import {faker} from '@faker-js/faker'; import {faker} from '@faker-js/faker';
import {container} from 'tsyringe'; import { Container } from '@needle-di/core';
import {afterAll, beforeAll, describe, expect, it, vi} from 'vitest'; import {afterAll, beforeAll, describe, expect, it, vi} from 'vitest';
import {CredentialsType} from '../databases/postgres/tables'; import {CredentialsType} from '../databases/postgres/tables';
import {CredentialsRepository} from '../repositories/credentials.repository'; import {CredentialsRepository} from '../repositories/credentials.repository';
@ -13,6 +13,7 @@ import {UsersService} from '../services/users.service';
import {WishlistsService} from '../services/wishlists.service'; import {WishlistsService} from '../services/wishlists.service';
describe('UsersService', () => { describe('UsersService', () => {
const container = new Container();
let service: UsersService; let service: UsersService;
const credentialsRepository = vi.mocked(CredentialsRepository.prototype); const credentialsRepository = vi.mocked(CredentialsRepository.prototype);
const drizzleService = vi.mocked(DrizzleService.prototype, { deep: true }); const drizzleService = vi.mocked(DrizzleService.prototype, { deep: true });
@ -38,15 +39,16 @@ describe('UsersService', () => {
})); }));
beforeAll(() => { beforeAll(() => {
service = container container
.register<CredentialsRepository>(CredentialsRepository, { useValue: credentialsRepository }) .bind<CredentialsRepository>({ provide: CredentialsRepository, useValue: credentialsRepository })
.register<DrizzleService>(DrizzleService, { useValue: drizzleService }) .bind<DrizzleService>({ provide: DrizzleService, useValue: drizzleService })
.register<TokensService>(TokensService, { useValue: tokensService }) .bind<TokensService>({ provide: TokensService, useValue: tokensService })
.register<UsersRepository>(UsersRepository, { useValue: usersRepository }) .bind<UsersRepository>({ provide: UsersRepository, useValue: usersRepository })
.register<UserRolesService>(UserRolesService, { useValue: userRolesService }) .bind<UserRolesService>({ provide: UserRolesService, useValue: userRolesService })
.register<WishlistsService>(WishlistsService, { useValue: wishlistsService }) .bind<WishlistsService>({ provide: WishlistsService, useValue: wishlistsService })
.register<CollectionsService>(CollectionsService, { useValue: collectionsService }) .bind<CollectionsService>({ provide: CollectionsService, useValue: collectionsService });
.resolve(UsersService);
service = container.get(UsersService);
drizzleService.db = { drizzleService.db = {
transaction: vi.fn().mockImplementation(async (callback) => { transaction: vi.fn().mockImplementation(async (callback) => {
@ -87,37 +89,22 @@ describe('UsersService', () => {
it('should resolve', async () => { it('should resolve', async () => {
const hashedPassword = 'testhash'; const hashedPassword = 'testhash';
tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword); tokensService.createHashedToken = vi.fn().mockResolvedValue(hashedPassword);
// drizzleService.db = {
// transaction: vi.fn().mockResolvedValue(dbUser satisfies Awaited<ReturnType<typeof drizzleService.db.transaction>>), drizzleService.db.transaction = vi.fn().mockImplementation(async (callback) => {
// } return dbUser satisfies Awaited<ReturnType<typeof callback>>
usersRepository.create = vi.fn().mockResolvedValue(dbUser satisfies Awaited<ReturnType<typeof usersRepository.create>>); });
credentialsRepository.create = vi.fn().mockResolvedValue(dbCredentials satisfies Awaited<ReturnType<typeof credentialsRepository.create>>);
userRolesService.addRoleToUser = vi.fn().mockResolvedValue(undefined);
wishlistsService.createEmptyNoName = vi.fn().mockResolvedValue(undefined);
collectionsService.createEmptyNoName = vi.fn().mockResolvedValue(undefined);
const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken'); const spy_tokensService_createHashToken = vi.spyOn(tokensService, 'createHashedToken');
const spy_usersRepository_create = vi.spyOn(usersRepository, 'create'); const createdUser = await service.create({
const spy_credentialsRepository_create = vi.spyOn(credentialsRepository, 'create'); firstName: faker.person.firstName(),
const spy_userRolesService_addRoleToUser = vi.spyOn(userRolesService, 'addRoleToUser'); lastName: faker.person.lastName(),
const spy_wishlistsService_createEmptyNoName = vi.spyOn(wishlistsService, 'createEmptyNoName'); email: faker.internet.email(),
const spy_collectionsService_createEmptyNoName = vi.spyOn(collectionsService, 'createEmptyNoName'); username: faker.internet.userName(),
await expect( password: faker.string.alphanumeric(10),
service.create({ confirm_password: faker.string.alphanumeric(10),
firstName: faker.person.firstName(), });
lastName: faker.person.lastName(), expect(createdUser).toEqual(dbUser);
email: faker.internet.email(),
username: faker.internet.userName(),
password: faker.string.alphanumeric(10),
confirm_password: faker.string.alphanumeric(10),
}),
).resolves.toEqual(dbUser);
expect(spy_tokensService_createHashToken).toBeCalledTimes(1); expect(spy_tokensService_createHashToken).toBeCalledTimes(1);
expect(spy_usersRepository_create).toBeCalledTimes(1);
expect(spy_credentialsRepository_create).toBeCalledTimes(1);
expect(spy_userRolesService_addRoleToUser).toBeCalledTimes(1);
expect(spy_wishlistsService_createEmptyNoName).toBeCalledTimes(1);
expect(spy_collectionsService_createEmptyNoName).toBeCalledTimes(1);
}); });
}); });
describe('Update User', () => { describe('Update User', () => {