Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2eafd7b
refactor: remove DTOs
tomast1337 Jan 11, 2026
56d6c52
feat: add validation package with DTOs
tomast1337 Jan 11, 2026
7c1a3b6
feat: integrate @nbw/validation package into project structure
tomast1337 Jan 11, 2026
33f330e
feat(validation): migrate to Zod in @nbw/validation; drop class-valid…
tomast1337 Apr 13, 2026
ed6f3f9
feat(validation): enhance JSON string handling in UploadSongDto schema
tomast1337 Apr 14, 2026
9a45ffb
docs(validation): update README to reflect package purpose and usage …
tomast1337 Apr 14, 2026
6d8f37b
chore(validation): remove jest configuration file
tomast1337 Apr 14, 2026
6dfc22f
refactor(validation): drop types.ts barrels; add uploadMeta and use D…
tomast1337 Apr 14, 2026
9ecfbc5
feat: user profiles
tomast1337 Apr 18, 2026
e8534d0
feat(ui): add Button, Input, Label, Textarea components and update pa…
tomast1337 Apr 18, 2026
7f40991
refactor(validation): update PageQuery DTO to use enum for order field
tomast1337 Apr 21, 2026
e3c0368
refactor(song): enhance song preview handling and introduce SongPrevi…
tomast1337 Apr 21, 2026
e09eb2c
refactor(imports): update imports from @nbw/database to @nbw/validation
tomast1337 Apr 21, 2026
10cff31
refactor(song-search): enhance song search functionality and introduc…
tomast1337 Apr 21, 2026
ca861ac
docs: Clarifying stripInvalidZodMarkersFromParameters function
tomast1337 Apr 21, 2026
f8ea8e0
refactor(validation): replace config-shim imports with direct imports…
tomast1337 Apr 21, 2026
12806e4
refactor(user): update user retrieval to support pagination and filte…
tomast1337 Apr 21, 2026
aadd8dd
refactor(user): simplify user index query handling
tomast1337 Apr 21, 2026
bf27491
refactor(song): update song entity and DTO to enforce maximum length …
tomast1337 Apr 21, 2026
9423ca8
Revert "feat(ui): add Button, Input, Label, Textarea components and u…
tomast1337 Apr 21, 2026
04dff80
Revert "feat: user profiles"
tomast1337 Apr 21, 2026
e0f8e44
refactor(imports): standardize import paths by removing file extensions
tomast1337 Apr 21, 2026
5582984
split assignment and return to separate files
tomast1337 Apr 21, 2026
5bf993c
refactor(validation): remove mongoose dependency and update import paths
tomast1337 Apr 21, 2026
51a3cd1
refactor(imports): update import path for jsonStringField in UploadSo…
tomast1337 Apr 21, 2026
b08bd6b
fix(validation): port develop song stats rules and fix migration impo…
tomast1337 Oct 11, 2026
1b595ac
Reapply "feat: user profiles"
tomast1337 Apr 21, 2026
987832a
Reapply "feat(ui): add Button, Input, Label, Textarea components and …
tomast1337 Apr 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@encode42/nbs.js": "^5.0.2",
"@nbw/config": "workspace:*",
"@nbw/database": "workspace:*",
"@nbw/validation": "workspace:*",
"@nbw/song": "workspace:*",
"@nbw/sounds": "workspace:*",
"@nbw/thumbnail": "workspace:*",
Expand All @@ -44,10 +45,10 @@
"axios": "^1.13.2",
"bcryptjs": "^3.0.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.3",
"esm": "^3.2.25",
"express": "^5.2.1",
"mongoose": "^9.0.1",
"nestjs-zod": "^5.0.1",
"multer": "2.1.1",
"nanoid": "^5.1.6",
"passport": "^0.7.0",
Expand Down
6 changes: 3 additions & 3 deletions apps/backend/scripts/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,6 @@ const build = async () => {
await Bun.$`rm -rf dist`;

const optionalRequirePackages = [
'class-transformer',
'class-transformer/storage',
'class-validator',
'@nestjs/microservices',
'@nestjs/websockets',
'@fastify/static',
Expand All @@ -76,8 +73,11 @@ const build = async () => {
}),
'@nbw/config',
'@nbw/database',
'@nbw/validation',
'@nbw/song',
'@nbw/sounds',
// @nestjs/swagger → @nestjs/mapped-types requires class-transformer metadata storage; bundler mis-resolves subpaths
'class-transformer',
],
splitting: true,
});
Expand Down
13 changes: 10 additions & 3 deletions apps/backend/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Logger, Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { APP_GUARD } from '@nestjs/core';
import { APP_GUARD, APP_PIPE } from '@nestjs/core';
import { MongooseModule, MongooseModuleFactoryOptions } from '@nestjs/mongoose';
import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler';
import { ZodValidationPipe } from 'nestjs-zod';
import { MailerModule } from '@nestjs-modules/mailer';
import { HandlebarsAdapter } from '@nestjs-modules/mailer/dist/adapters/handlebars.adapter';

import { AuthModule } from './auth/auth.module';
import { validate } from './config/EnvironmentVariables';
import { validateEnv } from '@nbw/validation';
import { EmailLoginModule } from './email-login/email-login.module';
import { FileModule } from './file/file.module';
import { ParseTokenPipe } from './lib/parseToken';
import { MailingModule } from './mailing/mailing.module';
import { MigrationModule } from './migration/migration.module';
import { ProfileModule } from './profile/profile.module';
import { SeedModule } from './seed/seed.module';
import { SongModule } from './song/song.module';
import { UserModule } from './user/user.module';
Expand All @@ -22,7 +24,7 @@ import { UserModule } from './user/user.module';
ConfigModule.forRoot({
isGlobal: true,
envFilePath: ['.env.test', '.env.development', '.env.production'],
validate,
validate: validateEnv,
}),
//DatabaseModule,
MongooseModule.forRootAsync({
Expand Down Expand Up @@ -74,6 +76,7 @@ import { UserModule } from './user/user.module';
]),
SongModule,
UserModule,
ProfileModule,
AuthModule.forRootAsync(),
FileModule.forRootAsync(),
SeedModule.forRoot(),
Expand All @@ -84,6 +87,10 @@ import { UserModule } from './user/user.module';
controllers: [],
providers: [
ParseTokenPipe,
{
provide: APP_PIPE,
useClass: ZodValidationPipe,
},
{
provide: APP_GUARD,
useClass: ThrottlerGuard,
Expand Down
1 change: 1 addition & 0 deletions apps/backend/src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,7 @@ describe('AuthService', () => {
profileImage: 'http://example.com/photo.jpg',
};

mockUserService.generateUsername.mockResolvedValue('testuser');
mockUserService.findByEmail.mockResolvedValue(null);
mockUserService.create.mockResolvedValue({ id: 'new-user-id' });

Expand Down
11 changes: 4 additions & 7 deletions apps/backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ import axios from 'axios';
import type { CookieOptions, Request, Response } from 'express';
import ms from 'ms';

import { CreateUser } from '@nbw/database';
import type { UserDocument } from '@nbw/database';
import { createUserSchema } from '@nbw/validation';
import { UserService } from '@server/user/user.service';

import { DiscordUser } from './types/discordProfile';
Expand Down Expand Up @@ -90,10 +90,9 @@ export class AuthService {

private async createNewUser(user: Profile) {
const { username, email, profileImage } = user;
const baseUsername = username;
const newUsername = await this.userService.generateUsername(baseUsername);
const newUsername = await this.userService.generateUsername(username);

const newUser = new CreateUser({
const newUser = createUserSchema.parse({
username: newUsername,
email: email,
profileImage: profileImage,
Expand Down Expand Up @@ -220,8 +219,6 @@ export class AuthService {
return null;
}

const user = await this.userService.findByID(decoded.id);

return user;
return await this.userService.findByID(decoded.id);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { VerifyFunction } from 'passport-oauth2';

import { DiscordStrategyConfig } from './DiscordStrategyConfig';
import type { DiscordStrategyConfig } from '@nbw/validation';

import DiscordStrategy from './Strategy';
import { DiscordPermissionScope, Profile } from './types';

Expand Down Expand Up @@ -42,16 +43,15 @@ describe('DiscordStrategy', () => {
prompt: 'consent',
};

await expect(strategy['validateConfig'](config)).resolves.toBeUndefined();
expect(() => strategy['validateConfig'](config)).not.toThrow();
});

it('should make API request', async () => {
const mockGet = jest.fn((url, accessToken, callback) => {
callback(null, JSON.stringify({ id: '123' }));
strategy['_oauth2'].get = jest.fn((url, accessToken, callback) => {
// oauth2 `dataCallback` typings omit `null`; runtime passes null on success.
callback(null as never, JSON.stringify({ id: '123' }));
});

strategy['_oauth2'].get = mockGet;

const result = await strategy['makeApiRequest']<{ id: string }>(
'https://discord.com/api/users/@me',
'test-access-token',
Expand Down
21 changes: 10 additions & 11 deletions apps/backend/src/auth/strategies/discord.strategy/Strategy.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { Logger } from '@nestjs/common';
import { plainToClass } from 'class-transformer';
import { validateOrReject } from 'class-validator';
import {
discordStrategyConfigSchema,
type DiscordStrategyConfig,
} from '@nbw/validation';
import {
InternalOAuthError,
Strategy as OAuth2Strategy,
StrategyOptions as OAuth2StrategyOptions,
VerifyCallback,
VerifyFunction,
} from 'passport-oauth2';

import { DiscordStrategyConfig } from './DiscordStrategyConfig';
import {
Profile,
ProfileConnection,
Expand Down Expand Up @@ -47,20 +47,19 @@ export default class Strategy extends OAuth2Strategy {
);

this.validateConfig(options);
this.scope = options.scope;
this.scope = options.scope as ScopeType;
this.scopeDelay = options.scopeDelay ?? 0;
this.fetchScopeEnabled = options.fetchScope ?? true;
this._oauth2.useAuthorizationHeaderforGET(true);
this.prompt = options.prompt;
}

private async validateConfig(config: DiscordStrategyConfig): Promise<void> {
private validateConfig(config: DiscordStrategyConfig): void {
try {
const validatedConfig = plainToClass(DiscordStrategyConfig, config);
await validateOrReject(validatedConfig);
} catch (errors) {
this.logger.error(errors);
throw new Error(`Configuration validation failed: ${errors}`);
discordStrategyConfigSchema.parse(config);
} catch (error) {
this.logger.error(error);
throw new Error(`Configuration validation failed: ${String(error)}`);
}
}

Expand Down
Loading
Loading