feat: inital workings of service
This commit is contained in:
parent
a71c33c085
commit
64ce205296
29 changed files with 975 additions and 22 deletions
|
@ -10,14 +10,14 @@
|
||||||
"service": "waterwolf-auth",
|
"service": "waterwolf-auth",
|
||||||
// The optional 'workspaceFolder' property is the path VS Code should open by default when
|
// The optional 'workspaceFolder' property is the path VS Code should open by default when
|
||||||
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
|
// connected. This is typically a file mount in .devcontainer/docker-compose.yml
|
||||||
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
|
"workspaceFolder": "/app/",
|
||||||
"features": {
|
"features": {
|
||||||
"ghcr.io/cirolosapio/devcontainers-features/alpine-git:0": {}
|
"ghcr.io/cirolosapio/devcontainers-features/alpine-git:0": {}
|
||||||
},
|
},
|
||||||
// Features to add to the dev container. More info: https://containers.dev/features.
|
// Features to add to the dev container. More info: https://containers.dev/features.
|
||||||
// "features": {},
|
// "features": {},
|
||||||
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
// Use 'forwardPorts' to make a list of ports inside the container available locally.
|
||||||
"forwardPorts": [3000, "mysql:3306"],
|
"forwardPorts": [3000, "mysql:3306", "redis:6379"],
|
||||||
|
|
||||||
"portsAttributes": {
|
"portsAttributes": {
|
||||||
"3000": {
|
"3000": {
|
||||||
|
|
|
@ -13,7 +13,7 @@ services:
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
# Update this to wherever you want VS Code to mount the folder of your project
|
# Update this to wherever you want VS Code to mount the folder of your project
|
||||||
- ..:/workspaces:cached
|
- ..:/app:cached
|
||||||
|
|
||||||
# Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
|
# Uncomment the next four lines if you will use a ptrace-based debugger like C++, Go, and Rust.
|
||||||
# cap_add:
|
# cap_add:
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
['THIS IS A SECURE THING']
|
["secure_token_1"]
|
|
@ -0,0 +1,17 @@
|
||||||
|
{
|
||||||
|
"keys": [
|
||||||
|
{
|
||||||
|
"p": "xA4KcjSuyAXWzjyG48B_s1Svo-srjm3HbzgNzXz3vyvpkZU3XF-z0TR_Vfoct5LBO8M6Rj4OXyo0vho3v1DrGgkC_V5hMfGvov4C9S31_i5Pl0gjSC_OfScqtTGftJSrA2a9ob21IhAYrE4xtCTf2ETuKaiMY5IZ-HFKuinNI7E",
|
||||||
|
"kty": "RSA",
|
||||||
|
"q": "s0sQRM3OTslJPCAXvgYpWrg1reqaKy7RBuidH90ovHWiQOySwtTcsawek5o3tD6wF-3AUZ6iu_i9Jy4T99Ocz-aOR421GHKTU2SmLmPDpM4d7WvZXvCbddF4k1ihf5OkYYV4HSC10_6SVnSLUxf34YSlfdrRlT3DVFfBg_m9aYc",
|
||||||
|
"d": "ZG1BW0fs-Wu-q9YYM7cdX7v6XixMaFEYDd7Qv-wLK1-sns7Jl0Xd6P8cKEneneRk7oBdEpjCkHiBxsrWydn4JfDZOL4ItD7lTm6q0jq_n6W2SKYu7_0Vxmm3ohuxYuQSoTofJ4G4T_yfAU3-vpF0a-jv9izl8YXZ7H3JZJawKH9TgkpYXQXmjzzro87-JtfsVBlnH_9vhUQrsBS3Eb61voUUH58swTfD0JxnfeogRD8Y0HxuwghhO_xAcE_ZMkOV0W-b4NY6jnM1MFTeP5itHAEEo5SLwXel4eRv5r9GV4XD_2eMYnzvoeA8BVOMWYQgYRanmfVvGbLZYTOmqLxNoQ",
|
||||||
|
"e": "AQAB",
|
||||||
|
"use": "sig",
|
||||||
|
"qi": "Gk6c0VY9i3ka6XNN0PcLEpgM0nIMgfOjYyjl6u0yPSA8-gbLJEKsH1xL4iJj31102VszFpGT5kCCIXUIgcAvetB0A0xxucdAO7CDMEHJoPyNYvd4i5erHuO7Kj2i6I67qmcZGHPzK-TbE4xekVSb8E5lz38J7VTwElleQ5wa08Q",
|
||||||
|
"dp": "biaJXfMVhBIrxsGg89MSrFnXONyHI0WweF9g-ePNeh4c44uXiBHJALBjHpYgjk8ovAAK_K4e-v7GlUw7qAS5om4PvPTK3PmyOXxHgyMog3_XfeKs2ADsHcrkptrTpOymTInr3zSr0RCEHELukAzrqyHHQaaOAd9zMe_NEV0tAXE",
|
||||||
|
"alg": "RS256",
|
||||||
|
"dq": "szd0IqJp950CZGRb9yknizQZDCg2RLX-YN6BuMkToBYhwq33IWMu2zaGNdpwle4XjUOs-qkMV8KSKKjJcu8Gj1YRoHqIq9BTbYdtCW_Vr1YM2jb0yA7QBpwE35w3ilOle4mzf8Ijnq2Xz22dmsiZkcZKuhvRZVGgfx1dJTOs3t8",
|
||||||
|
"n": "iU9N4HxKzLHkNwkbMxozz2fyxC5vl_2m67nFnlI7i_L9ZBPvF2UlEzShAsheriLu4zb4b-5soKVc4L5gII-98dSz_jkO2q6I2UjUDjGKVXht1zAtUqYY5WUTHR6l6Mv2Tt24Mksk2DM5NGzYdFhEZAeP1rWphjJnRXSVXMvvx_IxC5OiQeP6JrH-LZIYzidlwcVUXQaFspkTTDPdovaf29x-NNXxbRrX2fKG7nFehCcZ2S4o6j9annrcxwzbZuOYHtHXTg8euUDs-PNmfeZVB7rGvAPeuslB4QGs45zERa9e9_fKk3YpU-6WVGU5VwaYjuRyuQVUx_L6htwOsX5rVw"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
10
package.json
10
package.json
|
@ -32,21 +32,26 @@
|
||||||
"@nestjs/config": "^3.2.3",
|
"@nestjs/config": "^3.2.3",
|
||||||
"@nestjs/core": "^10.3.10",
|
"@nestjs/core": "^10.3.10",
|
||||||
"@nestjs/platform-express": "^10.3.10",
|
"@nestjs/platform-express": "^10.3.10",
|
||||||
|
"@nestjs/terminus": "^10.2.3",
|
||||||
"@nestjs/typeorm": "^10.0.2",
|
"@nestjs/typeorm": "^10.0.2",
|
||||||
|
"@opentelemetry/api": "^1.9.0",
|
||||||
"bullmq": "^5.9.0",
|
"bullmq": "^5.9.0",
|
||||||
"class-transformer": "^0.5.1",
|
"class-transformer": "^0.5.1",
|
||||||
"class-validator": "^0.14.1",
|
"class-validator": "^0.14.1",
|
||||||
"dotenv": "^16.4.5",
|
"dotenv": "^16.4.5",
|
||||||
|
"hbs": "^4.2.0",
|
||||||
"ioredis": "^5.4.1",
|
"ioredis": "^5.4.1",
|
||||||
"mysql2": "^3.10.2",
|
"mysql2": "^3.10.2",
|
||||||
"nanoid": "^5.0.7",
|
"nanoid": "^5.0.7",
|
||||||
|
"nestjs-otel": "^6.1.1",
|
||||||
"nestjs-postal-client": "^0.0.6",
|
"nestjs-postal-client": "^0.0.6",
|
||||||
"oidc-provider": "^8.5.1",
|
"oidc-provider": "^8.5.1",
|
||||||
"pug": "^3.0.3",
|
"pug": "^3.0.3",
|
||||||
"reflect-metadata": "^0.2.2",
|
"reflect-metadata": "^0.2.2",
|
||||||
"rxjs": "^7.8.1",
|
"rxjs": "^7.8.1",
|
||||||
"typeorm": "^0.3.20",
|
"typeorm": "^0.3.20",
|
||||||
"typia": "^6.5.1"
|
"typia": "^6.5.1",
|
||||||
|
"uuid": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/cli": "^10.4.2",
|
"@nestjs/cli": "^10.4.2",
|
||||||
|
@ -90,5 +95,6 @@
|
||||||
],
|
],
|
||||||
"coverageDirectory": "../coverage",
|
"coverageDirectory": "../coverage",
|
||||||
"testEnvironment": "node"
|
"testEnvironment": "node"
|
||||||
}
|
},
|
||||||
|
"packageManager": "pnpm@9.5.0+sha512.140036830124618d624a2187b50d04289d5a087f326c9edfc0ccd733d76c4f52c3a313d4fc148794a2a9d81553016004e6742e8cf850670268a7387fc220c903"
|
||||||
}
|
}
|
171
pnpm-lock.yaml
171
pnpm-lock.yaml
|
@ -26,9 +26,15 @@ importers:
|
||||||
'@nestjs/platform-express':
|
'@nestjs/platform-express':
|
||||||
specifier: ^10.3.10
|
specifier: ^10.3.10
|
||||||
version: 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10)
|
version: 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10)
|
||||||
|
'@nestjs/terminus':
|
||||||
|
specifier: ^10.2.3
|
||||||
|
version: 10.2.3(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/typeorm@10.0.2(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))
|
||||||
'@nestjs/typeorm':
|
'@nestjs/typeorm':
|
||||||
specifier: ^10.0.2
|
specifier: ^10.0.2
|
||||||
version: 10.0.2(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))
|
version: 10.0.2(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))
|
||||||
|
'@opentelemetry/api':
|
||||||
|
specifier: ^1.9.0
|
||||||
|
version: 1.9.0
|
||||||
bullmq:
|
bullmq:
|
||||||
specifier: ^5.9.0
|
specifier: ^5.9.0
|
||||||
version: 5.9.0
|
version: 5.9.0
|
||||||
|
@ -41,6 +47,9 @@ importers:
|
||||||
dotenv:
|
dotenv:
|
||||||
specifier: ^16.4.5
|
specifier: ^16.4.5
|
||||||
version: 16.4.5
|
version: 16.4.5
|
||||||
|
hbs:
|
||||||
|
specifier: ^4.2.0
|
||||||
|
version: 4.2.0
|
||||||
ioredis:
|
ioredis:
|
||||||
specifier: ^5.4.1
|
specifier: ^5.4.1
|
||||||
version: 5.4.1
|
version: 5.4.1
|
||||||
|
@ -50,6 +59,9 @@ importers:
|
||||||
nanoid:
|
nanoid:
|
||||||
specifier: ^5.0.7
|
specifier: ^5.0.7
|
||||||
version: 5.0.7
|
version: 5.0.7
|
||||||
|
nestjs-otel:
|
||||||
|
specifier: ^6.1.1
|
||||||
|
version: 6.1.1(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))
|
||||||
nestjs-postal-client:
|
nestjs-postal-client:
|
||||||
specifier: ^0.0.6
|
specifier: ^0.0.6
|
||||||
version: 0.0.6(rtrsvjp22aq2opwvwwi7bofxyi)
|
version: 0.0.6(rtrsvjp22aq2opwvwwi7bofxyi)
|
||||||
|
@ -71,6 +83,9 @@ importers:
|
||||||
typia:
|
typia:
|
||||||
specifier: ^6.5.1
|
specifier: ^6.5.1
|
||||||
version: 6.5.1(typescript@5.5.3)
|
version: 6.5.1(typescript@5.5.3)
|
||||||
|
uuid:
|
||||||
|
specifier: ^10.0.0
|
||||||
|
version: 10.0.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@nestjs/cli':
|
'@nestjs/cli':
|
||||||
specifier: ^10.4.2
|
specifier: ^10.4.2
|
||||||
|
@ -659,6 +674,54 @@ packages:
|
||||||
class-validator:
|
class-validator:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
'@nestjs/terminus@10.2.3':
|
||||||
|
resolution: {integrity: sha512-iX7gXtAooePcyQqFt57aDke5MzgdkBeYgF5YsFNNFwOiAFdIQEhfv3PR0G+HlH9F6D7nBCDZt9U87Pks/qHijg==}
|
||||||
|
peerDependencies:
|
||||||
|
'@grpc/grpc-js': '*'
|
||||||
|
'@grpc/proto-loader': '*'
|
||||||
|
'@mikro-orm/core': '*'
|
||||||
|
'@mikro-orm/nestjs': '*'
|
||||||
|
'@nestjs/axios': ^1.0.0 || ^2.0.0 || ^3.0.0
|
||||||
|
'@nestjs/common': ^9.0.0 || ^10.0.0
|
||||||
|
'@nestjs/core': ^9.0.0 || ^10.0.0
|
||||||
|
'@nestjs/microservices': ^9.0.0 || ^10.0.0
|
||||||
|
'@nestjs/mongoose': ^9.0.0 || ^10.0.0
|
||||||
|
'@nestjs/sequelize': ^9.0.0 || ^10.0.0
|
||||||
|
'@nestjs/typeorm': ^9.0.0 || ^10.0.0
|
||||||
|
'@prisma/client': '*'
|
||||||
|
mongoose: '*'
|
||||||
|
reflect-metadata: 0.1.x || 0.2.x
|
||||||
|
rxjs: 7.x
|
||||||
|
sequelize: '*'
|
||||||
|
typeorm: '*'
|
||||||
|
peerDependenciesMeta:
|
||||||
|
'@grpc/grpc-js':
|
||||||
|
optional: true
|
||||||
|
'@grpc/proto-loader':
|
||||||
|
optional: true
|
||||||
|
'@mikro-orm/core':
|
||||||
|
optional: true
|
||||||
|
'@mikro-orm/nestjs':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/axios':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/microservices':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/mongoose':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/sequelize':
|
||||||
|
optional: true
|
||||||
|
'@nestjs/typeorm':
|
||||||
|
optional: true
|
||||||
|
'@prisma/client':
|
||||||
|
optional: true
|
||||||
|
mongoose:
|
||||||
|
optional: true
|
||||||
|
sequelize:
|
||||||
|
optional: true
|
||||||
|
typeorm:
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@nestjs/testing@10.3.10':
|
'@nestjs/testing@10.3.10':
|
||||||
resolution: {integrity: sha512-i3HAtVQJijxNxJq1k39aelyJlyEIBRONys7IipH/4r8W0J+M1V+y5EKDOyi4j1SdNSb/vmNyWpZ2/ewZjl3kRA==}
|
resolution: {integrity: sha512-i3HAtVQJijxNxJq1k39aelyJlyEIBRONys7IipH/4r8W0J+M1V+y5EKDOyi4j1SdNSb/vmNyWpZ2/ewZjl3kRA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
@ -1056,6 +1119,9 @@ packages:
|
||||||
ajv@8.17.1:
|
ajv@8.17.1:
|
||||||
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
resolution: {integrity: sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==}
|
||||||
|
|
||||||
|
ansi-align@3.0.1:
|
||||||
|
resolution: {integrity: sha512-IOfwwBF5iczOjp/WeY4YxyjqAFMQoZufdQWDd19SEExbVLNXqvpzSJ/M7Za4/sCPmQ0+GRquoA7bGcINcxew6w==}
|
||||||
|
|
||||||
ansi-colors@4.1.3:
|
ansi-colors@4.1.3:
|
||||||
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
@ -1204,6 +1270,10 @@ packages:
|
||||||
resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
|
resolution: {integrity: sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA==}
|
||||||
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||||
|
|
||||||
|
boxen@5.1.2:
|
||||||
|
resolution: {integrity: sha512-9gYgQKXx+1nP8mP7CzFyaUARhg7D3n1dF/FnErWmu9l6JvGpNUN278h0aSb+QjoiKSWG+iZ3uHrcqk0qrY9RQQ==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
|
||||||
brace-expansion@1.1.11:
|
brace-expansion@1.1.11:
|
||||||
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==}
|
||||||
|
|
||||||
|
@ -1307,6 +1377,10 @@ packages:
|
||||||
chardet@0.7.0:
|
chardet@0.7.0:
|
||||||
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==}
|
||||||
|
|
||||||
|
check-disk-space@3.4.0:
|
||||||
|
resolution: {integrity: sha512-drVkSqfwA+TvuEhFipiR1OC9boEGZL5RrWvVsOthdcvQNXyCCuKkEiTOTXZ7qxSf/GLwq4GvzfrQD/Wz325hgw==}
|
||||||
|
engines: {node: '>=16'}
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==}
|
||||||
engines: {node: '>= 8.10.0'}
|
engines: {node: '>= 8.10.0'}
|
||||||
|
@ -1328,6 +1402,10 @@ packages:
|
||||||
class-validator@0.14.1:
|
class-validator@0.14.1:
|
||||||
resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==}
|
resolution: {integrity: sha512-2VEG9JICxIqTpoK1eMzZqaV+u/EiwEJkMGzTrZf6sU/fwsnOITVgYJ8yojSy6CaXtO9V0Cc6ZQZ8h8m4UBuLwQ==}
|
||||||
|
|
||||||
|
cli-boxes@2.2.1:
|
||||||
|
resolution: {integrity: sha512-y4coMcylgSCdVinjiDBuR8PCC2bLjyGTwEmPb9NHR/QaNU6EUOXcTY/s6VjGMD6ENSEaeQYHCY0GNGS5jfMwPw==}
|
||||||
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
cli-color@2.0.4:
|
cli-color@2.0.4:
|
||||||
resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==}
|
resolution: {integrity: sha512-zlnpg0jNcibNrO7GG9IeHH7maWFeCz+Ja1wx/7tZNU5ASSSSZ+/qZciM0/LHCYxSdqv5h2sdbQ/PXYdOuetXvA==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
|
@ -1920,6 +1998,9 @@ packages:
|
||||||
debug:
|
debug:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
|
foreachasync@3.0.0:
|
||||||
|
resolution: {integrity: sha512-J+ler7Ta54FwwNcx6wQRDhTIbNeyDcARMkOcguEqnEdtm0jKvN3Li3PDAb2Du3ubJYEWfYL83XMROXdsXAXycw==}
|
||||||
|
|
||||||
foreground-child@3.2.1:
|
foreground-child@3.2.1:
|
||||||
resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==}
|
resolution: {integrity: sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==}
|
||||||
engines: {node: '>=14'}
|
engines: {node: '>=14'}
|
||||||
|
@ -2044,6 +2125,11 @@ packages:
|
||||||
graphemer@1.4.0:
|
graphemer@1.4.0:
|
||||||
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
|
||||||
|
|
||||||
|
handlebars@4.7.7:
|
||||||
|
resolution: {integrity: sha512-aAcXm5OAfE/8IXkcZvCepKU3VzW1/39Fb5ZuqMtgI/hT8X2YgoMvBY5dLhq/cpOvw7Lk1nK/UF71aLG/ZnVYRA==}
|
||||||
|
engines: {node: '>=0.4.7'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
has-flag@3.0.0:
|
has-flag@3.0.0:
|
||||||
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
@ -2075,6 +2161,10 @@ packages:
|
||||||
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==}
|
||||||
engines: {node: '>= 0.4'}
|
engines: {node: '>= 0.4'}
|
||||||
|
|
||||||
|
hbs@4.2.0:
|
||||||
|
resolution: {integrity: sha512-dQwHnrfWlTk5PvG9+a45GYpg0VpX47ryKF8dULVd6DtwOE6TEcYQXQ5QM6nyOx/h7v3bvEQbdn19EDAcfUAgZg==}
|
||||||
|
engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
|
||||||
|
|
||||||
hexoid@1.0.0:
|
hexoid@1.0.0:
|
||||||
resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==}
|
resolution: {integrity: sha512-QFLV0taWQOZtvIRIAdBChesmogZrtuXvVWsFHZTk2SU+anspqZ2vMnoLg7IE1+Uk16N19APic1BuF8bC8c2m5g==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -3788,6 +3878,11 @@ packages:
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
typescript: '>=4.8.0 <5.6.0'
|
typescript: '>=4.8.0 <5.6.0'
|
||||||
|
|
||||||
|
uglify-js@3.18.0:
|
||||||
|
resolution: {integrity: sha512-SyVVbcNBCk0dzr9XL/R/ySrmYf0s372K6/hFklzgcp2lBFyXtw4I7BOdDjlLhE1aVqaI/SHWXWmYdlZxuyF38A==}
|
||||||
|
engines: {node: '>=0.8.0'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
uid@2.0.2:
|
uid@2.0.2:
|
||||||
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
|
resolution: {integrity: sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -3823,6 +3918,10 @@ packages:
|
||||||
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==}
|
||||||
engines: {node: '>= 0.4.0'}
|
engines: {node: '>= 0.4.0'}
|
||||||
|
|
||||||
|
uuid@10.0.0:
|
||||||
|
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
uuid@9.0.1:
|
uuid@9.0.1:
|
||||||
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
@ -3846,6 +3945,9 @@ packages:
|
||||||
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
walk@2.3.15:
|
||||||
|
resolution: {integrity: sha512-4eRTBZljBfIISK1Vnt69Gvr2w/wc3U6Vtrw7qiN5iqYJPH7LElcYh/iU4XWhdCy2dZqv1ToMyYlybDylfG/5Vg==}
|
||||||
|
|
||||||
walker@1.0.8:
|
walker@1.0.8:
|
||||||
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
|
resolution: {integrity: sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==}
|
||||||
|
|
||||||
|
@ -3889,6 +3991,10 @@ packages:
|
||||||
engines: {node: '>= 8'}
|
engines: {node: '>= 8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
widest-line@3.1.0:
|
||||||
|
resolution: {integrity: sha512-NsmoXalsWVDMGupxZ5R08ka9flZjjiLvHVAWYOKtiKM8ujtZWr9cRffak+uSE48+Ob8ObalXpwyeUiyDD6QFgg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
|
||||||
with@7.0.2:
|
with@7.0.2:
|
||||||
resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
|
resolution: {integrity: sha512-RNGKj82nUPg3g5ygxkQl0R937xLyho1J24ItRCBTr/m1YnZkzJy1hUiHUJrc/VlsDQzsCnInEGSg3bci0Lmd4w==}
|
||||||
engines: {node: '>= 10.0.0'}
|
engines: {node: '>= 10.0.0'}
|
||||||
|
@ -3897,6 +4003,9 @@ packages:
|
||||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
wordwrap@1.0.0:
|
||||||
|
resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==}
|
||||||
|
|
||||||
wrap-ansi@6.2.0:
|
wrap-ansi@6.2.0:
|
||||||
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
|
@ -4660,6 +4769,18 @@ snapshots:
|
||||||
class-transformer: 0.5.1
|
class-transformer: 0.5.1
|
||||||
class-validator: 0.14.1
|
class-validator: 0.14.1
|
||||||
|
|
||||||
|
'@nestjs/terminus@10.2.3(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/typeorm@10.0.2(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))':
|
||||||
|
dependencies:
|
||||||
|
'@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||||
|
'@nestjs/core': 10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||||
|
boxen: 5.1.2
|
||||||
|
check-disk-space: 3.4.0
|
||||||
|
reflect-metadata: 0.2.2
|
||||||
|
rxjs: 7.8.1
|
||||||
|
optionalDependencies:
|
||||||
|
'@nestjs/typeorm': 10.0.2(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(reflect-metadata@0.2.2)(rxjs@7.8.1)(typeorm@0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3)))
|
||||||
|
typeorm: 0.3.20(ioredis@5.4.1)(mysql2@3.10.2)(ts-node@10.9.2(@types/node@20.14.10)(typescript@5.5.3))
|
||||||
|
|
||||||
'@nestjs/testing@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10))':
|
'@nestjs/testing@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/platform-express@10.3.10(@nestjs/common@10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1))(@nestjs/core@10.3.10))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
'@nestjs/common': 10.3.10(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
|
||||||
|
@ -5116,6 +5237,10 @@ snapshots:
|
||||||
json-schema-traverse: 1.0.0
|
json-schema-traverse: 1.0.0
|
||||||
require-from-string: 2.0.2
|
require-from-string: 2.0.2
|
||||||
|
|
||||||
|
ansi-align@3.0.1:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.3
|
||||||
|
|
||||||
ansi-colors@4.1.3: {}
|
ansi-colors@4.1.3: {}
|
||||||
|
|
||||||
ansi-escapes@4.3.2:
|
ansi-escapes@4.3.2:
|
||||||
|
@ -5282,6 +5407,17 @@ snapshots:
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
|
boxen@5.1.2:
|
||||||
|
dependencies:
|
||||||
|
ansi-align: 3.0.1
|
||||||
|
camelcase: 6.3.0
|
||||||
|
chalk: 4.1.2
|
||||||
|
cli-boxes: 2.2.1
|
||||||
|
string-width: 4.2.3
|
||||||
|
type-fest: 0.20.2
|
||||||
|
widest-line: 3.1.0
|
||||||
|
wrap-ansi: 7.0.0
|
||||||
|
|
||||||
brace-expansion@1.1.11:
|
brace-expansion@1.1.11:
|
||||||
dependencies:
|
dependencies:
|
||||||
balanced-match: 1.0.2
|
balanced-match: 1.0.2
|
||||||
|
@ -5403,6 +5539,8 @@ snapshots:
|
||||||
|
|
||||||
chardet@0.7.0: {}
|
chardet@0.7.0: {}
|
||||||
|
|
||||||
|
check-disk-space@3.4.0: {}
|
||||||
|
|
||||||
chokidar@3.6.0:
|
chokidar@3.6.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
anymatch: 3.1.3
|
anymatch: 3.1.3
|
||||||
|
@ -5429,6 +5567,8 @@ snapshots:
|
||||||
libphonenumber-js: 1.11.4
|
libphonenumber-js: 1.11.4
|
||||||
validator: 13.12.0
|
validator: 13.12.0
|
||||||
|
|
||||||
|
cli-boxes@2.2.1: {}
|
||||||
|
|
||||||
cli-color@2.0.4:
|
cli-color@2.0.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
d: 1.0.2
|
d: 1.0.2
|
||||||
|
@ -6073,6 +6213,8 @@ snapshots:
|
||||||
|
|
||||||
follow-redirects@1.15.6: {}
|
follow-redirects@1.15.6: {}
|
||||||
|
|
||||||
|
foreachasync@3.0.0: {}
|
||||||
|
|
||||||
foreground-child@3.2.1:
|
foreground-child@3.2.1:
|
||||||
dependencies:
|
dependencies:
|
||||||
cross-spawn: 7.0.3
|
cross-spawn: 7.0.3
|
||||||
|
@ -6226,6 +6368,15 @@ snapshots:
|
||||||
|
|
||||||
graphemer@1.4.0: {}
|
graphemer@1.4.0: {}
|
||||||
|
|
||||||
|
handlebars@4.7.7:
|
||||||
|
dependencies:
|
||||||
|
minimist: 1.2.8
|
||||||
|
neo-async: 2.6.2
|
||||||
|
source-map: 0.6.1
|
||||||
|
wordwrap: 1.0.0
|
||||||
|
optionalDependencies:
|
||||||
|
uglify-js: 3.18.0
|
||||||
|
|
||||||
has-flag@3.0.0: {}
|
has-flag@3.0.0: {}
|
||||||
|
|
||||||
has-flag@4.0.0: {}
|
has-flag@4.0.0: {}
|
||||||
|
@ -6248,6 +6399,11 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.2
|
function-bind: 1.1.2
|
||||||
|
|
||||||
|
hbs@4.2.0:
|
||||||
|
dependencies:
|
||||||
|
handlebars: 4.7.7
|
||||||
|
walk: 2.3.15
|
||||||
|
|
||||||
hexoid@1.0.0: {}
|
hexoid@1.0.0: {}
|
||||||
|
|
||||||
highlight.js@10.7.3: {}
|
highlight.js@10.7.3: {}
|
||||||
|
@ -8123,6 +8279,9 @@ snapshots:
|
||||||
randexp: 0.5.3
|
randexp: 0.5.3
|
||||||
typescript: 5.5.3
|
typescript: 5.5.3
|
||||||
|
|
||||||
|
uglify-js@3.18.0:
|
||||||
|
optional: true
|
||||||
|
|
||||||
uid@2.0.2:
|
uid@2.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@lukeed/csprng': 1.1.0
|
'@lukeed/csprng': 1.1.0
|
||||||
|
@ -8151,6 +8310,8 @@ snapshots:
|
||||||
|
|
||||||
utils-merge@1.0.1: {}
|
utils-merge@1.0.1: {}
|
||||||
|
|
||||||
|
uuid@10.0.0: {}
|
||||||
|
|
||||||
uuid@9.0.1: {}
|
uuid@9.0.1: {}
|
||||||
|
|
||||||
v8-compile-cache-lib@3.0.1: {}
|
v8-compile-cache-lib@3.0.1: {}
|
||||||
|
@ -8167,6 +8328,10 @@ snapshots:
|
||||||
|
|
||||||
void-elements@3.1.0: {}
|
void-elements@3.1.0: {}
|
||||||
|
|
||||||
|
walk@2.3.15:
|
||||||
|
dependencies:
|
||||||
|
foreachasync: 3.0.0
|
||||||
|
|
||||||
walker@1.0.8:
|
walker@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
makeerror: 1.0.12
|
makeerror: 1.0.12
|
||||||
|
@ -8230,6 +8395,10 @@ snapshots:
|
||||||
dependencies:
|
dependencies:
|
||||||
isexe: 2.0.0
|
isexe: 2.0.0
|
||||||
|
|
||||||
|
widest-line@3.1.0:
|
||||||
|
dependencies:
|
||||||
|
string-width: 4.2.3
|
||||||
|
|
||||||
with@7.0.2:
|
with@7.0.2:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/parser': 7.24.8
|
'@babel/parser': 7.24.8
|
||||||
|
@ -8239,6 +8408,8 @@ snapshots:
|
||||||
|
|
||||||
word-wrap@1.2.5: {}
|
word-wrap@1.2.5: {}
|
||||||
|
|
||||||
|
wordwrap@1.0.0: {}
|
||||||
|
|
||||||
wrap-ansi@6.2.0:
|
wrap-ansi@6.2.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
ansi-styles: 4.3.0
|
ansi-styles: 4.3.0
|
||||||
|
|
2
src/app.const.ts
Normal file
2
src/app.const.ts
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
// This is the internal client ID for the application itself.
|
||||||
|
export const internalClientId = 'internal.management';
|
|
@ -1,4 +1,4 @@
|
||||||
import { Controller, Get } from '@nestjs/common';
|
import { Controller, Get, Render } from '@nestjs/common';
|
||||||
import { AppService } from './app.service';
|
import { AppService } from './app.service';
|
||||||
import { PostalClientService } from 'nestjs-postal-client';
|
import { PostalClientService } from 'nestjs-postal-client';
|
||||||
|
|
||||||
|
@ -10,8 +10,9 @@ export class AppController {
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
@Get()
|
@Get()
|
||||||
getHello(): string {
|
@Render('home/index')
|
||||||
return this.appService.getHello();
|
home() {
|
||||||
|
return { message: 'Hello world!'}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Get('email_test')
|
@Get('email_test')
|
||||||
|
|
|
@ -4,15 +4,37 @@ import { AppService } from './app.service';
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import config from './config/config';
|
import config from './config/config';
|
||||||
import { MailModule } from './mail/mail.module';
|
import { MailModule } from './mail/mail.module';
|
||||||
|
import { RedisModule } from './redis/redis.module';
|
||||||
|
import { OpenTelemetryModule } from 'nestjs-otel';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
|
OpenTelemetryModule.forRoot({
|
||||||
|
metrics: {
|
||||||
|
apiMetrics: {
|
||||||
|
enable: true,
|
||||||
|
ignoreRoutes: [
|
||||||
|
'/favicon.ico',
|
||||||
|
'/OidcServiceWorker.js',
|
||||||
|
'/swagger',
|
||||||
|
'/swagger-json',
|
||||||
|
'/swagger-yaml',
|
||||||
|
'/swagger/(.*)',
|
||||||
|
'/metrics',
|
||||||
|
'/interaction/(.*}',
|
||||||
|
],
|
||||||
|
ignoreUndefinedRoutes: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
ConfigModule.forRoot({
|
ConfigModule.forRoot({
|
||||||
cache: true,
|
cache: true,
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
load: [config],
|
load: [config],
|
||||||
}),
|
}),
|
||||||
MailModule,
|
MailModule,
|
||||||
|
RedisModule,
|
||||||
|
|
||||||
],
|
],
|
||||||
controllers: [AppController],
|
controllers: [AppController],
|
||||||
providers: [AppService],
|
providers: [AppService],
|
||||||
|
|
|
@ -3,4 +3,10 @@ export default async () => ({
|
||||||
port: process.env.PORT || 3000,
|
port: process.env.PORT || 3000,
|
||||||
host: process.env.HOST || 'localhost',
|
host: process.env.HOST || 'localhost',
|
||||||
},
|
},
|
||||||
|
redis: {
|
||||||
|
host: process.env['REDIS_HOST'] ?? 'localhost',
|
||||||
|
port: process.env['REDIS_POST'] ?? 6379,
|
||||||
|
password: process.env['REDIS_PASSWORD'] ?? '',
|
||||||
|
db: process.env['REDIS_DB'] ?? 0
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
32
src/database/models/api_keys.model.ts
Normal file
32
src/database/models/api_keys.model.ts
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import {
|
||||||
|
BaseEntity,
|
||||||
|
Column,
|
||||||
|
CreateDateColumn,
|
||||||
|
Entity,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryColumn,
|
||||||
|
UpdateDateColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import { User } from './user.model';
|
||||||
|
|
||||||
|
@Entity('api_keys')
|
||||||
|
export class ApiKey extends BaseEntity {
|
||||||
|
@PrimaryColumn()
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@PrimaryColumn()
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ManyToOne(() => User, (user) => user.apiKeys)
|
||||||
|
user: User;
|
||||||
|
|
||||||
|
@CreateDateColumn()
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdateDateColumn()
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
87
src/database/models/oidc_client.model.ts
Normal file
87
src/database/models/oidc_client.model.ts
Normal file
|
@ -0,0 +1,87 @@
|
||||||
|
import type {
|
||||||
|
ClientAuthMethod,
|
||||||
|
ResponseType,
|
||||||
|
SigningAlgorithmWithNone,
|
||||||
|
} from 'oidc-provider';
|
||||||
|
|
||||||
|
import {
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
JoinTable,
|
||||||
|
ManyToMany,
|
||||||
|
OneToMany,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import { OidcClientPermission } from './oidc_client_permissions.model';
|
||||||
|
import { MAX_STRING_LENGTH } from '../database.const';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class OidcClient {
|
||||||
|
// Client ID
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
client_id: string;
|
||||||
|
|
||||||
|
// Owner Org ID
|
||||||
|
@Column({ type: 'varchar', length: MAX_STRING_LENGTH, nullable: false })
|
||||||
|
ownerName: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: MAX_STRING_LENGTH, nullable: false })
|
||||||
|
client_secret: string;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
client_name: string;
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: false })
|
||||||
|
redirect_uris: string[];
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: true })
|
||||||
|
client_cors: string[];
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: true })
|
||||||
|
allowed_introspection_targets: string[];
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: false })
|
||||||
|
include_permissions_from_client: string[];
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: false })
|
||||||
|
post_logout_redirect_uris: string[];
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: false })
|
||||||
|
response_types: ResponseType[];
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: false })
|
||||||
|
grant_types: string[];
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: MAX_STRING_LENGTH, nullable: false })
|
||||||
|
token_endpoint_auth_method: ClientAuthMethod;
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: MAX_STRING_LENGTH, nullable: false })
|
||||||
|
application_type: 'web' | 'native';
|
||||||
|
|
||||||
|
@Column({ type: 'varchar', length: MAX_STRING_LENGTH, nullable: false })
|
||||||
|
logo_uri: string;
|
||||||
|
|
||||||
|
@Column({ type: 'boolean', nullable: false, default: false })
|
||||||
|
restricted: boolean;
|
||||||
|
|
||||||
|
@OneToMany(() => OidcClientPermission, (permission) => permission.client)
|
||||||
|
permissions: OidcClientPermission[];
|
||||||
|
|
||||||
|
@ManyToMany(
|
||||||
|
() => OidcClientPermission,
|
||||||
|
(permission) => permission.assignedClients,
|
||||||
|
)
|
||||||
|
@JoinTable({
|
||||||
|
name: 'oidc_client_permissions',
|
||||||
|
joinColumn: {
|
||||||
|
name: 'client_id',
|
||||||
|
},
|
||||||
|
inverseJoinColumn: {
|
||||||
|
name: 'permission_id',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
assignedPermissions: OidcClientPermission[];
|
||||||
|
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
28
src/database/models/oidc_client_permissions.model.ts
Normal file
28
src/database/models/oidc_client_permissions.model.ts
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
import {
|
||||||
|
BeforeInsert,
|
||||||
|
Column,
|
||||||
|
Entity,
|
||||||
|
ManyToMany,
|
||||||
|
ManyToOne,
|
||||||
|
PrimaryColumn,
|
||||||
|
PrimaryGeneratedColumn,
|
||||||
|
Unique,
|
||||||
|
} from 'typeorm';
|
||||||
|
|
||||||
|
import { OidcClient } from './oidc_client.model';
|
||||||
|
|
||||||
|
@Unique(['title', 'client'])
|
||||||
|
@Entity('oidc_client_permission')
|
||||||
|
export class OidcClientPermission {
|
||||||
|
@PrimaryGeneratedColumn('uuid')
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Column()
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
@ManyToMany(() => OidcClient, (oidcClient) => oidcClient.assignedPermissions)
|
||||||
|
assignedClients: OidcClient[];
|
||||||
|
|
||||||
|
@ManyToOne(() => OidcClient, (oidcClient) => oidcClient.permissions)
|
||||||
|
client: OidcClient;
|
||||||
|
}
|
69
src/database/models/oidc_grant.model.ts
Normal file
69
src/database/models/oidc_grant.model.ts
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
import type { AdapterPayload } from 'oidc-provider';
|
||||||
|
import { Column, Entity, Index, PrimaryColumn } from 'typeorm';
|
||||||
|
|
||||||
|
import {
|
||||||
|
convertFromNumberToTime,
|
||||||
|
convertFromTimeToNumber,
|
||||||
|
} from '../../util/time.util';
|
||||||
|
|
||||||
|
@Entity()
|
||||||
|
export class OidcGrant implements AdapterPayload {
|
||||||
|
@PrimaryColumn()
|
||||||
|
id: string;
|
||||||
|
|
||||||
|
@Index('oidc_account_id')
|
||||||
|
@Column({
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
accountId: string;
|
||||||
|
|
||||||
|
@Index('oidc_client_id')
|
||||||
|
@Column({
|
||||||
|
nullable: true,
|
||||||
|
})
|
||||||
|
clientId: string;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'timestamp',
|
||||||
|
nullable: true,
|
||||||
|
transformer: {
|
||||||
|
from: (value: Date) => convertFromTimeToNumber(value),
|
||||||
|
to: (value: number) => convertFromNumberToTime(value),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
iat?: number;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
type: 'timestamp',
|
||||||
|
nullable: true,
|
||||||
|
transformer: {
|
||||||
|
from: (value: Date) => convertFromTimeToNumber(value),
|
||||||
|
to: (value: number) => convertFromNumberToTime(value),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
exp?: number;
|
||||||
|
|
||||||
|
@Column({ type: 'json', nullable: true })
|
||||||
|
openid?: {
|
||||||
|
scope?: string;
|
||||||
|
claims?: string[];
|
||||||
|
};
|
||||||
|
|
||||||
|
@Column({ type: 'simple-array', nullable: true })
|
||||||
|
resources?: string[];
|
||||||
|
|
||||||
|
generateResponse(): AdapterPayload {
|
||||||
|
return {
|
||||||
|
iat: this.iat,
|
||||||
|
exp: this.exp,
|
||||||
|
accountId: this.accountId,
|
||||||
|
clientId: this.clientId,
|
||||||
|
kind: 'Grant',
|
||||||
|
jti: this.id,
|
||||||
|
openid: this.openid,
|
||||||
|
resources: this.resources,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
[key: string]: unknown;
|
||||||
|
}
|
0
src/database/models/oidc_resource_servers.model.ts
Normal file
0
src/database/models/oidc_resource_servers.model.ts
Normal file
41
src/database/models/organization.model.ts
Normal file
41
src/database/models/organization.model.ts
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
|
import { MAX_STRING_LENGTH } from '../database.const';
|
||||||
|
|
||||||
|
@Entity('organization')
|
||||||
|
export class Organization {
|
||||||
|
@PrimaryGeneratedColumn()
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH })
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
logo?: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
background?: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
website?: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
description?: string;
|
||||||
|
|
||||||
|
@Column({ name: 'owner_id' })
|
||||||
|
ownerId: number;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'created_at',
|
||||||
|
type: 'timestamp',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'updated_at',
|
||||||
|
type: 'timestamp',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
onUpdate: 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
0
src/database/models/organization_role.model.ts
Normal file
0
src/database/models/organization_role.model.ts
Normal file
|
@ -1,5 +1,6 @@
|
||||||
import { Column, Entity, PrimaryGeneratedColumn } from 'typeorm';
|
import { Column, Entity, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
|
||||||
import { MAX_STRING_LENGTH, UserRole } from '../database.const';
|
import { MAX_STRING_LENGTH, UserRole } from '../database.const';
|
||||||
|
import { ApiKey } from './api_keys.model';
|
||||||
|
|
||||||
@Entity('user')
|
@Entity('user')
|
||||||
export class User {
|
export class User {
|
||||||
|
@ -9,9 +10,15 @@ export class User {
|
||||||
@Column({ length: MAX_STRING_LENGTH })
|
@Column({ length: MAX_STRING_LENGTH })
|
||||||
username: string;
|
username: string;
|
||||||
|
|
||||||
|
@Column({ length: MAX_STRING_LENGTH, name: 'display_name', nullable: true })
|
||||||
|
displayName: string;
|
||||||
|
|
||||||
@Column({ length: MAX_STRING_LENGTH })
|
@Column({ length: MAX_STRING_LENGTH })
|
||||||
email: string;
|
email: string;
|
||||||
|
|
||||||
|
@Column({ name: 'email_verified', default: false })
|
||||||
|
emailVerified: boolean;
|
||||||
|
|
||||||
@Column({ length: MAX_STRING_LENGTH })
|
@Column({ length: MAX_STRING_LENGTH })
|
||||||
password: string;
|
password: string;
|
||||||
|
|
||||||
|
@ -25,5 +32,33 @@ export class User {
|
||||||
avatar: string;
|
avatar: string;
|
||||||
|
|
||||||
@Column({ length: 15, enum: UserRole, default: UserRole.USER })
|
@Column({ length: 15, enum: UserRole, default: UserRole.USER })
|
||||||
role: string;
|
role: UserRole;
|
||||||
|
|
||||||
|
@Column({ name: 'disabled', default: false })
|
||||||
|
disabled: boolean;
|
||||||
|
|
||||||
|
// This is for Gravatar Support
|
||||||
|
@Column({ name: 'email_hash', length: MAX_STRING_LENGTH, nullable: true })
|
||||||
|
emailHash: string;
|
||||||
|
|
||||||
|
// Relationship Mapping
|
||||||
|
|
||||||
|
@OneToMany(() => ApiKey, (apiKey) => apiKey.user)
|
||||||
|
apiKeys: ApiKey[];
|
||||||
|
|
||||||
|
// Created At/Updated At
|
||||||
|
@Column({
|
||||||
|
name: 'created_at',
|
||||||
|
type: 'timestamp',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@Column({
|
||||||
|
name: 'updated_at',
|
||||||
|
type: 'timestamp',
|
||||||
|
default: () => 'CURRENT_TIMESTAMP',
|
||||||
|
onUpdate: 'CURRENT_TIMESTAMP',
|
||||||
|
})
|
||||||
|
updatedAt: Date;
|
||||||
}
|
}
|
||||||
|
|
0
src/database/models/user_audit_log.model.ts
Normal file
0
src/database/models/user_audit_log.model.ts
Normal file
0
src/database/models/user_connection.model.ts
Normal file
0
src/database/models/user_connection.model.ts
Normal file
0
src/encryption/encryption.module.ts
Normal file
0
src/encryption/encryption.module.ts
Normal file
0
src/encryption/encryption.service.ts
Normal file
0
src/encryption/encryption.service.ts
Normal file
11
src/main.ts
11
src/main.ts
|
@ -1,8 +1,17 @@
|
||||||
import { NestFactory } from '@nestjs/core';
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { NestExpressApplication } from '@nestjs/platform-express';
|
||||||
|
import { join } from 'path';
|
||||||
import { AppModule } from './app.module';
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
async function bootstrap() {
|
async function bootstrap() {
|
||||||
const app = await NestFactory.create(AppModule);
|
const app = await NestFactory.create<NestExpressApplication>(
|
||||||
|
AppModule,
|
||||||
|
);
|
||||||
|
|
||||||
|
app.useStaticAssets(join(__dirname, '..', 'public'));
|
||||||
|
app.setBaseViewsDir(join(__dirname, '..', 'views'));
|
||||||
|
app.setViewEngine('hbs');
|
||||||
|
|
||||||
await app.listen(3000);
|
await app.listen(3000);
|
||||||
}
|
}
|
||||||
bootstrap();
|
bootstrap();
|
10
src/redis/redis.module.ts
Normal file
10
src/redis/redis.module.ts
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
|
||||||
|
import { RedisService } from './redis.service';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [RedisService, ConfigService],
|
||||||
|
exports: [RedisService],
|
||||||
|
})
|
||||||
|
export class RedisModule {}
|
286
src/redis/redis.service.ts
Normal file
286
src/redis/redis.service.ts
Normal file
|
@ -0,0 +1,286 @@
|
||||||
|
import { Injectable, Logger, OnApplicationShutdown } from '@nestjs/common';
|
||||||
|
import { ConfigService } from '@nestjs/config';
|
||||||
|
import { HealthIndicatorResult, HealthIndicatorStatus } from '@nestjs/terminus';
|
||||||
|
import { ObservableGauge } from '@opentelemetry/api';
|
||||||
|
import Redis, { ChainableCommander } from 'ioredis';
|
||||||
|
import { MetricService } from 'nestjs-otel';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class RedisService implements OnApplicationShutdown {
|
||||||
|
private _ioredis: Redis;
|
||||||
|
private readonly logger = new Logger(RedisService.name);
|
||||||
|
private readonly serviceConnectedObservableGauge: ObservableGauge;
|
||||||
|
|
||||||
|
private readonly host: string | undefined;
|
||||||
|
private readonly port: number;
|
||||||
|
private readonly password: string | undefined;
|
||||||
|
private readonly db: number;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
configService: ConfigService,
|
||||||
|
private readonly metricService: MetricService,
|
||||||
|
) {
|
||||||
|
this.host = configService.getOrThrow('redis.host');
|
||||||
|
this.port = +configService.get('redis.port') ?? 6789;
|
||||||
|
this.password = configService.get('redis.password') ?? '';
|
||||||
|
this.db = +configService.get('redis.db') ?? 0;
|
||||||
|
|
||||||
|
this.serviceConnectedObservableGauge = this.metricService.getObservableGauge(
|
||||||
|
'service.connected',
|
||||||
|
{ description: 'Whether a needed service is connected' },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.logger.debug(`Connecting to Redis at ${this.host}:${this.port} using db ${this.db}`);
|
||||||
|
this._ioredis = new Redis({
|
||||||
|
host: this.host,
|
||||||
|
port: this.port,
|
||||||
|
password: this.password,
|
||||||
|
db: this.db,
|
||||||
|
enableAutoPipelining: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
this._ioredis.on('ready', () => {
|
||||||
|
this.logger.log(`Connected to Redis at ${this.host}:${this.port} using db ${this.db}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
this._ioredis.on('error', (err) => {
|
||||||
|
this.logger.error(err, 'Redis error');
|
||||||
|
});
|
||||||
|
|
||||||
|
this.serviceConnectedObservableGauge.addCallback((result) => {
|
||||||
|
result.observe(this._ioredis.status === 'ready' ? 1 : 0, { target: 'redis' });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async onApplicationShutdown(): Promise<void> {
|
||||||
|
await this._ioredis.quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public get ioredis(): Redis {
|
||||||
|
return this._ioredis;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Store a value or object in redis
|
||||||
|
* @param key Key for the value to store
|
||||||
|
* @param value Value to store
|
||||||
|
* @param ttl Time to live in seconds
|
||||||
|
*/
|
||||||
|
public async set(key: string, value: string | object, ttl?: number): Promise<void> {
|
||||||
|
this.logger.debug(`Setting key ${key}`);
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
if (ttl) {
|
||||||
|
await this._ioredis.set(key, value, 'EX', ttl);
|
||||||
|
} else {
|
||||||
|
await this._ioredis.set(key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Will return a string or object based on if it was stored as one. Otherwise will return null if not found.
|
||||||
|
* @param key Key for the value to get
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async get(key: string): Promise<string | object | null> {
|
||||||
|
this.logger.debug(`Getting key ${key}`);
|
||||||
|
const value = await this._ioredis.get(key);
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch (error) {
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove a value from redis
|
||||||
|
* @param key Key for the value to remove
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async del(key: string): Promise<void> {
|
||||||
|
await this._ioredis.del(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get then delete a value from redis
|
||||||
|
* @param key Key for the value to get and remove
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public async getDel(key: string): Promise<string | object | null> {
|
||||||
|
const value = await this.get(key);
|
||||||
|
await this.del(key);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a redis multi command
|
||||||
|
* @returns Redis.Multi
|
||||||
|
*/
|
||||||
|
public multi(): ChainableCommander {
|
||||||
|
return this._ioredis.multi();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Execute a redis multi command
|
||||||
|
* @param multi Redis.Multi
|
||||||
|
* @returns Promise<any>
|
||||||
|
*/
|
||||||
|
public async exec(multi: ChainableCommander): Promise<any> {
|
||||||
|
return await multi.exec();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a ttl on a key
|
||||||
|
* @param key Key for the value to set the ttl on
|
||||||
|
* @param ttl Time to live in seconds
|
||||||
|
* @returns Promise<number>
|
||||||
|
*/
|
||||||
|
public async expire(key: string, ttl: number): Promise<number> {
|
||||||
|
return await this._ioredis.expire(key, ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set a key with a ttl
|
||||||
|
* @param key Key for the value to set
|
||||||
|
* @param value Value to set
|
||||||
|
* @param ttl Time to live in seconds
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
public async setex(key: string, value: string | object, ttl: number): Promise<void> {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
await this._ioredis.setex(key, ttl, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the ttl on a key
|
||||||
|
* @param key Key for the value to get the ttl on
|
||||||
|
* @returns Promise<number>
|
||||||
|
*/
|
||||||
|
public async ttl(key: string): Promise<number> {
|
||||||
|
return await this._ioredis.ttl(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a JSON.GET command and parse the result, return null if not found
|
||||||
|
* @param key Key for the value to get
|
||||||
|
* @returns Promise<object | null>
|
||||||
|
*/
|
||||||
|
public async jsonGet(key: string): Promise<object | null> {
|
||||||
|
const value = (await this._ioredis.call('JSON.GET', key)) as string | null;
|
||||||
|
if (!value) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return JSON.parse(value);
|
||||||
|
} catch (error) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a JSON.SET command and stringify the value if it is an object
|
||||||
|
* @param key Key for the value to set
|
||||||
|
* @param value Value to set
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
public async jsonSet(key: string, value: string | object): Promise<void> {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
await this._ioredis.set(key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Run a JSON.SET command with an expiration and stringify the value if it is an object
|
||||||
|
* @param key Key for the value to set
|
||||||
|
* @param value Value to set
|
||||||
|
* @param ttl Time to live in seconds
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
public async jsonSetEx(key: string, value: string | object, ttl: number): Promise<void> {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
await this._ioredis.set(key, value, 'EX', ttl);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Modify a value of a JSON.SET command
|
||||||
|
* @param key Key for the value to modify
|
||||||
|
* @param path Path to the value to modify
|
||||||
|
* @param value Value to set
|
||||||
|
* @returns Promise<void>
|
||||||
|
*/
|
||||||
|
public async jsonSetPath(
|
||||||
|
key: string,
|
||||||
|
path: string,
|
||||||
|
value: string | object | number,
|
||||||
|
): Promise<void> {
|
||||||
|
if (typeof value === 'object') {
|
||||||
|
value = JSON.stringify(value);
|
||||||
|
}
|
||||||
|
await this._ioredis.call('JSON.SET', key, path, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Preform a lrange command and parse the results
|
||||||
|
* @param key Key for the value to get
|
||||||
|
* @param start Start index
|
||||||
|
* @param stop Stop index
|
||||||
|
* @param parseJson Parse the results as JSON, normally false
|
||||||
|
* @returns Promise<object[]>
|
||||||
|
*/
|
||||||
|
public async lrange(
|
||||||
|
key: string,
|
||||||
|
start: number,
|
||||||
|
stop: number,
|
||||||
|
parseJson = false,
|
||||||
|
): Promise<object[] | any[]> {
|
||||||
|
const value = await this._ioredis.lrange(key, start, stop);
|
||||||
|
if (!value) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parseJson) {
|
||||||
|
return value.map((v) => JSON.parse(v));
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ping the redis server
|
||||||
|
*/
|
||||||
|
public async ping(): Promise<string> {
|
||||||
|
return await this._ioredis.ping();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check the health of the redis connection
|
||||||
|
* @returns Promise<string> up | down
|
||||||
|
*/
|
||||||
|
public async checkHealth(): Promise<HealthIndicatorResult> {
|
||||||
|
let status: HealthIndicatorStatus = 'down';
|
||||||
|
|
||||||
|
try {
|
||||||
|
await this.ping();
|
||||||
|
status = 'up';
|
||||||
|
} catch (error) {
|
||||||
|
status = 'down';
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
redis: {
|
||||||
|
status,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
7
src/util/time.util.ts
Normal file
7
src/util/time.util.ts
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
export const convertFromNumberToTime = (time: number | null): Date | null => {
|
||||||
|
return time ? new Date(time * 1000) : null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertFromTimeToNumber = (time: Date | null): number | null => {
|
||||||
|
return time ? time.getTime() / 1000 : null;
|
||||||
|
};
|
114
views/auth/login.hbs
Normal file
114
views/auth/login.hbs
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||||
|
<title>Login Page</title>
|
||||||
|
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
|
||||||
|
<style>
|
||||||
|
.video-bg {
|
||||||
|
position: fixed;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
.overlay {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background: rgba(0, 0, 0, 0.5);
|
||||||
|
z-index: -1;
|
||||||
|
}
|
||||||
|
@media (max-width: 640px) {
|
||||||
|
.video-bg, .overlay {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.login-prompt {
|
||||||
|
margin-left: 0;
|
||||||
|
width: 100%;
|
||||||
|
max-width: none;
|
||||||
|
position: static;
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body class="relative flex items-center justify-start min-h-screen">
|
||||||
|
<video autoplay muted loop class="video-bg">
|
||||||
|
<source src="file:///C:/Users/kakious/Desktop/login.webm" type="video/webm">
|
||||||
|
Your browser does not support the video tag.
|
||||||
|
</video>
|
||||||
|
<div class="overlay"></div>
|
||||||
|
<div class="relative bg-gray-800 p-8 rounded-lg shadow-lg w-full max-w-md ml-16 login-prompt">
|
||||||
|
<h2 class="text-2xl font-bold mb-2 text-white text-center">Welcome back!</h2>
|
||||||
|
<p class="text-gray-400 mb-6 text-center">We're so excited to see you again!</p>
|
||||||
|
<form action="{{ login_url }}" method="POST" class="space-y-6">
|
||||||
|
<div>
|
||||||
|
<label for="email" class="block text-sm font-medium text-gray-400">EMAIL OR USERNAME</label>
|
||||||
|
<input type="text" id="email" name="email" required class="mt-1 block w-full px-3 py-2 bg-gray-700 text-white border border-gray-600 rounded-md shadow-sm placeholder-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="password" class="block text-sm font-medium text-gray-400">PASSWORD</label>
|
||||||
|
<input type="password" id="password" name="password" required class="mt-1 block w-full px-3 py-2 bg-gray-700 text-white border border-gray-600 rounded-md shadow-sm placeholder-gray-500 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm">
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center justify-between">
|
||||||
|
<div class="text-sm">
|
||||||
|
<a href="{{ forgot_password }}" class="font-medium text-indigo-500 hover:text-indigo-400">Forgot your password?</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<button type="submit" class="w-full flex justify-center py-2 px-4 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">Log In</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
<div class="mt-4 text-sm text-center">
|
||||||
|
<span class="text-gray-400">Need an account? </span><a href="{{ register }}" class="font-medium text-indigo-500 hover:text-indigo-400">Register</a>
|
||||||
|
</div>
|
||||||
|
<div class="mt-6 flex items-center justify-center text-gray-400 text-xs">
|
||||||
|
<svg id="Layer_2" data-name="Layer 2" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 784.69 187.35" width="40%" height="40%">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.cls-1 {
|
||||||
|
fill: #fff;
|
||||||
|
stroke-width: 0px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
<g id="WatchingWaterwolf">
|
||||||
|
<g>
|
||||||
|
<g>
|
||||||
|
<polygon class="cls-1" points="269.31 78.37 260.14 136.91 248.7 78.37 244.28 78.37 239.92 78.37 235.38 78.37 223.91 137.03 214.7 78.37 198.81 78.37 214.82 165.36 220.98 165.36 225.1 165.36 230.48 165.36 242.08 109.33 253.48 165.36 258.92 165.36 262.92 165.36 269.19 165.36 285.15 78.37 269.31 78.37"/>
|
||||||
|
<path class="cls-1" d="M320.05,78.37h-10.52l-27.42,86.99h16.85l5.18-19.06h25.25l5.18,19.06h16.91l-27.54-86.99h-3.88ZM307.75,132.98l9.01-33.17,9.01,33.17h-18.02Z"/>
|
||||||
|
<polygon class="cls-1" points="365.7 78.37 344.13 78.37 344.13 91.76 365.7 91.76 365.7 165.36 381.59 165.36 381.59 91.76 403.52 91.76 403.52 78.37 381.59 78.37 365.7 78.37"/>
|
||||||
|
<polygon class="cls-1" points="425.51 127.07 453.59 127.07 453.59 114.1 425.51 114.1 425.51 91.76 458.43 91.76 458.43 78.37 425.51 78.37 420.91 78.37 409.5 78.37 409.5 165.36 420.91 165.36 425.51 165.36 458.67 165.36 458.67 152.04 425.51 152.04 425.51 127.07"/>
|
||||||
|
<path class="cls-1" d="M511.89,124.74c2.37-2.23,4.2-5.01,5.5-8.33,1.29-3.33,1.94-7.26,1.94-11.8,0-5.7-1.07-10.5-3.2-14.4-2.13-3.9-5.23-6.85-9.29-8.84-4.06-1.99-8.98-2.99-14.76-2.99h-27.07v86.99h16.01v-32.68h10.33l13.68,32.68h17.09v-.84l-15.69-36c2.03-1.05,3.85-2.3,5.44-3.79ZM481.03,91.76h11.05c2.67,0,4.83.56,6.48,1.67,1.65,1.12,2.87,2.72,3.64,4.81.78,2.09,1.17,4.59,1.17,7.5,0,2.67-.43,5.03-1.28,7.08-.86,2.05-2.15,3.65-3.88,4.81-1.73,1.16-3.89,1.73-6.48,1.73h-10.69v-27.6Z"/>
|
||||||
|
<polygon class="cls-1" points="584.07 136.91 572.63 78.37 568.2 78.37 563.84 78.37 559.3 78.37 547.84 137.03 538.63 78.37 522.74 78.37 538.75 165.36 544.9 165.36 549.03 165.36 554.4 165.36 566.01 109.33 577.4 165.36 582.84 165.36 586.85 165.36 593.12 165.36 609.07 78.37 593.24 78.37 584.07 136.91"/>
|
||||||
|
<path class="cls-1" d="M666.17,86.74c-2.73-3.15-5.97-5.53-9.74-7.14-3.76-1.61-7.96-2.42-12.58-2.42s-8.76.81-12.55,2.42c-3.78,1.61-7.04,3.99-9.77,7.14-2.73,3.15-4.83,7.07-6.3,11.77-1.47,4.7-2.21,10.18-2.21,16.43v14.64c0,6.13.75,11.51,2.24,16.13,1.49,4.62,3.6,8.47,6.33,11.56,2.73,3.09,5.98,5.41,9.77,6.96,3.78,1.55,7.99,2.33,12.61,2.33s8.76-.78,12.55-2.33c3.78-1.55,7.03-3.87,9.74-6.96,2.71-3.09,4.78-6.94,6.21-11.56,1.43-4.62,2.15-10,2.15-16.13v-14.64c0-6.25-.73-11.73-2.18-16.43-1.45-4.7-3.54-8.62-6.27-11.77ZM658.73,129.57c0,4.18-.31,7.77-.93,10.75-.62,2.99-1.55,5.42-2.81,7.29-1.25,1.87-2.81,3.26-4.66,4.15-1.85.9-3.97,1.34-6.36,1.34s-4.57-.45-6.42-1.34c-1.85-.9-3.43-2.28-4.72-4.15-1.29-1.87-2.26-4.3-2.9-7.29-.64-2.99-.96-6.57-.96-10.75v-14.76c0-4.26.32-7.93.96-10.99.64-3.07,1.58-5.57,2.84-7.5,1.25-1.93,2.81-3.36,4.66-4.27,1.85-.92,3.99-1.37,6.42-1.37s4.51.46,6.36,1.37c1.85.92,3.42,2.34,4.69,4.27,1.27,1.93,2.23,4.43,2.87,7.5.64,3.07.96,6.73.96,10.99v14.76Z"/>
|
||||||
|
<polygon class="cls-1" points="699.6 78.37 683.59 78.37 683.59 165.36 695 165.36 699.6 165.36 731.15 165.36 731.15 152.04 699.6 152.04 699.6 78.37"/>
|
||||||
|
<polygon class="cls-1" points="784.69 91.76 784.69 78.37 753.26 78.37 749.38 78.37 737.25 78.37 737.25 165.36 753.26 165.36 753.26 129.16 780.98 129.16 780.98 115.83 753.26 115.83 753.26 91.76 784.69 91.76"/>
|
||||||
|
<path class="cls-1" d="M206.63,54.66h6.38c2.79,0,5.18-.54,7.17-1.62,1.99-1.08,3.53-2.6,4.61-4.56,1.08-1.96,1.62-4.31,1.62-7.04s-.54-4.98-1.62-7.03c-1.08-2.05-2.61-3.66-4.61-4.84-1.99-1.17-4.38-1.76-7.17-1.76h-14.12v42.04h7.74v-15.19ZM213.01,34.28c1.33,0,2.4.34,3.22,1.01.82.67,1.42,1.56,1.79,2.67.38,1.11.56,2.29.56,3.54s-.19,2.44-.56,3.46c-.38,1.02-.97,1.81-1.79,2.38-.82.57-1.89.85-3.22.85h-6.38v-13.92h6.38Z"/>
|
||||||
|
<path class="cls-1" d="M233.41,65.94c1.32,1.49,2.89,2.61,4.72,3.36,1.83.75,3.86,1.13,6.09,1.13s4.23-.38,6.06-1.13c1.83-.75,3.4-1.87,4.71-3.36s2.31-3.35,3-5.59c.69-2.23,1.04-4.83,1.04-7.8v-7.07c0-3.02-.35-5.67-1.05-7.94-.7-2.27-1.71-4.17-3.03-5.69-1.32-1.52-2.89-2.67-4.71-3.45-1.82-.78-3.84-1.17-6.08-1.17s-4.23.39-6.06,1.17c-1.83.78-3.4,1.93-4.72,3.45-1.32,1.52-2.33,3.42-3.05,5.69-.71,2.27-1.07,4.92-1.07,7.94v7.07c0,2.96.36,5.56,1.08,7.8.72,2.23,1.74,4.1,3.06,5.59ZM236.98,45.42c0-2.06.15-3.83.46-5.31.31-1.48.77-2.69,1.37-3.62s1.36-1.62,2.25-2.06c.9-.44,1.93-.66,3.1-.66s2.18.22,3.08.66c.9.44,1.65,1.13,2.27,2.06.62.93,1.08,2.14,1.39,3.62.31,1.48.46,3.25.46,5.31v7.13c0,2.02-.15,3.75-.45,5.2-.3,1.44-.75,2.62-1.36,3.52-.61.91-1.36,1.57-2.25,2.01s-1.92.65-3.07.65-2.21-.22-3.1-.65-1.66-1.1-2.28-2.01c-.63-.9-1.09-2.08-1.4-3.52-.31-1.44-.46-3.18-.46-5.2v-7.13Z"/>
|
||||||
|
<polygon class="cls-1" points="271.89 69.85 273.88 69.85 276.48 69.85 282.09 42.77 287.59 69.85 290.22 69.85 292.16 69.85 295.19 69.85 302.9 27.81 295.25 27.81 290.81 56.1 285.29 27.81 283.15 27.81 281.04 27.81 278.85 27.81 273.31 56.16 268.86 27.81 261.18 27.81 268.91 69.85 271.89 69.85"/>
|
||||||
|
<polygon class="cls-1" points="313.3 69.85 329.32 69.85 329.32 63.41 313.3 63.41 313.3 51.34 326.86 51.34 326.86 45.08 313.3 45.08 313.3 34.28 329.2 34.28 329.2 27.81 313.3 27.81 311.07 27.81 305.56 27.81 305.56 69.85 311.07 69.85 313.3 69.85"/>
|
||||||
|
<path class="cls-1" d="M340.12,54.06h4.99l6.61,15.79h8.26v-.4l-7.58-17.4c.98-.51,1.86-1.11,2.63-1.83,1.14-1.08,2.03-2.42,2.66-4.03.62-1.61.94-3.51.94-5.7,0-2.75-.52-5.07-1.54-6.96-1.03-1.89-2.53-3.31-4.49-4.27s-4.34-1.44-7.13-1.44h-13.08v42.04h7.74v-15.79ZM345.46,34.28c1.29,0,2.33.27,3.13.81.8.54,1.39,1.31,1.76,2.32.38,1.01.56,2.22.56,3.62,0,1.29-.21,2.43-.62,3.42-.41.99-1.04,1.77-1.88,2.32-.84.56-1.88.84-3.13.84h-5.17v-13.34h5.34Z"/>
|
||||||
|
<polygon class="cls-1" points="370.21 69.85 386.23 69.85 386.23 63.41 370.21 63.41 370.21 51.34 383.78 51.34 383.78 45.08 370.21 45.08 370.21 34.28 386.12 34.28 386.12 27.81 370.21 27.81 367.99 27.81 362.47 27.81 362.47 69.85 367.99 69.85 370.21 69.85"/>
|
||||||
|
<path class="cls-1" d="M414.99,59.71c.75-2.19,1.13-4.71,1.13-7.54v-6.67c0-2.83-.38-5.35-1.13-7.55-.75-2.2-1.83-4.06-3.25-5.56-1.42-1.5-3.1-2.64-5.07-3.42-1.96-.78-4.16-1.17-6.58-1.17h-10.83v42.04h10.65c2.48,0,4.72-.39,6.71-1.17,1.99-.78,3.7-1.92,5.11-3.42,1.41-1.5,2.5-3.35,3.25-5.54ZM408.44,52.18c0,2.62-.26,4.75-.79,6.41-.53,1.66-1.41,2.87-2.66,3.65-1.24.78-2.93,1.17-5.07,1.17h-2.92v-29.13h3.09c1.56,0,2.87.21,3.93.64,1.06.42,1.92,1.08,2.57,1.96.65.89,1.13,2.04,1.41,3.45.29,1.42.43,3.12.43,5.12v6.73Z"/>
|
||||||
|
<path class="cls-1" d="M439.51,69.85h5.83c2.73,0,5.05-.47,6.96-1.4,1.91-.93,3.36-2.31,4.36-4.14,1-1.83,1.5-4.09,1.5-6.79,0-1.69-.3-3.29-.9-4.78-.6-1.49-1.55-2.71-2.86-3.65-.64-.46-1.37-.8-2.2-1.03.33-.15.65-.31.94-.5,1.45-.92,2.54-2.11,3.25-3.55.71-1.44,1.07-3.03,1.07-4.76,0-1.96-.29-3.66-.88-5.1-.59-1.43-1.44-2.62-2.56-3.57-1.12-.94-2.47-1.64-4.07-2.09-1.6-.45-3.42-.68-5.46-.68h-12.73v42.04h7.74ZM449.91,60.57c-.37.9-.92,1.59-1.66,2.09s-1.71.75-2.9.75h-5.83v-12.21h6.12c1.17,0,2.11.26,2.81.77.7.51,1.21,1.24,1.53,2.18.32.94.48,2.05.48,3.32,0,1.17-.18,2.21-.55,3.1ZM444.51,34.28c1.23,0,2.23.19,3,.58.77.39,1.33.99,1.69,1.8.36.82.53,1.9.53,3.25,0,1.19-.2,2.2-.59,3.03-.4.83-.98,1.46-1.76,1.89-.78.43-1.75.65-2.9.65h-4.97v-11.2h4.99Z"/>
|
||||||
|
<polygon class="cls-1" points="469.2 69.85 476.99 69.85 476.99 54.49 488.46 27.81 479.97 27.81 473.1 47.24 466.2 27.81 457.74 27.81 469.2 54.49 469.2 69.85"/>
|
||||||
|
</g>
|
||||||
|
<g id="svgg">
|
||||||
|
<path id="path0" class="cls-1" d="M80.35,1.12C23.73,10.38-12.01,65.53,3.72,119.36c2.66,9.11,2.57,9.09,4.65.98,1.01-3.96,2.15-7.97,2.51-8.91.46-1.16.46-3.35,0-6.88C1.7,33.58,80.91-15.25,140.59,24.61c62.01,41.42,41.22,138.57-32.4,151.41-9.96,1.74-9.44,1.91-17.14-5.73-7.48-7.42-7.33-7.49-4.35,2.04l1.5,4.82-4.37-.47c-6.64-.71-16.51-3.4-18.16-4.95-4.5-4.23-8.03-17.49-8.49-31.9-.16-5.13-.52-9.35-.8-9.37-3.57-.26-12.18,3.82-18.23,8.63-5.47,4.35-5.51,4.31-3.58-3.27,6.35-24.86,25.51-50.23,46.13-61.08l4.59-2.41-2.5-.47c-1.37-.26-5.19-.32-8.48-.13l-5.99.34,6.26-3.14c7.29-3.66,12.94-5.63,19.12-6.65,2.54-.42,4.42-1.06,4.42-1.5,0-2.75,17.52-24.58,19.72-24.58.32,0,.39,3,.17,6.71-.5,8.33-.65,8.12,9.92,13.97,9.29,5.14,10.94,6.49,10.67,8.77-.32,2.77-2.15,2.36-4.07-.92-1.99-3.39-3.89-4.57-8.77-5.46-4.07-.74-4.29-.51-3.37,3.57,1.14,5.08,4.17,7.32,12.24,9.03,9.23,1.96,24.42,8.39,23.96,10.15-.17.67.2,2,.83,2.95,1.1,1.67,1.09,1.81-.17,3.51-4.4,5.94-10.39,7.17-17.04,3.5-14.31-7.9-23.73-11.17-33.69-11.72-9.43-.51-17,1.63-9.42,2.67,3.57.49,7.31,2.31,9.46,4.63l1.59,1.7h-3.23c-21.98.09-38.75,19.17-33.92,38.6,1.68,6.75,6.31,15.09,12.02,21.63,2.26,2.59,2.38,2.46.96-.94-3.79-9.06-1.92-25.02,3.66-31.38,4.06-4.62,4.44-4.42,4.07,2.11-1.12,19.7,12.31,32.5,37.18,35.43,4.57.54,4.82.24,1.45-1.68-13.6-7.76-23.89-25.44-21.7-37.28,2.68-14.47,11.58-19.55,31.42-17.92l9.68.79,2.88-1.69c6.92-4.05,15.9-14.15,14.15-15.91-.53-.53-6.4-3.31-13.04-6.18l-12.07-5.22-.33-4.1c-.41-5.11-.38-5.07-6.27-9.04-4.45-3-4.87-3.48-4.87-5.5,0-5.65-3.19-17.27-4.52-16.45-.34.21-.82,3.62-1.08,7.58-.48,7.36-1.03,8.5-2.98,6.14-.62-.75-.88-3.38-.9-9.1-.02-8.59-.79-10.81-3.75-10.81-4.6,0-18.11,10.78-26.81,21.39-1.4,1.71-2.77,2.35-7.85,3.71-20.14,5.36-47.11,21.68-54.6,33.04-2.46,3.73-2.09,3.82,3.62.86,11.63-6.03,24.03-9.95,17.65-5.59-18.72,12.81-30.43,34.04-34.32,62.23-.94,6.84,20.19,26.42,37.06,34.32,69.91,32.76,147.48-28.19,131.88-103.62C175.69,26.85,127.77-6.64,80.35,1.12"/>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
10
views/home/index.hbs
Normal file
10
views/home/index.hbs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<title>App</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
{{ message }}
|
||||||
|
</body>
|
||||||
|
</html>
|
Loading…
Reference in a new issue