diff --git a/.forgejo/workflows/release.yml b/.forgejo/workflows/release.yml new file mode 100644 index 0000000..e69de29 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..e69de29 diff --git a/Dockerfile b/Dockerfile index b407e8a..d56e1de 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,58 +1,39 @@ -# base environment -FROM node:22.4.1-alpine3.20 AS base-stage -RUN mkdir /app && chown -R node:node /app -WORKDIR /app +# Setup Base Env +FROM node:20.18.0-alpine3.20 AS base ENV PNPM_HOME="/pnpm" ENV PATH="$PNPM_HOME:$PATH" RUN corepack enable +COPY . /app +WORKDIR /app -# dependency environment -FROM base-stage AS dependency-stage -USER node -COPY --link --chown=1000:1000 package*.json pnpm-lock.yaml ./ +# Install Prod Dependencies +FROM base AS prod-deps +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install typia ts-patch -g +RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --prod --frozen-lockfile + +# Install all Dependencies +FROM base AS all-deps RUN --mount=type=cache,id=pnpm,target=/pnpm/store pnpm install --frozen-lockfile -# test stage to stop at for testing -FROM dependency-stage AS all-source-stage -COPY --link --chown=1000:1000 . . - # lint -FROM all-source-stage AS lint +FROM all-deps AS lint RUN npm run lint -- --no-fix -# test-e2e -FROM all-source-stage AS test-e2e -USER root -RUN mkdir /keys -USER node -RUN --mount=type=secret,id=cookies,target=/keys/cookies.json,uid=1000,gid=1000,required=true \ - --mount=type=secret,id=jwks,target=/keys/jwks.json,uid=1000,gid=1000,required=true \ - npm run test:e2e:cov -- --ci --json --testLocationInResults --outputFile=/tmp/report.json -# Just the e2e report file -FROM scratch AS test-stage -COPY --link --from=test-e2e /tmp/report.json / +#Build the app +FROM all-deps AS build +RUN pnpm run build -# build environment -FROM dependency-stage AS build-stage -COPY --link --chown=1000:1000 . . -RUN npm run build -# prod dependency environment -FROM build-stage AS production-dependency-stage -RUN npm prune --production - -# production environment -FROM base-stage AS production-stage +#Build final image +FROM base RUN apk add --no-cache tini -USER node - -COPY --link --chown=1000:1000 --from=production-dependency-stage /app /app - +COPY --from=prod-deps /app/node_modules /app/node_modules +COPY --from=build /app/dist /app/dist ENV NODE_ENV=production - +EXPOSE 3000 ENTRYPOINT ["/sbin/tini", "--"] -CMD ["node", "/app/.dist/src/main.js"] +CMD ["node", "/app/dist/main.js"] ARG VERSION ENV VERSION=${VERSION} \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index 1595227..f034936 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,5 +16,18 @@ services: restart: unless-stopped ports: - "6379:6379" + + waterwolf-auth: + build: . + restart: unless-stopped + ports: + - "3000:3000" + depends_on: + - mysql + - redis + env_file: + - .env + volumes: + - ./keys:/keys volumes: mysql-data: \ No newline at end of file diff --git a/mail/html/verify-email.hbs b/mail/html/verify-email.hbs index 3b9ef41..0bf239b 100644 --- a/mail/html/verify-email.hbs +++ b/mail/html/verify-email.hbs @@ -1,5 +1,6 @@ + @@ -7,30 +8,48 @@ +

Verify Your Email Address

@@ -58,7 +77,8 @@ - + @@ -66,4 +86,5 @@
+ \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index bddbec6..d6cf73d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -34,10 +34,10 @@ importers: version: 7.4.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2) '@nestjs/terminus': specifier: ^10.2.3 - version: 10.2.3(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/typeorm@10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))) + version: 10.2.3(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/typeorm@10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))) '@nestjs/typeorm': specifier: ^10.0.2 - version: 10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))) + version: 10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))) '@opentelemetry/api': specifier: ^1.9.0 version: 1.9.0 @@ -85,7 +85,7 @@ importers: version: 6.1.1(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1)) nestjs-postal-client: specifier: ^0.0.6 - version: 0.0.6(jrcpsu45e3dhzjv3azmojl75yy) + version: 0.0.6(jdf445rhtpddlny4lqmttufnfe) oidc-provider: specifier: ^8.5.1 version: 8.5.1 @@ -103,10 +103,10 @@ importers: version: 7.8.1 typeorm: specifier: ^0.3.20 - version: 0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + version: 0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) typia: specifier: ^6.11.2 - version: 6.11.2(typescript@5.6.3) + version: 6.11.2(typescript@5.6.2) uuid: specifier: ^10.0.0 version: 10.0.0 @@ -119,7 +119,7 @@ importers: version: 10.4.5 '@nestjs/schematics': specifier: ^10.1.4 - version: 10.1.4(chokidar@3.6.0)(typescript@5.6.3) + version: 10.1.4(chokidar@3.6.0)(typescript@5.6.2) '@nestjs/testing': specifier: ^10.4.4 version: 10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4)) @@ -146,10 +146,10 @@ importers: version: 6.0.2 '@typescript-eslint/eslint-plugin': specifier: ^7.18.0 - version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3) + version: 7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2) '@typescript-eslint/parser': specifier: ^7.18.0 - version: 7.18.0(eslint@8.57.1)(typescript@5.6.3) + version: 7.18.0(eslint@8.57.1)(typescript@5.6.2) eslint: specifier: ^8.57.1 version: 8.57.1 @@ -161,7 +161,7 @@ importers: version: 5.2.1(@types/eslint@9.6.0)(eslint-config-prettier@9.1.0(eslint@8.57.1))(eslint@8.57.1)(prettier@3.3.3) jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + version: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) prettier: specifier: ^3.3.3 version: 3.3.3 @@ -176,16 +176,16 @@ importers: version: 7.0.0 tailwindcss: specifier: ^3.4.14 - version: 3.4.14(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + version: 3.4.14(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) ts-jest: specifier: ^29.2.5 - version: 29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)))(typescript@5.6.3) + version: 29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)))(typescript@5.6.2) ts-loader: specifier: ^9.5.1 - version: 9.5.1(typescript@5.6.3)(webpack@5.94.0) + version: 9.5.1(typescript@5.6.2)(webpack@5.94.0) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@20.16.11)(typescript@5.6.3) + version: 10.9.2(@types/node@20.16.11)(typescript@5.6.2) ts-patch: specifier: ^3.2.1 version: 3.2.1 @@ -193,8 +193,8 @@ importers: specifier: ^4.2.0 version: 4.2.0 typescript: - specifier: ^5.6.3 - version: 5.6.3 + specifier: ^5.6.2 + version: 5.6.2 packages: @@ -3974,8 +3974,8 @@ packages: engines: {node: '>=14.17'} hasBin: true - typescript@5.6.3: - resolution: {integrity: sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==} + typescript@5.6.2: + resolution: {integrity: sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==} engines: {node: '>=14.17'} hasBin: true @@ -4514,7 +4514,7 @@ snapshots: jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 @@ -4528,7 +4528,7 @@ snapshots: exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -4847,14 +4847,14 @@ snapshots: transitivePeerDependencies: - chokidar - '@nestjs/schematics@10.1.4(chokidar@3.6.0)(typescript@5.6.3)': + '@nestjs/schematics@10.1.4(chokidar@3.6.0)(typescript@5.6.2)': dependencies: '@angular-devkit/core': 17.3.8(chokidar@3.6.0) '@angular-devkit/schematics': 17.3.8(chokidar@3.6.0) comment-json: 4.2.3 jsonc-parser: 3.3.1 pluralize: 8.0.0 - typescript: 5.6.3 + typescript: 5.6.2 transitivePeerDependencies: - chokidar @@ -4882,7 +4882,7 @@ snapshots: class-transformer: 0.5.1 class-validator: 0.14.1 - '@nestjs/terminus@10.2.3(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/typeorm@10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)))': + '@nestjs/terminus@10.2.3(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/typeorm@10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)))': dependencies: '@nestjs/common': 10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -4891,8 +4891,8 @@ snapshots: reflect-metadata: 0.2.2 rxjs: 7.8.1 optionalDependencies: - '@nestjs/typeorm': 10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3))) - typeorm: 0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + '@nestjs/typeorm': 10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2))) + typeorm: 0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) '@nestjs/testing@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4))': dependencies: @@ -4902,13 +4902,13 @@ snapshots: optionalDependencies: '@nestjs/platform-express': 10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4) - '@nestjs/typeorm@10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)))': + '@nestjs/typeorm@10.0.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)))': dependencies: '@nestjs/common': 10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) '@nestjs/core': 10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1) reflect-metadata: 0.2.2 rxjs: 7.8.1 - typeorm: 0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + typeorm: 0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) uuid: 9.0.1 '@nodelib/fs.scandir@2.1.5': @@ -5160,34 +5160,34 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3))(eslint@8.57.1)(typescript@5.6.3)': + '@typescript-eslint/eslint-plugin@7.18.0(@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.2))(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@eslint-community/regexpp': 4.11.1 - '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/parser': 7.18.0(eslint@8.57.1)(typescript@5.6.2) '@typescript-eslint/scope-manager': 7.18.0 - '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/type-utils': 7.18.0(eslint@8.57.1)(typescript@5.6.2) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.2) '@typescript-eslint/visitor-keys': 7.18.0 eslint: 8.57.1 graphemer: 1.4.0 ignore: 5.3.2 natural-compare: 1.4.0 - ts-api-utils: 1.3.0(typescript@5.6.3) + ts-api-utils: 1.3.0(typescript@5.6.2) optionalDependencies: - typescript: 5.6.3 + typescript: 5.6.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.3)': + '@typescript-eslint/parser@7.18.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2) '@typescript-eslint/visitor-keys': 7.18.0 debug: 4.3.7 eslint: 8.57.1 optionalDependencies: - typescript: 5.6.3 + typescript: 5.6.2 transitivePeerDependencies: - supports-color @@ -5196,21 +5196,21 @@ snapshots: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 - '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.6.3)': + '@typescript-eslint/type-utils@7.18.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) - '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2) + '@typescript-eslint/utils': 7.18.0(eslint@8.57.1)(typescript@5.6.2) debug: 4.3.7 eslint: 8.57.1 - ts-api-utils: 1.3.0(typescript@5.6.3) + ts-api-utils: 1.3.0(typescript@5.6.2) optionalDependencies: - typescript: 5.6.3 + typescript: 5.6.2 transitivePeerDependencies: - supports-color '@typescript-eslint/types@7.18.0': {} - '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.3)': + '@typescript-eslint/typescript-estree@7.18.0(typescript@5.6.2)': dependencies: '@typescript-eslint/types': 7.18.0 '@typescript-eslint/visitor-keys': 7.18.0 @@ -5219,18 +5219,18 @@ snapshots: is-glob: 4.0.3 minimatch: 9.0.5 semver: 7.6.3 - ts-api-utils: 1.3.0(typescript@5.6.3) + ts-api-utils: 1.3.0(typescript@5.6.2) optionalDependencies: - typescript: 5.6.3 + typescript: 5.6.2 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.6.3)': + '@typescript-eslint/utils@7.18.0(eslint@8.57.1)(typescript@5.6.2)': dependencies: '@eslint-community/eslint-utils': 4.4.0(eslint@8.57.1) '@typescript-eslint/scope-manager': 7.18.0 '@typescript-eslint/types': 7.18.0 - '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.3) + '@typescript-eslint/typescript-estree': 7.18.0(typescript@5.6.2) eslint: 8.57.1 transitivePeerDependencies: - supports-color @@ -5884,13 +5884,13 @@ snapshots: optionalDependencies: typescript: 5.3.3 - create-jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + create-jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -6851,16 +6851,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + jest-cli@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + create-jest: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + jest-config: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -6870,7 +6870,7 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + jest-config@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)): dependencies: '@babel/core': 7.25.8 '@jest/test-sequencer': 29.7.0 @@ -6896,7 +6896,7 @@ snapshots: strip-json-comments: 3.1.1 optionalDependencies: '@types/node': 20.16.11 - ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.3) + ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.2) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -7122,12 +7122,12 @@ snapshots: merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + jest-cli: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -7484,7 +7484,7 @@ snapshots: '@opentelemetry/host-metrics': 0.35.4(@opentelemetry/api@1.9.0) response-time: 2.3.3 - nestjs-postal-client@0.0.6(jrcpsu45e3dhzjv3azmojl75yy): + nestjs-postal-client@0.0.6(jdf445rhtpddlny4lqmttufnfe): dependencies: '@nestjs/cache-manager': 2.2.2(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(cache-manager@5.7.2)(rxjs@7.8.1) '@nestjs/common': 10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1) @@ -7495,7 +7495,7 @@ snapshots: kakious-nestjs-http-promise: 0.0.1(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(axios@1.7.2) nestjs-cls: 4.4.1(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1) nestjs-otel: 6.1.1(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.4.4(@nestjs/common@10.4.4(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.4.4)(reflect-metadata@0.2.2)(rxjs@7.8.1)) - typia: 6.11.2(typescript@5.6.3) + typia: 6.11.2(typescript@5.6.2) transitivePeerDependencies: - typescript @@ -7719,13 +7719,13 @@ snapshots: camelcase-css: 2.0.1 postcss: 8.4.47 - postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + postcss-load-config@4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)): dependencies: lilconfig: 3.1.2 yaml: 2.6.0 optionalDependencies: postcss: 8.4.47 - ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.3) + ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.2) postcss-nested@6.2.0(postcss@8.4.47): dependencies: @@ -8227,7 +8227,7 @@ snapshots: systeminformation@5.22.9: {} - tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + tailwindcss@3.4.14(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)): dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -8246,7 +8246,7 @@ snapshots: postcss: 8.4.47 postcss-import: 15.1.0(postcss@8.4.47) postcss-js: 4.0.1(postcss@8.4.47) - postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + postcss-load-config: 4.0.2(postcss@8.4.47)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) postcss-nested: 6.2.0(postcss@8.4.47) postcss-selector-parser: 6.1.2 resolve: 1.22.8 @@ -8321,24 +8321,24 @@ snapshots: tree-kill@1.2.2: {} - ts-api-utils@1.3.0(typescript@5.6.3): + ts-api-utils@1.3.0(typescript@5.6.2): dependencies: - typescript: 5.6.3 + typescript: 5.6.2 ts-interface-checker@0.1.13: {} - ts-jest@29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)))(typescript@5.6.3): + ts-jest@29.2.5(@babel/core@7.25.8)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.25.8))(jest@29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)))(typescript@5.6.2): dependencies: bs-logger: 0.2.6 ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)) + jest: 29.7.0(@types/node@20.16.11)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)) jest-util: 29.7.0 json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 semver: 7.6.3 - typescript: 5.6.3 + typescript: 5.6.2 yargs-parser: 21.1.1 optionalDependencies: '@babel/core': 7.25.8 @@ -8346,17 +8346,17 @@ snapshots: '@jest/types': 29.6.3 babel-jest: 29.7.0(@babel/core@7.25.8) - ts-loader@9.5.1(typescript@5.6.3)(webpack@5.94.0): + ts-loader@9.5.1(typescript@5.6.2)(webpack@5.94.0): dependencies: chalk: 4.1.2 enhanced-resolve: 5.17.1 micromatch: 4.0.8 semver: 7.6.3 source-map: 0.7.4 - typescript: 5.6.3 + typescript: 5.6.2 webpack: 5.94.0 - ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3): + ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2): dependencies: '@cspotcode/source-map-support': 0.8.1 '@tsconfig/node10': 1.0.11 @@ -8370,7 +8370,7 @@ snapshots: create-require: 1.1.1 diff: 4.0.2 make-error: 1.3.6 - typescript: 5.6.3 + typescript: 5.6.2 v8-compile-cache-lib: 3.0.1 yn: 3.1.1 @@ -8422,7 +8422,7 @@ snapshots: typedarray@0.0.6: {} - typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.3)): + typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.11.3)(ts-node@10.9.2(@types/node@20.16.11)(typescript@5.6.2)): dependencies: '@sqltools/formatter': 1.2.5 app-root-path: 3.1.0 @@ -8442,15 +8442,15 @@ snapshots: optionalDependencies: ioredis: 5.4.1 mysql2: 3.11.3 - ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.3) + ts-node: 10.9.2(@types/node@20.16.11)(typescript@5.6.2) transitivePeerDependencies: - supports-color typescript@5.3.3: {} - typescript@5.6.3: {} + typescript@5.6.2: {} - typia@6.11.2(typescript@5.6.3): + typia@6.11.2(typescript@5.6.2): dependencies: '@samchon/openapi': 1.1.1 commander: 10.0.1 @@ -8458,7 +8458,7 @@ snapshots: inquirer: 8.2.6 package-manager-detector: 0.2.2 randexp: 0.5.3 - typescript: 5.6.3 + typescript: 5.6.2 uglify-js@3.19.3: optional: true diff --git a/src/app.controller.ts b/src/app.controller.ts index 5d4331d..3f68793 100644 --- a/src/app.controller.ts +++ b/src/app.controller.ts @@ -1,7 +1,7 @@ import { Controller, Get, Redirect, UseGuards } from '@nestjs/common'; import { AppService } from './app.service'; -import { LoginGuard } from './auth/guard/login.guard'; -import { MailService } from './mail/mail.service'; +import { ViewLoginGuard } from './auth/guard/viewLogin.guard'; +import { MailService } from './mail/service/mail.service'; import { ApiExcludeController } from '@nestjs/swagger'; @Controller() @@ -14,7 +14,7 @@ export class AppController { @Get() @Redirect('/auth/login') - @UseGuards(LoginGuard) + @UseGuards(ViewLoginGuard) getHello(): any { return; } diff --git a/src/auth/auth.const.ts b/src/auth/auth.const.ts index 84ce056..48083a6 100644 --- a/src/auth/auth.const.ts +++ b/src/auth/auth.const.ts @@ -29,3 +29,10 @@ export const REGISTRATION_EXPIRATION = 60 * 60 * 24; // 24 hours export const REGISTRATION_LIMIT = 10; export const getRegistrationKey = (ip: string): string => `${REGISTRATION_CACHE_KEY}${ip}`; + +export enum AuthMethods { + ClientSecret = 'ClientSecret', + Session = 'Session', + AccessToken = 'AccessToken', + APIToken = 'APIToken', +} diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 8582673..ee137b0 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -5,8 +5,8 @@ import { AuthService } from '../services/auth.service'; import { ForgotPasswordDto } from '../dto/forgotPassword.dto'; import { CreateUserDto } from '../dto/register.dto'; import { LoginUserDto } from '../dto/loginUser.dto'; -import { User } from '../decorators/user.decorator'; -import { LoginGuard } from '../guard/login.guard'; +import { CurrentUser } from '../decorators/user.decorator'; +import { ViewLoginGuard } from '../guard/viewLogin.guard'; import { Response, Request } from 'express'; import { LoginResponse } from '../dto/loginResponse.dto'; @@ -51,7 +51,7 @@ export class AuthController { // ==== Render pages ==== // @Get('verify-email') - @UseGuards(LoginGuard) + @UseGuards(ViewLoginGuard) @ApiExcludeEndpoint() public async verifyEmail(@Res() response: Response, @Query('code') code: string): Promise { if (!code) { diff --git a/src/auth/controllers/interaction.controller.ts b/src/auth/controllers/interaction.controller.ts index 6e19304..0363679 100644 --- a/src/auth/controllers/interaction.controller.ts +++ b/src/auth/controllers/interaction.controller.ts @@ -12,12 +12,12 @@ import { ApiExcludeController } from '@nestjs/swagger'; import { AuthService } from '../services/auth.service'; -import { User } from '../decorators/user.decorator'; +import { CurrentUser } from '../decorators/user.decorator'; import { Request, Response } from 'express'; import { InteractionService } from '../oidc/service/interaction.service'; import { OidcService } from '../oidc/service/core.service'; import { InteractionParams, InteractionSession } from '../oidc/types/interaction.type'; -import { User as UserObject } from 'src/database/models/user.model'; +import { User } from '../../database/models/user.model'; @Controller('interaction') @ApiExcludeController() @@ -31,7 +31,7 @@ export class InteractionController { logger = new Logger(InteractionController.name); @Get(':id') - async getUserInteraction(@Res() res: Response, @Req() req: Request, @User() user: UserObject) { + async getUserInteraction(@Res() res: Response, @Req() req: Request, @CurrentUser() user: User) { const details: { session?: InteractionSession; uid: string; diff --git a/src/auth/decorators/user.decorator.ts b/src/auth/decorators/user.decorator.ts index 5846b73..9861c0f 100644 --- a/src/auth/decorators/user.decorator.ts +++ b/src/auth/decorators/user.decorator.ts @@ -1,8 +1,8 @@ import { createParamDecorator } from '@nestjs/common'; import { ClsServiceManager } from 'nestjs-cls'; -import { User as UserObject } from 'src/database/models/user.model'; +import { User } from 'src/database/models/user.model'; -export const User = createParamDecorator((): UserObject | null => { +export const CurrentUser = createParamDecorator((): User | null => { const cls = ClsServiceManager.getClsService(); const authType = cls.get('authType'); diff --git a/src/auth/guard/auth.guard.ts b/src/auth/guard/auth.guard.ts index e69de29..0f8c637 100644 --- a/src/auth/guard/auth.guard.ts +++ b/src/auth/guard/auth.guard.ts @@ -0,0 +1,19 @@ +import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; +import { ClsService } from 'nestjs-cls'; +import { Observable } from 'rxjs'; +import { Response } from 'express'; + +@Injectable() +export class APILoginGuard implements CanActivate { + constructor(private readonly clsService: ClsService) {} + canActivate(context: ExecutionContext): boolean | Promise | Observable { + const authType = this.clsService.get('authType'); + const response = context.switchToHttp().getResponse() as Response; + + if (authType === 'session') { + response.redirect('/home'); + return false; + } + return true; + } +} diff --git a/src/auth/guard/login.guard.ts b/src/auth/guard/viewLogin.guard.ts similarity index 52% rename from src/auth/guard/login.guard.ts rename to src/auth/guard/viewLogin.guard.ts index 2187bac..e7e0c74 100644 --- a/src/auth/guard/login.guard.ts +++ b/src/auth/guard/viewLogin.guard.ts @@ -4,17 +4,34 @@ import { Observable } from 'rxjs'; import { Response } from 'express'; @Injectable() -export class LoginGuard implements CanActivate { +export class ViewLoginGuard implements CanActivate { constructor(private readonly clsService: ClsService) {} canActivate(context: ExecutionContext): boolean | Promise | Observable { const authType = this.clsService.get('authType'); const response = context.switchToHttp().getResponse() as Response; - console.log('test'); - if (authType === 'session') { - response.redirect('/auth/auth-test'); - return false; + const loginPages = [ + '/auth/login', + '/auth/forgot-password', + '/auth/reset-password', + '/auth/register', + '/auth/verify-email', + '/', + ]; + + if (loginPages.includes(context.switchToHttp().getRequest().url)) { + if (authType === 'session') { + response.redirect('/home'); + return false; + } + + return true; } - return true; + + if (authType === 'session') { + return true; + } + + return false; } } diff --git a/src/auth/interceptors/selfReflection.interceptor.ts b/src/auth/interceptors/selfReflection.interceptor.ts new file mode 100644 index 0000000..f75d849 --- /dev/null +++ b/src/auth/interceptors/selfReflection.interceptor.ts @@ -0,0 +1,30 @@ +import { + BadRequestException, + CallHandler, + ExecutionContext, + Injectable, + NestInterceptor, +} from '@nestjs/common'; +import { Observable, throwError } from 'rxjs'; + +import { AuthMethods } from '../auth.const'; +import { ClsService } from 'nestjs-cls'; + +@Injectable() +export class UserSelfReflection implements NestInterceptor { + constructor(private readonly clsService: ClsService) {} + + intercept(context: ExecutionContext, next: CallHandler): Observable { + const request = context.switchToHttp().getRequest(); + const authType = this.clsService.get('authType'); + if (request.params.id === '@me' && request.path.includes('/user/')) { + if (authType === AuthMethods.ClientSecret) { + return throwError( + () => new BadRequestException('Unable to use @me without User Auth Session'), + ); + } + request.params.id = this.clsService.get('user').id; + } + return next.handle().pipe(); + } +} diff --git a/src/auth/services/auth.service.ts b/src/auth/services/auth.service.ts index 9d94f41..5165c9f 100644 --- a/src/auth/services/auth.service.ts +++ b/src/auth/services/auth.service.ts @@ -8,7 +8,7 @@ import { Response, Request } from 'express'; import { UserService } from '../../user/service/user.service'; import { RedisService } from '../../redis/service/redis.service'; -import { MailService } from '../../mail/mail.service'; +import { MailService } from '../../mail/service/mail.service'; import { getEmailVerifyKey, getResetKey, diff --git a/src/config/config.ts b/src/config/config.ts index 438c29d..2b9391e 100644 --- a/src/config/config.ts +++ b/src/config/config.ts @@ -9,5 +9,6 @@ export default async () => ({ port: process.env['REDIS_POST'] ?? 6379, password: process.env['REDIS_PASSWORD'] ?? '', db: process.env['REDIS_DB'] ?? 0, + searchDisabled: process.env['REDIS_SEARCH_DISABLED'] ?? false, }, }); diff --git a/src/mail/postal.config.ts b/src/mail/config/postal.config.ts similarity index 90% rename from src/mail/postal.config.ts rename to src/mail/config/postal.config.ts index 161d1c8..a13296a 100644 --- a/src/mail/postal.config.ts +++ b/src/mail/config/postal.config.ts @@ -2,7 +2,7 @@ import { registerAs } from '@nestjs/config'; import type { tags } from 'typia'; import typia from 'typia'; -import { validateOrThrow } from '../util/validation.util'; +import { validateOrThrow } from '../../util/validation.util'; export const POSTAL_CONFIG_KEY = 'postal'; diff --git a/src/mail/mail.module.ts b/src/mail/mail.module.ts index e96c9f1..8d607aa 100644 --- a/src/mail/mail.module.ts +++ b/src/mail/mail.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { PostalClientModule } from 'nestjs-postal-client'; -import postalConfig from './postal.config'; -import { PostalConfigService } from './postal_config.service'; -import { MailService } from './mail.service'; +import postalConfig from './config/postal.config'; +import { PostalConfigService } from './service/postal_config.service'; +import { MailService } from './service/mail.service'; @Module({ imports: [ diff --git a/src/mail/mail.service.ts b/src/mail/service/mail.service.ts similarity index 100% rename from src/mail/mail.service.ts rename to src/mail/service/mail.service.ts diff --git a/src/mail/postal_config.service.ts b/src/mail/service/postal_config.service.ts similarity index 85% rename from src/mail/postal_config.service.ts rename to src/mail/service/postal_config.service.ts index b86be55..741c67e 100644 --- a/src/mail/postal_config.service.ts +++ b/src/mail/service/postal_config.service.ts @@ -3,8 +3,8 @@ import type { PostalModuleOptions, PostalModuleOptionsFactory } from 'nestjs-pos import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import type { PostalConfig } from './postal.config'; -import { POSTAL_CONFIG_KEY } from './postal.config'; +import type { PostalConfig } from '../config/postal.config'; +import { POSTAL_CONFIG_KEY } from '../config/postal.config'; @Injectable() export class PostalConfigService implements PostalModuleOptionsFactory { diff --git a/src/main.ts b/src/main.ts index 1f726ef..9366e4b 100644 --- a/src/main.ts +++ b/src/main.ts @@ -11,6 +11,8 @@ import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger'; async function bootstrap() { const app = await NestFactory.create(AppModule); + app.disable('x-powered-by'); + app.setGlobalPrefix('api', { exclude: [ { path: 'auth/login', method: RequestMethod.GET }, diff --git a/src/organization/controller/organization.controller.ts b/src/organization/controller/organization.controller.ts index 3a4d924..e37ce1d 100644 --- a/src/organization/controller/organization.controller.ts +++ b/src/organization/controller/organization.controller.ts @@ -3,8 +3,8 @@ import { ApiOperation, ApiTags } from '@nestjs/swagger'; import { OrganizationService } from '../service/organization.service'; import { OrgSetFlagDto } from '../dto/orgSetFlag.dto'; import { CreateOrgDto } from '../dto/orgCreate.dto'; -import { User as UserObject } from '../../database/models/user.model'; -import { User } from '../../auth/decorators/user.decorator'; +import { User } from '../../database/models/user.model'; +import { CurrentUser } from '../../auth/decorators/user.decorator'; @Controller('organization') @ApiTags('Organization') @@ -41,7 +41,7 @@ export class OrganizationController { @Post() public async createOrganization( @Body() body: CreateOrgDto, - @User() user: UserObject, + @CurrentUser() user: User, ): Promise { return await this.organizationService.createOrganization(user.id, body); } diff --git a/src/redis/redis.const.ts b/src/redis/redis.const.ts new file mode 100644 index 0000000..ff33930 --- /dev/null +++ b/src/redis/redis.const.ts @@ -0,0 +1 @@ +export const REDIS_SEARCH_DISABLED_ERROR = `Search is disabled.`; diff --git a/src/redis/service/search.service.ts b/src/redis/service/search.service.ts index dbba7e8..742c0da 100644 --- a/src/redis/service/search.service.ts +++ b/src/redis/service/search.service.ts @@ -1,14 +1,29 @@ -import { Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common'; +import { BadRequestException, Injectable, Logger, OnApplicationBootstrap } from '@nestjs/common'; import { RedisService } from './redis.service'; import { userCacheKey } from '../../user/user.constant'; +import { REDIS_SEARCH_DISABLED_ERROR } from '../redis.const'; +import { ConfigService } from '@nestjs/config'; @Injectable() export class SearchService implements OnApplicationBootstrap { private readonly logger = new Logger(SearchService.name); - constructor(private readonly redisService: RedisService) {} + + searchDisabled = true; + + constructor( + private readonly redisService: RedisService, + private readonly configService: ConfigService, + ) { + this.searchDisabled = this.configService.getOrThrow('redis.searchDisabled'); + } async onApplicationBootstrap() { - this.logger.debug('Checking Index in Redis Serch'); + if (this.searchDisabled) { + this.logger.warn(REDIS_SEARCH_DISABLED_ERROR); + return; + } + + this.logger.debug('Checking Index in Redis Search'); if (!(await this.checkIndexExists('idx:users'))) { this.logger.log('Created users redis index'); @@ -35,9 +50,12 @@ export class SearchService implements OnApplicationBootstrap { 'TEXT', ); } + + this.logger.log('Redis Search Initialized'); } public async createIndex(indexName: string, keyPrefix: string, ...schema: string[]) { + this.searchCheck(); this.redisService.ioredis.call( 'FT.CREATE', indexName, @@ -57,6 +75,7 @@ export class SearchService implements OnApplicationBootstrap { * @returns boolean */ public async checkIndexExists(indexName: string): Promise { + this.searchCheck(); try { await this.redisService.ioredis.call('FT.INFO', indexName); return true; @@ -67,7 +86,15 @@ export class SearchService implements OnApplicationBootstrap { } } - public async search(index: string, searchQuery: string, field?: string) { + public async search( + index: string, + searchQuery: string, + field?: string, + ): Promise<{ + totalResults: number; + results: any[]; + }> { + this.searchCheck(); let query: string = ''; searchQuery = searchQuery.replace(/[.@\\]/g, '\\$&'); @@ -83,7 +110,10 @@ export class SearchService implements OnApplicationBootstrap { const redisSearch = (await this.redisService.ioredis.call('FT.SEARCH', index, query)) as any; if (redisSearch[0] === 0) { - return []; + return { + totalResults: 0, + results: [], + }; } delete redisSearch[2][0]; @@ -105,4 +135,10 @@ export class SearchService implements OnApplicationBootstrap { results: redisSearchResults, }; } + + private searchCheck() { + if (this.searchDisabled) { + throw new BadRequestException(REDIS_SEARCH_DISABLED_ERROR); + } + } } diff --git a/src/user/controller/user.controller.ts b/src/user/controller/user.controller.ts index 5aaa984..fc41617 100644 --- a/src/user/controller/user.controller.ts +++ b/src/user/controller/user.controller.ts @@ -1,22 +1,28 @@ -import { Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; +import { + Controller, + Delete, + Get, + Param, + Patch, + Post, + Put, + Query, + UseInterceptors, +} from '@nestjs/common'; import { ApiParam, ApiTags } from '@nestjs/swagger'; import { UserService } from '../service/user.service'; -import { RedisService } from '../../redis/service/redis.service'; -import { SearchService } from '../../redis/service/search.service'; + +import { UserSelfReflection } from 'src/auth/interceptors/selfReflection.interceptor'; @Controller('user') @ApiTags('User') export class UserController { - constructor( - private readonly userService: UserService, - private readonly redisService: RedisService, - private readonly searchService: SearchService, - ) {} + constructor(private readonly userService: UserService) {} @Get('/search') @ApiParam({ name: 'field', required: false }) public async search(@Query('query') query: string, @Query('field') field?: string) { - return await this.searchService.search('idx:users', query, field); + return await this.userService.search(query, field); } @Get() @@ -28,6 +34,7 @@ export class UserController { @Get(':id') // Authenticated : Has to be publicly seen due to org inviting. Filter amount of data unless your an admin. // Allow self reflection to get all user data. @me + @UseInterceptors(UserSelfReflection) public async getUser(@Param('id') id: string): Promise { return await this.userService.getUserById(id); } diff --git a/src/user/service/user.service.ts b/src/user/service/user.service.ts index f004971..1333443 100644 --- a/src/user/service/user.service.ts +++ b/src/user/service/user.service.ts @@ -20,6 +20,7 @@ import { userCacheKeyGenerate, } from '../user.constant'; import { ClsService } from 'nestjs-cls'; +import { SearchService } from '../../redis/service/search.service'; @Injectable() export class UserService { @@ -27,6 +28,7 @@ export class UserService { @InjectRepository(User) private readonly userRepository: Repository, private readonly redisService: RedisService, + private readonly searchService: SearchService, private readonly clsService: ClsService, ) {} @@ -98,6 +100,21 @@ export class UserService { return user; } + async search(query: string, field?: string): Promise { + const result = await this.searchService.search('idx:users', query, field); + + // iterate through the search results and only return the following fields from each one. username, id, displayName, avatar + + result.results = result.results.map((user) => { + return { + id: user.id, + displayName: user.displayName || user.username, + avatar: user.avatar, + }; + }); + + return result; + } /** * Validate that a username and email is unique * @param username The username to validate diff --git a/src/view/controllers/view.controller.ts b/src/view/controllers/view.controller.ts index c5e30df..b7c6227 100644 --- a/src/view/controllers/view.controller.ts +++ b/src/view/controllers/view.controller.ts @@ -1,9 +1,9 @@ import { Controller, Get, Render, UseGuards } from '@nestjs/common'; import { ApiExcludeController, ApiExcludeEndpoint } from '@nestjs/swagger'; -import { LoginGuard } from '../../auth/guard/login.guard'; -import { User } from '../../auth/decorators/user.decorator'; -import { User as UserObject } from '../../database/models/user.model'; +import { ViewLoginGuard } from '../../auth/guard/viewLogin.guard'; +import { CurrentUser } from '../../auth/decorators/user.decorator'; +import { User } from '../../database/models/user.model'; @ApiExcludeController() @Controller() @@ -11,7 +11,7 @@ export class ViewController { @Get('auth/login') @Render('auth/login') @ApiExcludeEndpoint() - @UseGuards(LoginGuard) + @UseGuards(ViewLoginGuard) public async loginView(): Promise { return { forgot_password: 'forgot-password', @@ -24,7 +24,7 @@ export class ViewController { @Get('auth/login/totp') @Render('auth/login-totp') @ApiExcludeEndpoint() - @UseGuards(LoginGuard) + @UseGuards(ViewLoginGuard) public async getLoginTotp(): Promise { return { login: 'login', @@ -35,7 +35,7 @@ export class ViewController { @Get('auth/register') @Render('auth/register') @ApiExcludeEndpoint() - @UseGuards(LoginGuard) + @UseGuards(ViewLoginGuard) public async getRegister(): Promise { return { login: 'login', @@ -43,7 +43,7 @@ export class ViewController { } @Get('auth/forgot-password') - @UseGuards(LoginGuard) + @UseGuards(ViewLoginGuard) @Render('auth/forgot-password') @ApiExcludeEndpoint() public async getForgotPassword(): Promise { @@ -55,7 +55,8 @@ export class ViewController { @Get('home') @Render('home/index') @ApiExcludeEndpoint() - public async getHomeView(@User() user: UserObject): Promise { + @UseGuards(ViewLoginGuard) + public async getHomeView(@CurrentUser() user: User): Promise { return { user: { name: user.displayName ?? user.username, diff --git a/src/worker.module.ts b/src/worker.module.ts new file mode 100644 index 0000000..e69de29 diff --git a/src/worker.ts b/src/worker.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/app.e2e-spec.ts b/test/app.e2e-spec.ts index 50cda62..1a013be 100644 --- a/test/app.e2e-spec.ts +++ b/test/app.e2e-spec.ts @@ -16,9 +16,6 @@ describe('AppController (e2e)', () => { }); it('/ (GET)', () => { - return request(app.getHttpServer()) - .get('/') - .expect(200) - .expect('Hello World!'); + return request(app.getHttpServer()).get('/').expect(200).expect('Hello World!'); }); });