Feature: Use Api generated by swagger #5
|
@ -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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
|
@ -2,3 +2,5 @@ coverage
|
||||||
dist
|
dist
|
||||||
node_modules
|
node_modules
|
||||||
pnpm-lock.yaml
|
pnpm-lock.yaml
|
||||||
|
/frontend/pnpm-lock.yaml
|
||||||
|
/backend/pnpm-lock.yaml
|
||||||
|
|
|
@ -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',
|
|
||||||
},
|
|
||||||
};
|
|
|
@ -14,7 +14,7 @@
|
||||||
"ngVersion": "17.0.0",
|
"ngVersion": "17.0.0",
|
||||||
"npmRepository": null,
|
"npmRepository": null,
|
||||||
"configurationPrefix": null,
|
"configurationPrefix": null,
|
||||||
"apiModulePrefix" : "TicketApi",
|
"apiModulePrefix": "TicketApi",
|
||||||
"providedIn": "any",
|
"providedIn": "any",
|
||||||
"fileNaming": "camelCase",
|
"fileNaming": "camelCase",
|
||||||
"paramNaming": "camelCase",
|
"paramNaming": "camelCase",
|
||||||
|
|
|
@ -18,7 +18,9 @@
|
||||||
"test:cov": "jest --coverage",
|
"test:cov": "jest --coverage",
|
||||||
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
"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",
|
||||||
"build:api": "pnpm openapi-generator-cli generate"
|
"prettier:fix": "prettier --write .",
|
||||||
|
"prettier:check": "prettier --check .",
|
||||||
|
"foramt": "pnpm lint && prettier:fix"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@nestjs/common": "^10.0.0",
|
"@nestjs/common": "^10.0.0",
|
||||||
|
@ -32,10 +34,8 @@
|
||||||
"argon2": "^0.40.1",
|
"argon2": "^0.40.1",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"install": "^0.13.0",
|
|
||||||
"passport": "^0.7.0",
|
"passport": "^0.7.0",
|
||||||
"passport-jwt": "^4.0.1",
|
"passport-jwt": "^4.0.1",
|
||||||
"passport-local": "^1.0.0",
|
|
||||||
"pg": "^8.11.5",
|
"pg": "^8.11.5",
|
||||||
"reflect-metadata": "^0.2.0",
|
"reflect-metadata": "^0.2.0",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
|
@ -51,11 +51,8 @@
|
||||||
"@types/jest": "^29.5.2",
|
"@types/jest": "^29.5.2",
|
||||||
"@types/node": "^20.3.1",
|
"@types/node": "^20.3.1",
|
||||||
"@types/supertest": "^6.0.0",
|
"@types/supertest": "^6.0.0",
|
||||||
"@typescript-eslint/eslint-plugin": "^6.0.0",
|
|
||||||
"@typescript-eslint/parser": "^6.0.0",
|
"@typescript-eslint/parser": "^6.0.0",
|
||||||
"eslint": "^8.42.0",
|
"eslint": "^8.42.0",
|
||||||
"eslint-config-prettier": "^9.0.0",
|
|
||||||
"eslint-plugin-prettier": "^5.0.0",
|
|
||||||
"jest": "^29.5.0",
|
"jest": "^29.5.0",
|
||||||
"prettier": "^3.0.0",
|
"prettier": "^3.0.0",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
|
|
|
@ -38,18 +38,12 @@ dependencies:
|
||||||
class-validator:
|
class-validator:
|
||||||
specifier: ^0.14.1
|
specifier: ^0.14.1
|
||||||
version: 0.14.1
|
version: 0.14.1
|
||||||
install:
|
|
||||||
specifier: ^0.13.0
|
|
||||||
version: 0.13.0
|
|
||||||
passport:
|
passport:
|
||||||
specifier: ^0.7.0
|
specifier: ^0.7.0
|
||||||
version: 0.7.0
|
version: 0.7.0
|
||||||
passport-jwt:
|
passport-jwt:
|
||||||
specifier: ^4.0.1
|
specifier: ^4.0.1
|
||||||
version: 4.0.1
|
version: 4.0.1
|
||||||
passport-local:
|
|
||||||
specifier: ^1.0.0
|
|
||||||
version: 1.0.0
|
|
||||||
pg:
|
pg:
|
||||||
specifier: ^8.11.5
|
specifier: ^8.11.5
|
||||||
version: 8.11.5
|
version: 8.11.5
|
||||||
|
@ -91,21 +85,12 @@ devDependencies:
|
||||||
'@types/supertest':
|
'@types/supertest':
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.0.2
|
version: 6.0.2
|
||||||
'@typescript-eslint/eslint-plugin':
|
|
||||||
specifier: ^6.0.0
|
|
||||||
version: 6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.3)
|
|
||||||
'@typescript-eslint/parser':
|
'@typescript-eslint/parser':
|
||||||
specifier: ^6.0.0
|
specifier: ^6.0.0
|
||||||
version: 6.21.0(eslint@8.57.0)(typescript@5.4.3)
|
version: 6.21.0(eslint@8.57.0)(typescript@5.4.3)
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^8.42.0
|
specifier: ^8.42.0
|
||||||
version: 8.57.0
|
version: 8.57.0
|
||||||
eslint-config-prettier:
|
|
||||||
specifier: ^9.0.0
|
|
||||||
version: 9.1.0(eslint@8.57.0)
|
|
||||||
eslint-plugin-prettier:
|
|
||||||
specifier: ^5.0.0
|
|
||||||
version: 5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5)
|
|
||||||
jest:
|
jest:
|
||||||
specifier: ^29.5.0
|
specifier: ^29.5.0
|
||||||
version: 29.7.0(@types/node@20.12.4)(ts-node@10.9.2)
|
version: 29.7.0(@types/node@20.12.4)(ts-node@10.9.2)
|
||||||
|
@ -1192,11 +1177,6 @@ packages:
|
||||||
requiresBuild: true
|
requiresBuild: true
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
/@pkgr/core@0.1.1:
|
|
||||||
resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==}
|
|
||||||
engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@sinclair/typebox@0.27.8:
|
/@sinclair/typebox@0.27.8:
|
||||||
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
@ -1382,10 +1362,6 @@ packages:
|
||||||
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
|
resolution: {integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@types/semver@7.5.8:
|
|
||||||
resolution: {integrity: sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@types/send@0.17.4:
|
/@types/send@0.17.4:
|
||||||
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
|
resolution: {integrity: sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
|
@ -1433,35 +1409,6 @@ packages:
|
||||||
'@types/yargs-parser': 21.0.3
|
'@types/yargs-parser': 21.0.3
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/eslint-plugin@6.21.0(@typescript-eslint/parser@6.21.0)(eslint@8.57.0)(typescript@5.4.3):
|
|
||||||
resolution: {integrity: sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==}
|
|
||||||
engines: {node: ^16.0.0 || >=18.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
'@typescript-eslint/parser': ^6.0.0 || ^6.0.0-alpha
|
|
||||||
eslint: ^7.0.0 || ^8.0.0
|
|
||||||
typescript: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
typescript:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@eslint-community/regexpp': 4.10.0
|
|
||||||
'@typescript-eslint/parser': 6.21.0(eslint@8.57.0)(typescript@5.4.3)
|
|
||||||
'@typescript-eslint/scope-manager': 6.21.0
|
|
||||||
'@typescript-eslint/type-utils': 6.21.0(eslint@8.57.0)(typescript@5.4.3)
|
|
||||||
'@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.3)
|
|
||||||
'@typescript-eslint/visitor-keys': 6.21.0
|
|
||||||
debug: 4.3.4
|
|
||||||
eslint: 8.57.0
|
|
||||||
graphemer: 1.4.0
|
|
||||||
ignore: 5.3.1
|
|
||||||
natural-compare: 1.4.0
|
|
||||||
semver: 7.6.0
|
|
||||||
ts-api-utils: 1.3.0(typescript@5.4.3)
|
|
||||||
typescript: 5.4.3
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3):
|
/@typescript-eslint/parser@6.21.0(eslint@8.57.0)(typescript@5.4.3):
|
||||||
resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
|
resolution: {integrity: sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==}
|
||||||
engines: {node: ^16.0.0 || >=18.0.0}
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
@ -1491,26 +1438,6 @@ packages:
|
||||||
'@typescript-eslint/visitor-keys': 6.21.0
|
'@typescript-eslint/visitor-keys': 6.21.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/type-utils@6.21.0(eslint@8.57.0)(typescript@5.4.3):
|
|
||||||
resolution: {integrity: sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==}
|
|
||||||
engines: {node: ^16.0.0 || >=18.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: ^7.0.0 || ^8.0.0
|
|
||||||
typescript: '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
typescript:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.3)
|
|
||||||
'@typescript-eslint/utils': 6.21.0(eslint@8.57.0)(typescript@5.4.3)
|
|
||||||
debug: 4.3.4
|
|
||||||
eslint: 8.57.0
|
|
||||||
ts-api-utils: 1.3.0(typescript@5.4.3)
|
|
||||||
typescript: 5.4.3
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@typescript-eslint/types@6.21.0:
|
/@typescript-eslint/types@6.21.0:
|
||||||
resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==}
|
resolution: {integrity: sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==}
|
||||||
engines: {node: ^16.0.0 || >=18.0.0}
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
@ -1538,25 +1465,6 @@ packages:
|
||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@typescript-eslint/utils@6.21.0(eslint@8.57.0)(typescript@5.4.3):
|
|
||||||
resolution: {integrity: sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==}
|
|
||||||
engines: {node: ^16.0.0 || >=18.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
eslint: ^7.0.0 || ^8.0.0
|
|
||||||
dependencies:
|
|
||||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.57.0)
|
|
||||||
'@types/json-schema': 7.0.15
|
|
||||||
'@types/semver': 7.5.8
|
|
||||||
'@typescript-eslint/scope-manager': 6.21.0
|
|
||||||
'@typescript-eslint/types': 6.21.0
|
|
||||||
'@typescript-eslint/typescript-estree': 6.21.0(typescript@5.4.3)
|
|
||||||
eslint: 8.57.0
|
|
||||||
semver: 7.6.0
|
|
||||||
transitivePeerDependencies:
|
|
||||||
- supports-color
|
|
||||||
- typescript
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/@typescript-eslint/visitor-keys@6.21.0:
|
/@typescript-eslint/visitor-keys@6.21.0:
|
||||||
resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==}
|
resolution: {integrity: sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==}
|
||||||
engines: {node: ^16.0.0 || >=18.0.0}
|
engines: {node: ^16.0.0 || >=18.0.0}
|
||||||
|
@ -2559,36 +2467,6 @@ packages:
|
||||||
engines: {node: '>=12'}
|
engines: {node: '>=12'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/eslint-config-prettier@9.1.0(eslint@8.57.0):
|
|
||||||
resolution: {integrity: sha512-NSWl5BFQWEPi1j4TjVNItzYV7dZXZ+wP6I6ZhrBGpChQhZRUaElihE9uRRkcbRnNb76UMKDF3r+WTmNcGPKsqw==}
|
|
||||||
hasBin: true
|
|
||||||
peerDependencies:
|
|
||||||
eslint: '>=7.0.0'
|
|
||||||
dependencies:
|
|
||||||
eslint: 8.57.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/eslint-plugin-prettier@5.1.3(eslint-config-prettier@9.1.0)(eslint@8.57.0)(prettier@3.2.5):
|
|
||||||
resolution: {integrity: sha512-C9GCVAs4Eq7ZC/XFQHITLiHJxQngdtraXaM+LoUFoFp/lHNl2Zn8f3WQbe9HvTBBQ9YnKFB0/2Ajdqwo5D1EAw==}
|
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/eslint': '>=8.0.0'
|
|
||||||
eslint: '>=8.0.0'
|
|
||||||
eslint-config-prettier: '*'
|
|
||||||
prettier: '>=3.0.0'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/eslint':
|
|
||||||
optional: true
|
|
||||||
eslint-config-prettier:
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
eslint: 8.57.0
|
|
||||||
eslint-config-prettier: 9.1.0(eslint@8.57.0)
|
|
||||||
prettier: 3.2.5
|
|
||||||
prettier-linter-helpers: 1.0.0
|
|
||||||
synckit: 0.8.8
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/eslint-scope@5.1.1:
|
/eslint-scope@5.1.1:
|
||||||
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
|
resolution: {integrity: sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==}
|
||||||
engines: {node: '>=8.0.0'}
|
engines: {node: '>=8.0.0'}
|
||||||
|
@ -2792,10 +2670,6 @@ packages:
|
||||||
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/fast-diff@1.3.0:
|
|
||||||
resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==}
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/fast-glob@3.3.2:
|
/fast-glob@3.3.2:
|
||||||
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
|
resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==}
|
||||||
engines: {node: '>=8.6.0'}
|
engines: {node: '>=8.6.0'}
|
||||||
|
@ -3251,11 +3125,6 @@ packages:
|
||||||
wrap-ansi: 6.2.0
|
wrap-ansi: 6.2.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/install@0.13.0:
|
|
||||||
resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==}
|
|
||||||
engines: {node: '>= 0.10'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/interpret@1.4.0:
|
/interpret@1.4.0:
|
||||||
resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
|
resolution: {integrity: sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==}
|
||||||
engines: {node: '>= 0.10'}
|
engines: {node: '>= 0.10'}
|
||||||
|
@ -4406,13 +4275,6 @@ packages:
|
||||||
passport-strategy: 1.0.0
|
passport-strategy: 1.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/passport-local@1.0.0:
|
|
||||||
resolution: {integrity: sha512-9wCE6qKznvf9mQYYbgJ3sVOHmCWoUNMVFoZzNoznmISbhnNNPhN9xfY3sLmScHMetEJeoY7CXwfhCe7argfQow==}
|
|
||||||
engines: {node: '>= 0.4.0'}
|
|
||||||
dependencies:
|
|
||||||
passport-strategy: 1.0.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/passport-strategy@1.0.0:
|
/passport-strategy@1.0.0:
|
||||||
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
|
resolution: {integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==}
|
||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
|
@ -4587,13 +4449,6 @@ packages:
|
||||||
engines: {node: '>= 0.8.0'}
|
engines: {node: '>= 0.8.0'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/prettier-linter-helpers@1.0.0:
|
|
||||||
resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==}
|
|
||||||
engines: {node: '>=6.0.0'}
|
|
||||||
dependencies:
|
|
||||||
fast-diff: 1.3.0
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/prettier@3.2.5:
|
/prettier@3.2.5:
|
||||||
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
|
resolution: {integrity: sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
@ -5133,14 +4988,6 @@ packages:
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/synckit@0.8.8:
|
|
||||||
resolution: {integrity: sha512-HwOKAP7Wc5aRGYdKH+dw0PRRpbO841v2DENBtjnR5HFWoiNByAl7vrx3p0G/rCyYXQsrxqtX48TImFtPcIHSpQ==}
|
|
||||||
engines: {node: ^14.18.0 || >=16.0.0}
|
|
||||||
dependencies:
|
|
||||||
'@pkgr/core': 0.1.1
|
|
||||||
tslib: 2.6.2
|
|
||||||
dev: true
|
|
||||||
|
|
||||||
/tapable@2.2.1:
|
/tapable@2.2.1:
|
||||||
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import { Controller, Get } from '@nestjs/common';
|
import { Controller, Get } from '@nestjs/common';
|
||||||
|
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
|
|
||||||
@Controller()
|
@Controller()
|
||||||
export class AppController {
|
export class AppController {
|
||||||
constructor(private readonly appService: AppService) {}
|
public constructor(private readonly appService: AppService) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
getHello(): string {
|
public getHello(): string {
|
||||||
return this.appService.getHello();
|
return this.appService.getHello();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,14 +1,15 @@
|
||||||
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
import { MiddlewareConsumer, Module, RequestMethod } from '@nestjs/common';
|
||||||
|
import { ConfigModule } from '@nestjs/config';
|
||||||
|
|
||||||
import { AppController } from './app.controller';
|
import { AppController } from './app.controller';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { CorsMiddleware } from './middleware/cors-middleware/cors.middlware';
|
||||||
import { DatabaseModule } from './modules/database-module/database.module';
|
|
||||||
import { CspMiddleware } from './middleware/csp-middleware/csp.middleware';
|
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 { 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 { AuthModule } from './modules/auth-module/auth.module';
|
||||||
import { AccessTokenGuard } from './modules/auth-module/common/guards';
|
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({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
@ -22,7 +23,7 @@ import { CorsMiddleware } from './middleware/cors-middleware/cors.middlware';
|
||||||
providers: [AppService, { provide: 'APP_GUARD', useClass: AccessTokenGuard }],
|
providers: [AppService, { provide: 'APP_GUARD', useClass: AccessTokenGuard }],
|
||||||
})
|
})
|
||||||
export class AppModule {
|
export class AppModule {
|
||||||
configure(consumer: MiddlewareConsumer) {
|
public configure(consumer: MiddlewareConsumer): void {
|
||||||
consumer
|
consumer
|
||||||
// TODO Redirect via Reverse Proxy all HTTP requests to HTTPS
|
// TODO Redirect via Reverse Proxy all HTTP requests to HTTPS
|
||||||
.apply(
|
.apply(
|
||||||
|
|
|
@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppService {
|
export class AppService {
|
||||||
getHello(): string {
|
public getHello(): string {
|
||||||
return 'Hello World!';
|
return 'Hello World!';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,20 +9,20 @@ import {
|
||||||
@Entity()
|
@Entity()
|
||||||
export class UserCredentials {
|
export class UserCredentials {
|
||||||
@PrimaryGeneratedColumn('uuid')
|
@PrimaryGeneratedColumn('uuid')
|
||||||
id: number;
|
public id: number;
|
||||||
|
|
||||||
@Column({ unique: true })
|
@Column({ unique: true })
|
||||||
email: string;
|
public email: string;
|
||||||
|
|
||||||
@Column()
|
@Column()
|
||||||
hash: string;
|
public hash: string;
|
||||||
|
|
||||||
@Column({ nullable: true })
|
@Column({ nullable: true })
|
||||||
hashedRt?: string;
|
public hashedRt?: string;
|
||||||
|
|
||||||
@CreateDateColumn()
|
@CreateDateColumn()
|
||||||
createdAt: Date;
|
public createdAt: Date;
|
||||||
|
|
||||||
@UpdateDateColumn()
|
@UpdateDateColumn()
|
||||||
updatedAt: Date;
|
public updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
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 * as fs from 'fs';
|
||||||
import { join } from 'path';
|
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()
|
const config = new DocumentBuilder()
|
||||||
.setTitle('Tickets API')
|
.setTitle('Tickets API')
|
||||||
.setDescription('Description of the API')
|
.setDescription('Description of the API')
|
||||||
|
@ -13,9 +15,11 @@ async function setupSwagger(app) {
|
||||||
.build();
|
.build();
|
||||||
|
|
||||||
const document = SwaggerModule.createDocument(app, config);
|
const document = SwaggerModule.createDocument(app, config);
|
||||||
|
|
||||||
SwaggerModule.setup('api', app, document);
|
SwaggerModule.setup('api', app, document);
|
||||||
|
|
||||||
const docsDir = join(process.cwd(), 'docs');
|
const docsDir = join(process.cwd(), 'docs');
|
||||||
|
|
||||||
if (!fs.existsSync(docsDir)) {
|
if (!fs.existsSync(docsDir)) {
|
||||||
fs.mkdirSync(docsDir);
|
fs.mkdirSync(docsDir);
|
||||||
}
|
}
|
||||||
|
@ -26,16 +30,17 @@ async function setupSwagger(app) {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupPrefix(app) {
|
async function setupPrefix(app: INestApplication): Promise<void> {
|
||||||
app.setGlobalPrefix('api');
|
app.setGlobalPrefix('api');
|
||||||
}
|
}
|
||||||
|
|
||||||
async function setupClassValidator(app) {
|
async function setupClassValidator(app: INestApplication): Promise<void> {
|
||||||
app.useGlobalPipes(new ValidationPipe());
|
app.useGlobalPipes(new ValidationPipe());
|
||||||
}
|
}
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap(): Promise<void> {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create(AppModule);
|
||||||
|
|
||||||
await setupSwagger(app);
|
await setupSwagger(app);
|
||||||
await setupPrefix(app);
|
await setupPrefix(app);
|
||||||
await setupClassValidator(app);
|
await setupClassValidator(app);
|
||||||
|
|
|
@ -4,7 +4,7 @@ import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CorsMiddleware implements NestMiddleware {
|
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 {
|
public use(req: Request, res: Response, next: NextFunction): void {
|
||||||
if (this.configService.get<string>('NODE_ENV') === 'development') {
|
if (this.configService.get<string>('NODE_ENV') === 'development') {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||||
import { Request, Response, NextFunction } from 'express';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CspMiddleware implements NestMiddleware {
|
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 {
|
public use(req: Request, res: Response, next: NextFunction): void {
|
||||||
const cspDirectives = this.configService.get<string>('CSP_DIRECTIVES');
|
const cspDirectives = this.configService.get<string>('CSP_DIRECTIVES');
|
||||||
|
|
||||||
if (cspDirectives) {
|
if (cspDirectives) {
|
||||||
res.setHeader('Content-Security-Policy', cspDirectives);
|
res.setHeader('Content-Security-Policy', cspDirectives);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,12 +4,13 @@ import { NextFunction, Request, Response } from 'express';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class HttpsRedirectMiddleware implements NestMiddleware {
|
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 (this.configService.get<string>('NODE_ENV') === 'production') {
|
||||||
if (req.protocol === 'http') {
|
if (req.protocol === 'http') {
|
||||||
const httpsUrl = `https://${req.headers.host}${req.url}`;
|
const httpsUrl = `https://${req.headers.host}${req.url}`;
|
||||||
|
|
||||||
res.redirect(httpsUrl);
|
res.redirect(httpsUrl);
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
import { Injectable, NestMiddleware } from '@nestjs/common';
|
import { Injectable, NestMiddleware } from '@nestjs/common';
|
||||||
import { Request, Response, NextFunction } from 'express';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { Request, Response, NextFunction } from 'express';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class SecurityHeadersMiddleware implements NestMiddleware {
|
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 {
|
public use(req: Request, res: Response, next: NextFunction): void {
|
||||||
if (this.configService.get<string>('NODE_ENV') === 'production') {
|
if (this.configService.get<string>('NODE_ENV') === 'production') {
|
||||||
|
|
|
@ -1,13 +1,14 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { AuthService } from './services/auth.service';
|
|
||||||
import { AuthController } from './controller/auth.controller';
|
|
||||||
import { JwtModule } from '@nestjs/jwt';
|
import { JwtModule } from '@nestjs/jwt';
|
||||||
import { AccessTokenStrategy, RefreshTokenStrategy } from './strategies';
|
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
import { UserCredentials } from 'src/entities/user-credentials.entity';
|
import { UserCredentials } from 'src/entities/user-credentials.entity';
|
||||||
|
|
||||||
|
import { AuthController } from './controller/auth.controller';
|
||||||
import { UserRepository } from './repositories/user.repository';
|
import { UserRepository } from './repositories/user.repository';
|
||||||
|
import { AuthService } from './services/auth.service';
|
||||||
import { EncryptionService } from './services/encryption.service';
|
import { EncryptionService } from './services/encryption.service';
|
||||||
import { TokenManagementService } from './services/token-management.service';
|
import { TokenManagementService } from './services/token-management.service';
|
||||||
|
import { AccessTokenStrategy, RefreshTokenStrategy } from './strategies';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
|
|
@ -5,6 +5,7 @@ export const GetCurrentUserId = createParamDecorator(
|
||||||
(_: undefined, context: ExecutionContext): number => {
|
(_: undefined, context: ExecutionContext): number => {
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
const user = request.user as JwtPayload;
|
const user = request.user as JwtPayload;
|
||||||
|
|
||||||
return user.sub;
|
return user.sub;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
|
@ -7,6 +7,7 @@ export const GetCurrentUser = createParamDecorator(
|
||||||
context: ExecutionContext
|
context: ExecutionContext
|
||||||
) => {
|
) => {
|
||||||
const request = context.switchToHttp().getRequest();
|
const request = context.switchToHttp().getRequest();
|
||||||
|
|
||||||
if (!data) return request.user;
|
if (!data) return request.user;
|
||||||
return request.user[data];
|
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 { Injectable, ExecutionContext } from '@nestjs/common';
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
|
||||||
import { Reflector } from '@nestjs/core';
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AccessTokenGuard extends AuthGuard('jwt-access-token') {
|
export class AccessTokenGuard extends AuthGuard('jwt-access-token') {
|
||||||
constructor(private readonly reflector: Reflector) {
|
public constructor(private readonly reflector: Reflector) {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { AuthGuard } from '@nestjs/passport';
|
import { AuthGuard } from '@nestjs/passport';
|
||||||
|
|
||||||
export class RefreshTokenGuard extends AuthGuard('jwt-refresh-token') {
|
export class RefreshTokenGuard extends AuthGuard('jwt-refresh-token') {
|
||||||
constructor() {
|
public constructor() {
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,16 +6,17 @@ import {
|
||||||
HttpStatus,
|
HttpStatus,
|
||||||
UseGuards,
|
UseGuards,
|
||||||
} from '@nestjs/common';
|
} from '@nestjs/common';
|
||||||
import { AuthService } from '../services/auth.service';
|
|
||||||
import { TokensDto, UserCredentialsDto } from '../models/dto';
|
|
||||||
import { RefreshTokenGuard } from '../common/guards';
|
|
||||||
import { GetCurrentUser, GetCurrentUserId, Public } from '../common/decorators';
|
|
||||||
import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger';
|
import { ApiCreatedResponse, ApiTags } from '@nestjs/swagger';
|
||||||
|
|
||||||
|
import { GetCurrentUser, GetCurrentUserId, Public } from '../common/decorators';
|
||||||
|
import { RefreshTokenGuard } from '../common/guards';
|
||||||
|
import { TokensDto, UserCredentialsDto } from '../models/dto';
|
||||||
|
import { AuthService } from '../services/auth.service';
|
||||||
|
|
||||||
@ApiTags('Authentication')
|
@ApiTags('Authentication')
|
||||||
@Controller('auth')
|
@Controller('auth')
|
||||||
export class AuthController {
|
export class AuthController {
|
||||||
constructor(private readonly authService: AuthService) {}
|
public constructor(private readonly authService: AuthService) {}
|
||||||
|
|
||||||
@ApiCreatedResponse({
|
@ApiCreatedResponse({
|
||||||
description: 'User signed up successfully',
|
description: 'User signed up successfully',
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Repository } from 'typeorm';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class UserRepository {
|
export class UserRepository {
|
||||||
constructor(
|
public constructor(
|
||||||
@InjectRepository(UserCredentials)
|
@InjectRepository(UserCredentials)
|
||||||
private readonly repository: Repository<UserCredentials>
|
private readonly repository: Repository<UserCredentials>
|
||||||
) {}
|
) {}
|
||||||
|
@ -15,6 +15,7 @@ export class UserRepository {
|
||||||
hash: string
|
hash: string
|
||||||
): Promise<UserCredentials> {
|
): Promise<UserCredentials> {
|
||||||
const user = this.repository.create({ email, hash });
|
const user = this.repository.create({ email, hash });
|
||||||
|
|
||||||
return this.repository.save(user);
|
return this.repository.save(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ export class UserRepository {
|
||||||
hashedRt: string | null
|
hashedRt: string | null
|
||||||
): Promise<number> {
|
): Promise<number> {
|
||||||
const result = await this.repository.update(userId, { hashedRt });
|
const result = await this.repository.update(userId, { hashedRt });
|
||||||
|
|
||||||
return result.affected ?? 0;
|
return result.affected ?? 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,14 @@
|
||||||
import { ForbiddenException, Injectable } from '@nestjs/common';
|
import { ForbiddenException, Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
import { TokensDto, UserCredentialsDto } from '../models/dto';
|
import { TokensDto, UserCredentialsDto } from '../models/dto';
|
||||||
import { EncryptionService } from './encryption.service';
|
|
||||||
import { UserRepository } from '../repositories/user.repository';
|
import { UserRepository } from '../repositories/user.repository';
|
||||||
|
|
||||||
|
import { EncryptionService } from './encryption.service';
|
||||||
import { TokenManagementService } from './token-management.service';
|
import { TokenManagementService } from './token-management.service';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AuthService {
|
export class AuthService {
|
||||||
constructor(
|
public constructor(
|
||||||
private readonly userRepository: UserRepository,
|
private readonly userRepository: UserRepository,
|
||||||
private readonly tokenManagementService: TokenManagementService,
|
private readonly tokenManagementService: TokenManagementService,
|
||||||
private readonly encryptionService: EncryptionService
|
private readonly encryptionService: EncryptionService
|
||||||
|
@ -20,6 +22,7 @@ export class AuthService {
|
||||||
userCredentials.email,
|
userCredentials.email,
|
||||||
passwordHashed
|
passwordHashed
|
||||||
);
|
);
|
||||||
|
|
||||||
return this.generateAndPersistTokens(user.id, user.email);
|
return this.generateAndPersistTokens(user.id, user.email);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,6 +30,7 @@ export class AuthService {
|
||||||
const user = await this.userRepository.findUserByEmail(
|
const user = await this.userRepository.findUserByEmail(
|
||||||
userCredentials.email
|
userCredentials.email
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!user) {
|
if (!user) {
|
||||||
throw new ForbiddenException('Access Denied');
|
throw new ForbiddenException('Access Denied');
|
||||||
}
|
}
|
||||||
|
@ -35,6 +39,7 @@ export class AuthService {
|
||||||
userCredentials.password,
|
userCredentials.password,
|
||||||
user.hash
|
user.hash
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!passwordMatch) {
|
if (!passwordMatch) {
|
||||||
throw new ForbiddenException('Access Denied');
|
throw new ForbiddenException('Access Denied');
|
||||||
}
|
}
|
||||||
|
@ -47,6 +52,7 @@ export class AuthService {
|
||||||
refreshToken: string
|
refreshToken: string
|
||||||
): Promise<TokensDto> {
|
): Promise<TokensDto> {
|
||||||
const user = await this.userRepository.findUserById(userId);
|
const user = await this.userRepository.findUserById(userId);
|
||||||
|
|
||||||
if (!user || !user.hashedRt) {
|
if (!user || !user.hashedRt) {
|
||||||
throw new ForbiddenException('Access Denied');
|
throw new ForbiddenException('Access Denied');
|
||||||
}
|
}
|
||||||
|
@ -55,6 +61,7 @@ export class AuthService {
|
||||||
refreshToken,
|
refreshToken,
|
||||||
user.hashedRt
|
user.hashedRt
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!refreshTokenMatch) {
|
if (!refreshTokenMatch) {
|
||||||
throw new ForbiddenException('Access Denied');
|
throw new ForbiddenException('Access Denied');
|
||||||
}
|
}
|
||||||
|
@ -67,6 +74,7 @@ export class AuthService {
|
||||||
userId,
|
userId,
|
||||||
null
|
null
|
||||||
);
|
);
|
||||||
|
|
||||||
return affected > 0;
|
return affected > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,6 +89,7 @@ export class AuthService {
|
||||||
const hashedRefreshToken = await this.encryptionService.hashData(
|
const hashedRefreshToken = await this.encryptionService.hashData(
|
||||||
tokens.refresh_token
|
tokens.refresh_token
|
||||||
);
|
);
|
||||||
|
|
||||||
await this.userRepository.updateUserTokenHash(userId, hashedRefreshToken);
|
await this.userRepository.updateUserTokenHash(userId, hashedRefreshToken);
|
||||||
return tokens;
|
return tokens;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { JwtService } from '@nestjs/jwt';
|
import { JwtService } from '@nestjs/jwt';
|
||||||
|
|
||||||
import { TokensDto } from '../models/dto';
|
import { TokensDto } from '../models/dto';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -10,7 +11,7 @@ export class TokenManagementService {
|
||||||
private readonly JWT_SECRET_AT: string;
|
private readonly JWT_SECRET_AT: string;
|
||||||
private readonly JWT_SECRET_RT: string;
|
private readonly JWT_SECRET_RT: string;
|
||||||
|
|
||||||
constructor(
|
public constructor(
|
||||||
private readonly jwt: JwtService,
|
private readonly jwt: JwtService,
|
||||||
private readonly configService: ConfigService
|
private readonly configService: ConfigService
|
||||||
) {
|
) {
|
||||||
|
@ -30,6 +31,7 @@ export class TokenManagementService {
|
||||||
): Promise<TokensDto> {
|
): Promise<TokensDto> {
|
||||||
const access_token: string = await this.createAccessToken(userId, email);
|
const access_token: string = await this.createAccessToken(userId, email);
|
||||||
const refresh_token: string = await this.createRefreshToken(userId, email);
|
const refresh_token: string = await this.createRefreshToken(userId, email);
|
||||||
|
|
||||||
return { access_token, refresh_token };
|
return { access_token, refresh_token };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
import { PassportStrategy } from '@nestjs/passport';
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
import { Strategy, ExtractJwt } from 'passport-jwt';
|
import { Strategy, ExtractJwt } from 'passport-jwt';
|
||||||
import { ConfigService } from '@nestjs/config';
|
|
||||||
import { JwtPayload } from '../models/types';
|
import { JwtPayload } from '../models/types';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
|
@ -9,7 +10,7 @@ export class AccessTokenStrategy extends PassportStrategy(
|
||||||
Strategy,
|
Strategy,
|
||||||
'jwt-access-token'
|
'jwt-access-token'
|
||||||
) {
|
) {
|
||||||
constructor(private readonly configService: ConfigService) {
|
public constructor(private readonly configService: ConfigService) {
|
||||||
super(AccessTokenStrategy.getJwtConfig(configService));
|
super(AccessTokenStrategy.getJwtConfig(configService));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,15 +1,15 @@
|
||||||
import { Injectable, ForbiddenException } from '@nestjs/common';
|
import { Injectable, ForbiddenException } from '@nestjs/common';
|
||||||
import { PassportStrategy } from '@nestjs/passport';
|
|
||||||
import { Strategy, ExtractJwt } from 'passport-jwt';
|
|
||||||
import { ConfigService } from '@nestjs/config';
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { PassportStrategy } from '@nestjs/passport';
|
||||||
import { Request } from 'express';
|
import { Request } from 'express';
|
||||||
|
import { Strategy, ExtractJwt } from 'passport-jwt';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class RefreshTokenStrategy extends PassportStrategy(
|
export class RefreshTokenStrategy extends PassportStrategy(
|
||||||
Strategy,
|
Strategy,
|
||||||
'jwt-refresh-token'
|
'jwt-refresh-token'
|
||||||
) {
|
) {
|
||||||
constructor(private readonly configService: ConfigService) {
|
public constructor(private readonly configService: ConfigService) {
|
||||||
super(RefreshTokenStrategy.createJwtStrategyOptions(configService));
|
super(RefreshTokenStrategy.createJwtStrategyOptions(configService));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
|
||||||
import { ConfigService, ConfigModule } from '@nestjs/config';
|
import { ConfigService, ConfigModule } from '@nestjs/config';
|
||||||
|
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||||
|
|
||||||
import { databaseConfigFactory } from './database-config';
|
import { databaseConfigFactory } from './database-config';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
|
||||||
import { INestApplication } from '@nestjs/common';
|
import { INestApplication } from '@nestjs/common';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import * as request from 'supertest';
|
import * as request from 'supertest';
|
||||||
|
|
||||||
import { AppModule } from './../src/app.module';
|
import { AppModule } from './../src/app.module';
|
||||||
|
|
||||||
describe('AppController (e2e)', () => {
|
describe('AppController (e2e)', () => {
|
||||||
|
|
14
package.json
14
package.json
|
@ -10,6 +10,9 @@
|
||||||
"install:all": "pnpm install && pnpm run install:frontend && pnpm run install:backend",
|
"install:all": "pnpm install && pnpm run install:frontend && pnpm run install:backend",
|
||||||
"install:frontend": "cd frontend && pnpm install",
|
"install:frontend": "cd frontend && pnpm install",
|
||||||
"install:backend": "cd backend && 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"
|
"build:api": "openapi-generator-cli generate"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
|
@ -17,6 +20,15 @@
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@openapitools/openapi-generator-cli": "^2.13.4",
|
"@openapitools/openapi-generator-cli": "^2.13.4",
|
||||||
"concurrently": "^8.2.2"
|
"@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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1904
pnpm-lock.yaml
1904
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue