Merge pull request 'Feature: Use Api generated by swagger' (#5) from feature/use-generated-api into main
Reviewed-on: #5
This commit is contained in:
commit
6285e46b7a
|
@ -0,0 +1,210 @@
|
|||
{
|
||||
"root": true,
|
||||
"plugins": ["import", "prettier", "@stylistic/eslint-plugin-ts", "@stylistic/eslint-plugin", "sort-class-members", "unused-imports"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"prettier",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"rules": {
|
||||
"@stylistic/ts/lines-between-class-members": [
|
||||
"error",
|
||||
{
|
||||
"enforce": [
|
||||
{ "blankLine": "always", "prev": "*", "next": "method" },
|
||||
{ "blankLine": "always", "prev": "method", "next": "*" },
|
||||
{ "blankLine": "never", "prev": "field", "next": "field" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"@stylistic/ts/block-spacing": ["error"],
|
||||
"@stylistic/ts/brace-style": "off",
|
||||
"@stylistic/ts/key-spacing": ["error", { "afterColon": true }],
|
||||
"@stylistic/ts/keyword-spacing": ["error", { "before": true }],
|
||||
"@stylistic/ts/no-extra-parens": ["error", "all", {
|
||||
"nestedBinaryExpressions": false,
|
||||
"ternaryOperandBinaryExpressions": false
|
||||
}],
|
||||
"@stylistic/ts/no-extra-semi": ["error"],
|
||||
"@stylistic/ts/object-curly-spacing": ["error", "always"],
|
||||
"@stylistic/ts/quotes": ["error", "single"],
|
||||
"@stylistic/ts/semi": ["error", "always"],
|
||||
"@stylistic/ts/space-before-blocks": ["error"],
|
||||
"@stylistic/ts/space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
||||
"@stylistic/ts/space-infix-ops": ["error"],
|
||||
// "@stylistic/ts/max-statements-per-line": ["error", { "max": 1 }],
|
||||
// "@stylistic/ts/multiline-ternary": ["error", "always"],
|
||||
// "@stylistic/ts/newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }],
|
||||
// "@stylistic/ts/no-confusing-arrow": ["error"],
|
||||
// "@stylistic/ts/no-floating-decimal": ["error"],
|
||||
// "@stylistic/ts/no-mixed-operators": ["error"],
|
||||
// "@stylistic/ts/no-mixed-spaces-and-tabs": ["error"],/
|
||||
// "@stylistic/ts/no-multi-spaces": ["error"],
|
||||
// "@stylistic/ts/no-multiple-empty-lines": ["error", { "max": 2 }],
|
||||
// "@stylistic/ts/no-tabs": ["error", { "allowIndentationTabs": true }],
|
||||
// "@stylistic/ts/no-whitespace-before-property": ["error"],
|
||||
// "@stylistic/ts/nonblock-statement-body-position": ["error", "below"],
|
||||
// "@stylistic/ts/object-curly-newline": ["error", "always"],
|
||||
// "@stylistic/ts/object-property-newline": ["error"],
|
||||
// "@stylistic/ts/one-var-declaration-per-line": ["error", "always"],
|
||||
// "@stylistic/ts/operator-linebreak": ["error", "before"],
|
||||
// "@stylistic/ts/padded-blocks": ["error", "never"],
|
||||
// "@stylistic/ts/rest-spread-spacing": ["error", "never"],
|
||||
// "@stylistic/ts/semi-spacing": ["error"],
|
||||
// "@stylistic/ts/semi-style": ["error", "last"],
|
||||
// "@stylistic/ts/space-in-parens": ["error", "never"],
|
||||
// "@stylistic/ts/space-unary-ops": ["error"],
|
||||
// "@stylistic/ts/template-curly-spacing": ["error"],
|
||||
// "@stylistic/ts/template-tag-spacing": ["error"],
|
||||
// "@stylistic/ts/wrap-regex": ["error"],
|
||||
"no-console": ["warn", { "allow": ["warn", "error"] }],
|
||||
"no-debugger": "error",
|
||||
"no-var": ["error"],
|
||||
"eqeqeq": ["error", "always"],
|
||||
"no-eval": "error",
|
||||
"prefer-const": ["error", { "destructuring": "all", "ignoreReadBeforeAssign": true }],
|
||||
"prettier/prettier": ["error", { "printWidth": 80 }],
|
||||
"no-unused-vars": "off",
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error",
|
||||
{
|
||||
"accessibility": "explicit"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"error",
|
||||
{
|
||||
"allowExpressions": false,
|
||||
"allowTypedFunctionExpressions": true,
|
||||
"allowHigherOrderFunctions": false,
|
||||
"allowDirectConstAssertionInArrowFunctions": false,
|
||||
"allowConciseArrowFunctionExpressionsStartingWithVoid": false
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": [
|
||||
"error",
|
||||
{
|
||||
"default": [
|
||||
"public-static-field",
|
||||
"protected-static-field",
|
||||
"private-static-field",
|
||||
"public-instance-field",
|
||||
"protected-instance-field",
|
||||
"private-instance-field",
|
||||
"public-constructor",
|
||||
"protected-constructor",
|
||||
"private-constructor",
|
||||
"public-static-method",
|
||||
"protected-static-method",
|
||||
"private-static-method",
|
||||
"public-instance-method",
|
||||
"protected-instance-method",
|
||||
"private-instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
// https://github.com/bryanrsmith/eslint-plugin-sort-class-members -> Read Docs and replace @typescript-eslint/member-ordering
|
||||
// "sort-class-members/sort-class-members": [
|
||||
// 2,
|
||||
// {
|
||||
// "order": [
|
||||
// "[public-properties]",
|
||||
// "[protected-properties]",
|
||||
// "[private-properties]",
|
||||
// "everything-else"
|
||||
// ],
|
||||
// "groups": {
|
||||
// "public-properties": [{ "type": "property", "accessibility": "public" }],
|
||||
// "protected-properties": [{ "type": "property", "accessibility": "protected" }],
|
||||
// "private-properties": [{ "type": "property", "accessibility": "private" }]
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
"padding-line-between-statements": [
|
||||
"error",
|
||||
{ "blankLine": "always", "prev": "const", "next": "*" },
|
||||
{ "blankLine": "always", "prev": "let", "next": "*" },
|
||||
{ "blankLine": "always", "prev": "var", "next": "*" },
|
||||
{ "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"] }
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "after-used",
|
||||
"ignoreRestSiblings": false
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/typedef": [
|
||||
"error",
|
||||
{
|
||||
"arrayDestructuring": true,
|
||||
"arrowParameter": false,
|
||||
"memberVariableDeclaration": true,
|
||||
"objectDestructuring": true,
|
||||
"parameter": true,
|
||||
"propertyDeclaration": true,
|
||||
"variableDeclaration": false,
|
||||
"variableDeclarationIgnoreFunction": true
|
||||
}
|
||||
],
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"groups": [
|
||||
"builtin",
|
||||
"external",
|
||||
"internal",
|
||||
"parent",
|
||||
"sibling",
|
||||
"index"
|
||||
],
|
||||
"pathGroups": [
|
||||
{
|
||||
"pattern": "@angular/**",
|
||||
"group": "external",
|
||||
"position": "before"
|
||||
},
|
||||
{
|
||||
"pattern": "@app/**",
|
||||
"group": "internal",
|
||||
"position": "before"
|
||||
},
|
||||
{
|
||||
"pattern": "@env/**",
|
||||
"group": "internal",
|
||||
"position": "before"
|
||||
}
|
||||
],
|
||||
"pathGroupsExcludedImportTypes": ["builtin"],
|
||||
"newlines-between": "always",
|
||||
"alphabetize": {
|
||||
"order": "asc",
|
||||
"caseInsensitive": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["*.dto.ts", "*.entity.ts"],
|
||||
"rules": {
|
||||
"@stylistic/ts/lines-between-class-members": "off"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# Compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
|
||||
# IDEs and editors
|
||||
.idea/
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# Visual Studio Code
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# Miscellaneous
|
||||
/.angular/cache
|
||||
.sass-cache/
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
|
||||
.env
|
|
@ -0,0 +1,6 @@
|
|||
coverage
|
||||
dist
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
||||
/frontend/pnpm-lock.yaml
|
||||
/backend/pnpm-lock.yaml
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"npm.packageManager": "pnpm"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"extends": ["./../.eslintrc"],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"env": {
|
||||
"node": true,
|
||||
"jest": true
|
||||
}
|
||||
}
|
|
@ -1,25 +0,0 @@
|
|||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
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',
|
||||
},
|
||||
};
|
|
@ -2,6 +2,7 @@
|
|||
/dist
|
||||
/node_modules
|
||||
/build
|
||||
/docs
|
||||
|
||||
# Logs
|
||||
logs
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
coverage
|
||||
dist
|
||||
node_modules
|
|
@ -1,7 +0,0 @@
|
|||
{
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"semi": true,
|
||||
"endOfLine": "auto",
|
||||
"bracketSameLine": true
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
{
|
||||
"npmName": "Ticket-API-Services",
|
||||
"npmVersion": "0.0.0",
|
||||
"providedIn": "root",
|
||||
"withInterfaces": true,
|
||||
"enumNameSuffix": "Enum",
|
||||
"supportsES6": true,
|
||||
"ngVersion": "17.0.0",
|
||||
"modelSuffix": "Model",
|
||||
"stringEnums": true,
|
||||
"enumPropertyNaming": "PascalCase",
|
||||
"modelPropertyNaming": "original",
|
||||
"fileNaming": "camelCase",
|
||||
"paramNaming": "camelCase"
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
"ngVersion": "17.0.0",
|
||||
"npmRepository": null,
|
||||
"configurationPrefix": null,
|
||||
"apiModulePrefix" : "TicketApi",
|
||||
"apiModulePrefix": "TicketApi",
|
||||
"providedIn": "any",
|
||||
"fileNaming": "camelCase",
|
||||
"paramNaming": "camelCase",
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
"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",
|
||||
"build:api": "pnpm openapi-generator-cli generate"
|
||||
"prettier:fix": "prettier --write .",
|
||||
"prettier:check": "prettier --check .",
|
||||
"foramt": "pnpm lint && prettier:fix"
|
||||
},
|
||||
"dependencies": {
|
||||
"@nestjs/common": "^10.0.0",
|
||||
|
@ -32,10 +34,8 @@
|
|||
"argon2": "^0.40.1",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.14.1",
|
||||
"install": "^0.13.0",
|
||||
"passport": "^0.7.0",
|
||||
"passport-jwt": "^4.0.1",
|
||||
"passport-local": "^1.0.0",
|
||||
"pg": "^8.11.5",
|
||||
"reflect-metadata": "^0.2.0",
|
||||
"rxjs": "^7.8.1",
|
||||
|
@ -51,11 +51,8 @@
|
|||
"@types/jest": "^29.5.2",
|
||||
"@types/node": "^20.3.1",
|
||||
"@types/supertest": "^6.0.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
||||
"@typescript-eslint/parser": "^6.0.0",
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"prettier": "^3.0.0",
|
||||
"source-map-support": "^0.5.21",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,5 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
|
||||
|
|
|
@ -1,12 +1,13 @@
|
|||
import { Controller, Get } from '@nestjs/common';
|
||||
|
||||
import { AppService } from './app.service';
|
||||
|
||||
@Controller()
|
||||
export class AppController {
|
||||
constructor(private readonly appService: AppService) {}
|
||||
public constructor(private readonly appService: AppService) {}
|
||||
|
||||
@Get()
|
||||
getHello(): string {
|
||||
public getHello(): string {
|
||||
return this.appService.getHello();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
|
||||
import { AppController } from './app.controller';
|
||||
import { AppService } from './app.service';
|
||||
import { ConfigModule } from '@nestjs/config';
|
||||
import { DatabaseModule } from './modules/database-module/database.module';
|
||||
import { CorsMiddleware } from './middleware/cors-middleware/cors.middlware';
|
||||
import { CspMiddleware } from './middleware/csp-middleware/csp.middleware';
|
||||
import { SecurityHeadersMiddleware } from './middleware/security-middleware/security.middleware';
|
||||
import { HttpsRedirectMiddleware } from './middleware/https-middlware/https-redirect.middleware';
|
||||
import { SecurityHeadersMiddleware } from './middleware/security-middleware/security.middleware';
|
||||
import { AuthModule } from './modules/auth-module/auth.module';
|
||||
import { AccessTokenGuard } from './modules/auth-module/common/guards';
|
||||
import { CorsMiddleware } from './middleware/cors-middleware/cors.middlware';
|
||||
import { DatabaseModule } from './modules/database-module/database.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
@ -22,7 +23,7 @@ import { CorsMiddleware } from './middleware/cors-middleware/cors.middlware';
|
|||
providers: [AppService, { provide: 'APP_GUARD', useClass: AccessTokenGuard }],
|
||||
})
|
||||
export class AppModule {
|
||||
configure(consumer: MiddlewareConsumer) {
|
||||
public configure(consumer: MiddlewareConsumer): void {
|
||||
consumer
|
||||
// TODO Redirect via Reverse Proxy all HTTP requests to HTTPS
|
||||
.apply(
|
||||
|
|
|
@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
|||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
getHello(): string {
|
||||
public getHello(): string {
|
||||
return 'Hello World!';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,20 +9,20 @@ import {
|
|||
@Entity()
|
||||
export class UserCredentials {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: number;
|
||||
public id: number;
|
||||
|
||||
@Column({ unique: true })
|
||||
email: string;
|
||||
public email: string;
|
||||
|
||||
@Column()
|
||||
hash: string;
|
||||
public hash: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
hashedRt?: string;
|
||||
public hashedRt?: string;
|
||||
|
||||
@CreateDateColumn()
|
||||
createdAt: Date;
|
||||
public createdAt: Date;
|
||||
|
||||
@UpdateDateColumn()
|
||||
updatedAt: Date;
|
||||
public updatedAt: Date;
|
||||
}
|
||||
|
|
|
@ -1,28 +1,46 @@
|
|||
import { NestFactory } from '@nestjs/core';
|
||||
import { AppModule } from './app.module';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
import { ValidationPipe } from '@nestjs/common';
|
||||
import * as fs from 'fs';
|
||||
import { join } from 'path';
|
||||
|
||||
async function setupSwagger(app) {
|
||||
import { INestApplication, ValidationPipe } from '@nestjs/common';
|
||||
import { NestFactory } from '@nestjs/core';
|
||||
import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger';
|
||||
|
||||
import { AppModule } from './app.module';
|
||||
|
||||
async function setupSwagger(app: INestApplication): Promise<void> {
|
||||
const config = new DocumentBuilder()
|
||||
.setTitle('Tickets API')
|
||||
.setDescription('Description of the API')
|
||||
.setVersion('0.0.0')
|
||||
.build();
|
||||
|
||||
const document = SwaggerModule.createDocument(app, config);
|
||||
|
||||
SwaggerModule.setup('api', app, document);
|
||||
|
||||
const docsDir = join(process.cwd(), 'docs');
|
||||
|
||||
if (!fs.existsSync(docsDir)) {
|
||||
fs.mkdirSync(docsDir);
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
join(docsDir, 'swagger.json'),
|
||||
JSON.stringify(document, null, 2)
|
||||
);
|
||||
}
|
||||
|
||||
async function setupPrefix(app) {
|
||||
async function setupPrefix(app: INestApplication): Promise<void> {
|
||||
app.setGlobalPrefix('api');
|
||||
}
|
||||
|
||||
async function setupClassValidator(app) {
|
||||
async function setupClassValidator(app: INestApplication): Promise<void> {
|
||||
app.useGlobalPipes(new ValidationPipe());
|
||||
}
|
||||
|
||||
async function bootstrap() {
|
||||
async function bootstrap(): Promise<void> {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
|
||||
await setupSwagger(app);
|
||||
await setupPrefix(app);
|
||||
await setupClassValidator(app);
|
||||
|
|
|
@ -4,7 +4,7 @@ import { Request, Response, NextFunction } from 'express';
|
|||
|
||||
@Injectable()
|
||||
export class CorsMiddleware implements NestMiddleware {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
public constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
public use(req: Request, res: Response, next: NextFunction): void {
|
||||
if (this.configService.get<string>('NODE_ENV') === 'development') {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
@Injectable()
|
||||
export class CspMiddleware implements NestMiddleware {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
public constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
public use(req: Request, res: Response, next: NextFunction): void {
|
||||
const cspDirectives = this.configService.get<string>('CSP_DIRECTIVES');
|
||||
|
||||
if (cspDirectives) {
|
||||
res.setHeader('Content-Security-Policy', cspDirectives);
|
||||
}
|
||||
|
|
|
@ -4,12 +4,13 @@ import { NextFunction, Request, Response } from 'express';
|
|||
|
||||
@Injectable()
|
||||
export class HttpsRedirectMiddleware implements NestMiddleware {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
public constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
public use(req: Request, res: Response, next: NextFunction) {
|
||||
public use(req: Request, res: Response, next: NextFunction): void {
|
||||
if (this.configService.get<string>('NODE_ENV') === 'production') {
|
||||
if (req.protocol === 'http') {
|
||||
const httpsUrl = `https://${req.headers.host}${req.url}`;
|
||||
|
||||
res.redirect(httpsUrl);
|
||||
} else {
|
||||
next();
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Request, Response, NextFunction } from 'express';
|
||||
|
||||
@Injectable()
|
||||
export class SecurityHeadersMiddleware implements NestMiddleware {
|
||||
constructor(private readonly configService: ConfigService) {}
|
||||
public constructor(private readonly configService: ConfigService) {}
|
||||
|
||||
public use(req: Request, res: Response, next: NextFunction): void {
|
||||
if (this.configService.get<string>('NODE_ENV') === 'production') {
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { AuthController } from './controller/auth.controller';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { AccessTokenStrategy, RefreshTokenStrategy } from './strategies';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { UserCredentials } from 'src/entities/user-credentials.entity';
|
||||
|
||||
import { AuthController } from './controller/auth.controller';
|
||||
import { UserRepository } from './repositories/user.repository';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { EncryptionService } from './services/encryption.service';
|
||||
import { TokenManagementService } from './services/token-management.service';
|
||||
import { AccessTokenStrategy, RefreshTokenStrategy } from './strategies';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
|
|
|
@ -5,6 +5,7 @@ export const GetCurrentUserId = createParamDecorator(
|
|||
(_: undefined, context: ExecutionContext): number => {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
const user = request.user as JwtPayload;
|
||||
|
||||
return user.sub;
|
||||
}
|
||||
);
|
||||
|
|
|
@ -7,6 +7,7 @@ export const GetCurrentUser = createParamDecorator(
|
|||
context: ExecutionContext
|
||||
) => {
|
||||
const request = context.switchToHttp().getRequest();
|
||||
|
||||
if (!data) return request.user;
|
||||
return request.user[data];
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { SetMetadata } from '@nestjs/common';
|
||||
import { CustomDecorator, SetMetadata } from '@nestjs/common';
|
||||
|
||||
export const Public = () => SetMetadata('isPublic', true);
|
||||
export const Public = (): CustomDecorator<string> =>
|
||||
SetMetadata('isPublic', true);
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
import { Injectable, ExecutionContext } from '@nestjs/common';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import { AuthGuard } from '@nestjs/passport';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Injectable()
|
||||
export class AccessTokenGuard extends AuthGuard('jwt-access-token') {
|
||||
constructor(private readonly reflector: Reflector) {
|
||||
public constructor(private readonly reflector: Reflector) {
|
||||
super();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { AuthGuard } from '@nestjs/passport';
|
||||
|
||||
export class RefreshTokenGuard extends AuthGuard('jwt-refresh-token') {
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,16 +6,17 @@ import {
|
|||
HttpStatus,
|
||||
UseGuards,
|
||||
} from '@nestjs/common';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
import { TokensDto, UserCredentialsDto } from '../models/dto';
|
||||
import { RefreshTokenGuard } from '../common/guards';
|
||||
import { ApiCreatedResponse, ApiHeader, ApiTags } from '@nestjs/swagger';
|
||||
|
||||
import { GetCurrentUser, GetCurrentUserId, Public } from '../common/decorators';
|
||||
import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger';
|
||||
import { RefreshTokenGuard } from '../common/guards';
|
||||
import { TokensDto, UserCredentialsDto } from '../models/dto';
|
||||
import { AuthService } from '../services/auth.service';
|
||||
|
||||
@ApiTags('Authentication')
|
||||
@Controller('auth')
|
||||
export class AuthController {
|
||||
constructor(private readonly authService: AuthService) {}
|
||||
public constructor(private readonly authService: AuthService) {}
|
||||
|
||||
@ApiCreatedResponse({
|
||||
description: 'User signed up successfully',
|
||||
|
@ -53,6 +54,13 @@ export class AuthController {
|
|||
return this.authService.logout(userId);
|
||||
}
|
||||
|
||||
@ApiHeader({
|
||||
name: 'Authorization',
|
||||
required: true,
|
||||
schema: {
|
||||
example: 'Bearer <refresh_token>',
|
||||
},
|
||||
})
|
||||
@ApiCreatedResponse({
|
||||
description: 'User tokens refreshed successfully',
|
||||
type: TokensDto,
|
||||
|
|
|
@ -5,7 +5,7 @@ import { Repository } from 'typeorm';
|
|||
|
||||
@Injectable()
|
||||
export class UserRepository {
|
||||
constructor(
|
||||
public constructor(
|
||||
@InjectRepository(UserCredentials)
|
||||
private readonly repository: Repository<UserCredentials>
|
||||
) {}
|
||||
|
@ -15,6 +15,7 @@ export class UserRepository {
|
|||
hash: string
|
||||
): Promise<UserCredentials> {
|
||||
const user = this.repository.create({ email, hash });
|
||||
|
||||
return this.repository.save(user);
|
||||
}
|
||||
|
||||
|
@ -35,6 +36,7 @@ export class UserRepository {
|
|||
hashedRt: string | null
|
||||
): Promise<number> {
|
||||
const result = await this.repository.update(userId, { hashedRt });
|
||||
|
||||
return result.affected ?? 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,14 @@
|
|||
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||
|
||||
import { TokensDto, UserCredentialsDto } from '../models/dto';
|
||||
import { EncryptionService } from './encryption.service';
|
||||
import { UserRepository } from '../repositories/user.repository';
|
||||
|
||||
import { EncryptionService } from './encryption.service';
|
||||
import { TokenManagementService } from './token-management.service';
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
public constructor(
|
||||
private readonly userRepository: UserRepository,
|
||||
private readonly tokenManagementService: TokenManagementService,
|
||||
private readonly encryptionService: EncryptionService
|
||||
|
@ -20,6 +22,7 @@ export class AuthService {
|
|||
userCredentials.email,
|
||||
passwordHashed
|
||||
);
|
||||
|
||||
return this.generateAndPersistTokens(user.id, user.email);
|
||||
}
|
||||
|
||||
|
@ -27,6 +30,7 @@ export class AuthService {
|
|||
const user = await this.userRepository.findUserByEmail(
|
||||
userCredentials.email
|
||||
);
|
||||
|
||||
if (!user) {
|
||||
throw new ForbiddenException('Access Denied');
|
||||
}
|
||||
|
@ -35,6 +39,7 @@ export class AuthService {
|
|||
userCredentials.password,
|
||||
user.hash
|
||||
);
|
||||
|
||||
if (!passwordMatch) {
|
||||
throw new ForbiddenException('Access Denied');
|
||||
}
|
||||
|
@ -47,6 +52,7 @@ export class AuthService {
|
|||
refreshToken: string
|
||||
): Promise<TokensDto> {
|
||||
const user = await this.userRepository.findUserById(userId);
|
||||
|
||||
if (!user || !user.hashedRt) {
|
||||
throw new ForbiddenException('Access Denied');
|
||||
}
|
||||
|
@ -55,6 +61,7 @@ export class AuthService {
|
|||
refreshToken,
|
||||
user.hashedRt
|
||||
);
|
||||
|
||||
if (!refreshTokenMatch) {
|
||||
throw new ForbiddenException('Access Denied');
|
||||
}
|
||||
|
@ -67,6 +74,7 @@ export class AuthService {
|
|||
userId,
|
||||
null
|
||||
);
|
||||
|
||||
return affected > 0;
|
||||
}
|
||||
|
||||
|
@ -81,6 +89,7 @@ export class AuthService {
|
|||
const hashedRefreshToken = await this.encryptionService.hashData(
|
||||
tokens.refresh_token
|
||||
);
|
||||
|
||||
await this.userRepository.updateUserTokenHash(userId, hashedRefreshToken);
|
||||
return tokens;
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
|
||||
import { TokensDto } from '../models/dto';
|
||||
|
||||
@Injectable()
|
||||
|
@ -10,7 +11,7 @@ export class TokenManagementService {
|
|||
private readonly JWT_SECRET_AT: string;
|
||||
private readonly JWT_SECRET_RT: string;
|
||||
|
||||
constructor(
|
||||
public constructor(
|
||||
private readonly jwt: JwtService,
|
||||
private readonly configService: ConfigService
|
||||
) {
|
||||
|
@ -30,6 +31,7 @@ export class TokenManagementService {
|
|||
): Promise<TokensDto> {
|
||||
const access_token: string = await this.createAccessToken(userId, email);
|
||||
const refresh_token: string = await this.createRefreshToken(userId, email);
|
||||
|
||||
return { access_token, refresh_token };
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy, ExtractJwt } from 'passport-jwt';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
|
||||
import { JwtPayload } from '../models/types';
|
||||
|
||||
@Injectable()
|
||||
|
@ -9,7 +10,7 @@ export class AccessTokenStrategy extends PassportStrategy(
|
|||
Strategy,
|
||||
'jwt-access-token'
|
||||
) {
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
public constructor(private readonly configService: ConfigService) {
|
||||
super(AccessTokenStrategy.getJwtConfig(configService));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,15 +1,15 @@
|
|||
import { Injectable, ForbiddenException } from '@nestjs/common';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy, ExtractJwt } from 'passport-jwt';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Request } from 'express';
|
||||
import { Strategy, ExtractJwt } from 'passport-jwt';
|
||||
|
||||
@Injectable()
|
||||
export class RefreshTokenStrategy extends PassportStrategy(
|
||||
Strategy,
|
||||
'jwt-refresh-token'
|
||||
) {
|
||||
constructor(private readonly configService: ConfigService) {
|
||||
public constructor(private readonly configService: ConfigService) {
|
||||
super(RefreshTokenStrategy.createJwtStrategyOptions(configService));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
import { ConfigService, ConfigModule } from '@nestjs/config';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { databaseConfigFactory } from './database-config';
|
||||
|
||||
@Module({
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { INestApplication } from '@nestjs/common';
|
||||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import * as request from 'supertest';
|
||||
|
||||
import { AppModule } from './../src/app.module';
|
||||
|
||||
describe('AppController (e2e)', () => {
|
||||
|
|
|
@ -1,17 +0,0 @@
|
|||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
|
@ -1,87 +1,21 @@
|
|||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["projects/**/*"],
|
||||
"plugins": ["import", "prettier", "@stylistic/eslint-plugin-ts", "sort-class-members", "unused-imports"],
|
||||
"extends": ["./../.eslintrc"],
|
||||
"ignorePatterns": ["projects/**/*", "src/app/api"],
|
||||
"plugins": [
|
||||
"import",
|
||||
"prettier",
|
||||
"@stylistic/eslint-plugin-ts",
|
||||
"sort-class-members",
|
||||
"unused-imports"
|
||||
],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:@angular-eslint/recommended",
|
||||
"plugin:@angular-eslint/template/process-inline-templates",
|
||||
"prettier",
|
||||
"plugin:prettier/recommended"
|
||||
"plugin:@angular-eslint/template/process-inline-templates"
|
||||
],
|
||||
"rules": {
|
||||
"@stylistic/ts/lines-between-class-members": [
|
||||
"error",
|
||||
{
|
||||
"enforce": [
|
||||
{ "blankLine": "always", "prev": "*", "next": "method" },
|
||||
{ "blankLine": "always", "prev": "method", "next": "*" },
|
||||
{ "blankLine": "never", "prev": "field", "next": "field" }
|
||||
]
|
||||
}
|
||||
],
|
||||
"@stylistic/ts/block-spacing": ["error"],
|
||||
"@stylistic/ts/brace-style": "off",
|
||||
"@stylistic/ts/key-spacing": ["error", { "afterColon": true }],
|
||||
"@stylistic/ts/keyword-spacing": ["error", { "before": true }],
|
||||
"@stylistic/ts/no-extra-parens": ["error", "all", {
|
||||
"nestedBinaryExpressions": false,
|
||||
"ternaryOperandBinaryExpressions": false
|
||||
}],
|
||||
"@stylistic/ts/no-extra-semi": ["error"],
|
||||
"@stylistic/ts/object-curly-spacing": ["error", "always"],
|
||||
"@stylistic/ts/quotes": ["error", "single"],
|
||||
"@stylistic/ts/semi": ["error", "always"],
|
||||
"@stylistic/ts/space-before-blocks": ["error"],
|
||||
"@stylistic/ts/space-before-function-paren": ["error", {"anonymous": "always", "named": "never", "asyncArrow": "always"}],
|
||||
"@stylistic/ts/space-infix-ops": ["error"],
|
||||
// "@stylistic/ts/max-statements-per-line": ["error", { "max": 1 }],
|
||||
// "@stylistic/ts/multiline-ternary": ["error", "always"],
|
||||
// "@stylistic/ts/newline-per-chained-call": ["error", { "ignoreChainWithDepth": 2 }],
|
||||
// "@stylistic/ts/no-confusing-arrow": ["error"],
|
||||
// "@stylistic/ts/no-floating-decimal": ["error"],
|
||||
// "@stylistic/ts/no-mixed-operators": ["error"],
|
||||
// "@stylistic/ts/no-mixed-spaces-and-tabs": ["error"],/
|
||||
// "@stylistic/ts/no-multi-spaces": ["error"],
|
||||
// "@stylistic/ts/no-multiple-empty-lines": ["error", { "max": 2 }],
|
||||
// "@stylistic/ts/no-tabs": ["error", { "allowIndentationTabs": true }],
|
||||
// "@stylistic/ts/no-whitespace-before-property": ["error"],
|
||||
// "@stylistic/ts/nonblock-statement-body-position": ["error", "below"],
|
||||
// "@stylistic/ts/object-curly-newline": ["error", "always"],
|
||||
// "@stylistic/ts/object-property-newline": ["error"],
|
||||
// "@stylistic/ts/one-var-declaration-per-line": ["error", "always"],
|
||||
// "@stylistic/ts/operator-linebreak": ["error", "before"],
|
||||
// "@stylistic/ts/padded-blocks": ["error", "never"],
|
||||
// "@stylistic/ts/rest-spread-spacing": ["error", "never"],
|
||||
// "@stylistic/ts/semi-spacing": ["error"],
|
||||
// "@stylistic/ts/semi-style": ["error", "last"],
|
||||
// "@stylistic/ts/space-in-parens": ["error", "never"],
|
||||
// "@stylistic/ts/space-unary-ops": ["error"],
|
||||
// "@stylistic/ts/template-curly-spacing": ["error"],
|
||||
// "@stylistic/ts/template-tag-spacing": ["error"],
|
||||
// "@stylistic/ts/wrap-regex": ["error"],
|
||||
"no-console": ["warn", { "allow": ["warn", "error"] }],
|
||||
"no-debugger": "error",
|
||||
"no-var": ["error"],
|
||||
"eqeqeq": ["error", "always"],
|
||||
"no-eval": "error",
|
||||
"prefer-const": ["error", { "destructuring": "all", "ignoreReadBeforeAssign": true }],
|
||||
"prettier/prettier": ["error", { "printWidth": 80 }],
|
||||
"no-unused-vars": "off", // or "@typescript-eslint/no-unused-vars": "off",
|
||||
"unused-imports/no-unused-imports": "error",
|
||||
"unused-imports/no-unused-vars": [
|
||||
"warn",
|
||||
{
|
||||
"vars": "all",
|
||||
"varsIgnorePattern": "^_",
|
||||
"args": "after-used",
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
],
|
||||
"@angular-eslint/directive-selector": [
|
||||
"error",
|
||||
{
|
||||
|
@ -97,125 +31,6 @@
|
|||
"prefix": "app",
|
||||
"style": "kebab-case"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/explicit-member-accessibility": [
|
||||
"error",
|
||||
{
|
||||
"accessibility": "explicit"
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/explicit-function-return-type": [
|
||||
"error",
|
||||
{
|
||||
"allowExpressions": false,
|
||||
"allowTypedFunctionExpressions": true,
|
||||
"allowHigherOrderFunctions": false,
|
||||
"allowDirectConstAssertionInArrowFunctions": false,
|
||||
"allowConciseArrowFunctionExpressionsStartingWithVoid": false
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/member-ordering": [
|
||||
"error",
|
||||
{
|
||||
"default": [
|
||||
"public-static-field",
|
||||
"protected-static-field",
|
||||
"private-static-field",
|
||||
"public-instance-field",
|
||||
"protected-instance-field",
|
||||
"private-instance-field",
|
||||
"public-constructor",
|
||||
"protected-constructor",
|
||||
"private-constructor",
|
||||
"public-static-method",
|
||||
"protected-static-method",
|
||||
"private-static-method",
|
||||
"public-instance-method",
|
||||
"protected-instance-method",
|
||||
"private-instance-method"
|
||||
]
|
||||
}
|
||||
],
|
||||
// https://github.com/bryanrsmith/eslint-plugin-sort-class-members -> Read Docs and replace @typescript-eslint/member-ordering
|
||||
// "sort-class-members/sort-class-members": [
|
||||
// 2,
|
||||
// {
|
||||
// "order": [
|
||||
// "[public-properties]",
|
||||
// "[protected-properties]",
|
||||
// "[private-properties]",
|
||||
// "everything-else"
|
||||
// ],
|
||||
// "groups": {
|
||||
// "public-properties": [{ "type": "property", "accessibility": "public" }],
|
||||
// "protected-properties": [{ "type": "property", "accessibility": "protected" }],
|
||||
// "private-properties": [{ "type": "property", "accessibility": "private" }]
|
||||
// }
|
||||
// }
|
||||
// ],
|
||||
"padding-line-between-statements": [
|
||||
"error",
|
||||
{ "blankLine": "always", "prev": "const", "next": "*" },
|
||||
{ "blankLine": "always", "prev": "let", "next": "*" },
|
||||
{ "blankLine": "always", "prev": "var", "next": "*" },
|
||||
{ "blankLine": "any", "prev": ["const", "let", "var"], "next": ["const", "let", "var"] }
|
||||
],
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"vars": "all",
|
||||
"args": "after-used",
|
||||
"ignoreRestSiblings": false
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/typedef": [
|
||||
"error",
|
||||
{
|
||||
"arrayDestructuring": true,
|
||||
"arrowParameter": false,
|
||||
"memberVariableDeclaration": true,
|
||||
"objectDestructuring": true,
|
||||
"parameter": true,
|
||||
"propertyDeclaration": true,
|
||||
"variableDeclaration": false,
|
||||
"variableDeclarationIgnoreFunction": true
|
||||
}
|
||||
],
|
||||
"import/order": [
|
||||
"error",
|
||||
{
|
||||
"groups": [
|
||||
"builtin",
|
||||
"external",
|
||||
"internal",
|
||||
"parent",
|
||||
"sibling",
|
||||
"index"
|
||||
],
|
||||
"pathGroups": [
|
||||
{
|
||||
"pattern": "@angular/**",
|
||||
"group": "external",
|
||||
"position": "before"
|
||||
},
|
||||
{
|
||||
"pattern": "@app/**",
|
||||
"group": "internal",
|
||||
"position": "before"
|
||||
},
|
||||
{
|
||||
"pattern": "@env/**",
|
||||
"group": "internal",
|
||||
"position": "before"
|
||||
}
|
||||
],
|
||||
"pathGroupsExcludedImportTypes": ["builtin"],
|
||||
"newlines-between": "always",
|
||||
"alphabetize": {
|
||||
"order": "asc",
|
||||
"caseInsensitive": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
/tmp
|
||||
/out-tsc
|
||||
/bazel-out
|
||||
/src/app/api
|
||||
|
||||
# Node
|
||||
/node_modules
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
coverage
|
||||
dist
|
||||
node_modules
|
||||
pnpm-lock.yaml
|
|
@ -46,9 +46,6 @@
|
|||
"@angular-eslint/template-parser": "17.2.1",
|
||||
"@angular/cli": "^17.3.0",
|
||||
"@angular/compiler-cli": "^17.3.0",
|
||||
"@stylistic/eslint-plugin": "^2.1.0",
|
||||
"@stylistic/eslint-plugin-migrate": "^2.1.0",
|
||||
"@stylistic/eslint-plugin-ts": "^2.1.0",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/jest": "^29.5.12",
|
||||
"@typescript-eslint/eslint-plugin": "6.19.0",
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,9 +1,9 @@
|
|||
import { HttpClient, HttpHeaders } from '@angular/common/http';
|
||||
import { HttpClient } from '@angular/common/http';
|
||||
import { Injectable } from '@angular/core';
|
||||
|
||||
import { BehaviorSubject, Observable, tap } from 'rxjs';
|
||||
|
||||
import { environment } from '../../../environments/environment';
|
||||
import { AuthenticationApiService } from '../../api/api/authentication.api.service';
|
||||
import { LoginCredentials, Tokens } from '../types';
|
||||
|
||||
import { LocalStorageService } from './local-storage.service';
|
||||
|
@ -26,34 +26,50 @@ export class AuthService {
|
|||
public constructor(
|
||||
private readonly httpClient: HttpClient,
|
||||
private readonly localStorageService: LocalStorageService,
|
||||
private readonly sessionStorageService: SessionStorageService
|
||||
private readonly sessionStorageService: SessionStorageService,
|
||||
private readonly authenticationApiService: AuthenticationApiService
|
||||
) {
|
||||
this.autoLogin();
|
||||
}
|
||||
|
||||
public signin(credentials: LoginCredentials): void {
|
||||
this.httpClient
|
||||
.post<Tokens>(environment.api.base + `${this._path}/signin`, credentials)
|
||||
this.authenticationApiService
|
||||
.authControllerSignin(credentials)
|
||||
.subscribe((response: Tokens) => {
|
||||
this.handleSuccess(response);
|
||||
});
|
||||
}
|
||||
|
||||
public signup(credentials: LoginCredentials): void {
|
||||
this.httpClient
|
||||
.post<Tokens>(environment.api.base + `${this._path}/signup`, credentials)
|
||||
this.authenticationApiService
|
||||
.authControllerSignup(credentials)
|
||||
.subscribe((response: Tokens) => {
|
||||
//TODO The checked accept terms should be saved with a timestamp in the db
|
||||
this.handleSuccess(response);
|
||||
});
|
||||
}
|
||||
|
||||
public refreshToken(): Observable<Tokens> {
|
||||
if (this._refresh_token) {
|
||||
return this.authenticationApiService
|
||||
.authControllerRefresh(this._refresh_token)
|
||||
.pipe(tap((response: Tokens) => this.handleSuccess(response)));
|
||||
} else {
|
||||
throw new Error('Refresh token is missing');
|
||||
}
|
||||
}
|
||||
|
||||
public signout(): void {
|
||||
this._access_token = null;
|
||||
this._refresh_token = null;
|
||||
this.localStorageService.removeItem('access_token');
|
||||
this.sessionStorageService.removeItem('refresh_token');
|
||||
this._isAuthenticated$.next(false);
|
||||
this.authenticationApiService
|
||||
.authControllerLogout()
|
||||
.subscribe((response: boolean) => {
|
||||
if (response) {
|
||||
this._access_token = null;
|
||||
this._refresh_token = null;
|
||||
this.localStorageService.removeItem('access_token');
|
||||
this.sessionStorageService.removeItem('refresh_token');
|
||||
this._isAuthenticated$.next(false);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public autoLogin(): void {
|
||||
|
@ -71,25 +87,6 @@ export class AuthService {
|
|||
}
|
||||
}
|
||||
|
||||
public refreshToken(): Observable<Tokens> {
|
||||
const headers = new HttpHeaders().set(
|
||||
'Authorization',
|
||||
'Bearer ' + this._refresh_token
|
||||
);
|
||||
|
||||
return this.httpClient
|
||||
.post<Tokens>(
|
||||
environment.api.base + `${this._path}/refresh`,
|
||||
{},
|
||||
{ headers: headers }
|
||||
)
|
||||
.pipe(
|
||||
tap((response: Tokens) => {
|
||||
this.handleSuccess(response);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
private handleSuccess(tokens: Tokens): void {
|
||||
this._access_token = tokens.access_token;
|
||||
this._refresh_token = tokens.refresh_token;
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
|
||||
"spaces": 2,
|
||||
"generator-cli": {
|
||||
"version": "7.6.0",
|
||||
"generators": {
|
||||
"typescript-angular": {
|
||||
"generatorName": "typescript-angular",
|
||||
"output": "frontend/src/app/api",
|
||||
"inputSpec": "backend/docs/swagger.json",
|
||||
"additionalProperties": {
|
||||
"basePath": "http://localhost:3000/api",
|
||||
"npmName": "Ticket-API-Services",
|
||||
"npmVersion": "0.0.0",
|
||||
"ngVersion": "17.0.0",
|
||||
"npmRepository": null,
|
||||
"configurationPrefix": null,
|
||||
"apiModulePrefix" : "TicketApi",
|
||||
"providedIn": "any",
|
||||
"fileNaming": "camelCase",
|
||||
"paramNaming": "camelCase",
|
||||
"enumPropertyNamingReplaceSpecialChar": "false",
|
||||
"enumUnknownDefaultCase": "false",
|
||||
"enumNameSuffix": "ApiEnum",
|
||||
"enumPropertyNaming": "PascalCase",
|
||||
"modelPropertyNaming": "original",
|
||||
"modelSuffix": "ApiModel",
|
||||
"modelFileSuffix": ".api.model",
|
||||
"serviceSuffix": "ApiService",
|
||||
"serviceFileSuffix": ".api.service",
|
||||
"withInterfaces": true,
|
||||
"supportsES6": true,
|
||||
"stringEnums": true,
|
||||
"sortParamsByRequiredFlag": true,
|
||||
"sortModelPropertiesByRequiredFlag": true,
|
||||
"useSingleRequestParameter": false,
|
||||
"taggedUnions": false,
|
||||
"snapshot": false,
|
||||
"prependFormOrBodyParameters": false,
|
||||
"nullSafeAdditionalProps": false,
|
||||
"legacyDiscriminatorBehavior": true,
|
||||
"ensureUniqueParams": true,
|
||||
"disallowAdditionalPropertiesIfNotPresent": true,
|
||||
"allowUnicodeIdentifiers": false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"name": "mvp-ticket",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"start:frontend:dev": "cd frontend && pnpm run start:dev",
|
||||
"start:backend:dev": "cd backend && pnpm run start:dev",
|
||||
"start:all:dev": "concurrently --kill-others --prefix-colors \"bgBlue.bold,bgMagenta.bold\" \"pnpm run start:frontend:dev\" \"pnpm run start:backend:dev\" --open \"terminal\"",
|
||||
"install:all": "pnpm install && pnpm run install:frontend && pnpm run install:backend",
|
||||
"install:frontend": "cd frontend && pnpm install",
|
||||
"install:backend": "cd backend && pnpm install",
|
||||
"format:frontend": "cd frontend && pnpm run format",
|
||||
"format:backend": "cd backend && concurrently \"pnpm run lint\" \"pnpm run prettier:fix\"",
|
||||
"format:all": "concurrently \"pnpm run format:frontend\" \"pnpm run format:backend\"",
|
||||
"build:api": "openapi-generator-cli generate"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"devDependencies": {
|
||||
"@openapitools/openapi-generator-cli": "^2.13.4",
|
||||
"@stylistic/eslint-plugin": "^2.1.0",
|
||||
"@stylistic/eslint-plugin-ts": "^2.1.0",
|
||||
"@typescript-eslint/eslint-plugin": "6.19.0",
|
||||
"concurrently": "^8.2.2",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-import": "^2.29.1",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-sort-class-members": "^1.20.0",
|
||||
"eslint-plugin-unused-imports": "^3.2.0",
|
||||
"prettier": "^3.2.5"
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue