Package breakdown - initial commit 1.0.0
This commit is contained in:
132
.gitignore
vendored
Normal file
132
.gitignore
vendored
Normal file
@@ -0,0 +1,132 @@
|
|||||||
|
# ---> Node
|
||||||
|
# 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
|
||||||
|
|
||||||
|
# yarn v2
|
||||||
|
.yarn/cache
|
||||||
|
.yarn/unplugged
|
||||||
|
.yarn/build-state.yml
|
||||||
|
.yarn/install-state.gz
|
||||||
|
.pnp.*
|
||||||
|
|
||||||
28
Dockerfile
Normal file
28
Dockerfile
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
ARG ENV=production
|
||||||
|
ARG MONGO_VERSION=latest
|
||||||
|
ARG PORT=9001
|
||||||
|
|
||||||
|
## mongo build stage
|
||||||
|
FROM mongo:$MONGO_VERSION AS database
|
||||||
|
COPY docker-entrypoint-initdb.d/mongo-init-$MONGO_VERSION.sh ./docker-entrypoint-initdb.d/mongo-init.sh
|
||||||
|
|
||||||
|
## stage one, build the service
|
||||||
|
FROM node:20-alpine AS build
|
||||||
|
ENV NODE_ENV development
|
||||||
|
WORKDIR /home/node/app
|
||||||
|
COPY package*.json ./
|
||||||
|
COPY tsconfig.json ./
|
||||||
|
COPY lib ./lib
|
||||||
|
RUN ls -a
|
||||||
|
RUN yarn install
|
||||||
|
RUN yarn build
|
||||||
|
|
||||||
|
## this is stage two , where the app actually runs
|
||||||
|
FROM node:20-alpine AS containerize
|
||||||
|
ENV NODE_ENV $ENV
|
||||||
|
WORKDIR /home/node/app
|
||||||
|
COPY package*.json ./
|
||||||
|
RUN yarn install --frozen-lockfile --production
|
||||||
|
COPY --from=build /home/node/app/dist .
|
||||||
|
EXPOSE $PORT
|
||||||
|
CMD ["node","server/index.js"]
|
||||||
6
babel.config.js
Normal file
6
babel.config.js
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
module.exports = {
|
||||||
|
presets: [
|
||||||
|
['@babel/preset-env', { targets: { node: 'current' } }],
|
||||||
|
'@babel/preset-typescript',
|
||||||
|
],
|
||||||
|
};
|
||||||
49
docker-compose.dev.yml
Normal file
49
docker-compose.dev.yml
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
auth-service_mongo:
|
||||||
|
env_file: .env.dev
|
||||||
|
container_name: ${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: database
|
||||||
|
args:
|
||||||
|
MONGO_VERSION: 6.0.5
|
||||||
|
ports:
|
||||||
|
- 27017:27017
|
||||||
|
networks:
|
||||||
|
- backend
|
||||||
|
volumes:
|
||||||
|
- auth-db:/data/db
|
||||||
|
- auth-db:/data/configdb
|
||||||
|
restart: unless-stopped
|
||||||
|
image: mongo:latest
|
||||||
|
auth-service:
|
||||||
|
env_file: .env.dev
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: containerize
|
||||||
|
args:
|
||||||
|
- PORT
|
||||||
|
- ENV
|
||||||
|
container_name: ${CONTAINER_PREFIX}-auth-service
|
||||||
|
ports:
|
||||||
|
- 9001:9001
|
||||||
|
environment:
|
||||||
|
- DB_HOST=${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
|
networks:
|
||||||
|
- labs-net
|
||||||
|
- backend
|
||||||
|
restart: unless-stopped
|
||||||
|
image: node:20-alpine
|
||||||
|
depends_on:
|
||||||
|
- auth-service_mongo
|
||||||
|
networks:
|
||||||
|
backend:
|
||||||
|
name: backend
|
||||||
|
labs-net:
|
||||||
|
name: labs-net
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
auth-db:
|
||||||
|
external: false
|
||||||
58
docker-compose.staging-build.yml
Normal file
58
docker-compose.staging-build.yml
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
auth-service_mongo:
|
||||||
|
container_name: ${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
|
env_file:
|
||||||
|
- staging.env
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: database
|
||||||
|
args:
|
||||||
|
MONGO_VERSION: 4.4
|
||||||
|
networks:
|
||||||
|
- auth-backend
|
||||||
|
volumes:
|
||||||
|
- 'auth-db:/data/db'
|
||||||
|
- 'auth-db:/data/configdb'
|
||||||
|
restart: unless-stopped
|
||||||
|
image: mongo:4.4
|
||||||
|
auth-service:
|
||||||
|
container_name: ${CONTAINER_PREFIX}-auth-service
|
||||||
|
env_file:
|
||||||
|
- staging.env
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
target: containerize
|
||||||
|
args:
|
||||||
|
- PORT
|
||||||
|
- ENV
|
||||||
|
environment:
|
||||||
|
- DB_HOST=${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
|
labels:
|
||||||
|
- 'traefik.enable=true'
|
||||||
|
- 'traefik.docker.network=docknet'
|
||||||
|
- 'traefik.http.routers.labs-auth.rule=Host(`${HOST}`) && PathPrefix(`${ROUTE_PREFIX}`)'
|
||||||
|
- 'traefik.http.routers.labs-auth.entrypoints=websecure'
|
||||||
|
- 'traefik.http.routers.labs-auth.tls=true'
|
||||||
|
- 'traefik.http.routers.labs-auth.tls.certresolver=letsencrypt'
|
||||||
|
- 'traefik.http.routers.labs-auth.service=labs-auth-service'
|
||||||
|
- 'traefik.http.services.labs-auth-service.loadbalancer.server.port=${PORT}'
|
||||||
|
networks:
|
||||||
|
- auth-backend
|
||||||
|
- docknet
|
||||||
|
restart: unless-stopped
|
||||||
|
image: node:20-alpine
|
||||||
|
depends_on:
|
||||||
|
- auth-service_mongo
|
||||||
|
networks:
|
||||||
|
auth-backend:
|
||||||
|
driver: bridge
|
||||||
|
external: false
|
||||||
|
docknet:
|
||||||
|
name: docknet
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
auth-db:
|
||||||
|
external: false
|
||||||
43
docker-compose.staging-image.yml
Normal file
43
docker-compose.staging-image.yml
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
version: '3.8'
|
||||||
|
|
||||||
|
services:
|
||||||
|
auth-service_mongo:
|
||||||
|
container_name: ${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
|
env_file:
|
||||||
|
- staging.env
|
||||||
|
networks:
|
||||||
|
- docknet
|
||||||
|
volumes:
|
||||||
|
- auth-db:/data
|
||||||
|
- ./mongo-init.js:/docker-entrypoint-initdb.d/mongo-init.js:ro
|
||||||
|
restart: unless-stopped
|
||||||
|
image: mongo:4.4
|
||||||
|
auth-service:
|
||||||
|
env_file:
|
||||||
|
- staging.env
|
||||||
|
container_name: ${CONTAINER_PREFIX}-auth-service
|
||||||
|
environment:
|
||||||
|
- DB_HOST=${CONTAINER_PREFIX}-auth-service_mongo
|
||||||
|
labels:
|
||||||
|
- 'traefik.enable=true'
|
||||||
|
- 'traefik.http.routers.grow.rule=Host(`${HOST}`) && Path(`${ROUTE_PREFIX}`)'
|
||||||
|
- 'traefik.http.routers.grow.entrypoints=websecure'
|
||||||
|
- 'traefik.http.routers.grow.tls=true'
|
||||||
|
- 'traefik.http.routers.grow.tls.certresolver=letsencrypt'
|
||||||
|
- 'traefik.http.routers.grow.service=grow-service'
|
||||||
|
- 'traefik.http.services.grow-service.loadbalancer.server.port=${PORT}'
|
||||||
|
networks:
|
||||||
|
- docknet
|
||||||
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- auth-service_mongo
|
||||||
|
image: git.mifi.dev/mifi/mifi/auth:latest
|
||||||
|
|
||||||
|
networks:
|
||||||
|
docknet:
|
||||||
|
name: docknet
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
auth-db:
|
||||||
|
external: false
|
||||||
14
docker-entrypoint-initdb.d/mongo-init-4.4.sh
Normal file
14
docker-entrypoint-initdb.d/mongo-init-4.4.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
set -e
|
||||||
|
|
||||||
|
mongo <<EOF
|
||||||
|
use $MONGO_INITDB_DATABASE
|
||||||
|
|
||||||
|
db.createUser({
|
||||||
|
user: '$DB_USERNAME',
|
||||||
|
pwd: '$DB_PASSWORD',
|
||||||
|
roles: [{
|
||||||
|
role: 'readWrite',
|
||||||
|
db: '$MONGO_INITDB_DATABASE'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
EOF
|
||||||
14
docker-entrypoint-initdb.d/mongo-init-6.0.5.sh
Normal file
14
docker-entrypoint-initdb.d/mongo-init-6.0.5.sh
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
set -e
|
||||||
|
|
||||||
|
mongosh <<EOF
|
||||||
|
use $MONGO_INITDB_DATABASE
|
||||||
|
|
||||||
|
db.createUser({
|
||||||
|
user: '$DB_USERNAME',
|
||||||
|
pwd: '$DB_PASSWORD',
|
||||||
|
roles: [{
|
||||||
|
role: 'readWrite',
|
||||||
|
db: '$MONGO_INITDB_DATABASE'
|
||||||
|
}]
|
||||||
|
})
|
||||||
|
EOF
|
||||||
195
jest.config.ts
Normal file
195
jest.config.ts
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||||
|
* https://jestjs.io/docs/configuration
|
||||||
|
*/
|
||||||
|
|
||||||
|
export default {
|
||||||
|
// All imported modules in your tests should be mocked automatically
|
||||||
|
// automock: false,
|
||||||
|
|
||||||
|
// Stop running tests after `n` failures
|
||||||
|
// bail: 0,
|
||||||
|
|
||||||
|
// The directory where Jest should store its cached dependency information
|
||||||
|
// cacheDirectory: "/private/var/folders/75/3cyx0pq133n7gk3ysqf0mj4r0000gn/T/jest_dx",
|
||||||
|
|
||||||
|
// Automatically clear mock calls, instances, contexts and results before every test
|
||||||
|
clearMocks: true,
|
||||||
|
|
||||||
|
// Indicates whether the coverage information should be collected while executing the test
|
||||||
|
collectCoverage: true,
|
||||||
|
|
||||||
|
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||||
|
// collectCoverageFrom: undefined,
|
||||||
|
|
||||||
|
// The directory where Jest should output its coverage files
|
||||||
|
coverageDirectory: "coverage",
|
||||||
|
|
||||||
|
// An array of regexp pattern strings used to skip coverage collection
|
||||||
|
// coveragePathIgnorePatterns: [
|
||||||
|
// "/node_modules/"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// Indicates which provider should be used to instrument code for coverage
|
||||||
|
// coverageProvider: "babel",
|
||||||
|
|
||||||
|
// A list of reporter names that Jest uses when writing coverage reports
|
||||||
|
// coverageReporters: [
|
||||||
|
// "json",
|
||||||
|
// "text",
|
||||||
|
// "lcov",
|
||||||
|
// "clover"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An object that configures minimum threshold enforcement for coverage results
|
||||||
|
// coverageThreshold: undefined,
|
||||||
|
|
||||||
|
// A path to a custom dependency extractor
|
||||||
|
// dependencyExtractor: undefined,
|
||||||
|
|
||||||
|
// Make calling deprecated APIs throw helpful error messages
|
||||||
|
// errorOnDeprecated: false,
|
||||||
|
|
||||||
|
// The default configuration for fake timers
|
||||||
|
// fakeTimers: {
|
||||||
|
// "enableGlobally": false
|
||||||
|
// },
|
||||||
|
|
||||||
|
// Force coverage collection from ignored files using an array of glob patterns
|
||||||
|
// forceCoverageMatch: [],
|
||||||
|
|
||||||
|
// A path to a module which exports an async function that is triggered once before all test suites
|
||||||
|
// globalSetup: undefined,
|
||||||
|
|
||||||
|
// A path to a module which exports an async function that is triggered once after all test suites
|
||||||
|
// globalTeardown: undefined,
|
||||||
|
|
||||||
|
// A set of global variables that need to be available in all test environments
|
||||||
|
// globals: {},
|
||||||
|
|
||||||
|
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||||
|
// maxWorkers: "50%",
|
||||||
|
|
||||||
|
// An array of directory names to be searched recursively up from the requiring module's location
|
||||||
|
// moduleDirectories: [
|
||||||
|
// "node_modules"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An array of file extensions your modules use
|
||||||
|
// moduleFileExtensions: [
|
||||||
|
// "js",
|
||||||
|
// "mjs",
|
||||||
|
// "cjs",
|
||||||
|
// "jsx",
|
||||||
|
// "ts",
|
||||||
|
// "tsx",
|
||||||
|
// "json",
|
||||||
|
// "node"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||||
|
// moduleNameMapper: {},
|
||||||
|
|
||||||
|
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||||
|
// modulePathIgnorePatterns: [],
|
||||||
|
|
||||||
|
// Activates notifications for test results
|
||||||
|
// notify: false,
|
||||||
|
|
||||||
|
// An enum that specifies notification mode. Requires { notify: true }
|
||||||
|
// notifyMode: "failure-change",
|
||||||
|
|
||||||
|
// A preset that is used as a base for Jest's configuration
|
||||||
|
// preset: undefined,
|
||||||
|
|
||||||
|
// Run tests from one or more projects
|
||||||
|
// projects: undefined,
|
||||||
|
|
||||||
|
// Use this configuration option to add custom reporters to Jest
|
||||||
|
// reporters: undefined,
|
||||||
|
|
||||||
|
// Automatically reset mock state before every test
|
||||||
|
// resetMocks: false,
|
||||||
|
|
||||||
|
// Reset the module registry before running each individual test
|
||||||
|
// resetModules: false,
|
||||||
|
|
||||||
|
// A path to a custom resolver
|
||||||
|
// resolver: undefined,
|
||||||
|
|
||||||
|
// Automatically restore mock state and implementation before every test
|
||||||
|
// restoreMocks: false,
|
||||||
|
|
||||||
|
// The root directory that Jest should scan for tests and modules within
|
||||||
|
// rootDir: undefined,
|
||||||
|
|
||||||
|
// A list of paths to directories that Jest should use to search for files in
|
||||||
|
// roots: [
|
||||||
|
// "<rootDir>"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// Allows you to use a custom runner instead of Jest's default test runner
|
||||||
|
// runner: "jest-runner",
|
||||||
|
|
||||||
|
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||||
|
// setupFiles: [],
|
||||||
|
|
||||||
|
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||||
|
// setupFilesAfterEnv: [],
|
||||||
|
|
||||||
|
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||||
|
// slowTestThreshold: 5,
|
||||||
|
|
||||||
|
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||||
|
// snapshotSerializers: [],
|
||||||
|
|
||||||
|
// The test environment that will be used for testing
|
||||||
|
// testEnvironment: "jest-environment-node",
|
||||||
|
|
||||||
|
// Options that will be passed to the testEnvironment
|
||||||
|
// testEnvironmentOptions: {},
|
||||||
|
|
||||||
|
// Adds a location field to test results
|
||||||
|
// testLocationInResults: false,
|
||||||
|
|
||||||
|
// The glob patterns Jest uses to detect test files
|
||||||
|
// testMatch: [
|
||||||
|
// "**/__tests__/**/*.[jt]s?(x)",
|
||||||
|
// "**/?(*.)+(spec|test).[tj]s?(x)"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||||
|
// testPathIgnorePatterns: [
|
||||||
|
// "/node_modules/"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||||
|
// testRegex: [],
|
||||||
|
|
||||||
|
// This option allows the use of a custom results processor
|
||||||
|
// testResultsProcessor: undefined,
|
||||||
|
|
||||||
|
// This option allows use of a custom test runner
|
||||||
|
// testRunner: "jest-circus/runner",
|
||||||
|
|
||||||
|
// A map from regular expressions to paths to transformers
|
||||||
|
// transform: undefined,
|
||||||
|
|
||||||
|
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||||
|
// transformIgnorePatterns: [
|
||||||
|
// "/node_modules/",
|
||||||
|
// "\\.pnp\\.[^\\/]+$"
|
||||||
|
// ],
|
||||||
|
|
||||||
|
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||||
|
// unmockedModulePathPatterns: undefined,
|
||||||
|
|
||||||
|
// Indicates whether each individual test should be reported during the run
|
||||||
|
// verbose: undefined,
|
||||||
|
|
||||||
|
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||||
|
// watchPathIgnorePatterns: [],
|
||||||
|
|
||||||
|
// Whether to use watchman for file crawling
|
||||||
|
// watchman: true,
|
||||||
|
};
|
||||||
32
lib/app.ts
Normal file
32
lib/app.ts
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
import Koa from 'koa';
|
||||||
|
import bodyparser from 'koa-bodyparser';
|
||||||
|
import cookie from 'koa-cookie';
|
||||||
|
import session from 'koa-session';
|
||||||
|
|
||||||
|
import passport from './passport';
|
||||||
|
import { performanceLogger, performanceTimer } from './middleware/performance';
|
||||||
|
import { errorHandler } from './middleware/errorHandler';
|
||||||
|
import { authRouter } from './controllers/auth';
|
||||||
|
import { SESSION_KEY } from '../constants/env';
|
||||||
|
|
||||||
|
const app: Koa = new Koa();
|
||||||
|
|
||||||
|
app.use(errorHandler);
|
||||||
|
app.use(performanceTimer);
|
||||||
|
app.use(performanceLogger);
|
||||||
|
app.use(bodyparser());
|
||||||
|
app.use(cookie());
|
||||||
|
|
||||||
|
app.keys = [SESSION_KEY];
|
||||||
|
app.use(session({}, app));
|
||||||
|
|
||||||
|
app.use(passport.initialize());
|
||||||
|
app.use(passport.session());
|
||||||
|
|
||||||
|
app.use(authRouter.routes());
|
||||||
|
app.use(authRouter.allowedMethods());
|
||||||
|
|
||||||
|
// Application error logging.
|
||||||
|
app.on('error', console.error);
|
||||||
|
|
||||||
|
export default app;
|
||||||
9
lib/constants/action.ts
Normal file
9
lib/constants/action.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
export enum Action {
|
||||||
|
AUTHENTICATE = 'AUTHENTICATE',
|
||||||
|
AUTHENTICATE_FAILURE = 'AUTHENTICATE_FAILURE',
|
||||||
|
CREATE = 'CREATE',
|
||||||
|
DELETE = 'DELETE',
|
||||||
|
RESET = 'RESET',
|
||||||
|
RESET_REQUEST = 'RESET_REQUEST',
|
||||||
|
UPDATE = 'UPDATE',
|
||||||
|
}
|
||||||
8
lib/constants/auth.ts
Normal file
8
lib/constants/auth.ts
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
export enum Status {
|
||||||
|
ACTIVE,
|
||||||
|
BLOCK_HARD,
|
||||||
|
BLOCK_SOFT,
|
||||||
|
DELETED,
|
||||||
|
INACTIVE,
|
||||||
|
UNVERIFIED,
|
||||||
|
}
|
||||||
10
lib/constants/db.ts
Normal file
10
lib/constants/db.ts
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
export const DB_HOST = process.env.DB_HOST;
|
||||||
|
export const DB_PORT = process.env.DB_PORT || 27017;
|
||||||
|
export const DB_USERNAME = process.env.DB_USERNAME;
|
||||||
|
export const DB_PASSWORD = process.env.DB_PASSWORD;
|
||||||
|
export const DB_NAME = process.env.DB_NAME;
|
||||||
|
|
||||||
|
export const COLL_AUTH = 'Auth';
|
||||||
|
export const COLL_LOG = 'Log';
|
||||||
|
export const COLL_STRATEGY = 'Strategy';
|
||||||
|
export const COLL_TOKEN = 'Token';
|
||||||
20
lib/constants/env.ts
Normal file
20
lib/constants/env.ts
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
export const PACKAGE_NAME = '@mifi/auth';
|
||||||
|
export const PORT = process.env.PORT || 9000;
|
||||||
|
|
||||||
|
export const SESSION_KEY = process.env.SESSION_KEY || 'secret-key';
|
||||||
|
|
||||||
|
export const JWT_AUDIENCE = process.env.JWT_AUDIENCE || 'mifi.dev';
|
||||||
|
export const JWT_ISSUER = process.env.JWT_ISSUER || PACKAGE_NAME;
|
||||||
|
export const JWT_SECRET = process.env.JWT_SECRET || 'secret';
|
||||||
|
|
||||||
|
export const LOGIN_VALID_TIMEOUT = process.env.LOGIN_VALID_TIMEOUT || '12h'; // ###d|h|m
|
||||||
|
export const RESET_VALID_TIMEOUT = process.env.RESET_VALID_TIMEOUT || '15m'; // ###d|h|m
|
||||||
|
export const VERIFY_VALID_TIMEOUT = process.env.VERIFY_VALID_TIMEOUT || '60d'; // ###d|h|m
|
||||||
|
export const DEFAULT_TOKEN_DAYS = process.env.DEFAULT_TOKEN_DAYS || 365;
|
||||||
|
|
||||||
|
export const ROUTE_PREFIX = process.env.ROUTE_PREFIX || '/auth';
|
||||||
|
export const LOGIN_ROUTE = process.env.LOGIN_ROUTE || '/login';
|
||||||
|
export const RESET_ROUTE = process.env.RESET_ROUTE || '/reset';
|
||||||
|
export const VERIFICATION_ROUTE = process.env.VERIFICATION_ROUTE || '/verification';
|
||||||
|
|
||||||
|
export const REQUIRE_VERIFICATION = process.env.REQUIRE_VERIFICATION || true;
|
||||||
12
lib/constants/errors.ts
Normal file
12
lib/constants/errors.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
export enum ErrorCodes {
|
||||||
|
RESET_REQUEST_DATA = 'RESET_REQUEST_DATA',
|
||||||
|
}
|
||||||
|
|
||||||
|
export const ErrorMessages = {
|
||||||
|
[ErrorCodes.RESET_REQUEST_DATA]: 'A valid username and password must be provided',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getErrorBody = (code: ErrorCodes) => ({
|
||||||
|
code,
|
||||||
|
message: ErrorMessages[code],
|
||||||
|
});
|
||||||
7
lib/constants/strategies.ts
Normal file
7
lib/constants/strategies.ts
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
export enum STRATEGIES {
|
||||||
|
LOCAL,
|
||||||
|
APPLE,
|
||||||
|
FACEBOOK,
|
||||||
|
FIDO2,
|
||||||
|
GOOGLE,
|
||||||
|
}
|
||||||
4
lib/constants/tokens.ts
Normal file
4
lib/constants/tokens.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export enum TokenType {
|
||||||
|
RESET = 'RESET',
|
||||||
|
VERIFICATION = 'VERIFICATION',
|
||||||
|
}
|
||||||
80
lib/controllers/auth.ts
Normal file
80
lib/controllers/auth.ts
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
import { StatusCodes } from 'http-status-codes';
|
||||||
|
import Koa from 'koa';
|
||||||
|
import Router from 'koa-router';
|
||||||
|
import { StringSchemaDefinition } from 'mongoose';
|
||||||
|
|
||||||
|
import { Auth } from '@mifi/services-common/lib/db';
|
||||||
|
import { create } from '@mifi/services-common/lib/db/dao/create';
|
||||||
|
import { resetPasswordPost } from '@mifi/services-common/lib/db/api/resetPasswordPost';
|
||||||
|
import { resetPasswordGet } from '@mifi/services-common/lib/db/api/resetPasswordGet';
|
||||||
|
import { deleteById } from '@mifi/services-common/lib/db/dao/deleteById';
|
||||||
|
import { deleteStrategy } from '@mifi/services-common/lib/db/api/deleteStrategy';
|
||||||
|
import { AuthDocument } from '@mifi/services-common/lib/db/schema/auth';
|
||||||
|
|
||||||
|
import { ROUTE_PREFIX as prefix, RESET_ROUTE } from '../constants/env';
|
||||||
|
import passport from '../passport';
|
||||||
|
import { ErrorCodes, getErrorBody } from '../constants/errors';
|
||||||
|
import { authenticated } from '../middleware/authenication';
|
||||||
|
|
||||||
|
const routerOpts: Router.IRouterOptions = { prefix };
|
||||||
|
const router: Router = new Router(routerOpts);
|
||||||
|
|
||||||
|
router.get('/info', (ctx) => {
|
||||||
|
ctx.body = {
|
||||||
|
service: process.env.SERVICE_NAME,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/', async (ctx) => {
|
||||||
|
console.log('POST: /auth [ctx]', ctx);
|
||||||
|
const data = await create(<AuthDocument & { password: string }>ctx.request.body).catch((err) =>
|
||||||
|
console.error('POST: /auth [err]', err),
|
||||||
|
);
|
||||||
|
console.log('POST: /auth [data]', data);
|
||||||
|
ctx.body = { success: !!data, data };
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/strategy/:id', async (ctx) => {
|
||||||
|
ctx.body = { success: await deleteStrategy(ctx.params.id as StringSchemaDefinition) };
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/:id', async (ctx) => {
|
||||||
|
ctx.body = { success: await deleteById(ctx.params.id as StringSchemaDefinition) };
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/login', async (ctx, next) => {
|
||||||
|
return passport.authenticate('local', (err, user) => {
|
||||||
|
ctx.body = user;
|
||||||
|
return user ? ctx.login(user) : ctx.throw(StatusCodes.UNAUTHORIZED);
|
||||||
|
})(ctx, next);
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post(process.env.RESET_ROUTE || RESET_ROUTE, async (ctx) => {
|
||||||
|
const { password, token, username } = ctx.request.body as { token?: string; password?: string; username?: string };
|
||||||
|
let response: false | { record: StringSchemaDefinition; token: string } = false;
|
||||||
|
|
||||||
|
if (username) {
|
||||||
|
response = await resetPasswordGet(username);
|
||||||
|
} else if (token && password) {
|
||||||
|
response = await resetPasswordPost(token, password);
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx.body = { success: !!response, ...(response || getErrorBody(ErrorCodes.RESET_REQUEST_DATA)) };
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
ctx.throw(StatusCodes.BAD_REQUEST);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.patch('/:record', authenticated(), (ctx: Koa.Context) => {
|
||||||
|
if (ctx.user !== ctx.param.record) {
|
||||||
|
ctx.throw(StatusCodes.UNAUTHORIZED);
|
||||||
|
}
|
||||||
|
const data = Auth.findOneAndUpdate({ record: ctx.params.record });
|
||||||
|
if (!data) {
|
||||||
|
ctx.throw(StatusCodes.NOT_FOUND);
|
||||||
|
}
|
||||||
|
ctx.body = { success: true, data };
|
||||||
|
});
|
||||||
|
|
||||||
|
export { router as authRouter };
|
||||||
11
lib/index.ts
Normal file
11
lib/index.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import app from './app';
|
||||||
|
import { connection } from '../db';
|
||||||
|
import { PORT } from '../constants/env';
|
||||||
|
|
||||||
|
connection.then(
|
||||||
|
() => {
|
||||||
|
app.listen(PORT);
|
||||||
|
console.debug('Server up and listening', { env: process.env });
|
||||||
|
},
|
||||||
|
(err) => console.error('Could not reach database', { err, env: process.env }),
|
||||||
|
);
|
||||||
13
lib/middleware/authenication.ts
Normal file
13
lib/middleware/authenication.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { Middleware } from 'koa';
|
||||||
|
|
||||||
|
import { LOGIN_ROUTE } from '../constants/env';
|
||||||
|
|
||||||
|
export const authenticated = (): Middleware => {
|
||||||
|
return (ctx, next) => {
|
||||||
|
if (ctx.isAuthenticated()) {
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
ctx.redirect(process.env.LOGIN_ROUTE || LOGIN_ROUTE);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
};
|
||||||
13
lib/middleware/errorHandler.ts
Normal file
13
lib/middleware/errorHandler.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
import { StatusCodes } from 'http-status-codes';
|
||||||
|
import { Context, Next } from 'koa';
|
||||||
|
|
||||||
|
export const errorHandler = async (ctx: Context, next: Next) => {
|
||||||
|
try {
|
||||||
|
await next();
|
||||||
|
} catch (error: any) {
|
||||||
|
ctx.status = error.statusCode || error.status || StatusCodes.INTERNAL_SERVER_ERROR;
|
||||||
|
error.status = ctx.status;
|
||||||
|
ctx.body = { error };
|
||||||
|
ctx.app.emit('error', error, ctx);
|
||||||
|
}
|
||||||
|
};
|
||||||
14
lib/middleware/performance.ts
Normal file
14
lib/middleware/performance.ts
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
import { Context, Next } from 'koa';
|
||||||
|
|
||||||
|
export const performanceLogger = async (ctx: Context, next: Next) => {
|
||||||
|
await next();
|
||||||
|
const rt = ctx.response.get('X-Response-Time');
|
||||||
|
console.log(`${ctx.method} ${ctx.url} - ${rt}`);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const performanceTimer = async (ctx: Context, next: Next) => {
|
||||||
|
const start = Date.now();
|
||||||
|
await next();
|
||||||
|
const ms = Date.now() - start;
|
||||||
|
ctx.set('X-Response-Time', `${ms}ms`);
|
||||||
|
};
|
||||||
23
lib/passport/index.ts
Normal file
23
lib/passport/index.ts
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import passport from 'koa-passport';
|
||||||
|
import { Types } from 'mongoose';
|
||||||
|
|
||||||
|
import { AuthDocument } from '@mifi/services-common/lib/db/schema/auth';
|
||||||
|
import { readOneByRecord } from '@mifi/services-common/lib/db/dao/readOneByRecord';
|
||||||
|
import { readOneById } from '@mifi/services-common/lib/db/dao/readOneById';
|
||||||
|
|
||||||
|
import LocalStrategy from './strategies/local';
|
||||||
|
import JwtStrategy from './strategies/jwt';
|
||||||
|
|
||||||
|
passport.use(LocalStrategy);
|
||||||
|
passport.use(JwtStrategy);
|
||||||
|
|
||||||
|
passport.serializeUser((user, done) => {
|
||||||
|
done(null, (user as AuthDocument).record || (user as AuthDocument).id);
|
||||||
|
});
|
||||||
|
|
||||||
|
passport.deserializeUser(async (id, done) => {
|
||||||
|
const user = await readOneByRecord(<Types.ObjectId>id).catch(async () => await readOneById(<Types.ObjectId>id));
|
||||||
|
done(user ? null : 'user not found', user);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default passport;
|
||||||
17
lib/passport/strategies/jwt.ts
Normal file
17
lib/passport/strategies/jwt.ts
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import { ExtractJwt, Strategy as JwtStrategy } from 'passport-jwt';
|
||||||
|
|
||||||
|
import { readOneByRecord } from '@mifi/services-common/lib/db/dao/readOneByRecord';
|
||||||
|
|
||||||
|
import { JWT_SECRET } from '../../constants/env';
|
||||||
|
|
||||||
|
const opts = {
|
||||||
|
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||||
|
secretOrKey: JWT_SECRET,
|
||||||
|
issuer: process.env.JWT_ISSUER,
|
||||||
|
audience: process.env.JWT_AUDIENCE,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default new JwtStrategy(opts, async ({ sub }, done) => {
|
||||||
|
const auth = await readOneByRecord(sub);
|
||||||
|
return done(null, auth || false);
|
||||||
|
});
|
||||||
9
lib/passport/strategies/local.ts
Normal file
9
lib/passport/strategies/local.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
// eslint-disable-next-line import/named
|
||||||
|
import { Strategy as LocalStrategy } from 'passport-local';
|
||||||
|
|
||||||
|
import { authenticate } from '@mifi/services-common/lib/db/api/authenticate';
|
||||||
|
|
||||||
|
export default new LocalStrategy(async (username: string, password: string, done: any) => {
|
||||||
|
const user = await authenticate(username, password);
|
||||||
|
done(null, user);
|
||||||
|
});
|
||||||
35
lib/utils/jwt.ts
Normal file
35
lib/utils/jwt.ts
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
import { JWT_AUDIENCE, JWT_ISSUER, JWT_SECRET } from '../constants/env';
|
||||||
|
export interface TokenProps {
|
||||||
|
aud?: string;
|
||||||
|
exp?: number | Date;
|
||||||
|
iss?: string;
|
||||||
|
sub: string | null;
|
||||||
|
[key: string]: any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type SignProps = string | TokenProps | void;
|
||||||
|
|
||||||
|
export const sign = (props: SignProps) => {
|
||||||
|
const today = new Date();
|
||||||
|
const { sub = null, ...rest }: TokenProps =
|
||||||
|
typeof props === 'string' || typeof props === 'undefined' ? { sub: props || null } : props;
|
||||||
|
let { exp } = rest;
|
||||||
|
if (!exp) {
|
||||||
|
exp = new Date(today);
|
||||||
|
exp.setDate(today.getDate() + parseInt(process.env.JWT_DAYS_VALID as string));
|
||||||
|
exp = exp.getTime() / 1000;
|
||||||
|
}
|
||||||
|
return jwt.sign(
|
||||||
|
{
|
||||||
|
exp,
|
||||||
|
sub,
|
||||||
|
aud: rest.aud || JWT_AUDIENCE,
|
||||||
|
iat: today.getTime(),
|
||||||
|
iss: rest.iss || JWT_ISSUER,
|
||||||
|
},
|
||||||
|
JWT_SECRET,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const verify = (token: string) => jwt.verify(token, JWT_SECRET);
|
||||||
5
lib/utils/links.ts
Normal file
5
lib/utils/links.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
import { RESET_ROUTE, ROUTE_PREFIX, VERIFICATION_ROUTE } from '../constants/env';
|
||||||
|
|
||||||
|
export const getPasswordResetPath = (token: string) => `${ROUTE_PREFIX}${RESET_ROUTE}?t=${token}`;
|
||||||
|
|
||||||
|
export const getVerificationPath = (token: string) => `${ROUTE_PREFIX}${VERIFICATION_ROUTE}?t=${token}`;
|
||||||
13
lib/utils/parseTimeoutToMs.ts
Normal file
13
lib/utils/parseTimeoutToMs.ts
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
export const parseTimeoutToMs = (timeout: string) => {
|
||||||
|
const match = timeout.match(/(?<number>\d+)(?<unit>d|h|m)/gi)?.groups || {};
|
||||||
|
const { number, unit } = match;
|
||||||
|
switch (unit) {
|
||||||
|
case 'd':
|
||||||
|
return 1000 * 60 * 60 * 24 * parseInt(number);
|
||||||
|
case 'h':
|
||||||
|
return 1000 * 60 * 60 * parseInt(number);
|
||||||
|
case 'm':
|
||||||
|
default:
|
||||||
|
return 1000 * 60 * parseInt(number) || 1;
|
||||||
|
}
|
||||||
|
};
|
||||||
12
lib/utils/password.ts
Normal file
12
lib/utils/password.ts
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
import { pbkdf2Sync, randomBytes } from 'crypto';
|
||||||
|
|
||||||
|
export const encrypt = (password: string) => {
|
||||||
|
const salt = randomBytes(16).toString('hex');
|
||||||
|
const hash = pbkdf2Sync(password, salt, 10000, 512, 'sha512').toString('hex');
|
||||||
|
return `${salt}:${hash}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const verify = (test: string, secret: string) => {
|
||||||
|
const [salt, hash] = secret.split(':');
|
||||||
|
return pbkdf2Sync(test, salt, 10000, 512, 'sha512').toString('hex') === hash;
|
||||||
|
};
|
||||||
11
lib/utils/tokens.ts
Normal file
11
lib/utils/tokens.ts
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
import { sign } from './jwt';
|
||||||
|
import { LOGIN_VALID_TIMEOUT } from '../constants/env';
|
||||||
|
import { Status } from '../constants/auth';
|
||||||
|
import { parseTimeoutToMs } from './parseTimeoutToMs';
|
||||||
|
|
||||||
|
export const generateLoginToken = (sub: string, status: Status) =>
|
||||||
|
sign({
|
||||||
|
sub,
|
||||||
|
status,
|
||||||
|
exp: Date.now() + parseTimeoutToMs(LOGIN_VALID_TIMEOUT),
|
||||||
|
});
|
||||||
84
package.json
Normal file
84
package.json
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
{
|
||||||
|
"name": "@mifi/auth-service",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"author": "mifi (Mike Fitzpatrick)",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc",
|
||||||
|
"build:production": "tsc -p .",
|
||||||
|
"format": "prettier:fix && lint:fix",
|
||||||
|
"lint": "eslint --ext .ts,.tsx lib/",
|
||||||
|
"lint:fix": "eslint --fix --ext .ts,.tsx lib/",
|
||||||
|
"prettier": "prettier --check 'lib/**/*.ts'",
|
||||||
|
"prettier:fix": "prettier --write 'lib/**/*.ts'",
|
||||||
|
"serve": "node dist/lib/index.js",
|
||||||
|
"start": "nodemon",
|
||||||
|
"test": "jest --passWithNoTests"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/core": "^7.21.8",
|
||||||
|
"@babel/preset-env": "^7.21.5",
|
||||||
|
"@babel/preset-typescript": "^7.21.5",
|
||||||
|
"@tsconfig/node16": "^1.0.3",
|
||||||
|
"@types/jest": "^29.5.1",
|
||||||
|
"@types/jsonwebtoken": "^9.0.1",
|
||||||
|
"@types/koa": "^2.13.5",
|
||||||
|
"@types/koa-bodyparser": "^4.3.10",
|
||||||
|
"@types/koa-cookie": "^1.0.0",
|
||||||
|
"@types/koa-passport": "^4.0.3",
|
||||||
|
"@types/koa-router": "^7.4.4",
|
||||||
|
"@types/koa-session": "^5.10.6",
|
||||||
|
"@types/luxon": "^3.2.0",
|
||||||
|
"@types/node": "^18.14.0",
|
||||||
|
"@types/passport": "^1.0.12",
|
||||||
|
"@types/passport-facebook": "^2.1.11",
|
||||||
|
"@types/passport-fido2-webauthn": "^0.1.0",
|
||||||
|
"@types/passport-google-oauth": "^1.0.42",
|
||||||
|
"@types/passport-jwt": "^3.0.8",
|
||||||
|
"@types/passport-local": "^1.0.35",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.59.2",
|
||||||
|
"@typescript-eslint/parser": "^5.59.2",
|
||||||
|
"babel-jest": "^29.5.0",
|
||||||
|
"eslint": "^8.39.0",
|
||||||
|
"eslint-config-prettier": "^8.8.0",
|
||||||
|
"eslint-import-resolver-typescript": "^3.5.5",
|
||||||
|
"eslint-plugin-import": "^2.27.5",
|
||||||
|
"eslint-plugin-n": "^15.0.0",
|
||||||
|
"eslint-plugin-prettier": "^4.2.1",
|
||||||
|
"eslint-plugin-promise": "^6.0.0",
|
||||||
|
"jest": "^29.5.0",
|
||||||
|
"nodemon": "^2.0.20",
|
||||||
|
"prettier": "^2.8.4",
|
||||||
|
"prettier-eslint": "^15.0.1",
|
||||||
|
"prettier-eslint-cli": "^7.1.0",
|
||||||
|
"reflect-metadata": "^0.1.13",
|
||||||
|
"ts-node": "^10.9.1",
|
||||||
|
"typescript": "^4.9.5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@mifi/auth-db": "^1.0.0",
|
||||||
|
"@simplewebauthn/server": "^7.2.0",
|
||||||
|
"dotenv": "^16.0.3",
|
||||||
|
"http-status-codes": "^2.2.0",
|
||||||
|
"jsonwebtoken": "^9.0.0",
|
||||||
|
"koa": "^2.14.1",
|
||||||
|
"koa-bodyparser": "^4.3.0",
|
||||||
|
"koa-cookie": "^1.0.0",
|
||||||
|
"koa-passport": "^6.0.0",
|
||||||
|
"koa-router": "^12.0.0",
|
||||||
|
"koa-session": "^6.4.0",
|
||||||
|
"luxon": "^3.3.0",
|
||||||
|
"passport": "^0.6.0",
|
||||||
|
"passport-facebook": "^3.0.0",
|
||||||
|
"passport-fido2-webauthn": "^0.1.0",
|
||||||
|
"passport-google-oauth": "^2.0.0",
|
||||||
|
"passport-http-bearer": "^1.0.1",
|
||||||
|
"passport-jwt": "^4.0.1",
|
||||||
|
"passport-local": "^1.0.0"
|
||||||
|
},
|
||||||
|
"description": "",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://git.mifi.dev/mifi/auth-api.git"
|
||||||
|
}
|
||||||
|
}
|
||||||
12
tsconfig.json
Normal file
12
tsconfig.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"extends": "@tsconfig/node16/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"outDir": "./dist/",
|
||||||
|
"rootDirs": ["lib"],
|
||||||
|
"sourceMap": true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user