Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
30 changes: 30 additions & 0 deletions compose.dev.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,35 @@ services:
depends_on:
- db

user-server:
build:
context: ./user-server
dockerfile: ./docker/Dockerfile.dev
ports:
- 4200:4200
env_file:
- ./secrets/order-db/.db.env
develop:
watch:
- action: sync
path: ./user-server/src
target: /usr/user-server/src
ignore:
- node_modules/
- dist/
- action: rebuild
path: ./user-server/package.json
- action: rebuild
path: ./user-server/tsconfig.json
- action: rebuild
path: ./user-server/nest-cli.json
- action: rebuild
path: ./user-server/docker
- action: rebuild
path: ./user-server/prisma/schema.prisma
depends_on:
- db

ionic-client:
build:
context: ./ionic-client
Expand All @@ -92,6 +121,7 @@ services:
- ionic-client
- calc-server
- order-server
- user-server
- db

db:
Expand Down
14 changes: 14 additions & 0 deletions compose.prod.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ services:
volumes:
- ./order-server/static:/usr/order-server/static

user-server:
build:
context: ./user-server
dockerfile: ./docker/Dockerfile.prod
ports:
- 4200:4200
env_file:
- ./secrets/order-db/.db.env
environment:
- DB_HOST=host.docker.internal
extra_hosts:
- "host.docker.internal:host-gateway"

ionic-client:
build:
context: ./ionic-client
Expand All @@ -54,3 +67,4 @@ services:
- ionic-client
- calc-server
- order-server
- user-server
20 changes: 20 additions & 0 deletions user-server/docker/Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
FROM node:20-alpine

ENV NODE_ENV production

WORKDIR /usr/user-server

RUN npm i -g typescript
RUN npm i -g @nestjs/cli

# Install dependencies
COPY package*.json ./
RUN npm install

# Copy source files
COPY ./ ./

EXPOSE 4200
RUN npm run prisma:gen

CMD ["sh", "-c", "npx prisma migrate deploy && npm run start:dev"]
19 changes: 19 additions & 0 deletions user-server/docker/Dockerfile.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM node:20-alpine

ENV NODE_ENV production

WORKDIR /usr/user-server

RUN npm i -g typescript
RUN npm i -g @nestjs/cli

COPY ./package*.json ./
RUN npm install

COPY ./ ./
RUN npm run prisma:gen
RUN npm run build

EXPOSE 4200

CMD ["sh", "-c", "npx prisma migrate deploy && npm run start:prod"]
55 changes: 55 additions & 0 deletions user-server/eslint.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import js from '@eslint/js';
import typescript from '@typescript-eslint/eslint-plugin';
import typescriptParser from '@typescript-eslint/parser';
import prettier from 'eslint-config-prettier';
import globals from 'globals';

export default [
js.configs.recommended,
{
files: ['**/*.ts'],
languageOptions: {
parser: typescriptParser,
parserOptions: {
project: './tsconfig.json',
tsconfigRootDir: import.meta.dirname,
sourceType: 'module',
},
globals: {
...globals.node,
...globals.es2021,
},
},
plugins: {
'@typescript-eslint': typescript,
},
rules: {
...typescript.configs.recommended.rules,
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
},
},
{
files: ['**/*.spec.ts', '**/*.test.ts', 'test/**/*.ts'],
languageOptions: {
globals: {
...globals.jest,
describe: 'readonly',
it: 'readonly',
test: 'readonly',
expect: 'readonly',
beforeEach: 'readonly',
afterEach: 'readonly',
beforeAll: 'readonly',
afterAll: 'readonly',
jest: 'readonly',
},
},
},
prettier,
{
ignores: ['dist/**', 'node_modules/**', 'coverage/**'],
},
];
8 changes: 8 additions & 0 deletions user-server/nest-cli.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"compilerOptions": {
"deleteOutDir": true
}
}
79 changes: 79 additions & 0 deletions user-server/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"name": "user-server",
"version": "0.0.1",
"description": "User service for authentication and user management",
"author": "",
"private": true,
"license": "UNLICENSED",
"type": "module",
"scripts": {
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
"start": "nest start",
"start:dev": "nest start --watch",
"start:debug": "nest start --debug --watch",
"start:prod": "node dist/src/main",
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
"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",
"prisma:gen": "npx prisma generate",
"prisma:seed": "npx prisma db seed"
},
"dependencies": {
"@nestjs/common": "^10.0.0",
"@nestjs/config": "^3.2.0",
"@nestjs/core": "^10.0.0",
"@nestjs/jwt": "^10.2.0",
"@nestjs/platform-express": "^10.0.0",
"@prisma/client": "^6.1.0",
"bcryptjs": "^2.4.3",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.1",
"pg": "^8.13.1",
"reflect-metadata": "^0.2.0",
"rxjs": "^7.8.1"
},
"devDependencies": {
"@nestjs/cli": "^10.0.0",
"@nestjs/schematics": "^10.0.0",
"@nestjs/testing": "^10.0.0",
"@types/bcryptjs": "^2.4.6",
"@types/express": "^5.0.0",
"@types/jest": "^29.5.2",
"@types/node": "^22.10.2",
"@types/supertest": "^6.0.0",
"@typescript-eslint/eslint-plugin": "^8.0.0",
"@typescript-eslint/parser": "^8.0.0",
"eslint": "^9.0.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.0.0",
"jest": "^29.5.0",
"prettier": "^3.0.0",
"prisma": "^6.1.0",
"source-map-support": "^0.5.21",
"supertest": "^7.0.0",
"ts-jest": "^29.1.0",
"ts-loader": "^9.4.3",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.1.3"
},
"jest": {
"moduleFileExtensions": ["js", "json", "ts"],
"rootDir": "src",
"testRegex": ".*\\\\.spec\\\\.ts$",
"transform": {
"^.+\\\\.(t|j)s$": "ts-jest"
},
"collectCoverageFrom": ["**/*.(t|j)s"],
"coverageDirectory": "../coverage",
"testEnvironment": "node"
},
"prisma": {
"schema": "prisma/schema.prisma",
"seed": "ts-node --compiler-options '{\\\"module\\\":\\\"CommonJS\\\"}' prisma/seed.ts"
}
}
23 changes: 23 additions & 0 deletions user-server/prisma/migrations/00000000000000_init/migration.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
-- CreateTable
CREATE TABLE "User" (
"id" SERIAL PRIMARY KEY,
"email" TEXT NOT NULL UNIQUE,
"password" TEXT NOT NULL,
"name" TEXT,
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updatedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP
);

-- Trigger to keep updatedAt current (PostgreSQL specific)
CREATE OR REPLACE FUNCTION set_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW."updatedAt" = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER set_user_updated_at
BEFORE UPDATE ON "User"
FOR EACH ROW
EXECUTE PROCEDURE set_updated_at();
16 changes: 16 additions & 0 deletions user-server/prisma/prisma.service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { PrismaClient } from '@prisma/client';

@Injectable()
export class PrismaService
extends PrismaClient
implements OnModuleInit, OnModuleDestroy
{
async onModuleInit() {
await this.$connect();
}

async onModuleDestroy() {
await this.$disconnect();
}
}
17 changes: 17 additions & 0 deletions user-server/prisma/schema.prisma
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
generator client {
provider = "prisma-client-js"
}

datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}

model User {
id Int @id @default(autoincrement())
email String @unique
password String
name String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
35 changes: 35 additions & 0 deletions user-server/prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { PrismaClient } from '@prisma/client';
import { hashSync } from 'bcryptjs';

const prisma = new PrismaClient();

async function main() {
const email = process.env.DEFAULT_USER_EMAIL ?? 'admin@example.com';
const name = process.env.DEFAULT_USER_NAME ?? 'Default User';
const password = process.env.DEFAULT_USER_PASSWORD ?? 'changeme';
const hashedPassword = hashSync(password, 10);

await prisma.user.upsert({
where: { id: 1 },
update: {
email,
name,
password: hashedPassword,
},
create: {
id: 1,
email,
name,
password: hashedPassword,
},
});
}

main()
.catch((error) => {
console.error('Failed to seed default user', error);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
10 changes: 10 additions & 0 deletions user-server/src/app.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { AuthModule } from './auth/auth.module';
import { PrismaModule } from './prisma/prisma.module';
import { UsersModule } from './users/users.module';

@Module({
imports: [ConfigModule.forRoot({ isGlobal: true }), PrismaModule, UsersModule, AuthModule],
})
export class AppModule {}
19 changes: 19 additions & 0 deletions user-server/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Body, Controller, Post } from '@nestjs/common';
import { AuthService } from './auth.service';
import { LogInDto } from './dto/log-in.dto';
import { SignInDto } from './dto/sign-in.dto';

@Controller('auth')
export class AuthController {
constructor(private readonly authService: AuthService) {}

@Post('sign-in')
async signIn(@Body() dto: SignInDto) {
return this.authService.signIn(dto);
}

@Post('log-in')
async logIn(@Body() dto: LogInDto) {
return this.authService.logIn(dto);
}
}
18 changes: 18 additions & 0 deletions user-server/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { UsersModule } from '../users/users.module';
import { AuthController } from './auth.controller';
import { AuthService } from './auth.service';

@Module({
imports: [
UsersModule,
JwtModule.register({
secret: process.env.JWT_SECRET ?? 'dev-secret',
signOptions: { expiresIn: '1h' },
}),
],
controllers: [AuthController],
providers: [AuthService],
})
export class AuthModule {}
Loading
Loading