-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge remote-tracking branch 'origin/main' into feat-email-service
- Loading branch information
Showing
27 changed files
with
1,508 additions
and
697 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,4 +16,6 @@ models/ | |
|
||
*/**/database.sqlite | ||
./backend/src/database.sqlite | ||
.codefox | ||
.codefox | ||
|
||
.env |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Server Configuration | ||
PORT=8080 | ||
|
||
# DEV PROD OR TEST | ||
NODE_ENV="DEV" | ||
# JWT Configuration | ||
JWT_SECRET="your_jwt_secret_here" | ||
JWT_REFRESH="your_jwt_refresh_secret_here" | ||
SALT_ROUNDS=10 | ||
|
||
# OpenAI Configuration | ||
OPENAI_BASE_URI="http://localhost:3001" | ||
|
||
# S3/Cloudflare R2 Configuration (Optional) | ||
# If not provided, local file storage will be used | ||
S3_ACCESS_KEY_ID="your_s3_access_key_id" # Must be 32 characters for Cloudflare R2 | ||
S3_SECRET_ACCESS_KEY="your_s3_secret_access_key" | ||
S3_REGION="auto" # Use 'auto' for Cloudflare R2 | ||
S3_BUCKET_NAME="your_bucket_name" | ||
S3_ENDPOINT="https://<account_id>.r2.cloudflarestorage.com" # Cloudflare R2 endpoint | ||
S3_ACCOUNT_ID="your_cloudflare_account_id" # Your Cloudflare account ID | ||
S3_PUBLIC_URL="https://pub-xxx.r2.dev" # Your R2 public bucket URL | ||
|
||
# Set to false to disable all email functionality | ||
MAIL_ENABLED=false | ||
|
||
MAIL_HOST=smtp.example.com | ||
MAIL_USER=user@example.com | ||
MAIL_PASSWORD=topsecret | ||
MAIL_FROM=noreply@example.com | ||
MAIL_PORT=587 | ||
MAIL_DOMAIN=your_net | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -55,4 +55,5 @@ log-*/ | |
|
||
|
||
# Backend | ||
/backend/package-lock.json | ||
/backend/package-lock.json | ||
.env |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { Module } from '@nestjs/common'; | ||
import { ConfigModule as NestConfigModule } from '@nestjs/config'; | ||
import { AppConfigService } from './config.service'; | ||
import { EnvironmentVariables } from './env.validation'; | ||
import { plainToInstance } from 'class-transformer'; | ||
import { validateSync } from 'class-validator'; | ||
|
||
const validate = (config: Record<string, unknown>) => { | ||
const validatedConfig = plainToInstance(EnvironmentVariables, config, { | ||
enableImplicitConversion: true, | ||
}); | ||
|
||
const errors = validateSync(validatedConfig, { | ||
skipMissingProperties: false, | ||
}); | ||
|
||
if (errors.length > 0) { | ||
throw new Error(errors.toString()); | ||
} | ||
|
||
return validatedConfig; | ||
}; | ||
|
||
@Module({ | ||
imports: [ | ||
NestConfigModule.forRoot({ | ||
validate, | ||
isGlobal: true, | ||
}), | ||
], | ||
providers: [AppConfigService], | ||
exports: [AppConfigService], | ||
}) | ||
export class AppConfigModule {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { Injectable } from '@nestjs/common'; | ||
import { ConfigService as NestConfigService } from '@nestjs/config'; | ||
import { EnvironmentVariables } from './env.validation'; | ||
|
||
@Injectable() | ||
export class AppConfigService { | ||
constructor(private configService: NestConfigService<EnvironmentVariables>) {} | ||
|
||
/** | ||
* Get server port from environment | ||
*/ | ||
get port(): number { | ||
return this.configService.get('PORT'); | ||
} | ||
|
||
/** | ||
* Get JWT secret key for token generation | ||
*/ | ||
get jwtSecret(): string { | ||
return this.configService.get('JWT_SECRET'); | ||
} | ||
|
||
/** | ||
* Get JWT refresh token secret | ||
*/ | ||
get jwtRefresh(): string { | ||
return this.configService.get('JWT_REFRESH'); | ||
} | ||
|
||
/** | ||
* Get password hashing salt rounds | ||
*/ | ||
get saltRounds(): number { | ||
return this.configService.get('SALT_ROUNDS'); | ||
} | ||
|
||
/** | ||
* Get OpenAI API base URI | ||
*/ | ||
get openaiBaseUri(): string { | ||
return this.configService.get('OPENAI_BASE_URI'); | ||
} | ||
|
||
/** | ||
* Get S3/Cloudflare R2 configuration object | ||
*/ | ||
get s3Config() { | ||
return { | ||
accessKeyId: this.configService.get('S3_ACCESS_KEY_ID'), | ||
secretAccessKey: this.configService.get('S3_SECRET_ACCESS_KEY'), | ||
region: this.configService.get('S3_REGION'), | ||
bucketName: this.configService.get('S3_BUCKET_NAME'), | ||
endpoint: this.configService.get('S3_ENDPOINT'), | ||
accountId: this.configService.get('S3_ACCOUNT_ID'), | ||
publicUrl: this.configService.get('S3_PUBLIC_URL'), | ||
}; | ||
} | ||
|
||
/** | ||
* Check if S3 storage is properly configured | ||
*/ | ||
get hasS3Configured(): boolean { | ||
const config = this.s3Config; | ||
return !!( | ||
config.accessKeyId && | ||
config.secretAccessKey && | ||
config.region && | ||
(config.endpoint || config.accountId) | ||
); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
import { IsOptional, IsString, IsNumber, IsIn } from 'class-validator'; | ||
|
||
export class EnvironmentVariables { | ||
@IsNumber() | ||
PORT: number; | ||
|
||
@IsString() | ||
@IsIn(['DEV', 'PROD', 'TEST']) | ||
NODE_ENV: string; | ||
|
||
@IsString() | ||
JWT_SECRET: string; | ||
|
||
@IsString() | ||
JWT_REFRESH: string; | ||
|
||
@IsNumber() | ||
SALT_ROUNDS: number; | ||
|
||
@IsString() | ||
OPENAI_BASE_URI: string; | ||
|
||
// S3/Cloudflare R2 Configuration - all optional | ||
@IsOptional() | ||
@IsString() | ||
S3_ACCESS_KEY_ID?: string; | ||
|
||
@IsOptional() | ||
@IsString() | ||
S3_SECRET_ACCESS_KEY?: string; | ||
|
||
@IsOptional() | ||
@IsString() | ||
S3_REGION?: string; | ||
|
||
@IsOptional() | ||
@IsString() | ||
S3_BUCKET_NAME?: string; | ||
|
||
@IsOptional() | ||
@IsString() | ||
S3_ENDPOINT?: string; | ||
|
||
@IsOptional() | ||
@IsString() | ||
S3_ACCOUNT_ID?: string; | ||
|
||
@IsOptional() | ||
@IsString() | ||
S3_PUBLIC_URL?: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
import { ForbiddenException, HttpStatus } from '@nestjs/common'; | ||
import { GraphQLError } from 'graphql'; | ||
|
||
export const PROJECT_DAILY_LIMIT = 3; // Maximum number of projects a user can create per day | ||
|
||
export enum ProjectErrorCode { | ||
DAILY_LIMIT_EXCEEDED = 'DAILY_LIMIT_EXCEEDED', | ||
} | ||
|
||
export class ProjectRateLimitException extends ForbiddenException { | ||
constructor(limit: number) { | ||
super( | ||
`Daily project creation limit of ${limit} reached. Please try again tomorrow.`, | ||
); | ||
} | ||
|
||
getGraphQLError(): GraphQLError { | ||
return new GraphQLError(this.message, { | ||
extensions: { | ||
code: ProjectErrorCode.DAILY_LIMIT_EXCEEDED, | ||
limit: PROJECT_DAILY_LIMIT, | ||
status: HttpStatus.TOO_MANY_REQUESTS, | ||
}, | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.