This commit is contained in:
48
backend/Dockerfile
Normal file
48
backend/Dockerfile
Normal file
@ -0,0 +1,48 @@
|
||||
# Build stage
|
||||
FROM node:20-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy package files
|
||||
COPY package*.json ./
|
||||
|
||||
# Install ALL dependencies (including devDependencies for build)
|
||||
RUN npm install --include=dev
|
||||
|
||||
# Copy source code
|
||||
COPY . .
|
||||
|
||||
# Build the application
|
||||
RUN npm run build
|
||||
|
||||
# Production stage
|
||||
FROM node:20-alpine
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install only production dependencies
|
||||
COPY package*.json ./
|
||||
RUN npm install --only=production && npm cache clean --force
|
||||
|
||||
# Copy built application from builder
|
||||
COPY --from=builder /app/dist ./dist
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nestjs -u 1001
|
||||
|
||||
# Change ownership
|
||||
RUN chown -R nestjs:nodejs /app
|
||||
|
||||
# Switch to non-root user
|
||||
USER nestjs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 4001
|
||||
|
||||
# Health check
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s \
|
||||
CMD node -e "require('http').get('http://localhost:4001/health', (r) => {process.exit(r.statusCode === 200 ? 0 : 1)})"
|
||||
|
||||
# Start the application
|
||||
CMD ["node", "dist/main"]
|
||||
@ -6,7 +6,7 @@ import tseslint from 'typescript-eslint';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: ['eslint.config.mjs'],
|
||||
ignores: ['eslint.config.mjs', 'coverage'],
|
||||
},
|
||||
eslint.configs.recommended,
|
||||
...tseslint.configs.recommendedTypeChecked,
|
||||
@ -27,8 +27,12 @@ export default tseslint.config(
|
||||
{
|
||||
rules: {
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
'@typescript-eslint/no-floating-promises': 'warn',
|
||||
'@typescript-eslint/no-unsafe-argument': 'warn',
|
||||
'@typescript-eslint/no-floating-promises': 'error',
|
||||
'@typescript-eslint/no-unsafe-argument': 'error',
|
||||
'@typescript-eslint/no-unsafe-assignment': 'error',
|
||||
'@typescript-eslint/no-unsafe-call': 'error',
|
||||
'@typescript-eslint/no-unsafe-member-access': 'error',
|
||||
'@typescript-eslint/no-unsafe-return': 'error',
|
||||
"prettier/prettier": ["error", { endOfLine: "auto" }],
|
||||
},
|
||||
},
|
||||
|
||||
@ -13,12 +13,13 @@
|
||||
"start:dev": "nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||
"lint": "tsc --noEmit && eslint \"{src,apps,libs,test}/**/*.ts\" && prettier --check \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||
"test": "jest",
|
||||
"test:watch": "jest --watch",
|
||||
"test:cov": "jest --coverage",
|
||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||
"clean": "rm -rf dist node_modules"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^11.0.1",
|
||||
|
||||
@ -9,4 +9,9 @@ export class AppController {
|
||||
getHello(): string {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
|
||||
@Get('health')
|
||||
health(): { status: string } {
|
||||
return { status: 'ok' };
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,9 +1,4 @@
|
||||
import {
|
||||
IsString,
|
||||
IsOptional,
|
||||
IsEnum,
|
||||
MaxLength,
|
||||
} from 'class-validator';
|
||||
import { IsString, IsOptional, IsEnum, MaxLength } from 'class-validator';
|
||||
import { IdeaStatus, IdeaPriority } from '../entities/idea.entity';
|
||||
|
||||
export class CreateIdeaDto {
|
||||
|
||||
@ -49,7 +49,12 @@ export class Idea {
|
||||
@Column({ type: 'varchar', length: 100, nullable: true })
|
||||
module: string | null;
|
||||
|
||||
@Column({ name: 'target_audience', type: 'varchar', length: 255, nullable: true })
|
||||
@Column({
|
||||
name: 'target_audience',
|
||||
type: 'varchar',
|
||||
length: 255,
|
||||
nullable: true,
|
||||
})
|
||||
targetAudience: string | null;
|
||||
|
||||
@Column({ type: 'text', nullable: true })
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { Injectable, NotFoundException } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
import { Repository, Like, FindOptionsWhere } from 'typeorm';
|
||||
import { Repository, FindOptionsWhere } from 'typeorm';
|
||||
import { Idea } from './entities/idea.entity';
|
||||
import { CreateIdeaDto, UpdateIdeaDto, QueryIdeasDto } from './dto';
|
||||
|
||||
@ -119,7 +119,7 @@ export class IdeasService {
|
||||
.createQueryBuilder('idea')
|
||||
.select('DISTINCT idea.module', 'module')
|
||||
.where('idea.module IS NOT NULL')
|
||||
.getRawMany();
|
||||
.getRawMany<{ module: string }>();
|
||||
|
||||
return result.map((r) => r.module).filter(Boolean);
|
||||
}
|
||||
|
||||
@ -29,4 +29,4 @@ async function bootstrap() {
|
||||
await app.listen(port);
|
||||
console.log(`Backend running on http://localhost:${port}`);
|
||||
}
|
||||
bootstrap();
|
||||
void bootstrap();
|
||||
|
||||
Reference in New Issue
Block a user