Skip to content

Commit 5ac142e

Browse files
committed
Enhance configuration and improve file handling in API and web applications: Updated .gitignore to exclude cache keys and certificates, modified turbo.json to include additional output paths, and refined environment variables in .env.example. Improved file and image handling in the storage service, added image validation, and updated user service to support image uploads. Introduced a new ImagesService for better image processing and integrated it into user updates. Enhanced error handling in mail service and updated various components for improved user experience.
1 parent 5bf37e2 commit 5ac142e

28 files changed

+325
-185
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,3 +36,5 @@ yarn-error.log*
3636
# Misc
3737
.DS_Store
3838
*.pem
39+
volume/cache/*.key
40+
volume/cache/*.crt

apps/api/.env.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ CACHE_URL=redis://localhost:6379
1515

1616
# email
1717
MAIL_DOMAIN=example.com
18+
MAILPIT_URL=http://localhost:8025
1819

1920
# storage
2021
STORAGE_URL=http://localhost:9000
2122
STORAGE_BUCKET_NAME=public
2223
STORAGE_REGION=us-east-1
2324
STORAGE_ACCESS_KEY=admin
24-
STORAGE_SECRET_KEY=admin
25+
STORAGE_SECRET_KEY=admin

apps/api/src/app.controller.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
import { Controller, Get } from '@nestjs/common';
2+
import { Auth, AuthType } from './auth/decorators/auth.decorator';
23

34
@Controller()
45
export class AppController {
56
@Get('/healthz')
7+
@Auth(AuthType.Public)
68
healthz() {
79
return {
810
status: 'ok',

apps/api/src/app.module.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,21 @@ import { DRIZZLE_PROVIDER } from './databases/drizzle.provider';
2424
@Module({
2525
imports: [
2626
ConfigifyModule.forRootAsync(),
27-
MulterModule,
27+
MulterModule.register({
28+
limits: {
29+
fileSize: 5 * 1024 * 1024,
30+
},
31+
}),
2832
ThrottlerModule.forRootAsync({
2933
inject: [CacheConfig],
3034
useFactory: (cacheConfig: CacheConfig) => {
3135
return {
32-
throttlers: [],
36+
throttlers: [
37+
{
38+
ttl: 60_000,
39+
limit: 100,
40+
},
41+
],
3342
storage: new ThrottlerStorageRedisService(cacheConfig.url),
3443
};
3544
},

apps/api/src/auth/better-auth.provider.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ export const BetterAuthProvider = {
8686
return mailService.sendVerificationEmail({
8787
to: user.email,
8888
props: {
89-
expirationHours: 24,
89+
expirationHours: 1,
9090
userEmail: user.email,
9191
verificationUrl: url,
9292
},
Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
11
import { User } from 'better-auth';
2-
import { Transform } from 'class-transformer';
3-
4-
const STORAGE_URL = process.env['STORAGE_URL']?.replace(/\/+$/, '') ?? 'http://localhost:9000';
5-
const STORAGE_BUCKET = process.env['STORAGE_BUCKET_NAME'] ?? 'public';
62

73
export class ActiveUserDto implements User {
84
id!: string;
@@ -11,6 +7,5 @@ export class ActiveUserDto implements User {
117
email!: string;
128
createdAt!: Date;
139
updatedAt!: Date;
14-
@Transform(({ value }) => (value ? `${STORAGE_URL}/${STORAGE_BUCKET}/${value}` : null))
1510
image?: string | null;
1611
}

apps/api/src/auth/guards/auth.guard.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,10 @@ import { CanActivate, ExecutionContext, Injectable, UnauthorizedException } from
22
import { AUTH_TYPE_KEY, AuthType } from '../decorators/auth.decorator';
33
import { Reflector } from '@nestjs/core';
44
import { Session, User } from 'better-auth';
5+
import type { Request } from 'express';
56
import { BetterAuth, InjectBetterAuth } from '../better-auth.provider';
7+
import { StorageConfig } from 'src/common/config/storage.config';
8+
import { toPublicStorageUrl } from 'src/storage/storage.utils';
69

710
export interface AuthGuardRequest extends Request {
811
session?: Session;
@@ -16,6 +19,7 @@ export class AuthGuard implements CanActivate {
1619
constructor(
1720
@InjectBetterAuth() private readonly betterAuth: BetterAuth,
1821
private readonly reflector: Reflector,
22+
private readonly storageConfig: StorageConfig,
1923
) {}
2024

2125
async canActivate(context: ExecutionContext): Promise<boolean> {
@@ -35,7 +39,12 @@ export class AuthGuard implements CanActivate {
3539

3640
// set session and user on request
3741
request.session = session?.session;
38-
request.user = session?.user;
42+
request.user = session?.user
43+
? {
44+
...session.user,
45+
image: toPublicStorageUrl(this.storageConfig, session.user.image),
46+
}
47+
: undefined;
3948

4049
// if auth type is required, check if session exists
4150
if (authType === AuthType.Required) {

apps/api/src/common/config/app.config.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,21 @@ export class AppConfig {
2525

2626
@IsBoolean()
2727
@IsNotEmpty()
28-
isProduction: boolean = process.env['NODE_ENV'] === 'production';
28+
@Value('NODE_ENV', {
29+
default: 'development',
30+
parse: (value) => value === 'production',
31+
})
32+
isProduction!: boolean;
2933

3034
@IsObject()
3135
@IsNotEmpty()
32-
cors: CorsOptions = {
33-
origin: process.env['APP_WEB_URL']!,
34-
credentials: true,
35-
methods: ['GET', 'PATCH', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'],
36-
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'user-agent', 'Accept'],
37-
};
36+
@Value('APP_WEB_URL', {
37+
parse: (origin: string): CorsOptions => ({
38+
origin,
39+
credentials: true,
40+
methods: ['GET', 'PATCH', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'],
41+
allowedHeaders: ['Content-Type', 'Authorization', 'X-Requested-With', 'user-agent', 'Accept'],
42+
}),
43+
})
44+
cors!: CorsOptions;
3845
}

apps/api/src/common/config/auth.config.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,8 @@ export class AuthConfig {
1414

1515
@IsString({ each: true })
1616
@IsNotEmpty()
17-
trustedOrigins = [process.env['APP_WEB_URL']!];
17+
@Value('APP_WEB_URL', {
18+
parse: (origin: string) => [origin],
19+
})
20+
trustedOrigins!: string[];
1821
}
Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { Configuration, Value } from '@itgorillaz/configify';
2-
import { IsNotEmpty, IsString } from 'class-validator';
2+
import { IsNotEmpty, IsString, IsUrl } from 'class-validator';
33

44
@Configuration()
55
export class MailConfig {
@@ -8,8 +8,10 @@ export class MailConfig {
88
@Value('MAIL_DOMAIN')
99
domain!: string;
1010

11+
@IsUrl({ require_tld: false })
1112
@IsNotEmpty()
12-
mailpit = {
13-
url: 'http://localhost:8025',
14-
};
13+
@Value('MAILPIT_URL', {
14+
default: 'http://localhost:8025',
15+
})
16+
mailpitUrl!: string;
1517
}

0 commit comments

Comments
 (0)