mirror of
https://github.com/apricote/Listory.git
synced 2026-01-13 13:11:02 +00:00
feat(api): user authentication
This commit is contained in:
parent
f14eda16ac
commit
f253a66f86
41 changed files with 657 additions and 338 deletions
4
.dockerignore
Normal file
4
.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
dist
|
||||
|
||||
frontend/node_modules
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -2,6 +2,7 @@
|
|||
/dist
|
||||
/node_modules
|
||||
|
||||
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ services:
|
|||
- 3000
|
||||
labels:
|
||||
- "traefik.enable=true" # Enable reverse-proxy for this service
|
||||
- "traefik.http.routers.api.rule=PathPrefix(`/api/v1`)"
|
||||
- "traefik.http.routers.api.rule=PathPrefix(`/api`)"
|
||||
- "traefik.http.routers.api.entrypoints=web"
|
||||
networks:
|
||||
- web
|
||||
|
|
@ -52,7 +52,7 @@ services:
|
|||
proxy:
|
||||
image: traefik
|
||||
command:
|
||||
- --log.level=debug
|
||||
#- --log.level=debug
|
||||
#- --accesslog=true
|
||||
- --api # Enables the web UI
|
||||
- --api.insecure=true
|
||||
|
|
|
|||
426
package-lock.json
generated
426
package-lock.json
generated
|
|
@ -615,6 +615,38 @@
|
|||
"uuid": "3.4.0"
|
||||
}
|
||||
},
|
||||
"@nestjs/jwt": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/jwt/-/jwt-6.1.1.tgz",
|
||||
"integrity": "sha512-XZEYC+p69N+Accktjho0B98TkwAKCZNt91+t08eukw7Gwk6FvfJB+aBzHCmQEaWUiAOpAo4eObgac86P12XOkw==",
|
||||
"requires": {
|
||||
"@types/jsonwebtoken": "7.2.8",
|
||||
"jsonwebtoken": "8.4.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonwebtoken": {
|
||||
"version": "8.4.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-8.4.0.tgz",
|
||||
"integrity": "sha512-coyXjRTCy0pw5WYBpMvWOMN+Kjaik2MwTUIq9cna/W7NpO9E+iYbumZONAz3hcr+tXFJECoQVrtmIoC3Oz0gvg==",
|
||||
"requires": {
|
||||
"jws": "^3.1.5",
|
||||
"lodash.includes": "^4.3.0",
|
||||
"lodash.isboolean": "^3.0.3",
|
||||
"lodash.isinteger": "^4.0.4",
|
||||
"lodash.isnumber": "^3.0.3",
|
||||
"lodash.isplainobject": "^4.0.6",
|
||||
"lodash.isstring": "^4.0.1",
|
||||
"lodash.once": "^4.0.0",
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"@nestjs/passport": {
|
||||
"version": "6.1.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/passport/-/passport-6.1.1.tgz",
|
||||
|
|
@ -632,6 +664,15 @@
|
|||
"tslib": "1.10.0"
|
||||
}
|
||||
},
|
||||
"@nestjs/schedule": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schedule/-/schedule-0.2.0.tgz",
|
||||
"integrity": "sha512-VkPPQYzsKexltKYf3yE2f1ZmUebGlkd4hfJ8kNtzLYfcfn4ZN+nn7zsJ0yyNvWlCeq3oD1cFLW3Y5qoS4+Glag==",
|
||||
"requires": {
|
||||
"cron": "1.8.2",
|
||||
"uuid": "3.4.0"
|
||||
}
|
||||
},
|
||||
"@nestjs/schematics": {
|
||||
"version": "6.8.2",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/schematics/-/schematics-6.8.2.tgz",
|
||||
|
|
@ -643,6 +684,15 @@
|
|||
"fs-extra": "8.1.0"
|
||||
}
|
||||
},
|
||||
"@nestjs/swagger": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/swagger/-/swagger-4.2.3.tgz",
|
||||
"integrity": "sha512-5QFy6/PRFG65m0FO1VR9XYHtXo6fdbqXJlkQKw4QmtTmIdcGgVQcByeBafE+OTArWYlDCnpMTI5G1Rh7OPGhlQ==",
|
||||
"requires": {
|
||||
"lodash": "4.17.15",
|
||||
"path-to-regexp": "3.2.0"
|
||||
}
|
||||
},
|
||||
"@nestjs/testing": {
|
||||
"version": "6.11.1",
|
||||
"resolved": "https://registry.npmjs.org/@nestjs/testing/-/testing-6.11.1.tgz",
|
||||
|
|
@ -739,6 +789,7 @@
|
|||
"version": "1.17.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.17.1.tgz",
|
||||
"integrity": "sha512-RoX2EZjMiFMjZh9lmYrwgoP9RTpAjSHiJxdp4oidAQVO02T7HER3xj9UKue5534ULWeqVEkujhWcyvUce+d68w==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/connect": "*",
|
||||
"@types/node": "*"
|
||||
|
|
@ -753,6 +804,7 @@
|
|||
"version": "3.4.33",
|
||||
"resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.33.tgz",
|
||||
"integrity": "sha512-2+FrkXY4zllzTNfJth7jOqEHC+enpLeGslEhpnTAkg21GkRrWV4SsAtqchtT4YS9/nODBU2/ZfsBY2X4J/dX7A==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
|
|
@ -767,38 +819,23 @@
|
|||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/express/-/express-4.17.2.tgz",
|
||||
"integrity": "sha512-5mHFNyavtLoJmnusB8OKJ5bshSzw+qkMIBAobLrIM48HJvunFva9mOa6aBwh64lBFyNwBbs0xiEFuj4eU/NjCA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/body-parser": "*",
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/serve-static": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-jwt": {
|
||||
"version": "0.0.42",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-jwt/-/express-jwt-0.0.42.tgz",
|
||||
"integrity": "sha512-WszgUddvM1t5dPpJ3LhWNH8kfNN8GPIBrAGxgIYXVCEGx6Bx4A036aAuf/r5WH9DIEdlmp7gHOYvSM6U87B0ag==",
|
||||
"requires": {
|
||||
"@types/express": "*",
|
||||
"@types/express-unless": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-serve-static-core": {
|
||||
"version": "4.17.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-4.17.2.tgz",
|
||||
"integrity": "sha512-El9yMpctM6tORDAiBwZVLMcxoTMcqqRO9dVyYcn7ycLWbvR8klrDn8CAOwRfZujZtWD7yS/mshTdz43jMOejbg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/node": "*",
|
||||
"@types/range-parser": "*"
|
||||
}
|
||||
},
|
||||
"@types/express-unless": {
|
||||
"version": "0.5.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/express-unless/-/express-unless-0.5.1.tgz",
|
||||
"integrity": "sha512-5fuvg7C69lemNgl0+v+CUxDYWVPSfXHhJPst4yTLcqi4zKJpORCxnDrnnilk3k0DTq/WrAUdvXFs01+vUqUZHw==",
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/istanbul-lib-coverage": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz",
|
||||
|
|
@ -839,25 +876,66 @@
|
|||
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/jsonwebtoken": {
|
||||
"version": "7.2.8",
|
||||
"resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-7.2.8.tgz",
|
||||
"integrity": "sha512-XENN3YzEB8D6TiUww0O8SRznzy1v+77lH7UmuN54xq/IHIsyWjWOzZuFFTtoiRuaE782uAoRwBe/wwow+vQXZw==",
|
||||
"requires": {
|
||||
"@types/node": "*"
|
||||
}
|
||||
},
|
||||
"@types/mime": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-2.0.1.tgz",
|
||||
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw=="
|
||||
"integrity": "sha512-FwI9gX75FgVBJ7ywgnq/P7tw+/o1GUbtP0KzbtusLigAOgIgNISRK0ZPl4qertvXSIE8YbsVJueQ90cDt9YYyw==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/node": {
|
||||
"version": "12.12.25",
|
||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.25.tgz",
|
||||
"integrity": "sha512-nf1LMGZvgFX186geVZR1xMZKKblJiRfiASTHw85zED2kI1yDKHDwTKMdkaCbTlXoRKlGKaDfYywt+V0As30q3w=="
|
||||
},
|
||||
"@types/passport": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport/-/passport-1.0.2.tgz",
|
||||
"integrity": "sha512-Pf39AYKf8q+YoONym3150cEwfUD66dtwHJWvbeOzKxnA0GZZ/vAXhNWv9vMhKyRQBQZiQyWQnhYBEBlKW6G8wg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*"
|
||||
}
|
||||
},
|
||||
"@types/passport-jwt": {
|
||||
"version": "3.0.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-jwt/-/passport-jwt-3.0.3.tgz",
|
||||
"integrity": "sha512-RlOCXiTitE8kazj9jZc6/BfGCSqnv2w/eYPDm3+3iNsquHn7ratu7oIUskZx9ZtnwMdpvdpy+Z/QYClocH5NvQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*",
|
||||
"@types/jsonwebtoken": "*",
|
||||
"@types/passport-strategy": "*"
|
||||
}
|
||||
},
|
||||
"@types/passport-strategy": {
|
||||
"version": "0.2.35",
|
||||
"resolved": "https://registry.npmjs.org/@types/passport-strategy/-/passport-strategy-0.2.35.tgz",
|
||||
"integrity": "sha512-o5D19Jy2XPFoX2rKApykY15et3Apgax00RRLf0RUotPDUsYrQa7x4howLYr9El2mlUApHmCMv5CZ1IXqKFQ2+g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express": "*",
|
||||
"@types/passport": "*"
|
||||
}
|
||||
},
|
||||
"@types/range-parser": {
|
||||
"version": "1.2.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.3.tgz",
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA=="
|
||||
"integrity": "sha512-ewFXqrQHlFsgc09MK5jP5iR7vumV/BYayNC6PgJO2LPe8vrnNFyjQjSppfEngITi0qvfKtzFvgKymGheFM9UOA==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/serve-static": {
|
||||
"version": "1.13.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.13.3.tgz",
|
||||
"integrity": "sha512-oprSwp094zOglVrXdlo/4bAHtKTAxX6VT8FOZlBKrmyLbNvE1zxZyJ6yikMVtHIvwP45+ZQGJn+FdXGKTozq0g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/express-serve-static-core": "*",
|
||||
"@types/mime": "*"
|
||||
|
|
@ -917,6 +995,11 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"@types/validator": {
|
||||
"version": "10.11.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/validator/-/validator-10.11.3.tgz",
|
||||
"integrity": "sha512-GKF2VnEkMmEeEGvoo03ocrP9ySMuX1ypKazIYMlsjfslfBMhOAtC5dmEWKdJioW4lJN7MZRS88kalTsVClyQ9w=="
|
||||
},
|
||||
"@types/webpack": {
|
||||
"version": "4.41.3",
|
||||
"resolved": "https://registry.npmjs.org/@types/webpack/-/webpack-4.41.3.tgz",
|
||||
|
|
@ -1202,6 +1285,7 @@
|
|||
"version": "6.9.1",
|
||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.9.1.tgz",
|
||||
"integrity": "sha512-XDN92U311aINL77ieWHmqCcNlwjoP5cHXDxIxbf2MaPYuCXOHS7gHH8jktxeK5omgd52XbSTX6a4Piwd1pQmzA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"fast-json-stable-stringify": "^2.0.0",
|
||||
|
|
@ -1324,6 +1408,7 @@
|
|||
"version": "0.2.4",
|
||||
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safer-buffer": "~2.1.0"
|
||||
}
|
||||
|
|
@ -1369,7 +1454,8 @@
|
|||
"assert-plus": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=",
|
||||
"dev": true
|
||||
},
|
||||
"assign-symbols": {
|
||||
"version": "1.0.0",
|
||||
|
|
@ -1398,7 +1484,8 @@
|
|||
"asynckit": {
|
||||
"version": "0.4.0",
|
||||
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
|
||||
"dev": true
|
||||
},
|
||||
"atob": {
|
||||
"version": "2.1.2",
|
||||
|
|
@ -1409,12 +1496,14 @@
|
|||
"aws-sign2": {
|
||||
"version": "0.7.0",
|
||||
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=",
|
||||
"dev": true
|
||||
},
|
||||
"aws4": {
|
||||
"version": "1.9.1",
|
||||
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.1.tgz",
|
||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug=="
|
||||
"integrity": "sha512-wMHVg2EOHaMRxbzgFJ9gtjOOCrI80OHLG14rxi28XwOW8ux6IiEbRCGGGqCtdAIg4FQCbW20k9RsT4y3gJlFug==",
|
||||
"dev": true
|
||||
},
|
||||
"axios": {
|
||||
"version": "0.19.2",
|
||||
|
|
@ -1591,6 +1680,7 @@
|
|||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"tweetnacl": "^0.14.3"
|
||||
}
|
||||
|
|
@ -1952,7 +2042,8 @@
|
|||
"caseless": {
|
||||
"version": "0.12.0",
|
||||
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=",
|
||||
"dev": true
|
||||
},
|
||||
"chalk": {
|
||||
"version": "2.4.2",
|
||||
|
|
@ -2022,6 +2113,11 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"class-transformer": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/class-transformer/-/class-transformer-0.2.3.tgz",
|
||||
"integrity": "sha512-qsP+0xoavpOlJHuYsQJsN58HXSl8Jvveo+T37rEvCEeRfMWoytAyR0Ua/YsFgpM6AZYZ/og2PJwArwzJl1aXtQ=="
|
||||
},
|
||||
"class-utils": {
|
||||
"version": "0.3.6",
|
||||
"resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz",
|
||||
|
|
@ -2045,6 +2141,16 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"class-validator": {
|
||||
"version": "0.11.0",
|
||||
"resolved": "https://registry.npmjs.org/class-validator/-/class-validator-0.11.0.tgz",
|
||||
"integrity": "sha512-niAmmSPFku9xsnpYYrddy8NZRrCX3yyoZ/rgPKOilE5BG0Ma1eVCIxpR4X0LasL/6BzbYzsutG+mSbAXlh4zNw==",
|
||||
"requires": {
|
||||
"@types/validator": "10.11.3",
|
||||
"google-libphonenumber": "^3.1.6",
|
||||
"validator": "12.0.0"
|
||||
}
|
||||
},
|
||||
"cli-color": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/cli-color/-/cli-color-2.0.0.tgz",
|
||||
|
|
@ -2332,6 +2438,7 @@
|
|||
"version": "1.0.8",
|
||||
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"delayed-stream": "~1.0.0"
|
||||
}
|
||||
|
|
@ -2550,6 +2657,14 @@
|
|||
"sha.js": "^2.4.8"
|
||||
}
|
||||
},
|
||||
"cron": {
|
||||
"version": "1.8.2",
|
||||
"resolved": "https://registry.npmjs.org/cron/-/cron-1.8.2.tgz",
|
||||
"integrity": "sha512-Gk2c4y6xKEO8FSAUTklqtfSr7oTq0CiPQeLBG5Fl0qoXpZyMcj1SG59YL+hqq04bu6/IuEA7lMkYDAplQNKkyg==",
|
||||
"requires": {
|
||||
"moment-timezone": "^0.5.x"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "6.0.5",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
|
||||
|
|
@ -2616,6 +2731,7 @@
|
|||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
|
|
@ -2731,7 +2847,8 @@
|
|||
"delayed-stream": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=",
|
||||
"dev": true
|
||||
},
|
||||
"depd": {
|
||||
"version": "1.1.2",
|
||||
|
|
@ -2864,6 +2981,7 @@
|
|||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"jsbn": "~0.1.0",
|
||||
"safer-buffer": "^2.1.0"
|
||||
|
|
@ -3274,41 +3392,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"express-session": {
|
||||
"version": "1.17.0",
|
||||
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz",
|
||||
"integrity": "sha512-t4oX2z7uoSqATbMfsxWMbNjAL0T5zpvcJCk3Z9wnPPN7ibddhnmDZXHfEcoBMG2ojKXZoCyPMc5FbtK+G7SoDg==",
|
||||
"requires": {
|
||||
"cookie": "0.4.0",
|
||||
"cookie-signature": "1.0.6",
|
||||
"debug": "2.6.9",
|
||||
"depd": "~2.0.0",
|
||||
"on-headers": "~1.0.2",
|
||||
"parseurl": "~1.3.3",
|
||||
"safe-buffer": "5.2.0",
|
||||
"uid-safe": "~2.1.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
|
||||
"requires": {
|
||||
"ms": "2.0.0"
|
||||
}
|
||||
},
|
||||
"depd": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
|
||||
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
|
||||
},
|
||||
"safe-buffer": {
|
||||
"version": "5.2.0",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
|
||||
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"ext": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
|
||||
|
|
@ -3327,7 +3410,8 @@
|
|||
"extend": {
|
||||
"version": "3.0.2",
|
||||
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
|
||||
"dev": true
|
||||
},
|
||||
"extend-shallow": {
|
||||
"version": "3.0.2",
|
||||
|
|
@ -3429,12 +3513,14 @@
|
|||
"extsprintf": {
|
||||
"version": "1.3.0",
|
||||
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-deep-equal": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
|
||||
"integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-diff": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -3445,7 +3531,8 @@
|
|||
"fast-json-stable-stringify": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz",
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I="
|
||||
"integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=",
|
||||
"dev": true
|
||||
},
|
||||
"fast-levenshtein": {
|
||||
"version": "2.0.6",
|
||||
|
|
@ -3619,7 +3706,8 @@
|
|||
"forever-agent": {
|
||||
"version": "0.6.1",
|
||||
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
|
||||
"dev": true
|
||||
},
|
||||
"fork-ts-checker-webpack-plugin": {
|
||||
"version": "4.0.2",
|
||||
|
|
@ -3640,6 +3728,7 @@
|
|||
"version": "2.3.3",
|
||||
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asynckit": "^0.4.0",
|
||||
"combined-stream": "^1.0.6",
|
||||
|
|
@ -4335,6 +4424,7 @@
|
|||
"version": "0.1.7",
|
||||
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0"
|
||||
}
|
||||
|
|
@ -4379,6 +4469,11 @@
|
|||
"integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
|
||||
"dev": true
|
||||
},
|
||||
"google-libphonenumber": {
|
||||
"version": "3.2.6",
|
||||
"resolved": "https://registry.npmjs.org/google-libphonenumber/-/google-libphonenumber-3.2.6.tgz",
|
||||
"integrity": "sha512-6QCQAaKJlSd/1dUqvdQf7zzfb3uiZHsG8yhCfOdCVRfMuPZ/VDIEB47y5SYwjPQJPs7ebfW5jj6PeobB9JJ4JA=="
|
||||
},
|
||||
"graceful-fs": {
|
||||
"version": "4.2.3",
|
||||
"resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
|
||||
|
|
@ -4394,12 +4489,14 @@
|
|||
"har-schema": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=",
|
||||
"dev": true
|
||||
},
|
||||
"har-validator": {
|
||||
"version": "5.1.3",
|
||||
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
|
||||
"integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"ajv": "^6.5.5",
|
||||
"har-schema": "^2.0.0"
|
||||
|
|
@ -4538,6 +4635,7 @@
|
|||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"jsprim": "^1.2.2",
|
||||
|
|
@ -4940,7 +5038,8 @@
|
|||
"is-typedarray": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=",
|
||||
"dev": true
|
||||
},
|
||||
"is-windows": {
|
||||
"version": "1.0.2",
|
||||
|
|
@ -4974,7 +5073,8 @@
|
|||
"isstream": {
|
||||
"version": "0.1.2",
|
||||
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=",
|
||||
"dev": true
|
||||
},
|
||||
"istanbul-lib-coverage": {
|
||||
"version": "2.0.5",
|
||||
|
|
@ -5538,7 +5638,8 @@
|
|||
"jsbn": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=",
|
||||
"dev": true
|
||||
},
|
||||
"jsdom": {
|
||||
"version": "11.12.0",
|
||||
|
|
@ -5597,17 +5698,20 @@
|
|||
"json-schema": {
|
||||
"version": "0.2.3",
|
||||
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
|
||||
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=",
|
||||
"dev": true
|
||||
},
|
||||
"json-schema-traverse": {
|
||||
"version": "0.4.1",
|
||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
|
||||
"dev": true
|
||||
},
|
||||
"json-stringify-safe": {
|
||||
"version": "5.0.1",
|
||||
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=",
|
||||
"dev": true
|
||||
},
|
||||
"json5": {
|
||||
"version": "1.0.1",
|
||||
|
|
@ -5663,6 +5767,7 @@
|
|||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "1.0.0",
|
||||
"extsprintf": "1.3.0",
|
||||
|
|
@ -5680,35 +5785,6 @@
|
|||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
},
|
||||
"jwks-rsa": {
|
||||
"version": "1.6.2",
|
||||
"resolved": "https://registry.npmjs.org/jwks-rsa/-/jwks-rsa-1.6.2.tgz",
|
||||
"integrity": "sha512-c/mFFq/wVXSkHzHGH+hLUwLeRKSCofNHJZKPzHho4YmO9LGwAazk7akfABvWhduS9OejWvqBS2jA69YeruEvNA==",
|
||||
"requires": {
|
||||
"@types/express-jwt": "0.0.42",
|
||||
"debug": "^4.1.0",
|
||||
"jsonwebtoken": "^8.5.1",
|
||||
"limiter": "^1.1.4",
|
||||
"lru-memoizer": "^2.0.1",
|
||||
"ms": "^2.1.2",
|
||||
"request": "^2.88.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"debug": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||
"requires": {
|
||||
"ms": "^2.1.1"
|
||||
}
|
||||
},
|
||||
"ms": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||
}
|
||||
}
|
||||
},
|
||||
"jws": {
|
||||
"version": "3.2.2",
|
||||
"resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz",
|
||||
|
|
@ -5752,11 +5828,6 @@
|
|||
"type-check": "~0.3.2"
|
||||
}
|
||||
},
|
||||
"limiter": {
|
||||
"version": "1.1.5",
|
||||
"resolved": "https://registry.npmjs.org/limiter/-/limiter-1.1.5.tgz",
|
||||
"integrity": "sha512-FWWMIEOxz3GwUI4Ts/IvgVy6LPvoMPgjMdQ185nN6psJyBJ4yOpzqm695/h5umdLJg2vW3GR5iG11MAkR2AzJA=="
|
||||
},
|
||||
"lines-and-columns": {
|
||||
"version": "1.1.6",
|
||||
"resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.1.6.tgz",
|
||||
|
|
@ -5812,13 +5883,7 @@
|
|||
"lodash": {
|
||||
"version": "4.17.15",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==",
|
||||
"dev": true
|
||||
},
|
||||
"lodash.clonedeep": {
|
||||
"version": "4.5.0",
|
||||
"resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz",
|
||||
"integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8="
|
||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
||||
},
|
||||
"lodash.debounce": {
|
||||
"version": "4.0.8",
|
||||
|
|
@ -5916,31 +5981,6 @@
|
|||
"yallist": "^3.0.2"
|
||||
}
|
||||
},
|
||||
"lru-memoizer": {
|
||||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/lru-memoizer/-/lru-memoizer-2.0.1.tgz",
|
||||
"integrity": "sha512-kGl+zlIqdQL24f0Q9IUSUZeSvA7nqXPFLA7suFh00v4KVqfXkZJtkPfTfXV/oQMSPfNr6VT4xGkRAUPhFnGyxQ==",
|
||||
"requires": {
|
||||
"lodash.clonedeep": "^4.5.0",
|
||||
"lru-cache": "~4.0.0"
|
||||
},
|
||||
"dependencies": {
|
||||
"lru-cache": {
|
||||
"version": "4.0.2",
|
||||
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.0.2.tgz",
|
||||
"integrity": "sha1-HRdnnAac2l0ECZGgnbwsDbN35V4=",
|
||||
"requires": {
|
||||
"pseudomap": "^1.0.1",
|
||||
"yallist": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"yallist": {
|
||||
"version": "2.1.2",
|
||||
"resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz",
|
||||
"integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI="
|
||||
}
|
||||
}
|
||||
},
|
||||
"lru-queue": {
|
||||
"version": "0.1.0",
|
||||
"resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz",
|
||||
|
|
@ -6223,6 +6263,19 @@
|
|||
"minimist": "0.0.8"
|
||||
}
|
||||
},
|
||||
"moment": {
|
||||
"version": "2.24.0",
|
||||
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
|
||||
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
|
||||
},
|
||||
"moment-timezone": {
|
||||
"version": "0.5.27",
|
||||
"resolved": "https://registry.npmjs.org/moment-timezone/-/moment-timezone-0.5.27.tgz",
|
||||
"integrity": "sha512-EIKQs7h5sAsjhPCqN6ggx6cEbs94GK050254TIJySD1bzoM5JTYDwAU1IoVOeTOL6Gm27kYJ51/uuvq1kIlrbw==",
|
||||
"requires": {
|
||||
"moment": ">= 2.9.0"
|
||||
}
|
||||
},
|
||||
"move-concurrently": {
|
||||
"version": "1.0.1",
|
||||
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
|
||||
|
|
@ -6268,11 +6321,6 @@
|
|||
"xtend": "^4.0.0"
|
||||
}
|
||||
},
|
||||
"mustache": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/mustache/-/mustache-4.0.0.tgz",
|
||||
"integrity": "sha512-FJgjyX/IVkbXBXYUwH+OYwQKqWpFPLaLVESd70yHjSDunwzV2hZOoTBvPf4KLoxesUzzyfTH6F784Uqd7Wm5yA=="
|
||||
},
|
||||
"mute-stream": {
|
||||
"version": "0.0.7",
|
||||
"resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz",
|
||||
|
|
@ -6538,7 +6586,8 @@
|
|||
"oauth-sign": {
|
||||
"version": "0.9.0",
|
||||
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
|
||||
"dev": true
|
||||
},
|
||||
"object-assign": {
|
||||
"version": "4.1.1",
|
||||
|
|
@ -6641,11 +6690,6 @@
|
|||
"ee-first": "1.1.1"
|
||||
}
|
||||
},
|
||||
"on-headers": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
|
||||
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
|
||||
},
|
||||
"once": {
|
||||
"version": "1.4.0",
|
||||
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
|
||||
|
|
@ -6994,16 +7038,6 @@
|
|||
"pause": "0.0.1"
|
||||
}
|
||||
},
|
||||
"passport-auth0": {
|
||||
"version": "1.3.1",
|
||||
"resolved": "https://registry.npmjs.org/passport-auth0/-/passport-auth0-1.3.1.tgz",
|
||||
"integrity": "sha512-HT7qbvw1dlRrNAIsisgsACaiDBcLLBMUFIFCt4dZ4vLrJF8bkNWj9QgeTAcXjQoYJ5sXfpaD/wYUGBSe98cG0g==",
|
||||
"requires": {
|
||||
"passport-oauth": "^1.0.0",
|
||||
"passport-oauth2": "^1.5.0",
|
||||
"request": "^2.88.0"
|
||||
}
|
||||
},
|
||||
"passport-jwt": {
|
||||
"version": "4.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-jwt/-/passport-jwt-4.0.0.tgz",
|
||||
|
|
@ -7044,6 +7078,16 @@
|
|||
"utils-merge": "1.x.x"
|
||||
}
|
||||
},
|
||||
"passport-spotify": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-spotify/-/passport-spotify-1.1.0.tgz",
|
||||
"integrity": "sha512-jLVtH7Z+5XW854H0mAsdpGJq5dMSwxQQu1jcXhq+3JlB8HuP5xkCLMuQ5K24S7WYh+cAHLrEKJPAoeo1D8CbUg==",
|
||||
"requires": {
|
||||
"passport-oauth": "1.0.0",
|
||||
"querystring": "~0.2.0",
|
||||
"util": "~0.11.0"
|
||||
}
|
||||
},
|
||||
"passport-strategy": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
|
||||
|
|
@ -7126,7 +7170,8 @@
|
|||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=",
|
||||
"dev": true
|
||||
},
|
||||
"pg": {
|
||||
"version": "7.17.1",
|
||||
|
|
@ -7329,15 +7374,11 @@
|
|||
"integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=",
|
||||
"dev": true
|
||||
},
|
||||
"pseudomap": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz",
|
||||
"integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM="
|
||||
},
|
||||
"psl": {
|
||||
"version": "1.7.0",
|
||||
"resolved": "https://registry.npmjs.org/psl/-/psl-1.7.0.tgz",
|
||||
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ=="
|
||||
"integrity": "sha512-5NsSEDv8zY70ScRnOTn7bK7eanl2MvFrOrS/R6x+dBt5g1ghnj9Zv90kO8GwT8gxcu2ANyFprnFYB85IogIJOQ==",
|
||||
"dev": true
|
||||
},
|
||||
"public-encrypt": {
|
||||
"version": "4.0.3",
|
||||
|
|
@ -7389,7 +7430,8 @@
|
|||
"punycode": {
|
||||
"version": "2.1.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.7.0",
|
||||
|
|
@ -7399,8 +7441,7 @@
|
|||
"querystring": {
|
||||
"version": "0.2.0",
|
||||
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA=",
|
||||
"dev": true
|
||||
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||
},
|
||||
"querystring-es3": {
|
||||
"version": "0.2.1",
|
||||
|
|
@ -7408,11 +7449,6 @@
|
|||
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
|
||||
"dev": true
|
||||
},
|
||||
"random-bytes": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
|
||||
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
|
||||
},
|
||||
"randombytes": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
|
||||
|
|
@ -7575,6 +7611,7 @@
|
|||
"version": "2.88.0",
|
||||
"resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz",
|
||||
"integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"aws-sign2": "~0.7.0",
|
||||
"aws4": "^1.8.0",
|
||||
|
|
@ -7601,17 +7638,20 @@
|
|||
"punycode": {
|
||||
"version": "1.4.1",
|
||||
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz",
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4="
|
||||
"integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=",
|
||||
"dev": true
|
||||
},
|
||||
"qs": {
|
||||
"version": "6.5.2",
|
||||
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==",
|
||||
"dev": true
|
||||
},
|
||||
"tough-cookie": {
|
||||
"version": "2.4.3",
|
||||
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz",
|
||||
"integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"psl": "^1.1.24",
|
||||
"punycode": "^1.4.1"
|
||||
|
|
@ -8186,6 +8226,7 @@
|
|||
"version": "1.16.1",
|
||||
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"asn1": "~0.2.3",
|
||||
"assert-plus": "^1.0.0",
|
||||
|
|
@ -8527,6 +8568,19 @@
|
|||
"has-flag": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"swagger-ui-dist": {
|
||||
"version": "3.25.0",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-dist/-/swagger-ui-dist-3.25.0.tgz",
|
||||
"integrity": "sha512-vwvJPPbdooTvDwLGzjIXinOXizDJJ6U1hxnJL3y6U3aL1d2MSXDmKg2139XaLBhsVZdnQJV2bOkX4reB+RXamg=="
|
||||
},
|
||||
"swagger-ui-express": {
|
||||
"version": "4.1.3",
|
||||
"resolved": "https://registry.npmjs.org/swagger-ui-express/-/swagger-ui-express-4.1.3.tgz",
|
||||
"integrity": "sha512-f8SEn4YWkKh/HGK0ZjuA2VqA78i1aY6OIa5cqYNgOkBobfHV6Mz4dphQW/us8HYhEFfbENq329PyfIonWfzFrw==",
|
||||
"requires": {
|
||||
"swagger-ui-dist": "^3.18.1"
|
||||
}
|
||||
},
|
||||
"symbol-observable": {
|
||||
"version": "1.2.0",
|
||||
"resolved": "https://registry.npmjs.org/symbol-observable/-/symbol-observable-1.2.0.tgz",
|
||||
|
|
@ -9021,6 +9075,7 @@
|
|||
"version": "0.6.0",
|
||||
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"safe-buffer": "^5.0.1"
|
||||
}
|
||||
|
|
@ -9028,7 +9083,8 @@
|
|||
"tweetnacl": {
|
||||
"version": "0.14.5",
|
||||
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=",
|
||||
"dev": true
|
||||
},
|
||||
"type": {
|
||||
"version": "1.2.0",
|
||||
|
|
@ -9120,14 +9176,6 @@
|
|||
"integrity": "sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==",
|
||||
"dev": true
|
||||
},
|
||||
"uid-safe": {
|
||||
"version": "2.1.5",
|
||||
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
|
||||
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
|
||||
"requires": {
|
||||
"random-bytes": "~1.0.0"
|
||||
}
|
||||
},
|
||||
"uid2": {
|
||||
"version": "0.0.3",
|
||||
"resolved": "https://registry.npmjs.org/uid2/-/uid2-0.0.3.tgz",
|
||||
|
|
@ -9230,6 +9278,7 @@
|
|||
"version": "4.2.2",
|
||||
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz",
|
||||
"integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"punycode": "^2.1.0"
|
||||
}
|
||||
|
|
@ -9268,7 +9317,6 @@
|
|||
"version": "0.11.1",
|
||||
"resolved": "https://registry.npmjs.org/util/-/util-0.11.1.tgz",
|
||||
"integrity": "sha512-HShAsny+zS2TZfaXxD9tYj4HQGlBezXZMZuM/S5PKLLoZkShZiGk9o5CzukI1LVHZvjdvZ2Sj1aW/Ndn2NB/HQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"inherits": "2.0.3"
|
||||
}
|
||||
|
|
@ -9310,6 +9358,11 @@
|
|||
"spdx-expression-parse": "^3.0.0"
|
||||
}
|
||||
},
|
||||
"validator": {
|
||||
"version": "12.0.0",
|
||||
"resolved": "https://registry.npmjs.org/validator/-/validator-12.0.0.tgz",
|
||||
"integrity": "sha512-r5zA1cQBEOgYlesRmSEwc9LkbfNLTtji+vWyaHzRZUxCTHdsX3bd+sdHfs5tGZ2W6ILGGsxWxCNwT/h3IY/3ng=="
|
||||
},
|
||||
"vary": {
|
||||
"version": "1.1.2",
|
||||
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
|
||||
|
|
@ -9319,6 +9372,7 @@
|
|||
"version": "1.10.0",
|
||||
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"assert-plus": "^1.0.0",
|
||||
"core-util-is": "1.0.2",
|
||||
|
|
|
|||
12
package.json
12
package.json
|
|
@ -23,19 +23,22 @@
|
|||
"@nestjs/common": "^6.7.2",
|
||||
"@nestjs/config": "^0.1.0",
|
||||
"@nestjs/core": "^6.7.2",
|
||||
"@nestjs/jwt": "^6.1.1",
|
||||
"@nestjs/passport": "^6.1.1",
|
||||
"@nestjs/platform-express": "^6.7.2",
|
||||
"@nestjs/schedule": "^0.2.0",
|
||||
"@nestjs/swagger": "^4.2.3",
|
||||
"@nestjs/typeorm": "^6.2.0",
|
||||
"express-session": "^1.17.0",
|
||||
"jwks-rsa": "^1.6.2",
|
||||
"mustache": "^4.0.0",
|
||||
"class-transformer": "^0.2.3",
|
||||
"class-validator": "^0.11.0",
|
||||
"passport": "^0.4.1",
|
||||
"passport-auth0": "^1.3.1",
|
||||
"passport-jwt": "^4.0.0",
|
||||
"passport-spotify": "^1.1.0",
|
||||
"pg": "^7.17.1",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.0",
|
||||
"rxjs": "^6.5.3",
|
||||
"swagger-ui-express": "^4.1.3",
|
||||
"typeorm": "^0.2.22"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
|
@ -45,6 +48,7 @@
|
|||
"@types/express": "^4.17.1",
|
||||
"@types/jest": "^24.0.18",
|
||||
"@types/node": "^12.7.5",
|
||||
"@types/passport-jwt": "^3.0.3",
|
||||
"@types/supertest": "^2.0.8",
|
||||
"jest": "^24.9.0",
|
||||
"prettier": "^1.18.2",
|
||||
|
|
|
|||
|
|
@ -1,17 +1,17 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { ConfigModule } from "@nestjs/config";
|
||||
import { AuthenticationModule } from "./authentication/authentication.module";
|
||||
import { AuthModule } from "./auth/auth.module";
|
||||
import { DatabaseModule } from "./database/database.module";
|
||||
import { ConnectionsModule } from "./connections/connections.module";
|
||||
import { FrontendModule } from './frontend/frontend.module';
|
||||
import { SourcesModule } from "./sources/sources.module";
|
||||
import { UsersModule } from "./users/users.module";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
ConfigModule.forRoot({ isGlobal: true }),
|
||||
DatabaseModule,
|
||||
AuthenticationModule,
|
||||
ConnectionsModule,
|
||||
FrontendModule
|
||||
AuthModule,
|
||||
UsersModule,
|
||||
SourcesModule
|
||||
]
|
||||
})
|
||||
export class AppModule {}
|
||||
|
|
|
|||
40
src/auth/auth.controller.ts
Normal file
40
src/auth/auth.controller.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
import { Controller, Get, Res, UseGuards } from "@nestjs/common";
|
||||
import { AuthGuard } from "@nestjs/passport";
|
||||
import { Response } from "express";
|
||||
import { User } from "../users/user.entity";
|
||||
import { ReqUser } from "./decorators/req-user.decorator";
|
||||
import { AuthService } from "./auth.service";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
|
||||
@Controller("api/v1/auth")
|
||||
export class AuthController {
|
||||
constructor(
|
||||
private readonly authService: AuthService,
|
||||
private readonly config: ConfigService
|
||||
) {}
|
||||
|
||||
@Get("spotify")
|
||||
@UseGuards(AuthGuard("spotify"))
|
||||
spotifyRedirect() {
|
||||
// User is redirected by AuthGuard
|
||||
}
|
||||
|
||||
@Get("spotify/callback")
|
||||
@UseGuards(AuthGuard("spotify"))
|
||||
async spotifyCallback(@ReqUser() user: User, @Res() res: Response) {
|
||||
const { accessToken } = await this.authService.createToken(user);
|
||||
|
||||
// Transmit accessToken to Frontend
|
||||
res.cookie("listory_access_token", accessToken, {
|
||||
// SPA will directly read cookie, save it to local storage and delete it
|
||||
// 15 Minutes should be enough
|
||||
maxAge: 15 * 60 * 1000,
|
||||
|
||||
// Must be readable by SPA
|
||||
httpOnly: false
|
||||
});
|
||||
|
||||
// Redirect User to SPA
|
||||
res.redirect("/");
|
||||
}
|
||||
}
|
||||
27
src/auth/auth.module.ts
Normal file
27
src/auth/auth.module.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { JwtModule } from "@nestjs/jwt";
|
||||
import { PassportModule } from "@nestjs/passport";
|
||||
import { UsersModule } from "../users/users.module";
|
||||
import { AuthController } from "./auth.controller";
|
||||
import { AuthService } from "./auth.service";
|
||||
import { JwtStrategy } from "./jwt.strategy";
|
||||
import { SpotifyStrategy } from "./spotify.strategy";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
PassportModule.register({ defaultStrategy: "jwt" }),
|
||||
JwtModule.registerAsync({
|
||||
useFactory: (config: ConfigService) => ({
|
||||
secret: config.get<string>("JWT_SECRET"),
|
||||
signOptions: { expiresIn: config.get<string>("JWT_EXPIRATION_TIME") }
|
||||
}),
|
||||
inject: [ConfigService]
|
||||
}),
|
||||
UsersModule
|
||||
],
|
||||
providers: [AuthService, SpotifyStrategy, JwtStrategy],
|
||||
exports: [PassportModule],
|
||||
controllers: [AuthController]
|
||||
})
|
||||
export class AuthModule {}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { ConnectionsService } from "./connections.service";
|
||||
import { AuthService } from "./auth.service";
|
||||
|
||||
describe("ConnectionsService", () => {
|
||||
let service: ConnectionsService;
|
||||
describe("AuthService", () => {
|
||||
let service: AuthService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [ConnectionsService]
|
||||
providers: [AuthService]
|
||||
}).compile();
|
||||
|
||||
service = module.get<ConnectionsService>(ConnectionsService);
|
||||
service = module.get<AuthService>(AuthService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
47
src/auth/auth.service.ts
Normal file
47
src/auth/auth.service.ts
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { User } from "../users/user.entity";
|
||||
import { UsersService } from "../users/users.service";
|
||||
import { LoginDto } from "./dto/login.dto";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
constructor(
|
||||
private readonly usersService: UsersService,
|
||||
private readonly jwtService: JwtService
|
||||
) {}
|
||||
|
||||
async spotifyLogin({
|
||||
accessToken,
|
||||
refreshToken,
|
||||
profile
|
||||
}: LoginDto): Promise<User> {
|
||||
const user = await this.usersService.createOrUpdate({
|
||||
displayName: profile.displayName,
|
||||
photo: profile.photos.length > 0 ? profile.photos[0] : null,
|
||||
spotify: {
|
||||
id: profile.id,
|
||||
accessToken,
|
||||
refreshToken
|
||||
}
|
||||
});
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async createToken(user: User): Promise<{ accessToken }> {
|
||||
const payload = {
|
||||
sub: user.id,
|
||||
name: user.displayName,
|
||||
picture: user.photo
|
||||
};
|
||||
|
||||
const token = await this.jwtService.signAsync(payload);
|
||||
|
||||
return { accessToken: token };
|
||||
}
|
||||
|
||||
async findUser(id: string): Promise<User> {
|
||||
return this.usersService.findById(id);
|
||||
}
|
||||
}
|
||||
11
src/auth/decorators/auth.decorator.ts
Normal file
11
src/auth/decorators/auth.decorator.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
import { applyDecorators, UseGuards } from "@nestjs/common";
|
||||
import { AuthGuard } from "@nestjs/passport";
|
||||
import { ApiBearerAuth, ApiUnauthorizedResponse } from "@nestjs/swagger";
|
||||
|
||||
export function Auth() {
|
||||
return applyDecorators(
|
||||
UseGuards(AuthGuard("jwt")),
|
||||
ApiBearerAuth(),
|
||||
ApiUnauthorizedResponse({ description: 'Unauthorized"' })
|
||||
);
|
||||
}
|
||||
3
src/auth/decorators/req-user.decorator.ts
Normal file
3
src/auth/decorators/req-user.decorator.ts
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
import { createParamDecorator } from "@nestjs/common";
|
||||
|
||||
export const ReqUser = createParamDecorator((data, req) => req.user);
|
||||
9
src/auth/dto/login.dto.ts
Normal file
9
src/auth/dto/login.dto.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
export class LoginDto {
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
profile: {
|
||||
id: string;
|
||||
displayName: string;
|
||||
photos: string[];
|
||||
};
|
||||
}
|
||||
23
src/auth/jwt.strategy.ts
Normal file
23
src/auth/jwt.strategy.ts
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { Strategy, ExtractJwt } from "passport-jwt";
|
||||
import { AuthService } from "./auth.service";
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(
|
||||
private readonly config: ConfigService,
|
||||
private readonly authService: AuthService
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: config.get<string>("JWT_SECRET")
|
||||
});
|
||||
}
|
||||
|
||||
async validate(payload: any) {
|
||||
return this.authService.findUser(payload.sub);
|
||||
}
|
||||
}
|
||||
33
src/auth/spotify.strategy.ts
Normal file
33
src/auth/spotify.strategy.ts
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { Strategy } from "passport-spotify";
|
||||
import { AuthService } from "./auth.service";
|
||||
|
||||
@Injectable()
|
||||
export class SpotifyStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(
|
||||
private readonly config: ConfigService,
|
||||
private readonly authService: AuthService
|
||||
) {
|
||||
super({
|
||||
clientID: config.get<string>("SPOTIFY_CLIENT_ID"),
|
||||
clientSecret: config.get<string>("SPOTIFY_CLIENT_SECRET"),
|
||||
callbackURL: `${config.get<string>("BASE_DOMAIN") ||
|
||||
"http://localhost:3000"}/api/v1/auth/spotify/callback`,
|
||||
scope: [
|
||||
"user-read-private",
|
||||
"user-read-email",
|
||||
"user-read-recently-played"
|
||||
]
|
||||
});
|
||||
}
|
||||
|
||||
async validate(accessToken: string, refreshToken: string, profile: any) {
|
||||
return await this.authService.spotifyLogin({
|
||||
accessToken,
|
||||
refreshToken,
|
||||
profile
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { PassportModule } from "@nestjs/passport";
|
||||
import { JwtStrategy } from "./jwt.strategy";
|
||||
|
||||
@Module({
|
||||
imports: [PassportModule.register({ defaultStrategy: "jwt" })],
|
||||
providers: [JwtStrategy],
|
||||
exports: [PassportModule]
|
||||
})
|
||||
export class AuthenticationModule {}
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { PassportStrategy } from "@nestjs/passport";
|
||||
import { passportJwtSecret } from "jwks-rsa";
|
||||
import { ExtractJwt, Strategy } from "passport-jwt";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
|
||||
@Injectable()
|
||||
export class JwtStrategy extends PassportStrategy(Strategy) {
|
||||
constructor(private readonly config: ConfigService) {
|
||||
super({
|
||||
secretOrKeyProvider: passportJwtSecret({
|
||||
cache: true,
|
||||
rateLimit: true,
|
||||
jwksRequestsPerMinute: 5,
|
||||
jwksUri: `${config.get<string>("AUTH0_DOMAIN")}.well-known/jwks.json`
|
||||
}),
|
||||
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
audience: config.get<string>("AUTH0_AUDIENCE"),
|
||||
issuer: config.get<string>("AUTH0_DOMAIN"),
|
||||
algorithms: ["RS256"]
|
||||
});
|
||||
}
|
||||
|
||||
validate(payload: any) {
|
||||
return payload;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +0,0 @@
|
|||
export enum ConnectionType {
|
||||
SPOTIFY = "spotify"
|
||||
}
|
||||
|
|
@ -1,14 +0,0 @@
|
|||
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
|
||||
import { ConnectionType } from "./connection-type.enum";
|
||||
|
||||
@Entity()
|
||||
export class Connection {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
userID: string;
|
||||
|
||||
@Column()
|
||||
type: ConnectionType;
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { ConnectionsController } from "./connections.controller";
|
||||
|
||||
describe("Connections Controller", () => {
|
||||
let controller: ConnectionsController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [ConnectionsController]
|
||||
}).compile();
|
||||
|
||||
controller = module.get<ConnectionsController>(ConnectionsController);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(controller).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
import { Controller, Get, UseGuards } from "@nestjs/common";
|
||||
import { AuthGuard } from "@nestjs/passport";
|
||||
|
||||
@Controller("api/v1/connections")
|
||||
export class ConnectionsController {
|
||||
@Get()
|
||||
@UseGuards(AuthGuard("jwt"))
|
||||
get() {
|
||||
return { msg: "Success!" };
|
||||
}
|
||||
}
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { ConnectionsController } from "./connections.controller";
|
||||
import { ConnectionsRepository } from "./connections.repository";
|
||||
import { ConnectionsService } from "./connections.service";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([ConnectionsRepository])],
|
||||
controllers: [ConnectionsController],
|
||||
providers: [ConnectionsService]
|
||||
})
|
||||
export class ConnectionsModule {}
|
||||
|
|
@ -1,5 +0,0 @@
|
|||
import { EntityRepository, Repository } from "typeorm";
|
||||
import { Connection } from "./connection.entity";
|
||||
|
||||
@EntityRepository(Connection)
|
||||
export class ConnectionsRepository extends Repository<Connection> {}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import { Injectable } from "@nestjs/common";
|
||||
import { ConnectionsRepository } from "./connections.repository";
|
||||
|
||||
@Injectable()
|
||||
export class ConnectionsService {
|
||||
constructor(private readonly connectionRepository: ConnectionsRepository) {}
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { ConfigModule, ConfigService } from "@nestjs/config";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { join } from "path";
|
||||
|
||||
|
|
@ -17,9 +17,9 @@ export const DatabaseModule = TypeOrmModule.forRootAsync({
|
|||
entities: [join(__dirname, "..", "**/*.entity.{ts,js}")],
|
||||
|
||||
// Migrations
|
||||
migrationsRun: true,
|
||||
migrations: [join(__dirname, "migrations", "*.{ts,js}")],
|
||||
synchronize: false
|
||||
// migrationsRun: true,
|
||||
// migrations: [join(__dirname, "migrations", "*.{ts,js}")],
|
||||
synchronize: true
|
||||
}),
|
||||
inject: [ConfigService]
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,7 +0,0 @@
|
|||
import { Controller, Get } from "@nestjs/common";
|
||||
|
||||
@Controller("")
|
||||
export class FrontendController {
|
||||
@Get()
|
||||
index() {}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { FrontendController } from './frontend.controller';
|
||||
|
||||
@Module({
|
||||
controllers: [FrontendController]
|
||||
})
|
||||
export class FrontendModule {}
|
||||
17
src/main.ts
17
src/main.ts
|
|
@ -1,14 +1,23 @@
|
|||
import { NestFactory } from "@nestjs/core";
|
||||
import { NestExpressApplication } from "@nestjs/platform-express";
|
||||
import { join } from "path";
|
||||
import { DocumentBuilder, SwaggerModule } from "@nestjs/swagger";
|
||||
import { AppModule } from "./app.module";
|
||||
|
||||
async function bootstrap() {
|
||||
const app = await NestFactory.create<NestExpressApplication>(AppModule);
|
||||
|
||||
app.useStaticAssets(join(__dirname, "frontend", "public"));
|
||||
app.setBaseViewsDir(join(__dirname, "frontend", "views"));
|
||||
app.setViewEngine("mustache");
|
||||
// Setup API Docs
|
||||
const options = new DocumentBuilder()
|
||||
.setTitle("Listory")
|
||||
.setDescription("Track and analyze your Spotify Listens")
|
||||
.setVersion("1.0")
|
||||
.addBearerAuth()
|
||||
.addTag("user")
|
||||
.addTag("listens")
|
||||
.addTag("auth")
|
||||
.build();
|
||||
const document = SwaggerModule.createDocument(app, options);
|
||||
SwaggerModule.setup("api/docs", app, document);
|
||||
|
||||
await app.listen(3000);
|
||||
}
|
||||
|
|
|
|||
7
src/sources/sources.module.ts
Normal file
7
src/sources/sources.module.ts
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { SpotifyModule } from './spotify/spotify.module';
|
||||
|
||||
@Module({
|
||||
imports: [SpotifyModule]
|
||||
})
|
||||
export class SourcesModule {}
|
||||
12
src/sources/spotify/spotify-connection.entity.ts
Normal file
12
src/sources/spotify/spotify-connection.entity.ts
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { Column } from "typeorm";
|
||||
|
||||
export class SpotifyConnection {
|
||||
@Column()
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
accessToken: string;
|
||||
|
||||
@Column()
|
||||
refreshToken: string;
|
||||
}
|
||||
9
src/sources/spotify/spotify.module.ts
Normal file
9
src/sources/spotify/spotify.module.ts
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
import { Module } from '@nestjs/common';
|
||||
import { SpotifyService } from './spotify.service';
|
||||
import { SpotifyApiModule } from './spotify-api/spotify-api.module';
|
||||
|
||||
@Module({
|
||||
providers: [SpotifyService],
|
||||
imports: [SpotifyApiModule]
|
||||
})
|
||||
export class SpotifyModule {}
|
||||
18
src/sources/spotify/spotify.service.spec.ts
Normal file
18
src/sources/spotify/spotify.service.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { SpotifyService } from './spotify.service';
|
||||
|
||||
describe('SpotifyService', () => {
|
||||
let service: SpotifyService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [SpotifyService],
|
||||
}).compile();
|
||||
|
||||
service = module.get<SpotifyService>(SpotifyService);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
8
src/sources/spotify/spotify.service.ts
Normal file
8
src/sources/spotify/spotify.service.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
import { Injectable } from '@nestjs/common';
|
||||
import {Interval} from "@nestjs/cron"
|
||||
|
||||
@Injectable()
|
||||
export class SpotifyService {
|
||||
@Interval
|
||||
|
||||
}
|
||||
10
src/users/dto/create-or-update.dto.ts
Normal file
10
src/users/dto/create-or-update.dto.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
export class CreateOrUpdateDto {
|
||||
displayName: string;
|
||||
photo?: string;
|
||||
|
||||
spotify: {
|
||||
id: string;
|
||||
accessToken: string;
|
||||
refreshToken: string;
|
||||
};
|
||||
}
|
||||
17
src/users/user.entity.ts
Normal file
17
src/users/user.entity.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Entity, Column, PrimaryGeneratedColumn, OneToOne } from "typeorm";
|
||||
import { SpotifyConnection } from "../sources/spotify/spotify-connection.entity";
|
||||
|
||||
@Entity()
|
||||
export class User {
|
||||
@PrimaryGeneratedColumn("uuid")
|
||||
id: string;
|
||||
|
||||
@Column()
|
||||
displayName: string;
|
||||
|
||||
@Column({ nullable: true })
|
||||
photo?: string;
|
||||
|
||||
@Column(type => SpotifyConnection)
|
||||
spotify: SpotifyConnection;
|
||||
}
|
||||
5
src/users/user.repository.ts
Normal file
5
src/users/user.repository.ts
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import { EntityRepository, Repository } from "typeorm";
|
||||
import { User } from "./user.entity";
|
||||
|
||||
@EntityRepository(User)
|
||||
export class UserRepository extends Repository<User> {}
|
||||
|
|
@ -1,15 +1,15 @@
|
|||
import { Test, TestingModule } from '@nestjs/testing';
|
||||
import { FrontendController } from './frontend.controller';
|
||||
import { UsersController } from './users.controller';
|
||||
|
||||
describe('Frontend Controller', () => {
|
||||
let controller: FrontendController;
|
||||
describe('Users Controller', () => {
|
||||
let controller: UsersController;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
controllers: [FrontendController],
|
||||
controllers: [UsersController],
|
||||
}).compile();
|
||||
|
||||
controller = module.get<FrontendController>(FrontendController);
|
||||
controller = module.get<UsersController>(UsersController);
|
||||
});
|
||||
|
||||
it('should be defined', () => {
|
||||
17
src/users/users.controller.ts
Normal file
17
src/users/users.controller.ts
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
import { Controller, Get } from "@nestjs/common";
|
||||
import { Auth } from "../auth/decorators/auth.decorator";
|
||||
import { ReqUser } from "../auth/decorators/req-user.decorator";
|
||||
import { User } from "./user.entity";
|
||||
|
||||
@Controller("api/v1/users")
|
||||
export class UsersController {
|
||||
@Get("me")
|
||||
@Auth()
|
||||
getMe(@ReqUser() user: User): Omit<User, "spotify"> {
|
||||
return {
|
||||
id: user.id,
|
||||
displayName: user.displayName,
|
||||
photo: user.photo
|
||||
};
|
||||
}
|
||||
}
|
||||
13
src/users/users.module.ts
Normal file
13
src/users/users.module.ts
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
import { UserRepository } from "./user.repository";
|
||||
import { UsersService } from "./users.service";
|
||||
import { UsersController } from './users.controller';
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([UserRepository])],
|
||||
providers: [UsersService],
|
||||
exports: [UsersService],
|
||||
controllers: [UsersController]
|
||||
})
|
||||
export class UsersModule {}
|
||||
18
src/users/users.service.spec.ts
Normal file
18
src/users/users.service.spec.ts
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import { Test, TestingModule } from "@nestjs/testing";
|
||||
import { UsersService } from "./users.service";
|
||||
|
||||
describe("UsersService", () => {
|
||||
let service: UsersService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const module: TestingModule = await Test.createTestingModule({
|
||||
providers: [UsersService]
|
||||
}).compile();
|
||||
|
||||
service = module.get<UsersService>(UsersService);
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(service).toBeDefined();
|
||||
});
|
||||
});
|
||||
42
src/users/users.service.ts
Normal file
42
src/users/users.service.ts
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
import { Injectable, NotFoundException } from "@nestjs/common";
|
||||
import { CreateOrUpdateDto } from "./dto/create-or-update.dto";
|
||||
import { User } from "./user.entity";
|
||||
import { UserRepository } from "./user.repository";
|
||||
|
||||
@Injectable()
|
||||
export class UsersService {
|
||||
constructor(private readonly userRepository: UserRepository) {}
|
||||
|
||||
async findById(id: string): Promise<User> {
|
||||
const user = await this.userRepository.findOne(id);
|
||||
|
||||
if (!user) {
|
||||
throw new NotFoundException("UserNotFound");
|
||||
}
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
async createOrUpdate(data: CreateOrUpdateDto): Promise<User> {
|
||||
let user = await this.userRepository.findOne({
|
||||
where: { spotify: { id: data.spotify.id } }
|
||||
});
|
||||
|
||||
if (!user) {
|
||||
user = this.userRepository.create({
|
||||
spotify: {
|
||||
id: data.spotify.id
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
user.spotify.accessToken = data.spotify.accessToken;
|
||||
user.spotify.refreshToken = data.spotify.refreshToken;
|
||||
user.displayName = data.displayName;
|
||||
user.photo = data.photo;
|
||||
|
||||
await this.userRepository.save(user);
|
||||
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue