Implement contact form backend
This commit is contained in:
parent
e643bf6df0
commit
e3c3b93818
2
ex
2
ex
@ -125,6 +125,8 @@ if [ ! -d "./node_modules" ]; then
|
|||||||
|
|
||||||
echoRun "$ENV_PNPM" i
|
echoRun "$ENV_PNPM" i
|
||||||
|
|
||||||
|
echoRun ./node_modules/.bin/ts-patch i
|
||||||
|
|
||||||
if [ ! -f "./.env" ]; then
|
if [ ! -f "./.env" ]; then
|
||||||
echoRun cp example.env .env
|
echoRun cp example.env .env
|
||||||
fi
|
fi
|
||||||
|
@ -8,9 +8,10 @@
|
|||||||
"lib": "lib"
|
"lib": "lib"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@extollo/lib": "^0.9.31",
|
"@extollo/lib": "^0.9.32",
|
||||||
"copyfiles": "^2.4.1",
|
"copyfiles": "^2.4.1",
|
||||||
"feed": "^4.2.2",
|
"feed": "^4.2.2",
|
||||||
|
"gotify": "^1.1.0",
|
||||||
"rimraf": "^3.0.2",
|
"rimraf": "^3.0.2",
|
||||||
"ts-expose-internals": "^4.5.4",
|
"ts-expose-internals": "^4.5.4",
|
||||||
"ts-patch": "^2.0.1",
|
"ts-patch": "^2.0.1",
|
||||||
|
224
pnpm-lock.yaml
224
pnpm-lock.yaml
@ -2,9 +2,10 @@ lockfileVersion: 5.3
|
|||||||
|
|
||||||
specifiers:
|
specifiers:
|
||||||
'@extollo/cc': ^0.6.0
|
'@extollo/cc': ^0.6.0
|
||||||
'@extollo/lib': ^0.9.31
|
'@extollo/lib': ^0.9.32
|
||||||
copyfiles: ^2.4.1
|
copyfiles: ^2.4.1
|
||||||
feed: ^4.2.2
|
feed: ^4.2.2
|
||||||
|
gotify: ^1.1.0
|
||||||
rimraf: ^3.0.2
|
rimraf: ^3.0.2
|
||||||
ts-expose-internals: ^4.5.4
|
ts-expose-internals: ^4.5.4
|
||||||
ts-patch: ^2.0.1
|
ts-patch: ^2.0.1
|
||||||
@ -13,9 +14,10 @@ specifiers:
|
|||||||
zod: ^3.11.6
|
zod: ^3.11.6
|
||||||
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@extollo/lib': 0.9.31
|
'@extollo/lib': 0.9.32
|
||||||
copyfiles: 2.4.1
|
copyfiles: 2.4.1
|
||||||
feed: 4.2.2
|
feed: 4.2.2
|
||||||
|
gotify: 1.1.0
|
||||||
rimraf: 3.0.2
|
rimraf: 3.0.2
|
||||||
ts-expose-internals: 4.5.4
|
ts-expose-internals: 4.5.4
|
||||||
ts-patch: 2.0.1_typescript@4.3.2
|
ts-patch: 2.0.1_typescript@4.3.2
|
||||||
@ -112,8 +114,8 @@ packages:
|
|||||||
- supports-color
|
- supports-color
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@extollo/lib/0.9.31:
|
/@extollo/lib/0.9.32:
|
||||||
resolution: {integrity: sha512-sYtqWTL+hmkPZ44fwWdRsHK1081m98547r9snFm8i+ou13Npw3i4RBf8k+uFRUCayMM4PKVWqgo0bZJLvO4W1g==}
|
resolution: {integrity: sha512-3DuRrLFmYY6w0rK5QKSlNWbGZnel6Lj2vjSGwwbj1IEJXGcZSA+TMXLghAwPkKyUsyX0WwdXVn6S51jmRHyWJw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@atao60/fse-cli': 0.1.7
|
'@atao60/fse-cli': 0.1.7
|
||||||
'@extollo/ui': 0.1.0_@types+node@14.18.12
|
'@extollo/ui': 0.1.0_@types+node@14.18.12
|
||||||
@ -305,6 +307,18 @@ packages:
|
|||||||
resolution: {integrity: sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==}
|
resolution: {integrity: sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@sindresorhus/is/2.1.1:
|
||||||
|
resolution: {integrity: sha512-/aPsuoj/1Dw/kzhkgz+ES6TxG0zfTMGLwuK2ZG00k/iJzYHTLCE8mVU8EPqEOp/lmxPoq1C1C9RYToRKb2KEfg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/@szmarczak/http-timer/4.0.6:
|
||||||
|
resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
defer-to-connect: 2.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@tsconfig/node10/1.0.8:
|
/@tsconfig/node10/1.0.8:
|
||||||
resolution: {integrity: sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==}
|
resolution: {integrity: sha512-6XFfSQmMgq0CFLY1MslA/CPUfhIL919M1rMsa5lP2P097N2Wd1sSX0tx1u4olM16fLNhtHZpRhedZJphNJqmZg==}
|
||||||
|
|
||||||
@ -333,6 +347,15 @@ packages:
|
|||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/cacheable-request/6.0.2:
|
||||||
|
resolution: {integrity: sha512-B3xVo+dlKM6nnKTcmm5ZtY/OL8bOAOd2Olee9M1zft65ox50OzjEHW91sDiU9j6cvW8Ejg1/Qkf4xd2kugApUA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/http-cache-semantics': 4.0.1
|
||||||
|
'@types/keyv': 3.1.4
|
||||||
|
'@types/node': 17.0.23
|
||||||
|
'@types/responselike': 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/cli-color/2.0.2:
|
/@types/cli-color/2.0.2:
|
||||||
resolution: {integrity: sha512-1ErQIcmNHtNViGKTtB/TIKqMkC2RkKI2nBneCr9hSCPo9H05g9VzjlaXPW3H0vaI8zFGjJZvSav+VKDKCtKgKA==}
|
resolution: {integrity: sha512-1ErQIcmNHtNViGKTtB/TIKqMkC2RkKI2nBneCr9hSCPo9H05g9VzjlaXPW3H0vaI8zFGjJZvSav+VKDKCtKgKA==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -353,18 +376,32 @@ packages:
|
|||||||
'@types/minimatch': 3.0.5
|
'@types/minimatch': 3.0.5
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
|
|
||||||
|
/@types/http-cache-semantics/4.0.1:
|
||||||
|
resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/ioredis/4.28.10:
|
/@types/ioredis/4.28.10:
|
||||||
resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==}
|
resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/json-buffer/3.0.0:
|
||||||
|
resolution: {integrity: sha512-3YP80IxxFJB4b5tYC2SUPwkg0XQLiu0nWvhRgEatgjf+29IcWO9X1k8xRv5DGssJ/lCrjYTjQPcobJr2yWIVuQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/jsonwebtoken/8.5.8:
|
/@types/jsonwebtoken/8.5.8:
|
||||||
resolution: {integrity: sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==}
|
resolution: {integrity: sha512-zm6xBQpFDIDM6o9r6HSgDeIcLy82TKWctCXEPbJJcXb5AKmi5BNNdLXneixK4lplX3PqIVcwLBCGE/kAGnlD4A==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@types/node': 17.0.23
|
'@types/node': 17.0.23
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/keyv/3.1.4:
|
||||||
|
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 17.0.23
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/mime-types/2.1.1:
|
/@types/mime-types/2.1.1:
|
||||||
resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==}
|
resolution: {integrity: sha512-vXOTGVSLR2jMw440moWTC7H19iUyLtP3Z1YTj7cSsubOICinjMxFeb/V57v9QdyyPGbbWolUFSSmSiRSn94tFw==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -404,6 +441,12 @@ packages:
|
|||||||
resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
|
resolution: {integrity: sha512-SnHmG9wN1UVmagJOnyo/qkk0Z7gejYxOYYmaAwr5u2yFYfsupN3sg10kyzN8Hep/2zbHxCnsumxOoRIRMBwKCg==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@types/responselike/1.0.0:
|
||||||
|
resolution: {integrity: sha512-85Y2BjiufFzaMIlvJDvTTB8Fxl2xfLo4HgmHzVBz08w4wDePCTjYw66PdrolO0kzli3yam/YCgRufyo1DdQVTA==}
|
||||||
|
dependencies:
|
||||||
|
'@types/node': 17.0.23
|
||||||
|
dev: false
|
||||||
|
|
||||||
/@types/rimraf/3.0.2:
|
/@types/rimraf/3.0.2:
|
||||||
resolution: {integrity: sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==}
|
resolution: {integrity: sha512-F3OznnSLAUxFrCEu/L5PY8+ny8DtcFRjx7fZZ9bycvXRi3KPTRS9HOitGZwvPg0juRhXFWIeKX58cnX5YqLohQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -638,6 +681,27 @@ packages:
|
|||||||
dicer: 0.3.0
|
dicer: 0.3.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/cacheable-lookup/2.0.1:
|
||||||
|
resolution: {integrity: sha512-EMMbsiOTcdngM/K6gV/OxF2x0t07+vMOWxZNSCRQMjO2MY2nhZQ6OYhOOpyQrbhqsgtvKGI7hcq6xjnA92USjg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
'@types/keyv': 3.1.4
|
||||||
|
keyv: 4.2.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/cacheable-request/7.0.2:
|
||||||
|
resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
clone-response: 1.0.2
|
||||||
|
get-stream: 5.2.0
|
||||||
|
http-cache-semantics: 4.1.0
|
||||||
|
keyv: 4.2.1
|
||||||
|
lowercase-keys: 2.0.0
|
||||||
|
normalize-url: 6.1.0
|
||||||
|
responselike: 2.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/call-bind/1.0.2:
|
/call-bind/1.0.2:
|
||||||
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
resolution: {integrity: sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -734,6 +798,12 @@ packages:
|
|||||||
wrap-ansi: 7.0.0
|
wrap-ansi: 7.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/clone-response/1.0.2:
|
||||||
|
resolution: {integrity: sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=}
|
||||||
|
dependencies:
|
||||||
|
mimic-response: 1.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/clone/1.0.4:
|
/clone/1.0.4:
|
||||||
resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=}
|
resolution: {integrity: sha1-2jCcwmPfFZlMaIypAheco8fNfH4=}
|
||||||
engines: {node: '>=0.8'}
|
engines: {node: '>=0.8'}
|
||||||
@ -771,6 +841,14 @@ packages:
|
|||||||
resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==}
|
resolution: {integrity: sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/compress-brotli/1.3.6:
|
||||||
|
resolution: {integrity: sha512-au99/GqZtUtiCBliqLFbWlhnCxn+XSYjwZ77q6mKN4La4qOXDoLVPZ50iXr0WmAyMxl8yqoq3Yq4OeQNPPkyeQ==}
|
||||||
|
engines: {node: '>= 12'}
|
||||||
|
dependencies:
|
||||||
|
'@types/json-buffer': 3.0.0
|
||||||
|
json-buffer: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/concat-map/0.0.1:
|
/concat-map/0.0.1:
|
||||||
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
|
resolution: {integrity: sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=}
|
||||||
|
|
||||||
@ -843,11 +921,23 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
ms: 2.1.2
|
ms: 2.1.2
|
||||||
|
|
||||||
|
/decompress-response/5.0.0:
|
||||||
|
resolution: {integrity: sha512-TLZWWybuxWgoW7Lykv+gq9xvzOsUjQ9tF09Tj6NSTYGMTCHNXzrPnD6Hi+TgZq19PyTAGH4Ll/NIM/eTGglnMw==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
mimic-response: 2.1.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/defaults/1.0.3:
|
/defaults/1.0.3:
|
||||||
resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=}
|
resolution: {integrity: sha1-xlYFHpgX2f8I7YgUd/P+QBnz730=}
|
||||||
dependencies:
|
dependencies:
|
||||||
clone: 1.0.4
|
clone: 1.0.4
|
||||||
|
|
||||||
|
/defer-to-connect/2.0.1:
|
||||||
|
resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/delegates/1.0.0:
|
/delegates/1.0.0:
|
||||||
resolution: {integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=}
|
resolution: {integrity: sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=}
|
||||||
dev: false
|
dev: false
|
||||||
@ -892,6 +982,10 @@ packages:
|
|||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/duplexer3/0.1.4:
|
||||||
|
resolution: {integrity: sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/ecdsa-sig-formatter/1.0.11:
|
/ecdsa-sig-formatter/1.0.11:
|
||||||
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
resolution: {integrity: sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -901,6 +995,12 @@ packages:
|
|||||||
/emoji-regex/8.0.0:
|
/emoji-regex/8.0.0:
|
||||||
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==}
|
||||||
|
|
||||||
|
/end-of-stream/1.4.4:
|
||||||
|
resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==}
|
||||||
|
dependencies:
|
||||||
|
once: 1.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/es5-ext/0.10.59:
|
/es5-ext/0.10.59:
|
||||||
resolution: {integrity: sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==}
|
resolution: {integrity: sha512-cOgyhW0tIJyQY1Kfw6Kr0viu9ZlUctVchRMZ7R0HiH3dxTSp5zJDLecwxUqPUrGKMsgBI1wd1FL+d9Jxfi4cLw==}
|
||||||
engines: {node: '>=0.10'}
|
engines: {node: '>=0.10'}
|
||||||
@ -1096,6 +1196,13 @@ packages:
|
|||||||
has-symbols: 1.0.3
|
has-symbols: 1.0.3
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/get-stream/5.2.0:
|
||||||
|
resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
pump: 3.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/glob-parent/5.1.2:
|
/glob-parent/5.1.2:
|
||||||
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@ -1131,6 +1238,33 @@ packages:
|
|||||||
merge2: 1.4.1
|
merge2: 1.4.1
|
||||||
slash: 3.0.0
|
slash: 3.0.0
|
||||||
|
|
||||||
|
/got/10.7.0:
|
||||||
|
resolution: {integrity: sha512-aWTDeNw9g+XqEZNcTjMMZSy7B7yE9toWOFYip7ofFTLleJhvZwUxxTxkTpKvF+p1SAA4VHmuEy7PiHTHyq8tJg==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dependencies:
|
||||||
|
'@sindresorhus/is': 2.1.1
|
||||||
|
'@szmarczak/http-timer': 4.0.6
|
||||||
|
'@types/cacheable-request': 6.0.2
|
||||||
|
cacheable-lookup: 2.0.1
|
||||||
|
cacheable-request: 7.0.2
|
||||||
|
decompress-response: 5.0.0
|
||||||
|
duplexer3: 0.1.4
|
||||||
|
get-stream: 5.2.0
|
||||||
|
lowercase-keys: 2.0.0
|
||||||
|
mimic-response: 2.1.0
|
||||||
|
p-cancelable: 2.1.1
|
||||||
|
p-event: 4.2.0
|
||||||
|
responselike: 2.0.0
|
||||||
|
to-readable-stream: 2.1.0
|
||||||
|
type-fest: 0.10.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/gotify/1.1.0:
|
||||||
|
resolution: {integrity: sha512-f3PUh08i+1JCJuzv9CRYuIx1QOb9DoWXRvWaPttgiZEG7XtetDQVv6S9cr6suPNKkZI4ry8dqdU2qdb9AiPinw==}
|
||||||
|
dependencies:
|
||||||
|
got: 10.7.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/graceful-fs/4.2.9:
|
/graceful-fs/4.2.9:
|
||||||
resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==}
|
resolution: {integrity: sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==}
|
||||||
|
|
||||||
@ -1173,6 +1307,10 @@ packages:
|
|||||||
dependencies:
|
dependencies:
|
||||||
function-bind: 1.1.1
|
function-bind: 1.1.1
|
||||||
|
|
||||||
|
/http-cache-semantics/4.1.0:
|
||||||
|
resolution: {integrity: sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/https-proxy-agent/5.0.0:
|
/https-proxy-agent/5.0.0:
|
||||||
resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==}
|
resolution: {integrity: sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==}
|
||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
@ -1339,6 +1477,10 @@ packages:
|
|||||||
resolution: {integrity: sha1-Fzb939lyTyijaCrcYjCufk6Weds=}
|
resolution: {integrity: sha1-Fzb939lyTyijaCrcYjCufk6Weds=}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/json-buffer/3.0.1:
|
||||||
|
resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/jsonc-parser/3.0.0:
|
/jsonc-parser/3.0.0:
|
||||||
resolution: {integrity: sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==}
|
resolution: {integrity: sha512-fQzRfAbIBnR0IQvftw9FJveWiHp72Fg20giDrHz6TdfB12UH/uue0D3hm57UB5KgAVuniLMCaS8P1IMj9NR7cA==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -1393,6 +1535,13 @@ packages:
|
|||||||
safe-buffer: 5.1.2
|
safe-buffer: 5.1.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/keyv/4.2.1:
|
||||||
|
resolution: {integrity: sha512-cAJq5cTfxQdq1DHZEVNpnk4mEvhP+8UP8UQftLtTtJ98beKkRHf+62M0mIDM2u/IWXyP8bmGB375/6uGdSX2MA==}
|
||||||
|
dependencies:
|
||||||
|
compress-brotli: 1.3.6
|
||||||
|
json-buffer: 3.0.1
|
||||||
|
dev: false
|
||||||
|
|
||||||
/kind-of/6.0.3:
|
/kind-of/6.0.3:
|
||||||
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
|
resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
@ -1447,6 +1596,11 @@ packages:
|
|||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
is-unicode-supported: 0.1.0
|
is-unicode-supported: 0.1.0
|
||||||
|
|
||||||
|
/lowercase-keys/2.0.0:
|
||||||
|
resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/lru-cache/6.0.0:
|
/lru-cache/6.0.0:
|
||||||
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@ -1523,6 +1677,16 @@ packages:
|
|||||||
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
|
|
||||||
|
/mimic-response/1.0.1:
|
||||||
|
resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/mimic-response/2.1.0:
|
||||||
|
resolution: {integrity: sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/minimatch/3.0.4:
|
/minimatch/3.0.4:
|
||||||
resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==}
|
resolution: {integrity: sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1624,6 +1788,11 @@ packages:
|
|||||||
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
/normalize-url/6.1.0:
|
||||||
|
resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==}
|
||||||
|
engines: {node: '>=10'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/npmlog/5.0.1:
|
/npmlog/5.0.1:
|
||||||
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
resolution: {integrity: sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1670,11 +1839,35 @@ packages:
|
|||||||
resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=}
|
resolution: {integrity: sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=}
|
||||||
engines: {node: '>=0.10.0'}
|
engines: {node: '>=0.10.0'}
|
||||||
|
|
||||||
|
/p-cancelable/2.1.1:
|
||||||
|
resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/p-event/4.2.0:
|
||||||
|
resolution: {integrity: sha512-KXatOjCRXXkSePPb1Nbi0p0m+gQAwdlbhi4wQKJPI1HsMQS9g+Sqp2o+QHziPr7eYJyOZet836KoHEVM1mwOrQ==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
p-timeout: 3.2.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
|
/p-finally/1.0.0:
|
||||||
|
resolution: {integrity: sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/p-map/2.1.0:
|
/p-map/2.1.0:
|
||||||
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
|
resolution: {integrity: sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/p-timeout/3.2.0:
|
||||||
|
resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dependencies:
|
||||||
|
p-finally: 1.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/packet-reader/1.0.0:
|
/packet-reader/1.0.0:
|
||||||
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
|
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -1888,6 +2081,13 @@ packages:
|
|||||||
pug-strip-comments: 2.0.0
|
pug-strip-comments: 2.0.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/pump/3.0.0:
|
||||||
|
resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==}
|
||||||
|
dependencies:
|
||||||
|
end-of-stream: 1.4.4
|
||||||
|
once: 1.4.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/queue-microtask/1.2.3:
|
/queue-microtask/1.2.3:
|
||||||
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
|
||||||
|
|
||||||
@ -1969,6 +2169,12 @@ packages:
|
|||||||
path-parse: 1.0.7
|
path-parse: 1.0.7
|
||||||
supports-preserve-symlinks-flag: 1.0.0
|
supports-preserve-symlinks-flag: 1.0.0
|
||||||
|
|
||||||
|
/responselike/2.0.0:
|
||||||
|
resolution: {integrity: sha512-xH48u3FTB9VsZw7R+vvgaKeLKzT6jOogbQhEe/jewwnZgzPcnyWui2Av6JpoYZF/91uueC+lqhWqeURw5/qhCw==}
|
||||||
|
dependencies:
|
||||||
|
lowercase-keys: 2.0.0
|
||||||
|
dev: false
|
||||||
|
|
||||||
/restore-cursor/3.1.0:
|
/restore-cursor/3.1.0:
|
||||||
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==}
|
||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
@ -2221,6 +2427,11 @@ packages:
|
|||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/to-readable-stream/2.1.0:
|
||||||
|
resolution: {integrity: sha512-o3Qa6DGg1CEXshSdvWNX2sN4QHqg03SPq7U6jPXRahlQdl5dK8oXjkU/2/sGrnOZKeGV1zLSO8qPwyKklPPE7w==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/to-regex-range/5.0.1:
|
/to-regex-range/5.0.1:
|
||||||
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
|
||||||
engines: {node: '>=8.0'}
|
engines: {node: '>=8.0'}
|
||||||
@ -2393,6 +2604,11 @@ packages:
|
|||||||
resolution: {integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=}
|
resolution: {integrity: sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/type-fest/0.10.0:
|
||||||
|
resolution: {integrity: sha512-EUV9jo4sffrwlg8s0zDhP0T2WD3pru5Xi0+HTE3zTUmBaZNhfkite9PdSJwdXLwPVW0jnAHT56pZHIOYckPEiw==}
|
||||||
|
engines: {node: '>=8'}
|
||||||
|
dev: false
|
||||||
|
|
||||||
/type-fest/0.21.3:
|
/type-fest/0.21.3:
|
||||||
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
|
@ -1,9 +1,19 @@
|
|||||||
import {ORMUser, Singleton, Unit} from '@extollo/lib'
|
import {Config, Inject, ORMUser, Singleton, Unit} from '@extollo/lib'
|
||||||
import {User} from './models/User.model'
|
import {User} from './models/User.model'
|
||||||
|
import {Gotify} from 'gotify'
|
||||||
|
|
||||||
@Singleton()
|
@Singleton()
|
||||||
export class AppUnit extends Unit {
|
export class AppUnit extends Unit {
|
||||||
|
@Inject()
|
||||||
|
protected readonly config!: Config
|
||||||
|
|
||||||
async up(): Promise<void> {
|
async up(): Promise<void> {
|
||||||
this.container().registerStaticOverride(ORMUser, User)
|
this.container().registerStaticOverride(ORMUser, User)
|
||||||
|
|
||||||
|
const gotify = new Gotify({
|
||||||
|
server: this.config.safe('gotify.server').string(),
|
||||||
|
})
|
||||||
|
|
||||||
|
this.container().registerSingletonInstance(Gotify, gotify)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
6
src/app/configs/gotify.config.ts
Normal file
6
src/app/configs/gotify.config.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { env } from '@extollo/lib'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
server: env('GOTIFY_SERVER'),
|
||||||
|
app: env('GOTIFY_TOKEN'),
|
||||||
|
}
|
@ -1,6 +1,22 @@
|
|||||||
import {Controller, view, Injectable, SecurityContext, Inject, Collection, Config, Routing, file, Application, plaintext} from '@extollo/lib'
|
import {
|
||||||
|
Controller,
|
||||||
|
view,
|
||||||
|
Injectable,
|
||||||
|
SecurityContext,
|
||||||
|
Inject,
|
||||||
|
Collection,
|
||||||
|
Config,
|
||||||
|
Routing,
|
||||||
|
file,
|
||||||
|
Application,
|
||||||
|
make,
|
||||||
|
Valid,
|
||||||
|
} from '@extollo/lib'
|
||||||
import {WorkItem} from '../../models/WorkItem.model'
|
import {WorkItem} from '../../models/WorkItem.model'
|
||||||
import {FeedPost} from '../../models/FeedPost.model'
|
import {FeedPost} from '../../models/FeedPost.model'
|
||||||
|
import {ContactForm} from '../../types/ContactForm.type'
|
||||||
|
import {ContactSubmission} from '../../models/ContactSubmission.model'
|
||||||
|
import {Gotify} from 'gotify'
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class Home extends Controller {
|
export class Home extends Controller {
|
||||||
@ -13,6 +29,9 @@ export class Home extends Controller {
|
|||||||
@Inject()
|
@Inject()
|
||||||
protected readonly routing!: Routing
|
protected readonly routing!: Routing
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
protected readonly gotify!: Gotify
|
||||||
|
|
||||||
public async welcome(feedPosts: Collection<FeedPost>) {
|
public async welcome(feedPosts: Collection<FeedPost>) {
|
||||||
const workItems = await this.getWorkItems()
|
const workItems = await this.getWorkItems()
|
||||||
|
|
||||||
@ -88,4 +107,29 @@ export class Home extends Controller {
|
|||||||
.appPath('resources', 'assets', 'humans.txt')
|
.appPath('resources', 'assets', 'humans.txt')
|
||||||
.read()
|
.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async contact(data: Valid<ContactForm>) {
|
||||||
|
const submission = make<ContactSubmission>(ContactSubmission)
|
||||||
|
submission.name = data.name
|
||||||
|
submission.email = data.email
|
||||||
|
submission.message = data.message
|
||||||
|
await submission.save()
|
||||||
|
|
||||||
|
this.gotify.send({
|
||||||
|
app: this.config.get('gotify.app'),
|
||||||
|
title: `Contact form submission from ${data.name}`,
|
||||||
|
message: [
|
||||||
|
`From: ${data.name}`,
|
||||||
|
`E-mail: ${data.email}`,
|
||||||
|
'Message:',
|
||||||
|
data.message,
|
||||||
|
].join('\n'),
|
||||||
|
})
|
||||||
|
|
||||||
|
return view('message', {
|
||||||
|
title: 'Message Sent',
|
||||||
|
message: 'Your message has been sent. Thanks! I\'ll be in touch soon.',
|
||||||
|
buttonAction: this.routing.getNamedPath('home').toRemote,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,15 @@
|
|||||||
|
import {ParameterMiddleware, Injectable, Either, ResponseObject, Validator, Valid, right} from '@extollo/lib'
|
||||||
|
import {ContactForm} from '../../../types/ContactForm.type'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ContactForm Middleware
|
||||||
|
* --------------------------------------------
|
||||||
|
* Parse the contact form data and validate it. Provide the fields as middleware.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class ValidContactForm extends ParameterMiddleware<Valid<ContactForm>> {
|
||||||
|
async handle(): Promise<Either<ResponseObject, Valid<ContactForm>>> {
|
||||||
|
const validator = new Validator<ContactForm>()
|
||||||
|
return right(validator.parse(this.request.input()))
|
||||||
|
}
|
||||||
|
}
|
@ -6,6 +6,7 @@ import {GoLinks} from '../controllers/GoLinks.controller'
|
|||||||
import {Feed} from '../controllers/Feed.controller'
|
import {Feed} from '../controllers/Feed.controller'
|
||||||
import {LoadSnippet} from '../middlewares/parameters/LoadSnippet.middleware'
|
import {LoadSnippet} from '../middlewares/parameters/LoadSnippet.middleware'
|
||||||
import {LoadFeedPosts} from '../middlewares/parameters/LoadFeedPosts.middleware'
|
import {LoadFeedPosts} from '../middlewares/parameters/LoadFeedPosts.middleware'
|
||||||
|
import {ValidContactForm} from '../middlewares/parameters/ValidContactForm.middleware'
|
||||||
|
|
||||||
Route
|
Route
|
||||||
.group('/', () => {
|
.group('/', () => {
|
||||||
@ -14,6 +15,11 @@ Route
|
|||||||
.calls<Home>(Home, home => home.welcome)
|
.calls<Home>(Home, home => home.welcome)
|
||||||
.alias('home')
|
.alias('home')
|
||||||
|
|
||||||
|
Route.post('/contact')
|
||||||
|
.parameterMiddleware(ValidContactForm)
|
||||||
|
.calls<Home>(Home, home => home.contact)
|
||||||
|
.alias('contact')
|
||||||
|
|
||||||
Route.get('/humans.txt')
|
Route.get('/humans.txt')
|
||||||
.calls<Home>(Home, home => home.humans)
|
.calls<Home>(Home, home => home.humans)
|
||||||
|
|
||||||
|
@ -0,0 +1,51 @@
|
|||||||
|
import {Injectable, Migration, Inject, DatabaseService, FieldType, raw} from '@extollo/lib'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CreateContactSubmissionsTableMigration
|
||||||
|
* ----------------------------------
|
||||||
|
* Put some description here.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export default class CreateContactSubmissionsTableMigration extends Migration {
|
||||||
|
@Inject()
|
||||||
|
protected readonly db!: DatabaseService
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Apply the migration.
|
||||||
|
*/
|
||||||
|
async up(): Promise<void> {
|
||||||
|
const schema = this.db.get().schema()
|
||||||
|
const table = await schema.table('contact_submissions')
|
||||||
|
|
||||||
|
table.primaryKey('contact_submission_id').required()
|
||||||
|
|
||||||
|
table.column('email')
|
||||||
|
.type(FieldType.varchar)
|
||||||
|
.required()
|
||||||
|
|
||||||
|
table.column('name')
|
||||||
|
.type(FieldType.varchar)
|
||||||
|
.required()
|
||||||
|
|
||||||
|
table.column('message')
|
||||||
|
.type(FieldType.text)
|
||||||
|
|
||||||
|
table.column('sent_at')
|
||||||
|
.type(FieldType.timestamp)
|
||||||
|
.default(raw('NOW()'))
|
||||||
|
|
||||||
|
await schema.commit(table)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undo the migration.
|
||||||
|
*/
|
||||||
|
async down(): Promise<void> {
|
||||||
|
const schema = this.db.get().schema()
|
||||||
|
const table = await schema.table('contact_submissions')
|
||||||
|
|
||||||
|
table.dropIfExists()
|
||||||
|
|
||||||
|
await schema.commit(table)
|
||||||
|
}
|
||||||
|
}
|
27
src/app/models/ContactSubmission.model.ts
Normal file
27
src/app/models/ContactSubmission.model.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import {Field, FieldType, Injectable, Model} from '@extollo/lib'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ContactSubmission Model
|
||||||
|
* -----------------------------------
|
||||||
|
* A message submitted via the contact form on my website.
|
||||||
|
*/
|
||||||
|
@Injectable()
|
||||||
|
export class ContactSubmission extends Model<ContactSubmission> {
|
||||||
|
protected static table = 'contact_submissions'
|
||||||
|
protected static key = 'contact_submission_id'
|
||||||
|
|
||||||
|
@Field(FieldType.serial, 'contact_submission_id')
|
||||||
|
protected id?: number
|
||||||
|
|
||||||
|
@Field(FieldType.varchar)
|
||||||
|
public email!: string
|
||||||
|
|
||||||
|
@Field(FieldType.varchar)
|
||||||
|
public name!: string
|
||||||
|
|
||||||
|
@Field(FieldType.text)
|
||||||
|
public message!: string
|
||||||
|
|
||||||
|
@Field(FieldType.timestamp, 'sent_at')
|
||||||
|
public sentAt = new Date()
|
||||||
|
}
|
@ -40,13 +40,11 @@ block content
|
|||||||
p I'd love to hear from you if you have questions or inquiries related to me or my projects. You can get in touch by text, e-mail, or using this form. I also occasionally share thoughts on my <a href="/blog">blog</a>.
|
p I'd love to hear from you if you have questions or inquiries related to me or my projects. You can get in touch by text, e-mail, or using this form. I also occasionally share thoughts on my <a href="/blog">blog</a>.
|
||||||
p <b>E-mail:</b> <a href="mailto:shout@garrettmills.dev">shout@garrettmills.dev</a>
|
p <b>E-mail:</b> <a href="mailto:shout@garrettmills.dev">shout@garrettmills.dev</a>
|
||||||
.form
|
.form
|
||||||
form#contact-form
|
form#contact-form(method='post' action=named('contact'))
|
||||||
.form-group
|
.form-group
|
||||||
input#contactEmail.form-control(type='email' name='email' placeholder='E-Mail Address' required)
|
input#contactEmail.form-control(type='email' name='email' placeholder='E-Mail Address' required)
|
||||||
.form-group
|
.form-group
|
||||||
input#contactFirst.form-control(name='first' placeholder='First Name' required)
|
input#contactFirst.form-control(name='name' placeholder='Name' required)
|
||||||
.form-group
|
|
||||||
input#contactLast.form-control(name='last' placeholder='Last Name' required)
|
|
||||||
.form-group
|
.form-group
|
||||||
textarea.form-control#contactMessage(name='message' placeholder='Message' required rows=6)
|
textarea.form-control#contactMessage(name='message' placeholder='Message' required rows=6)
|
||||||
.form-group
|
.form-group
|
||||||
|
12
src/app/types/ContactForm.type.ts
Normal file
12
src/app/types/ContactForm.type.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
/** A contact form submission. */
|
||||||
|
export interface ContactForm {
|
||||||
|
/** @email */
|
||||||
|
email: string
|
||||||
|
|
||||||
|
/** The submitter's name */
|
||||||
|
name: string
|
||||||
|
|
||||||
|
/** The body of the contact form submission. */
|
||||||
|
message: string
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user