feat: implement base db support
feat: implement dev containers
This commit is contained in:
parent
ca8ee728fb
commit
a71c33c085
22 changed files with 1408 additions and 262 deletions
63
.devcontainer/devcontainer.json
Normal file
63
.devcontainer/devcontainer.json
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
|
||||||
|
// README at: https://github.com/devcontainers/templates/tree/main/src/docker-existing-docker-compose
|
||||||
|
{
|
||||||
|
"name": "waterwolf-auth",
|
||||||
|
// Update the 'dockerComposeFile' list if you have more compose files or use different names.
|
||||||
|
// The .devcontainer/docker-compose.yml file contains any overrides you need/want to make.
|
||||||
|
"dockerComposeFile": ["../docker-compose.yml", "docker-compose.yml"],
|
||||||
|
// The 'service' property is the name of the service for the container that VS Code should
|
||||||
|
// use. Update this value and .devcontainer/docker-compose.yml to the real service name.
|
||||||
|
"service": "waterwolf-auth",
|
||||||
|
// The optional 'workspaceFolder' property is the path VS Code should open by default when
|
||||||
|
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
|
||||||
|
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
||||||
|
"features": {
|
||||||
|
"ghcr.io/cirolosapio/devcontainers-features/alpine-git:0": {}
|
||||||
|
},
|
||||||
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
|
// "features": {},
|
||||||
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
|
"forwardPorts": [3000, "mysql:3306"],
|
||||||
|
|
||||||
|
"portsAttributes": {
|
||||||
|
"3000": {
|
||||||
|
"label": "Application port"
|
||||||
|
},
|
||||||
|
"9464": {
|
||||||
|
"label": "Metrics port",
|
||||||
|
"onAutoForward": "ignore"
|
||||||
|
},
|
||||||
|
"mysql:3306": {
|
||||||
|
"label": "MySQL"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
// Uncomment the next line if you want start specific services in your Docker Compose config.
|
||||||
|
// "runServices": [],
|
||||||
|
// Uncomment the next line if you want to keep your containers running after VS Code shuts down.
|
||||||
|
// "shutdownAction": "none",
|
||||||
|
// Uncomment the next line to run commands after the container is created.
|
||||||
|
"postCreateCommand": "./.devcontainer/postCreateCommand.sh",
|
||||||
|
// Configure tool-specific properties.
|
||||||
|
"customizations": {
|
||||||
|
"vscode": {
|
||||||
|
"extensions": [
|
||||||
|
"dbaeumer.vscode-eslint",
|
||||||
|
"editorconfig.editorconfig",
|
||||||
|
"github.vscode-github-actions",
|
||||||
|
"visualstudioexptteam.vscodeintellicode",
|
||||||
|
"orta.vscode-jest",
|
||||||
|
"eamodio.gitlens",
|
||||||
|
"firsttris.vscode-jest-runner",
|
||||||
|
"christian-kohler.path-intellisense",
|
||||||
|
"esbenp.prettier-vscode",
|
||||||
|
"rvest.vs-code-prettier-eslint",
|
||||||
|
"steoates.autoimport",
|
||||||
|
"pmneo.tsimporter",
|
||||||
|
"christian-kohler.npm-intellisense",
|
||||||
|
"rohinivsenthil.rabbitrace"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Uncomment to connect as an existing user other than the container default. More info: https://aka.ms/dev-containers-non-root.
|
||||||
|
// "remoteUser": "devcontainer"
|
||||||
|
}
|
26
.devcontainer/docker-compose.yml
Normal file
26
.devcontainer/docker-compose.yml
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
version: '3.8'
|
||||||
|
services:
|
||||||
|
# Update this to the name of the service you want to work with in your docker-compose.yml file
|
||||||
|
waterwolf-auth:
|
||||||
|
# Uncomment if you want to override the service's Dockerfile to one in the .devcontainer
|
||||||
|
# folder. Note that the path of the Dockerfile and context is relative to the *primary*
|
||||||
|
# docker-compose.yml file (the first in the devcontainer.json "dockerComposeFile"
|
||||||
|
# array). The sample below assumes your primary file is in the root of your project.
|
||||||
|
#
|
||||||
|
# build:
|
||||||
|
# context: .
|
||||||
|
# dockerfile: .devcontainer/Dockerfile
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
# Update this to wherever you want VS Code to mount the folder of your project
|
||||||
|
- ..:/workspaces:cached
|
||||||
|
|
||||||
|
# Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
|
||||||
|
# cap_add:
|
||||||
|
# - SYS_PTRACE
|
||||||
|
# security_opt:
|
||||||
|
# - seccomp:unconfined
|
||||||
|
|
||||||
|
# Overrides default command so things don't shut down after the process ends.
|
||||||
|
command: /bin/sh -c "while sleep 1000; do :; done"
|
||||||
|
|
140
.dockerignore
Normal file
140
.dockerignore
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
node_modules/
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# Snowpack dependency directory (https://snowpack.dev/)
|
||||||
|
web_modules/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional stylelint cache
|
||||||
|
.stylelintcache
|
||||||
|
|
||||||
|
# Microbundle cache
|
||||||
|
.rpt2_cache/
|
||||||
|
.rts2_cache_cjs/
|
||||||
|
.rts2_cache_es/
|
||||||
|
.rts2_cache_umd/
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
.parcel-cache
|
||||||
|
|
||||||
|
# Next.js build output
|
||||||
|
# .next
|
||||||
|
# out
|
||||||
|
|
||||||
|
# Nuxt.js build / generate output
|
||||||
|
.nuxt
|
||||||
|
dist
|
||||||
|
|
||||||
|
# Gatsby files
|
||||||
|
.cache/
|
||||||
|
# Comment in the public line in if your project uses Gatsby and not Next.js
|
||||||
|
# https://nextjs.org/blog/next-9-1#public-directory-support
|
||||||
|
# public
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# vuepress v2.x temp and cache directory
|
||||||
|
.temp
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# Docusaurus cache and generated files
|
||||||
|
.docusaurus
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
||||||
|
|
||||||
|
# TernJS port file
|
||||||
|
.tern-port
|
||||||
|
|
||||||
|
# Stores VSCode versions used for testing VSCode extensions
|
||||||
|
.vscode-test
|
||||||
|
|
||||||
|
bin/
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
keys/
|
||||||
|
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.github
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
**/README.md
|
58
Dockerfile
Normal file
58
Dockerfile
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
# base environment
|
||||||
|
FROM node:22.4.1-alpine3.20 AS base-stage
|
||||||
|
RUN mkdir /app && chown -R node:node /app
|
||||||
|
WORKDIR /app
|
||||||
|
ENV PNPM_HOME="/pnpm"
|
||||||
|
ENV PATH="$PNPM_HOME:$PATH"
|
||||||
|
RUN corepack enable
|
||||||
|
|
||||||
|
# dependency environment
|
||||||
|
FROM base-stage AS dependency-stage
|
||||||
|
USER node
|
||||||
|
COPY --link --chown=1000:1000 package*.json pnpm-lock.yaml ./
|
||||||
|
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
|
||||||
|
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 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
|
||||||
|
RUN apk add --no-cache tini
|
||||||
|
USER node
|
||||||
|
|
||||||
|
COPY --link --chown=1000:1000 --from=production-dependency-stage /app /app
|
||||||
|
|
||||||
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
|
ENTRYPOINT ["/sbin/tini", "--"]
|
||||||
|
CMD ["node", "/app/.dist/src/main.js"]
|
||||||
|
|
||||||
|
ARG VERSION
|
||||||
|
ENV VERSION=${VERSION}
|
38
README.md
38
README.md
|
@ -1,8 +1,27 @@
|
||||||
## Waterwolf Identity Solution
|
# Waterwolf Identity Solution
|
||||||
|
|
||||||
This is a re-imagination on how BOOP works with an optimized OAUTH 2.0 flow. This will hook into the existing user database to read usernames and passwords.
|
## Description
|
||||||
|
|
||||||
|
This is the identity service for the Waterwolf project. It is a NestJS application that provides an API for managing users and authentication.
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
To get started with development, first clone the repository.
|
||||||
|
|
||||||
|
Create a GitHub classic PAT with permission to read repos and packages in order to be able to install the private furality npm packages .
|
||||||
|
https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens#creating-a-personal-access-token-classic
|
||||||
|
Create the token and put it in a file in the root of the repository named `.github_token`.
|
||||||
|
|
||||||
|
Then install vscode which supports devcontainers and open the repository in vscode as a devcontainer.
|
||||||
|
This will set up all the necessary supporting services needed to run, test, and debug this service.
|
||||||
|
|
||||||
|
Once it finishes building initially, goto the testing tab of vscode and run the tests to make sure
|
||||||
|
they pass and that your environment is properly set up.
|
||||||
|
|
||||||
|
Next, you can start up the app by going to the run and debug tab and selecting the `nest start watch` task.
|
||||||
|
Once it is started, in the bottom pane of vscode, goto the PORTS tab and look for the `Application port` row.
|
||||||
|
Either right click or copy the forwarded address to open it in your browser and then navigate to the path:
|
||||||
|
http://localhost:{PORT}/v1/api/ to ensure it has started correctly
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
|
@ -17,3 +36,18 @@ pnpm install
|
||||||
```bash
|
```bash
|
||||||
pnpm start
|
pnpm start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
This uses `@nestjs/config` which uses [dotenv](https://github.com/motdotla/dotenv).
|
||||||
|
Configuration can be done by using a `.env` file or environment variables.
|
||||||
|
|
||||||
|
Mailing Configuration
|
||||||
|
- `POSTAL_BASE_URL` - Base URL for the Postal API
|
||||||
|
- `POSTAL_API_KEY` - API Key for the Postal API
|
||||||
|
|
||||||
|
## Credits
|
||||||
|
|
||||||
|
- [ConiCaw](https://github.com/ttshivers) - Teaching Kakious about documentation and base documenation from other projects
|
||||||
|
- [Kakious](https://rawr.ing) - Main Developer and Documentation Writer
|
28
docker-compose.yml
Normal file
28
docker-compose.yml
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
mysql:
|
||||||
|
image: mysql:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
MYSQL_ROOT_PASSWORD: password
|
||||||
|
MYSQL_DATABASE: waterwolf-auth
|
||||||
|
volumes:
|
||||||
|
- mysql-data:/var/lib/mysql
|
||||||
|
redis:
|
||||||
|
image: redis/redis-stack-server:6.2.2-v5
|
||||||
|
restart: unless-stopped
|
||||||
|
waterwolf-auth:
|
||||||
|
depends_on:
|
||||||
|
- mysql
|
||||||
|
- redis
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: all-source-stage
|
||||||
|
restart: unless-stopped
|
||||||
|
expose:
|
||||||
|
- '3000'
|
||||||
|
stdin_open: true
|
||||||
|
tty: true
|
||||||
|
volumes:
|
||||||
|
mysql-data:
|
1
keys/cookies.json
Normal file
1
keys/cookies.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
['THIS IS A SECURE THING']
|
0
keys/jwks.json
Normal file
0
keys/jwks.json
Normal file
18
package.json
18
package.json
|
@ -18,25 +18,34 @@
|
||||||
"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",
|
||||||
|
"typeorm": "ts-node --project tsconfig.json ./node_modules/typeorm/cli",
|
||||||
|
"typeorm:run-migrations": "npm run typeorm migration:run -- -d ./src/app.datasource.ts",
|
||||||
|
"typeorm:generate-migration": "npm run typeorm -- -d ./src/app.datasource.ts migration:generate ./src/database/migrations/$npm_config_name",
|
||||||
|
"typeorm:create-migration": "npm run typeorm -- migration:create ./src/database/migrations/$npm_config_name",
|
||||||
|
"typeorm:revert-migration": "npm run typeorm -- -d ./src/app.datasource.ts migration:revert",
|
||||||
"prepare": "ts-patch install && typia patch"
|
"prepare": "ts-patch install && typia patch"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@atech/postal": "^1.0.0",
|
"@atech/postal": "^1.0.0",
|
||||||
|
"@nestjs/bullmq": "^10.1.1",
|
||||||
"@nestjs/common": "^10.3.10",
|
"@nestjs/common": "^10.3.10",
|
||||||
"@nestjs/config": "^3.2.3",
|
"@nestjs/config": "^3.2.3",
|
||||||
"@nestjs/core": "^10.3.10",
|
"@nestjs/core": "^10.3.10",
|
||||||
"@nestjs/platform-express": "^10.3.10",
|
"@nestjs/platform-express": "^10.3.10",
|
||||||
|
"@nestjs/typeorm": "^10.0.2",
|
||||||
|
"bullmq": "^5.9.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
|
"dotenv": "^16.4.5",
|
||||||
|
"ioredis": "^5.4.1",
|
||||||
"mysql2": "^3.10.2",
|
"mysql2": "^3.10.2",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
"nestjs-postal-client": "^0.0.5",
|
"nestjs-postal-client": "^0.0.6",
|
||||||
"oidc-provider": "^8.5.1",
|
"oidc-provider": "^8.5.1",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"sequelize": "^6.37.3",
|
"typeorm": "^0.3.20",
|
||||||
"sequelize-typescript": "^2.1.6",
|
|
||||||
"typia": "^6.5.1"
|
"typia": "^6.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
@ -46,7 +55,6 @@
|
||||||
"@types/express": "^4.17.21",
|
"@types/express": "^4.17.21",
|
||||||
"@types/jest": "^29.5.12",
|
"@types/jest": "^29.5.12",
|
||||||
"@types/node": "^20.14.10",
|
"@types/node": "^20.14.10",
|
||||||
"@types/sequelize": "^4.28.20",
|
|
||||||
"@types/supertest": "^6.0.2",
|
"@types/supertest": "^6.0.2",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.16.1",
|
"@typescript-eslint/eslint-plugin": "^7.16.1",
|
||||||
"@typescript-eslint/parser": "^7.16.1",
|
"@typescript-eslint/parser": "^7.16.1",
|
||||||
|
@ -55,8 +63,10 @@
|
||||||
"eslint-plugin-prettier": "^5.1.3",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"jest": "^29.7.0",
|
"jest": "^29.7.0",
|
||||||
"prettier": "^3.3.3",
|
"prettier": "^3.3.3",
|
||||||
|
"sequelize-cli": "^6.6.2",
|
||||||
"source-map-support": "^0.5.21",
|
"source-map-support": "^0.5.21",
|
||||||
"supertest": "^7.0.0",
|
"supertest": "^7.0.0",
|
||||||
|
"tailwindcss": "^3.4.5",
|
||||||
"ts-jest": "^29.2.2",
|
"ts-jest": "^29.2.2",
|
||||||
"ts-loader": "^9.5.1",
|
"ts-loader": "^9.5.1",
|
||||||
"ts-node": "^10.9.2",
|
"ts-node": "^10.9.2",
|
||||||
|
|
1123
pnpm-lock.yaml
1123
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
22
src/app.datasource.ts
Normal file
22
src/app.datasource.ts
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { config } from 'dotenv';
|
||||||
|
import { DataSource } from 'typeorm';
|
||||||
|
|
||||||
|
import { DATABASE_MIGRATION } from './database/database.migration';
|
||||||
|
import { DATABASE_ENTITIES } from './database/database.entities';
|
||||||
|
|
||||||
|
config();
|
||||||
|
|
||||||
|
const configService = new ConfigService();
|
||||||
|
|
||||||
|
export default new DataSource({
|
||||||
|
type: 'mysql',
|
||||||
|
host: configService.getOrThrow<string>('DATABASE_HOST'),
|
||||||
|
port: configService.getOrThrow<number>('DATABASE_PORT'),
|
||||||
|
username: configService.getOrThrow<string>('DATABASE_USER'),
|
||||||
|
password: configService.getOrThrow<string>('DATABASE_PASSWORD', ''),
|
||||||
|
database: configService.getOrThrow<string>('DATABASE_NAME'),
|
||||||
|
charset: 'UTF8MB4',
|
||||||
|
entities: DATABASE_ENTITIES,
|
||||||
|
migrations: DATABASE_MIGRATION,
|
||||||
|
});
|
12
src/auth/model/user.model.ts
Normal file
12
src/auth/model/user.model.ts
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
import { IsEmail } from 'class-validator';
|
||||||
|
import { Column, Model, Table } from 'sequelize-typescript';
|
||||||
|
|
||||||
|
@Table
|
||||||
|
export class User extends Model<User> {
|
||||||
|
@Column
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
@IsEmail()
|
||||||
|
email: string;
|
||||||
|
}
|
68
src/auth/oidc/core.service.ts
Normal file
68
src/auth/oidc/core.service.ts
Normal file
|
@ -0,0 +1,68 @@
|
||||||
|
import { Injectable, Logger, OnModuleInit } from '@nestjs/common';
|
||||||
|
import type Provider from 'oidc-provider';
|
||||||
|
import type { Configuration, errors, KoaContextWithOIDC } from 'oidc-provider';
|
||||||
|
|
||||||
|
// This is an async import for the oidc-provider package as it's now only esm and we need to use it in a commonjs environment.
|
||||||
|
async function getProvider(): Promise<{
|
||||||
|
provider: any;
|
||||||
|
providerErrors: typeof errors;
|
||||||
|
}> {
|
||||||
|
let provider: {
|
||||||
|
default: any;
|
||||||
|
errors: typeof errors;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Unfortunately, there is a Node/Jest bug that causes segmentation faults when doing such an import in Jest:
|
||||||
|
// https://github.com/nodejs/node/issues/35889
|
||||||
|
// To work around that, we do the import differently, in case we are in a Jest test run.
|
||||||
|
// This can be detected via the env variables: https://jestjs.io/docs/environment-variables.
|
||||||
|
// There have been reports of `JEST_WORKER_ID` being undefined, so to be sure we check both.
|
||||||
|
if (process.env['JEST_WORKER_ID'] ?? process.env.NODE_ENV === 'test') {
|
||||||
|
provider = jest.requireActual('oidc-provider');
|
||||||
|
} else {
|
||||||
|
provider = await (eval(`import('oidc-provider')`) as Promise<{
|
||||||
|
default: any;
|
||||||
|
errors: typeof errors;
|
||||||
|
}>);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
provider: provider.default,
|
||||||
|
providerErrors: provider.errors,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
/**
|
||||||
|
* @class OidcService
|
||||||
|
* OidcService is the service that handles all interaction with the OIDC library and package
|
||||||
|
*/
|
||||||
|
export class OidcService implements OnModuleInit {
|
||||||
|
private provider!: Provider;
|
||||||
|
private jwks: any;
|
||||||
|
private cookies: any;
|
||||||
|
private readonly logger = new Logger(OidcService.name);
|
||||||
|
|
||||||
|
constructor() {}
|
||||||
|
|
||||||
|
async onModuleInit() {
|
||||||
|
const { provider, providerErrors } = await getProvider();
|
||||||
|
|
||||||
|
const isOrigin = (value: string): boolean => {
|
||||||
|
if (typeof value !== 'string') {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const { origin } = new URL(value);
|
||||||
|
// Origin: <scheme> "://" <hostname> [ ":" <port> ]
|
||||||
|
return value == origin;
|
||||||
|
} catch (err) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadKeys() {}
|
||||||
|
}
|
0
src/auth/oidc/oidc.config.ts
Normal file
0
src/auth/oidc/oidc.config.ts
Normal file
15
src/auth/oidc/oidc.const.ts
Normal file
15
src/auth/oidc/oidc.const.ts
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
/**
|
||||||
|
* OIDC Constants
|
||||||
|
*/
|
||||||
|
|
||||||
|
// OIDC TTLs
|
||||||
|
export const REFRESH_TOKEN_LIFE = 5 * 24 * 60 * 60; // 5 days
|
||||||
|
export const ID_TOKEN_LIFE = 15 * 60; // 15 minutes
|
||||||
|
export const AUTHORIZATION_TOKEN_LIFE = 30 * 60; // 1 hour
|
||||||
|
export const SESSION_LIFE = 5 * 24 * 60 * 60; // 5 days
|
||||||
|
export const ACCESS_TOKEN_LIFE = 15 * 60; // 15 minutes
|
||||||
|
export const DEVICE_CODE_LIFE = 10 * 60; // 10 minutes
|
||||||
|
export const GRANT_LIFE = 60 * 60 * 24 * 30; // 1 month
|
||||||
|
export const INTERACTION_LIFE = 1 * 60 * 60; // 1 hour
|
||||||
|
export const PUSHED_AUTH_REQ_LIFE = 15 * 60; // 15 minutes
|
||||||
|
export const CLIENT_CREDENTIALS_TOKEN_LIFE = 7 * 24 * 60 * 60; // 7 days
|
|
@ -1,6 +1,6 @@
|
||||||
export default async () => ({
|
export default async () => ({
|
||||||
mail: {
|
base: {
|
||||||
baseUrl: process.env.MAIL_BASE_URL,
|
port: process.env.PORT || 3000,
|
||||||
apiKey: process.env.MAIL_API_KEY,
|
host: process.env.HOST || 'localhost',
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
0
src/database/database-config.service.ts
Normal file
0
src/database/database-config.service.ts
Normal file
8
src/database/database.const.ts
Normal file
8
src/database/database.const.ts
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
export const MAX_STRING_LENGTH = 255;
|
||||||
|
export const FETCH_AFTER_CREATION_FAILED =
|
||||||
|
'Failed to fetch entity after creation';
|
||||||
|
|
||||||
|
export enum UserRole {
|
||||||
|
ADMIN = 'admin',
|
||||||
|
USER = 'user',
|
||||||
|
}
|
4
src/database/database.entities.ts
Normal file
4
src/database/database.entities.ts
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { User } from './models/user.model';
|
||||||
|
|
||||||
|
// Database Entities Array
|
||||||
|
export const DATABASE_ENTITIES = [User];
|
2
src/database/database.migration.ts
Normal file
2
src/database/database.migration.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// Database Migration Array
|
||||||
|
export const DATABASE_MIGRATION = [];
|
29
src/database/models/user.model.ts
Normal file
29
src/database/models/user.model.ts
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
import { MAX_STRING_LENGTH, UserRole } from '../database.const';
|
||||||
|
|
||||||
|
@Entity('user')
|
||||||
|
export class User {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH })
|
||||||
|
username: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH })
|
||||||
|
email: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH })
|
||||||
|
password: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
pronouns: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
avatar: string;
|
||||||
|
|
||||||
|
@Column({ length: 15, enum: UserRole, default: UserRole.USER })
|
||||||
|
role: string;
|
||||||
|
}
|
9
tailwind.config.js
Normal file
9
tailwind.config.js
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
module.exports = {
|
||||||
|
content: [],
|
||||||
|
theme: {
|
||||||
|
extend: {},
|
||||||
|
},
|
||||||
|
plugins: [],
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue