mirror of
https://github.com/documenso/documenso.git
synced 2025-11-10 12:32:34 +10:00
Compare commits
4 Commits
v1.5.4-rc.
...
experiment
| Author | SHA1 | Date | |
|---|---|---|---|
| fc329464ec | |||
| 574098f103 | |||
| 2819251ec4 | |||
| 53abb8f00b |
@ -11,6 +11,17 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- 54320:5432
|
- 54320:5432
|
||||||
|
|
||||||
|
queue:
|
||||||
|
image: postgres:15
|
||||||
|
container_name: queue
|
||||||
|
user: postgres
|
||||||
|
command: -c jit=off
|
||||||
|
environment:
|
||||||
|
POSTGRES_PASSWORD: password
|
||||||
|
POSTGRES_DB: queue
|
||||||
|
ports:
|
||||||
|
- 54321:5432
|
||||||
|
|
||||||
inbucket:
|
inbucket:
|
||||||
image: inbucket/inbucket
|
image: inbucket/inbucket
|
||||||
container_name: mailserver
|
container_name: mailserver
|
||||||
|
|||||||
211
package-lock.json
generated
211
package-lock.json
generated
@ -8443,6 +8443,18 @@
|
|||||||
"node": ">= 6.0.0"
|
"node": ">= 6.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/aggregate-error": {
|
||||||
|
"version": "3.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz",
|
||||||
|
"integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==",
|
||||||
|
"dependencies": {
|
||||||
|
"clean-stack": "^2.0.0",
|
||||||
|
"indent-string": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=8"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ajv": {
|
"node_modules/ajv": {
|
||||||
"version": "8.12.0",
|
"version": "8.12.0",
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz",
|
||||||
@ -9417,6 +9429,14 @@
|
|||||||
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
|
||||||
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
|
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
|
||||||
},
|
},
|
||||||
|
"node_modules/clean-stack": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cli-cursor": {
|
"node_modules/cli-cursor": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz",
|
||||||
@ -10192,6 +10212,17 @@
|
|||||||
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
"integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
|
||||||
"devOptional": true
|
"devOptional": true
|
||||||
},
|
},
|
||||||
|
"node_modules/cron-parser": {
|
||||||
|
"version": "4.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cron-parser/-/cron-parser-4.9.0.tgz",
|
||||||
|
"integrity": "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q==",
|
||||||
|
"dependencies": {
|
||||||
|
"luxon": "^3.2.1"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/cross-fetch": {
|
"node_modules/cross-fetch": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz",
|
||||||
@ -10558,6 +10589,17 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/delay": {
|
||||||
|
"version": "5.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delay/-/delay-5.0.0.tgz",
|
||||||
|
"integrity": "sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/delayed-stream": {
|
"node_modules/delayed-stream": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
@ -13672,7 +13714,6 @@
|
|||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
|
||||||
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
"integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
|
||||||
"dev": true,
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8"
|
"node": ">=8"
|
||||||
}
|
}
|
||||||
@ -17250,6 +17291,20 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/p-map": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"aggregate-error": "^3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/p-try": {
|
"node_modules/p-try": {
|
||||||
"version": "2.2.0",
|
"version": "2.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
|
||||||
@ -17559,6 +17614,124 @@
|
|||||||
"is-reference": "^3.0.0"
|
"is-reference": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/pg": {
|
||||||
|
"version": "8.11.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg/-/pg-8.11.5.tgz",
|
||||||
|
"integrity": "sha512-jqgNHSKL5cbDjFlHyYsCXmQDrfIX/3RsNwYqpd4N0Kt8niLuNoRNH+aazv6cOd43gPh9Y4DjQCtb+X0MH0Hvnw==",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-connection-string": "^2.6.4",
|
||||||
|
"pg-pool": "^3.6.2",
|
||||||
|
"pg-protocol": "^1.6.1",
|
||||||
|
"pg-types": "^2.1.0",
|
||||||
|
"pgpass": "1.x"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 8.0.0"
|
||||||
|
},
|
||||||
|
"optionalDependencies": {
|
||||||
|
"pg-cloudflare": "^1.1.1"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg-native": ">=3.0.1"
|
||||||
|
},
|
||||||
|
"peerDependenciesMeta": {
|
||||||
|
"pg-native": {
|
||||||
|
"optional": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-boss": {
|
||||||
|
"version": "9.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-boss/-/pg-boss-9.0.3.tgz",
|
||||||
|
"integrity": "sha512-cUWUiv3sr563yNy0nCZ25Tv5U0m59Y9MhX/flm0vTR012yeVCrqpfboaZP4xFOQPdWipMJpuu4g94HR0SncTgw==",
|
||||||
|
"dependencies": {
|
||||||
|
"cron-parser": "^4.0.0",
|
||||||
|
"delay": "^5.0.0",
|
||||||
|
"lodash.debounce": "^4.0.8",
|
||||||
|
"p-map": "^4.0.0",
|
||||||
|
"pg": "^8.5.1",
|
||||||
|
"serialize-error": "^8.1.0",
|
||||||
|
"uuid": "^9.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=16"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-boss/node_modules/uuid": {
|
||||||
|
"version": "9.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
|
||||||
|
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
|
||||||
|
"funding": [
|
||||||
|
"https://github.com/sponsors/broofa",
|
||||||
|
"https://github.com/sponsors/ctavan"
|
||||||
|
],
|
||||||
|
"bin": {
|
||||||
|
"uuid": "dist/bin/uuid"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-cloudflare": {
|
||||||
|
"version": "1.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-cloudflare/-/pg-cloudflare-1.1.1.tgz",
|
||||||
|
"integrity": "sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"node_modules/pg-connection-string": {
|
||||||
|
"version": "2.6.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-connection-string/-/pg-connection-string-2.6.4.tgz",
|
||||||
|
"integrity": "sha512-v+Z7W/0EO707aNMaAEfiGnGL9sxxumwLl2fJvCQtMn9Fxsg+lPpPkdcyBSv/KFgpGdYkMfn+EI1Or2EHjpgLCA=="
|
||||||
|
},
|
||||||
|
"node_modules/pg-int8": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-int8/-/pg-int8-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-pool": {
|
||||||
|
"version": "3.6.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-pool/-/pg-pool-3.6.2.tgz",
|
||||||
|
"integrity": "sha512-Htjbg8BlwXqSBQ9V8Vjtc+vzf/6fVUuak/3/XXKA9oxZprwW3IMDQTGHP+KDmVL7rtd+R1QjbnCFPuTHm3G4hg==",
|
||||||
|
"peerDependencies": {
|
||||||
|
"pg": ">=8.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pg-protocol": {
|
||||||
|
"version": "1.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-protocol/-/pg-protocol-1.6.1.tgz",
|
||||||
|
"integrity": "sha512-jPIlvgoD63hrEuihvIg+tJhoGjUsLPn6poJY9N5CnlPd91c2T18T/9zBtLxZSb1EhYxBRoZJtzScCaWlYLtktg=="
|
||||||
|
},
|
||||||
|
"node_modules/pg-types": {
|
||||||
|
"version": "2.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/pg-types/-/pg-types-2.2.0.tgz",
|
||||||
|
"integrity": "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==",
|
||||||
|
"dependencies": {
|
||||||
|
"pg-int8": "1.0.1",
|
||||||
|
"postgres-array": "~2.0.0",
|
||||||
|
"postgres-bytea": "~1.0.0",
|
||||||
|
"postgres-date": "~1.0.4",
|
||||||
|
"postgres-interval": "^1.1.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pgpass": {
|
||||||
|
"version": "1.0.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/pgpass/-/pgpass-1.0.5.tgz",
|
||||||
|
"integrity": "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug==",
|
||||||
|
"dependencies": {
|
||||||
|
"split2": "^4.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/pgpass/node_modules/split2": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 10.x"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/picocolors": {
|
"node_modules/picocolors": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz",
|
||||||
@ -17817,6 +17990,41 @@
|
|||||||
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
},
|
},
|
||||||
|
"node_modules/postgres-array": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-array/-/postgres-array-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=4"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-bytea": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-bytea/-/postgres-bytea-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-date": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-date/-/postgres-date-1.0.7.tgz",
|
||||||
|
"integrity": "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q==",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/postgres-interval": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postgres-interval/-/postgres-interval-1.2.0.tgz",
|
||||||
|
"integrity": "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ==",
|
||||||
|
"dependencies": {
|
||||||
|
"xtend": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/posthog-js": {
|
"node_modules/posthog-js": {
|
||||||
"version": "1.93.2",
|
"version": "1.93.2",
|
||||||
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.93.2.tgz",
|
"resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.93.2.tgz",
|
||||||
@ -24926,6 +25134,7 @@
|
|||||||
"next-auth": "4.24.5",
|
"next-auth": "4.24.5",
|
||||||
"oslo": "^0.17.0",
|
"oslo": "^0.17.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
|
"pg-boss": "^9.0.3",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"remeda": "^1.27.1",
|
"remeda": "^1.27.1",
|
||||||
"stripe": "^12.7.0",
|
"stripe": "^12.7.0",
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import type { SendMailOptions } from 'nodemailer';
|
||||||
import { createTransport } from 'nodemailer';
|
import { createTransport } from 'nodemailer';
|
||||||
|
|
||||||
import { ResendTransport } from '@documenso/nodemailer-resend';
|
import { ResendTransport } from '@documenso/nodemailer-resend';
|
||||||
@ -54,3 +55,4 @@ const getTransport = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const mailer = getTransport();
|
export const mailer = getTransport();
|
||||||
|
export type MailOptions = SendMailOptions;
|
||||||
|
|||||||
@ -27,18 +27,19 @@
|
|||||||
"@next-auth/prisma-adapter": "1.0.7",
|
"@next-auth/prisma-adapter": "1.0.7",
|
||||||
"@noble/ciphers": "0.4.0",
|
"@noble/ciphers": "0.4.0",
|
||||||
"@noble/hashes": "1.3.2",
|
"@noble/hashes": "1.3.2",
|
||||||
|
"@node-rs/bcrypt": "^1.10.0",
|
||||||
"@pdf-lib/fontkit": "^1.1.1",
|
"@pdf-lib/fontkit": "^1.1.1",
|
||||||
"@scure/base": "^1.1.3",
|
"@scure/base": "^1.1.3",
|
||||||
"@sindresorhus/slugify": "^2.2.1",
|
"@sindresorhus/slugify": "^2.2.1",
|
||||||
"@upstash/redis": "^1.20.6",
|
"@upstash/redis": "^1.20.6",
|
||||||
"@vvo/tzdb": "^6.117.0",
|
"@vvo/tzdb": "^6.117.0",
|
||||||
"@node-rs/bcrypt": "^1.10.0",
|
|
||||||
"luxon": "^3.4.0",
|
"luxon": "^3.4.0",
|
||||||
"nanoid": "^4.0.2",
|
"nanoid": "^4.0.2",
|
||||||
"next": "14.0.3",
|
"next": "14.0.3",
|
||||||
"next-auth": "4.24.5",
|
"next-auth": "4.24.5",
|
||||||
"oslo": "^0.17.0",
|
"oslo": "^0.17.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
|
"pg-boss": "^9.0.3",
|
||||||
"react": "18.2.0",
|
"react": "18.2.0",
|
||||||
"remeda": "^1.27.1",
|
"remeda": "^1.27.1",
|
||||||
"stripe": "^12.7.0",
|
"stripe": "^12.7.0",
|
||||||
|
|||||||
@ -2,12 +2,11 @@
|
|||||||
|
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import {
|
import { diffDocumentMetaChanges } from '@documenso/lib/utils/document-audit-logs';
|
||||||
createDocumentAuditLogData,
|
|
||||||
diffDocumentMetaChanges,
|
|
||||||
} from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type CreateDocumentMetaOptions = {
|
export type CreateDocumentMetaOptions = {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
subject?: string;
|
subject?: string;
|
||||||
@ -65,46 +64,45 @@ export const upsertDocumentMeta = async ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return await prisma.$transaction(async (tx) => {
|
const upsertedDocumentMeta = await prisma.documentMeta.upsert({
|
||||||
const upsertedDocumentMeta = await tx.documentMeta.upsert({
|
where: {
|
||||||
where: {
|
documentId,
|
||||||
|
},
|
||||||
|
create: {
|
||||||
|
subject,
|
||||||
|
message,
|
||||||
|
password,
|
||||||
|
dateFormat,
|
||||||
|
timezone,
|
||||||
|
documentId,
|
||||||
|
redirectUrl,
|
||||||
|
},
|
||||||
|
update: {
|
||||||
|
subject,
|
||||||
|
message,
|
||||||
|
password,
|
||||||
|
dateFormat,
|
||||||
|
timezone,
|
||||||
|
redirectUrl,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const changes = diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta);
|
||||||
|
|
||||||
|
if (changes.length > 0) {
|
||||||
|
await queueJob({
|
||||||
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED,
|
||||||
documentId,
|
documentId,
|
||||||
},
|
user,
|
||||||
create: {
|
requestMetadata,
|
||||||
subject,
|
data: {
|
||||||
message,
|
changes: diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta),
|
||||||
password,
|
},
|
||||||
dateFormat,
|
|
||||||
timezone,
|
|
||||||
documentId,
|
|
||||||
redirectUrl,
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
subject,
|
|
||||||
message,
|
|
||||||
password,
|
|
||||||
dateFormat,
|
|
||||||
timezone,
|
|
||||||
redirectUrl,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const changes = diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta);
|
return upsertedDocumentMeta;
|
||||||
|
|
||||||
if (changes.length > 0) {
|
|
||||||
await tx.documentAuditLog.create({
|
|
||||||
data: createDocumentAuditLogData({
|
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_META_UPDATED,
|
|
||||||
documentId,
|
|
||||||
user,
|
|
||||||
requestMetadata,
|
|
||||||
data: {
|
|
||||||
changes: diffDocumentMetaChanges(originalDocumentMeta ?? {}, upsertedDocumentMeta),
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return upsertedDocumentMeta;
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,15 +2,14 @@
|
|||||||
|
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import type { TRecipientActionAuth } from '../../types/document-auth';
|
import type { TRecipientActionAuth } from '../../types/document-auth';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||||
import { sealDocument } from './seal-document';
|
import { sealDocument } from './seal-document';
|
||||||
import { sendPendingEmail } from './send-pending-email';
|
|
||||||
|
|
||||||
export type CompleteDocumentWithTokenOptions = {
|
export type CompleteDocumentWithTokenOptions = {
|
||||||
token: string;
|
token: string;
|
||||||
@ -93,35 +92,34 @@ export const completeDocumentWithToken = async ({
|
|||||||
// throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values');
|
// throw new AppError(AppErrorCode.UNAUTHORIZED, 'Invalid authentication values');
|
||||||
// }
|
// }
|
||||||
|
|
||||||
await prisma.$transaction(async (tx) => {
|
await prisma.recipient.update({
|
||||||
await tx.recipient.update({
|
where: {
|
||||||
where: {
|
id: recipient.id,
|
||||||
id: recipient.id,
|
},
|
||||||
},
|
data: {
|
||||||
data: {
|
signingStatus: SigningStatus.SIGNED,
|
||||||
signingStatus: SigningStatus.SIGNED,
|
signedAt: new Date(),
|
||||||
signedAt: new Date(),
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
|
args: {
|
||||||
documentId: document.id,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_RECIPIENT_COMPLETED,
|
||||||
user: {
|
documentId: document.id,
|
||||||
name: recipient.name,
|
user: {
|
||||||
email: recipient.email,
|
name: recipient.name,
|
||||||
},
|
email: recipient.email,
|
||||||
requestMetadata,
|
},
|
||||||
data: {
|
requestMetadata,
|
||||||
recipientEmail: recipient.email,
|
data: {
|
||||||
recipientName: recipient.name,
|
recipientEmail: recipient.email,
|
||||||
recipientId: recipient.id,
|
recipientName: recipient.name,
|
||||||
recipientRole: recipient.role,
|
recipientId: recipient.id,
|
||||||
// actionAuth: derivedRecipientActionAuth || undefined,
|
recipientRole: recipient.role,
|
||||||
},
|
// actionAuth: derivedRecipientActionAuth || undefined,
|
||||||
}),
|
},
|
||||||
});
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const pendingRecipients = await prisma.recipient.count({
|
const pendingRecipients = await prisma.recipient.count({
|
||||||
@ -134,7 +132,13 @@ export const completeDocumentWithToken = async ({
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (pendingRecipients > 0) {
|
if (pendingRecipients > 0) {
|
||||||
await sendPendingEmail({ documentId, recipientId: recipient.id });
|
await queueJob({
|
||||||
|
job: 'send-pending-email',
|
||||||
|
args: {
|
||||||
|
documentId: document.id,
|
||||||
|
recipientId: recipient.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const documents = await prisma.document.updateMany({
|
const documents = await prisma.document.updateMany({
|
||||||
|
|||||||
@ -3,10 +3,10 @@
|
|||||||
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
import { AppError, AppErrorCode } from '@documenso/lib/errors/app-error';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||||
|
|
||||||
export type CreateDocumentOptions = {
|
export type CreateDocumentOptions = {
|
||||||
@ -44,35 +44,34 @@ export const createDocument = async ({
|
|||||||
throw new AppError(AppErrorCode.NOT_FOUND, 'Team not found');
|
throw new AppError(AppErrorCode.NOT_FOUND, 'Team not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
return await prisma.$transaction(async (tx) => {
|
const document = await prisma.document.create({
|
||||||
const document = await tx.document.create({
|
data: {
|
||||||
data: {
|
title,
|
||||||
title,
|
documentDataId,
|
||||||
documentDataId,
|
|
||||||
userId,
|
|
||||||
teamId,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
|
||||||
data: createDocumentAuditLogData({
|
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
|
|
||||||
documentId: document.id,
|
|
||||||
user,
|
|
||||||
requestMetadata,
|
|
||||||
data: {
|
|
||||||
title,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
await triggerWebhook({
|
|
||||||
event: WebhookTriggerEvents.DOCUMENT_CREATED,
|
|
||||||
data: document,
|
|
||||||
userId,
|
userId,
|
||||||
teamId,
|
teamId,
|
||||||
});
|
},
|
||||||
|
|
||||||
return document;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await queueJob({
|
||||||
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_CREATED,
|
||||||
|
documentId: document.id,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
title,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await triggerWebhook({
|
||||||
|
event: WebhookTriggerEvents.DOCUMENT_CREATED,
|
||||||
|
data: document,
|
||||||
|
userId,
|
||||||
|
teamId,
|
||||||
|
});
|
||||||
|
|
||||||
|
return document;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { render } from '@documenso/email/render';
|
import { render } from '@documenso/email/render';
|
||||||
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
|
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
@ -12,7 +11,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
|||||||
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
|
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type DeleteDocumentOptions = {
|
export type DeleteDocumentOptions = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -61,23 +60,22 @@ export const deleteDocument = async ({
|
|||||||
|
|
||||||
// if the document is a draft, hard-delete
|
// if the document is a draft, hard-delete
|
||||||
if (status === DocumentStatus.DRAFT) {
|
if (status === DocumentStatus.DRAFT) {
|
||||||
return await prisma.$transaction(async (tx) => {
|
// Currently redundant since deleting a document will delete the audit logs.
|
||||||
// Currently redundant since deleting a document will delete the audit logs.
|
// However may be useful if we disassociate audit lgos and documents if required.
|
||||||
// However may be useful if we disassociate audit lgos and documents if required.
|
await queueJob({
|
||||||
await tx.documentAuditLog.create({
|
job: 'create-document-audit-log',
|
||||||
data: createDocumentAuditLogData({
|
args: {
|
||||||
documentId: id,
|
documentId: id,
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
||||||
user,
|
user,
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
data: {
|
data: {
|
||||||
type: 'HARD',
|
type: 'HARD',
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
|
||||||
|
|
||||||
return await tx.document.delete({ where: { id, status: DocumentStatus.DRAFT } });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return await prisma.document.delete({ where: { id, status: DocumentStatus.DRAFT } });
|
||||||
}
|
}
|
||||||
|
|
||||||
// if the document is pending, send cancellation emails to all recipients
|
// if the document is pending, send cancellation emails to all recipients
|
||||||
@ -93,44 +91,46 @@ export const deleteDocument = async ({
|
|||||||
assetBaseUrl,
|
assetBaseUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
await mailer.sendMail({
|
await queueJob({
|
||||||
to: {
|
job: 'send-mail',
|
||||||
address: recipient.email,
|
args: {
|
||||||
name: recipient.name,
|
to: {
|
||||||
|
address: recipient.email,
|
||||||
|
name: recipient.name,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: 'Document Cancelled',
|
||||||
|
html: render(template),
|
||||||
|
text: render(template, { plainText: true }),
|
||||||
},
|
},
|
||||||
from: {
|
|
||||||
name: FROM_NAME,
|
|
||||||
address: FROM_ADDRESS,
|
|
||||||
},
|
|
||||||
subject: 'Document Cancelled',
|
|
||||||
html: render(template),
|
|
||||||
text: render(template, { plainText: true }),
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the document is not a draft, only soft-delete.
|
// If the document is not a draft, only soft-delete.
|
||||||
return await prisma.$transaction(async (tx) => {
|
await queueJob({
|
||||||
await tx.documentAuditLog.create({
|
job: 'create-document-audit-log',
|
||||||
data: createDocumentAuditLogData({
|
args: {
|
||||||
documentId: id,
|
documentId: id,
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
||||||
user,
|
user,
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
data: {
|
|
||||||
type: 'SOFT',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
return await tx.document.update({
|
|
||||||
where: {
|
|
||||||
id,
|
|
||||||
},
|
|
||||||
data: {
|
data: {
|
||||||
deletedAt: new Date().toISOString(),
|
type: 'SOFT',
|
||||||
},
|
},
|
||||||
});
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return await prisma.document.update({
|
||||||
|
where: {
|
||||||
|
id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
deletedAt: new Date().toISOString(),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { render } from '@documenso/email/render';
|
import { render } from '@documenso/email/render';
|
||||||
import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite';
|
import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document-invite';
|
||||||
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
|
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
|
||||||
@ -10,13 +9,13 @@ import {
|
|||||||
} from '@documenso/lib/constants/recipient-roles';
|
} from '@documenso/lib/constants/recipient-roles';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
|
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
||||||
import type { Prisma } from '@documenso/prisma/client';
|
import type { Prisma } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
import { getDocumentWhereInput } from './get-document-by-id';
|
import { getDocumentWhereInput } from './get-document-by-id';
|
||||||
|
|
||||||
export type ResendDocumentOptions = {
|
export type ResendDocumentOptions = {
|
||||||
@ -110,43 +109,42 @@ export const resendDocument = async ({
|
|||||||
|
|
||||||
const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role];
|
const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role];
|
||||||
|
|
||||||
await prisma.$transaction(
|
await queueJob({
|
||||||
async (tx) => {
|
job: 'send-mail',
|
||||||
await mailer.sendMail({
|
args: {
|
||||||
to: {
|
to: {
|
||||||
address: email,
|
address: email,
|
||||||
name,
|
name,
|
||||||
},
|
},
|
||||||
from: {
|
from: {
|
||||||
name: FROM_NAME,
|
name: FROM_NAME,
|
||||||
address: FROM_ADDRESS,
|
address: FROM_ADDRESS,
|
||||||
},
|
},
|
||||||
subject: customEmail?.subject
|
subject: customEmail?.subject
|
||||||
? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate)
|
? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate)
|
||||||
: `Please ${actionVerb.toLowerCase()} this document`,
|
: `Please ${actionVerb.toLowerCase()} this document`,
|
||||||
html: render(template),
|
html: render(template),
|
||||||
text: render(template, { plainText: true }),
|
text: render(template, { plainText: true }),
|
||||||
});
|
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
|
||||||
data: createDocumentAuditLogData({
|
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
|
||||||
documentId: document.id,
|
|
||||||
user,
|
|
||||||
requestMetadata,
|
|
||||||
data: {
|
|
||||||
emailType: recipientEmailType,
|
|
||||||
recipientEmail: recipient.email,
|
|
||||||
recipientName: recipient.name,
|
|
||||||
recipientRole: recipient.role,
|
|
||||||
recipientId: recipient.id,
|
|
||||||
isResending: true,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
{ timeout: 30_000 },
|
});
|
||||||
);
|
|
||||||
|
await queueJob({
|
||||||
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||||
|
documentId: document.id,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
emailType: recipientEmailType,
|
||||||
|
recipientEmail: recipient.email,
|
||||||
|
recipientName: recipient.name,
|
||||||
|
recipientRole: recipient.role,
|
||||||
|
recipientId: recipient.id,
|
||||||
|
isResending: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -5,21 +5,20 @@ import path from 'node:path';
|
|||||||
import { PDFDocument } from 'pdf-lib';
|
import { PDFDocument } from 'pdf-lib';
|
||||||
|
|
||||||
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
|
import PostHogServerClient from '@documenso/lib/server-only/feature-flags/get-post-hog-server-client';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, RecipientRole, SigningStatus } from '@documenso/prisma/client';
|
||||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||||
import { signPdf } from '@documenso/signing';
|
import { signPdf } from '@documenso/signing';
|
||||||
|
|
||||||
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { getFile } from '../../universal/upload/get-file';
|
import { getFile } from '../../universal/upload/get-file';
|
||||||
import { putFile } from '../../universal/upload/put-file';
|
import { putFile } from '../../universal/upload/put-file';
|
||||||
import { flattenAnnotations } from '../pdf/flatten-annotations';
|
import { flattenAnnotations } from '../pdf/flatten-annotations';
|
||||||
import { insertFieldInPDF } from '../pdf/insert-field-in-pdf';
|
import { insertFieldInPDF } from '../pdf/insert-field-in-pdf';
|
||||||
import { normalizeSignatureAppearances } from '../pdf/normalize-signature-appearances';
|
import { normalizeSignatureAppearances } from '../pdf/normalize-signature-appearances';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||||
import { sendCompletedEmail } from './send-completed-email';
|
|
||||||
|
|
||||||
export type SealDocumentOptions = {
|
export type SealDocumentOptions = {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
@ -126,31 +125,33 @@ export const sealDocument = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.$transaction(async (tx) => {
|
await prisma.documentData.update({
|
||||||
await tx.documentData.update({
|
where: {
|
||||||
where: {
|
id: documentData.id,
|
||||||
id: documentData.id,
|
},
|
||||||
},
|
data: {
|
||||||
data: {
|
data: newData,
|
||||||
data: newData,
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
|
args: {
|
||||||
documentId: document.id,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_COMPLETED,
|
||||||
requestMetadata,
|
documentId: document.id,
|
||||||
user: null,
|
requestMetadata,
|
||||||
data: {
|
user: null,
|
||||||
transactionId: nanoid(),
|
data: {
|
||||||
},
|
transactionId: nanoid(),
|
||||||
}),
|
},
|
||||||
});
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
if (sendEmail && !isResealing) {
|
if (sendEmail && !isResealing) {
|
||||||
await sendCompletedEmail({ documentId, requestMetadata });
|
await queueJob({
|
||||||
|
job: 'send-completed-email',
|
||||||
|
args: { documentId, requestMetadata },
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await triggerWebhook({
|
await triggerWebhook({
|
||||||
|
|||||||
@ -9,7 +9,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
|||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { getFile } from '../../universal/upload/get-file';
|
import { getFile } from '../../universal/upload/get-file';
|
||||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export interface SendDocumentOptions {
|
export interface SendDocumentOptions {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
@ -86,8 +86,9 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
await prisma.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||||
documentId: document.id,
|
documentId: document.id,
|
||||||
user: null,
|
user: null,
|
||||||
@ -100,7 +101,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
|||||||
recipientRole: 'OWNER',
|
recipientRole: 'OWNER',
|
||||||
isResending: false,
|
isResending: false,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -136,8 +137,9 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
await prisma.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||||
documentId: document.id,
|
documentId: document.id,
|
||||||
user: null,
|
user: null,
|
||||||
@ -150,7 +152,7 @@ export const sendCompletedEmail = async ({ documentId, requestMetadata }: SendDo
|
|||||||
recipientRole: recipient.role,
|
recipientRole: recipient.role,
|
||||||
isResending: false,
|
isResending: false,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|||||||
@ -6,7 +6,6 @@ import { DocumentInviteEmailTemplate } from '@documenso/email/templates/document
|
|||||||
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
|
import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
|
import { renderCustomEmailTemplate } from '@documenso/lib/utils/render-custom-email-template';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { DocumentStatus, RecipientRole, SendStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, RecipientRole, SendStatus } from '@documenso/prisma/client';
|
||||||
@ -17,6 +16,7 @@ import {
|
|||||||
RECIPIENT_ROLES_DESCRIPTION,
|
RECIPIENT_ROLES_DESCRIPTION,
|
||||||
RECIPIENT_ROLE_TO_EMAIL_TYPE,
|
RECIPIENT_ROLE_TO_EMAIL_TYPE,
|
||||||
} from '../../constants/recipient-roles';
|
} from '../../constants/recipient-roles';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||||
|
|
||||||
export type SendDocumentOptions = {
|
export type SendDocumentOptions = {
|
||||||
@ -113,79 +113,75 @@ export const sendDocument = async ({
|
|||||||
|
|
||||||
const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role];
|
const { actionVerb } = RECIPIENT_ROLES_DESCRIPTION[recipient.role];
|
||||||
|
|
||||||
await prisma.$transaction(
|
// TODO: Move this to a seperate queue of it's own
|
||||||
async (tx) => {
|
await mailer.sendMail({
|
||||||
await mailer.sendMail({
|
to: {
|
||||||
to: {
|
address: email,
|
||||||
address: email,
|
name,
|
||||||
name,
|
|
||||||
},
|
|
||||||
from: {
|
|
||||||
name: FROM_NAME,
|
|
||||||
address: FROM_ADDRESS,
|
|
||||||
},
|
|
||||||
subject: customEmail?.subject
|
|
||||||
? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate)
|
|
||||||
: `Please ${actionVerb.toLowerCase()} this document`,
|
|
||||||
html: render(template),
|
|
||||||
text: render(template, { plainText: true }),
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.recipient.update({
|
|
||||||
where: {
|
|
||||||
id: recipient.id,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
sendStatus: SendStatus.SENT,
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
|
||||||
data: createDocumentAuditLogData({
|
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
|
||||||
documentId: document.id,
|
|
||||||
user,
|
|
||||||
requestMetadata,
|
|
||||||
data: {
|
|
||||||
emailType: recipientEmailType,
|
|
||||||
recipientEmail: recipient.email,
|
|
||||||
recipientName: recipient.name,
|
|
||||||
recipientRole: recipient.role,
|
|
||||||
recipientId: recipient.id,
|
|
||||||
isResending: false,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
{ timeout: 30_000 },
|
from: {
|
||||||
);
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: customEmail?.subject
|
||||||
|
? renderCustomEmailTemplate(customEmail.subject, customEmailTemplate)
|
||||||
|
: `Please ${actionVerb.toLowerCase()} this document`,
|
||||||
|
html: render(template),
|
||||||
|
text: render(template, { plainText: true }),
|
||||||
|
});
|
||||||
|
|
||||||
|
await prisma.recipient.update({
|
||||||
|
where: {
|
||||||
|
id: recipient.id,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
sendStatus: SendStatus.SENT,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await queueJob({
|
||||||
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.EMAIL_SENT,
|
||||||
|
documentId: document.id,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
emailType: recipientEmailType,
|
||||||
|
recipientEmail: recipient.email,
|
||||||
|
recipientName: recipient.name,
|
||||||
|
recipientRole: recipient.role,
|
||||||
|
recipientId: recipient.id,
|
||||||
|
isResending: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
const updatedDocument = await prisma.$transaction(async (tx) => {
|
if (document.status === DocumentStatus.DRAFT) {
|
||||||
if (document.status === DocumentStatus.DRAFT) {
|
await queueJob({
|
||||||
await tx.documentAuditLog.create({
|
job: 'create-document-audit-log',
|
||||||
data: createDocumentAuditLogData({
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_SENT,
|
||||||
documentId: document.id,
|
documentId: document.id,
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
user,
|
user,
|
||||||
data: {},
|
data: {},
|
||||||
}),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return await tx.document.update({
|
|
||||||
where: {
|
|
||||||
id: documentId,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
status: DocumentStatus.PENDING,
|
|
||||||
},
|
|
||||||
include: {
|
|
||||||
Recipient: true,
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedDocument = await prisma.document.update({
|
||||||
|
where: {
|
||||||
|
id: documentId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
status: DocumentStatus.PENDING,
|
||||||
|
},
|
||||||
|
include: {
|
||||||
|
Recipient: true,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await triggerWebhook({
|
await triggerWebhook({
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { render } from '@documenso/email/render';
|
import { render } from '@documenso/email/render';
|
||||||
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
|
import DocumentCancelTemplate from '@documenso/email/templates/document-cancel';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
@ -12,7 +11,7 @@ import { NEXT_PUBLIC_WEBAPP_URL } from '../../constants/app';
|
|||||||
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
|
import { FROM_ADDRESS, FROM_NAME } from '../../constants/email';
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type SuperDeleteDocumentOptions = {
|
export type SuperDeleteDocumentOptions = {
|
||||||
id: number;
|
id: number;
|
||||||
@ -49,37 +48,39 @@ export const superDeleteDocument = async ({ id, requestMetadata }: SuperDeleteDo
|
|||||||
assetBaseUrl,
|
assetBaseUrl,
|
||||||
});
|
});
|
||||||
|
|
||||||
await mailer.sendMail({
|
await queueJob({
|
||||||
to: {
|
job: 'send-mail',
|
||||||
address: recipient.email,
|
args: {
|
||||||
name: recipient.name,
|
to: {
|
||||||
|
address: recipient.email,
|
||||||
|
name: recipient.name,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: 'Document Cancelled',
|
||||||
|
html: render(template),
|
||||||
|
text: render(template, { plainText: true }),
|
||||||
},
|
},
|
||||||
from: {
|
|
||||||
name: FROM_NAME,
|
|
||||||
address: FROM_ADDRESS,
|
|
||||||
},
|
|
||||||
subject: 'Document Cancelled',
|
|
||||||
html: render(template),
|
|
||||||
text: render(template, { plainText: true }),
|
|
||||||
});
|
});
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// always hard delete if deleted from admin
|
await queueJob({
|
||||||
return await prisma.$transaction(async (tx) => {
|
job: 'create-document-audit-log',
|
||||||
await tx.documentAuditLog.create({
|
args: {
|
||||||
data: createDocumentAuditLogData({
|
documentId: id,
|
||||||
documentId: id,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_DELETED,
|
user,
|
||||||
user,
|
requestMetadata,
|
||||||
requestMetadata,
|
data: {
|
||||||
data: {
|
type: 'HARD',
|
||||||
type: 'HARD',
|
},
|
||||||
},
|
},
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
return await tx.document.delete({ where: { id } });
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// always hard delete if deleted from admin
|
||||||
|
return await prisma.document.delete({ where: { id } });
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,9 +2,10 @@
|
|||||||
|
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type UpdateTitleOptions = {
|
export type UpdateTitleOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
teamId?: number;
|
teamId?: number;
|
||||||
@ -51,33 +52,32 @@ export const updateTitle = async ({
|
|||||||
return document;
|
return document;
|
||||||
}
|
}
|
||||||
|
|
||||||
return await prisma.$transaction(async (tx) => {
|
// Instead of doing everything in a transaction we can use our knowledge
|
||||||
// Instead of doing everything in a transaction we can use our knowledge
|
// of the current document title to ensure we aren't performing a conflicting
|
||||||
// of the current document title to ensure we aren't performing a conflicting
|
// update.
|
||||||
// update.
|
const updatedDocument = await prisma.document.update({
|
||||||
const updatedDocument = await tx.document.update({
|
where: {
|
||||||
where: {
|
id: documentId,
|
||||||
id: documentId,
|
title: document.title,
|
||||||
title: document.title,
|
},
|
||||||
},
|
data: {
|
||||||
data: {
|
title,
|
||||||
title,
|
},
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
|
||||||
data: createDocumentAuditLogData({
|
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED,
|
|
||||||
documentId,
|
|
||||||
user,
|
|
||||||
requestMetadata,
|
|
||||||
data: {
|
|
||||||
from: document.title,
|
|
||||||
to: updatedDocument.title,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedDocument;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await queueJob({
|
||||||
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_TITLE_UPDATED,
|
||||||
|
documentId,
|
||||||
|
user,
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
from: document.title,
|
||||||
|
to: updatedDocument.title,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedDocument;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,11 +1,11 @@
|
|||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { ReadStatus } from '@documenso/prisma/client';
|
import { ReadStatus } from '@documenso/prisma/client';
|
||||||
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
import { WebhookTriggerEvents } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
|
import type { TDocumentAccessAuthTypes } from '../../types/document-auth';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
import { triggerWebhook } from '../webhooks/trigger/trigger-webhook';
|
||||||
import { getDocumentAndRecipientByToken } from './get-document-by-token';
|
import { getDocumentAndRecipientByToken } from './get-document-by-token';
|
||||||
|
|
||||||
@ -33,34 +33,33 @@ export const viewedDocument = async ({
|
|||||||
|
|
||||||
const { documentId } = recipient;
|
const { documentId } = recipient;
|
||||||
|
|
||||||
await prisma.$transaction(async (tx) => {
|
await prisma.recipient.update({
|
||||||
await tx.recipient.update({
|
where: {
|
||||||
where: {
|
id: recipient.id,
|
||||||
id: recipient.id,
|
},
|
||||||
},
|
data: {
|
||||||
data: {
|
readStatus: ReadStatus.OPENED,
|
||||||
readStatus: ReadStatus.OPENED,
|
},
|
||||||
},
|
});
|
||||||
});
|
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
args: {
|
||||||
documentId,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_OPENED,
|
||||||
user: {
|
documentId,
|
||||||
name: recipient.name,
|
user: {
|
||||||
email: recipient.email,
|
name: recipient.name,
|
||||||
},
|
email: recipient.email,
|
||||||
requestMetadata,
|
},
|
||||||
data: {
|
requestMetadata,
|
||||||
recipientEmail: recipient.email,
|
data: {
|
||||||
recipientId: recipient.id,
|
recipientEmail: recipient.email,
|
||||||
recipientName: recipient.name,
|
recipientId: recipient.id,
|
||||||
recipientRole: recipient.role,
|
recipientName: recipient.name,
|
||||||
accessAuth: recipientAccessAuth || undefined,
|
recipientRole: recipient.role,
|
||||||
},
|
accessAuth: recipientAccessAuth || undefined,
|
||||||
}),
|
},
|
||||||
});
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const document = await getDocumentAndRecipientByToken({ token, requireAccessAuth: false });
|
const document = await getDocumentAndRecipientByToken({ token, requireAccessAuth: false });
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
|
|||||||
import type { FieldType, Team } from '@documenso/prisma/client';
|
import type { FieldType, Team } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type CreateFieldOptions = {
|
export type CreateFieldOptions = {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
@ -103,8 +103,9 @@ export const createField = async ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await prisma.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: 'FIELD_CREATED',
|
type: 'FIELD_CREATED',
|
||||||
documentId,
|
documentId,
|
||||||
user: {
|
user: {
|
||||||
@ -119,7 +120,7 @@ export const createField = async ({
|
|||||||
fieldType: field.type,
|
fieldType: field.type,
|
||||||
},
|
},
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return field;
|
return field;
|
||||||
|
|||||||
@ -2,7 +2,7 @@ import { prisma } from '@documenso/prisma';
|
|||||||
import type { Team } from '@documenso/prisma/client';
|
import type { Team } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type DeleteFieldOptions = {
|
export type DeleteFieldOptions = {
|
||||||
fieldId: number;
|
fieldId: number;
|
||||||
@ -67,8 +67,9 @@ export const deleteField = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: 'FIELD_DELETED',
|
type: 'FIELD_DELETED',
|
||||||
documentId,
|
documentId,
|
||||||
user: {
|
user: {
|
||||||
@ -83,7 +84,7 @@ export const deleteField = async ({
|
|||||||
fieldType: field.type,
|
fieldType: field.type,
|
||||||
},
|
},
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return field;
|
return field;
|
||||||
|
|||||||
@ -2,10 +2,11 @@
|
|||||||
|
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '@documenso/lib/types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
import type { RequestMetadata } from '@documenso/lib/universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '@documenso/lib/utils/document-audit-logs';
|
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
import { DocumentStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type RemovedSignedFieldWithTokenOptions = {
|
export type RemovedSignedFieldWithTokenOptions = {
|
||||||
token: string;
|
token: string;
|
||||||
fieldId: number;
|
fieldId: number;
|
||||||
@ -65,21 +66,22 @@ export const removeSignedFieldWithToken = async ({
|
|||||||
fieldId: field.id,
|
fieldId: field.id,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED,
|
args: {
|
||||||
documentId: document.id,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_UNINSERTED,
|
||||||
user: {
|
documentId: document.id,
|
||||||
name: recipient?.name,
|
user: {
|
||||||
email: recipient?.email,
|
name: recipient?.name,
|
||||||
},
|
email: recipient?.email,
|
||||||
requestMetadata,
|
},
|
||||||
data: {
|
requestMetadata,
|
||||||
field: field.type,
|
data: {
|
||||||
fieldId: field.secondaryId,
|
field: field.type,
|
||||||
},
|
fieldId: field.secondaryId,
|
||||||
}),
|
},
|
||||||
});
|
},
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import { prisma } from '@documenso/prisma';
|
|||||||
import type { Field, FieldType } from '@documenso/prisma/client';
|
import type { Field, FieldType } from '@documenso/prisma/client';
|
||||||
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
|
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export interface SetFieldsForDocumentOptions {
|
export interface SetFieldsForDocumentOptions {
|
||||||
userId: number;
|
userId: number;
|
||||||
documentId: number;
|
documentId: number;
|
||||||
@ -155,8 +157,9 @@ export const setFieldsForDocument = async ({
|
|||||||
|
|
||||||
// Handle field updated audit log.
|
// Handle field updated audit log.
|
||||||
if (field._persisted && changes.length > 0) {
|
if (field._persisted && changes.length > 0) {
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED,
|
||||||
documentId: documentId,
|
documentId: documentId,
|
||||||
user,
|
user,
|
||||||
@ -165,14 +168,15 @@ export const setFieldsForDocument = async ({
|
|||||||
changes,
|
changes,
|
||||||
...baseAuditLog,
|
...baseAuditLog,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle field created audit log.
|
// Handle field created audit log.
|
||||||
if (!field._persisted) {
|
if (!field._persisted) {
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_CREATED,
|
||||||
documentId: documentId,
|
documentId: documentId,
|
||||||
user,
|
user,
|
||||||
@ -180,7 +184,7 @@ export const setFieldsForDocument = async ({
|
|||||||
data: {
|
data: {
|
||||||
...baseAuditLog,
|
...baseAuditLog,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -12,9 +12,9 @@ import { AppError, AppErrorCode } from '../../errors/app-error';
|
|||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { TRecipientActionAuth } from '../../types/document-auth';
|
import type { TRecipientActionAuth } from '../../types/document-auth';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
|
||||||
import { extractDocumentAuthMethods } from '../../utils/document-auth';
|
import { extractDocumentAuthMethods } from '../../utils/document-auth';
|
||||||
import { isRecipientAuthorized } from '../document/is-recipient-authorized';
|
import { isRecipientAuthorized } from '../document/is-recipient-authorized';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type SignFieldWithTokenOptions = {
|
export type SignFieldWithTokenOptions = {
|
||||||
token: string;
|
token: string;
|
||||||
@ -168,8 +168,9 @@ export const signFieldWithToken = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.DOCUMENT_FIELD_INSERTED,
|
||||||
documentId: document.id,
|
documentId: document.id,
|
||||||
user: {
|
user: {
|
||||||
@ -199,7 +200,7 @@ export const signFieldWithToken = async ({
|
|||||||
}
|
}
|
||||||
: undefined,
|
: undefined,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedField;
|
return updatedField;
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import type { FieldType, Team } from '@documenso/prisma/client';
|
|||||||
|
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData, diffFieldChanges } from '../../utils/document-audit-logs';
|
import { diffFieldChanges } from '../../utils/document-audit-logs';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type UpdateFieldOptions = {
|
export type UpdateFieldOptions = {
|
||||||
fieldId: number;
|
fieldId: number;
|
||||||
@ -77,8 +78,9 @@ export const updateField = async ({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.FIELD_UPDATED,
|
||||||
documentId,
|
documentId,
|
||||||
user: {
|
user: {
|
||||||
@ -94,7 +96,7 @@ export const updateField = async ({
|
|||||||
changes: diffFieldChanges(oldField, updatedField),
|
changes: diffFieldChanges(oldField, updatedField),
|
||||||
},
|
},
|
||||||
requestMetadata,
|
requestMetadata,
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return updatedField;
|
return updatedField;
|
||||||
|
|||||||
52
packages/lib/server-only/queue/index.ts
Normal file
52
packages/lib/server-only/queue/index.ts
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import type { WorkHandler } from 'pg-boss';
|
||||||
|
import PgBoss from 'pg-boss';
|
||||||
|
|
||||||
|
import { jobHandlers } from './job';
|
||||||
|
|
||||||
|
type QueueState = {
|
||||||
|
isReady: boolean;
|
||||||
|
queue: PgBoss | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
let initPromise: Promise<PgBoss> | null = null;
|
||||||
|
const state: QueueState = {
|
||||||
|
isReady: false,
|
||||||
|
queue: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function initQueue() {
|
||||||
|
if (state.isReady) {
|
||||||
|
return state.queue as PgBoss;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (initPromise) {
|
||||||
|
return initPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
initPromise = (async () => {
|
||||||
|
const queue = new PgBoss({
|
||||||
|
connectionString: 'postgres://postgres:password@127.0.0.1:54321/queue',
|
||||||
|
|
||||||
|
schema: 'documenso_queue',
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await queue.start();
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Failed to start queue', error);
|
||||||
|
}
|
||||||
|
|
||||||
|
await Promise.all(
|
||||||
|
Object.entries(jobHandlers).map(async ([job, jobHandler]) => {
|
||||||
|
await queue.work(job, jobHandler as WorkHandler<unknown>);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
state.isReady = true;
|
||||||
|
state.queue = queue;
|
||||||
|
|
||||||
|
return queue;
|
||||||
|
})();
|
||||||
|
|
||||||
|
return initPromise;
|
||||||
|
}
|
||||||
85
packages/lib/server-only/queue/job.ts
Normal file
85
packages/lib/server-only/queue/job.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import type { WorkHandler } from 'pg-boss';
|
||||||
|
|
||||||
|
import type { MailOptions } from '@documenso/email/mailer';
|
||||||
|
import { mailer } from '@documenso/email/mailer';
|
||||||
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { initQueue } from '.';
|
||||||
|
import type { CreateDocumentAuditLogDataOptions } from '../../utils/document-audit-logs';
|
||||||
|
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
||||||
|
import {
|
||||||
|
type SendDocumentOptions as SendCompletedDocumentOptions,
|
||||||
|
sendCompletedEmail,
|
||||||
|
} from '../document/send-completed-email';
|
||||||
|
import { type SendPendingEmailOptions, sendPendingEmail } from '../document/send-pending-email';
|
||||||
|
|
||||||
|
type JobOptions = {
|
||||||
|
'send-mail': MailOptions;
|
||||||
|
'send-completed-email': SendCompletedDocumentOptions;
|
||||||
|
'send-pending-email': SendPendingEmailOptions;
|
||||||
|
'create-document-audit-log': CreateDocumentAuditLogDataOptions;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const jobHandlers: {
|
||||||
|
[K in keyof JobOptions]: WorkHandler<JobOptions[K]>;
|
||||||
|
} = {
|
||||||
|
'send-completed-email': async ({ id, name, data: { documentId, requestMetadata } }) => {
|
||||||
|
console.log('Running Queue: ', name, ' ', id);
|
||||||
|
|
||||||
|
await sendCompletedEmail({
|
||||||
|
documentId,
|
||||||
|
requestMetadata,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'send-pending-email': async ({ id, name, data: { documentId, recipientId } }) => {
|
||||||
|
console.log('Running Queue: ', name, ' ', id);
|
||||||
|
|
||||||
|
await sendPendingEmail({
|
||||||
|
documentId,
|
||||||
|
recipientId,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
'send-mail': async ({ id, name, data: { attachments, to, from, subject, html, text } }) => {
|
||||||
|
console.log('Running Queue: ', name, ' ', id);
|
||||||
|
|
||||||
|
await mailer.sendMail({
|
||||||
|
to,
|
||||||
|
from,
|
||||||
|
subject,
|
||||||
|
html,
|
||||||
|
text,
|
||||||
|
attachments,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// Audit Logs Queue
|
||||||
|
'create-document-audit-log': async ({
|
||||||
|
name,
|
||||||
|
data: { documentId, type, requestMetadata, user, data },
|
||||||
|
id,
|
||||||
|
}) => {
|
||||||
|
console.log('Running Queue: ', name, ' ', id);
|
||||||
|
|
||||||
|
await prisma.documentAuditLog.create({
|
||||||
|
data: createDocumentAuditLogData({
|
||||||
|
type,
|
||||||
|
documentId,
|
||||||
|
requestMetadata,
|
||||||
|
user,
|
||||||
|
data,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const queueJob = async ({
|
||||||
|
job,
|
||||||
|
args,
|
||||||
|
}: {
|
||||||
|
job: keyof JobOptions;
|
||||||
|
args?: JobOptions[keyof JobOptions];
|
||||||
|
}) => {
|
||||||
|
const queue = await initQueue();
|
||||||
|
|
||||||
|
await queue.send(job, args ?? {});
|
||||||
|
};
|
||||||
@ -3,7 +3,7 @@ import type { Team } from '@documenso/prisma/client';
|
|||||||
import { SendStatus } from '@documenso/prisma/client';
|
import { SendStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData } from '../../utils/document-audit-logs';
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type DeleteRecipientOptions = {
|
export type DeleteRecipientOptions = {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
@ -73,33 +73,30 @@ export const deleteRecipient = async ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const deletedRecipient = await prisma.$transaction(async (tx) => {
|
const deletedRecipient = await prisma.recipient.delete({
|
||||||
const deleted = await tx.recipient.delete({
|
where: {
|
||||||
where: {
|
id: recipient.id,
|
||||||
id: recipient.id,
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await queueJob({
|
||||||
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
|
type: 'RECIPIENT_DELETED',
|
||||||
|
documentId,
|
||||||
|
user: {
|
||||||
|
id: team?.id ?? user.id,
|
||||||
|
email: team?.name ?? user.email,
|
||||||
|
name: team ? '' : user.name,
|
||||||
},
|
},
|
||||||
});
|
data: {
|
||||||
|
recipientEmail: recipient.email,
|
||||||
await tx.documentAuditLog.create({
|
recipientName: recipient.name,
|
||||||
data: createDocumentAuditLogData({
|
recipientId: recipient.id,
|
||||||
type: 'RECIPIENT_DELETED',
|
recipientRole: recipient.role,
|
||||||
documentId,
|
},
|
||||||
user: {
|
requestMetadata,
|
||||||
id: team?.id ?? user.id,
|
},
|
||||||
email: team?.name ?? user.email,
|
|
||||||
name: team ? '' : user.name,
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
recipientEmail: recipient.email,
|
|
||||||
recipientName: recipient.name,
|
|
||||||
recipientId: recipient.id,
|
|
||||||
recipientRole: recipient.role,
|
|
||||||
},
|
|
||||||
requestMetadata,
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
return deleted;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return deletedRecipient;
|
return deletedRecipient;
|
||||||
|
|||||||
@ -17,6 +17,7 @@ import { RecipientRole } from '@documenso/prisma/client';
|
|||||||
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
|
import { SendStatus, SigningStatus } from '@documenso/prisma/client';
|
||||||
|
|
||||||
import { AppError, AppErrorCode } from '../../errors/app-error';
|
import { AppError, AppErrorCode } from '../../errors/app-error';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export interface SetRecipientsForDocumentOptions {
|
export interface SetRecipientsForDocumentOptions {
|
||||||
userId: number;
|
userId: number;
|
||||||
@ -203,8 +204,9 @@ export const setRecipientsForDocument = async ({
|
|||||||
|
|
||||||
// Handle recipient updated audit log.
|
// Handle recipient updated audit log.
|
||||||
if (recipient._persisted && changes.length > 0) {
|
if (recipient._persisted && changes.length > 0) {
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
|
||||||
documentId: documentId,
|
documentId: documentId,
|
||||||
user,
|
user,
|
||||||
@ -213,14 +215,15 @@ export const setRecipientsForDocument = async ({
|
|||||||
changes,
|
changes,
|
||||||
...baseAuditLog,
|
...baseAuditLog,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle recipient created audit log.
|
// Handle recipient created audit log.
|
||||||
if (!recipient._persisted) {
|
if (!recipient._persisted) {
|
||||||
await tx.documentAuditLog.create({
|
await queueJob({
|
||||||
data: createDocumentAuditLogData({
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED,
|
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_CREATED,
|
||||||
documentId: documentId,
|
documentId: documentId,
|
||||||
user,
|
user,
|
||||||
@ -229,7 +232,7 @@ export const setRecipientsForDocument = async ({
|
|||||||
...baseAuditLog,
|
...baseAuditLog,
|
||||||
actionAuth: recipient.actionAuth || undefined,
|
actionAuth: recipient.actionAuth || undefined,
|
||||||
},
|
},
|
||||||
}),
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,7 +3,8 @@ import type { RecipientRole, Team } from '@documenso/prisma/client';
|
|||||||
|
|
||||||
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
import { DOCUMENT_AUDIT_LOG_TYPE } from '../../types/document-audit-logs';
|
||||||
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../../universal/extract-request-metadata';
|
||||||
import { createDocumentAuditLogData, diffRecipientChanges } from '../../utils/document-audit-logs';
|
import { diffRecipientChanges } from '../../utils/document-audit-logs';
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type UpdateRecipientOptions = {
|
export type UpdateRecipientOptions = {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
@ -75,44 +76,43 @@ export const updateRecipient = async ({
|
|||||||
throw new Error('Recipient not found');
|
throw new Error('Recipient not found');
|
||||||
}
|
}
|
||||||
|
|
||||||
const updatedRecipient = await prisma.$transaction(async (tx) => {
|
const updatedRecipient = await prisma.recipient.update({
|
||||||
const persisted = await prisma.recipient.update({
|
where: {
|
||||||
where: {
|
id: recipient.id,
|
||||||
id: recipient.id,
|
},
|
||||||
},
|
data: {
|
||||||
data: {
|
email: email?.toLowerCase() ?? recipient.email,
|
||||||
email: email?.toLowerCase() ?? recipient.email,
|
name: name ?? recipient.name,
|
||||||
name: name ?? recipient.name,
|
role: role ?? recipient.role,
|
||||||
role: role ?? recipient.role,
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const changes = diffRecipientChanges(recipient, updatedRecipient);
|
||||||
|
|
||||||
|
if (changes.length > 0) {
|
||||||
|
await queueJob({
|
||||||
|
job: 'create-document-audit-log',
|
||||||
|
args: {
|
||||||
|
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
|
||||||
|
documentId: documentId,
|
||||||
|
user: {
|
||||||
|
id: team?.id ?? user.id,
|
||||||
|
name: team?.name ?? user.name,
|
||||||
|
email: team ? '' : user.email,
|
||||||
|
},
|
||||||
|
requestMetadata,
|
||||||
|
data: {
|
||||||
|
changes,
|
||||||
|
recipientId,
|
||||||
|
recipientEmail: updatedRecipient.email,
|
||||||
|
recipientName: updatedRecipient.name,
|
||||||
|
recipientRole: updatedRecipient.role,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const changes = diffRecipientChanges(recipient, persisted);
|
return updatedRecipient;
|
||||||
|
}
|
||||||
if (changes.length > 0) {
|
|
||||||
await tx.documentAuditLog.create({
|
|
||||||
data: createDocumentAuditLogData({
|
|
||||||
type: DOCUMENT_AUDIT_LOG_TYPE.RECIPIENT_UPDATED,
|
|
||||||
documentId: documentId,
|
|
||||||
user: {
|
|
||||||
id: team?.id ?? user.id,
|
|
||||||
name: team?.name ?? user.name,
|
|
||||||
email: team ? '' : user.email,
|
|
||||||
},
|
|
||||||
requestMetadata,
|
|
||||||
data: {
|
|
||||||
changes,
|
|
||||||
recipientId,
|
|
||||||
recipientEmail: persisted.email,
|
|
||||||
recipientName: persisted.name,
|
|
||||||
recipientRole: persisted.role,
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
});
|
|
||||||
|
|
||||||
return persisted;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return updatedRecipient;
|
return updatedRecipient;
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { createElement } from 'react';
|
|||||||
|
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { render } from '@documenso/email/render';
|
import { render } from '@documenso/email/render';
|
||||||
import { ConfirmTeamEmailTemplate } from '@documenso/email/templates/confirm-team-email';
|
import { ConfirmTeamEmailTemplate } from '@documenso/email/templates/confirm-team-email';
|
||||||
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
||||||
@ -13,6 +12,8 @@ import { createTokenVerification } from '@documenso/lib/utils/token-verification
|
|||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
import { Prisma } from '@documenso/prisma/client';
|
import { Prisma } from '@documenso/prisma/client';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type CreateTeamEmailVerificationOptions = {
|
export type CreateTeamEmailVerificationOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
teamId: number;
|
teamId: number;
|
||||||
@ -122,14 +123,17 @@ export const sendTeamEmailVerificationEmail = async (
|
|||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
|
|
||||||
await mailer.sendMail({
|
await queueJob({
|
||||||
to: email,
|
job: 'send-mail',
|
||||||
from: {
|
args: {
|
||||||
name: FROM_NAME,
|
to: email,
|
||||||
address: FROM_ADDRESS,
|
from: {
|
||||||
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: `A request to use your email has been initiated by ${teamName} on Documenso`,
|
||||||
|
html: render(template),
|
||||||
|
text: render(template, { plainText: true }),
|
||||||
},
|
},
|
||||||
subject: `A request to use your email has been initiated by ${teamName} on Documenso`,
|
|
||||||
html: render(template),
|
|
||||||
text: render(template, { plainText: true }),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -2,7 +2,6 @@ import { createElement } from 'react';
|
|||||||
|
|
||||||
import { nanoid } from 'nanoid';
|
import { nanoid } from 'nanoid';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { render } from '@documenso/email/render';
|
import { render } from '@documenso/email/render';
|
||||||
import type { TeamInviteEmailProps } from '@documenso/email/templates/team-invite';
|
import type { TeamInviteEmailProps } from '@documenso/email/templates/team-invite';
|
||||||
import { TeamInviteEmailTemplate } from '@documenso/email/templates/team-invite';
|
import { TeamInviteEmailTemplate } from '@documenso/email/templates/team-invite';
|
||||||
@ -15,6 +14,8 @@ import { prisma } from '@documenso/prisma';
|
|||||||
import { TeamMemberInviteStatus } from '@documenso/prisma/client';
|
import { TeamMemberInviteStatus } from '@documenso/prisma/client';
|
||||||
import type { TCreateTeamMemberInvitesMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
import type { TCreateTeamMemberInvitesMutationSchema } from '@documenso/trpc/server/team-router/schema';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type CreateTeamMemberInvitesOptions = {
|
export type CreateTeamMemberInvitesOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
userName: string;
|
userName: string;
|
||||||
@ -148,14 +149,17 @@ export const sendTeamMemberInviteEmail = async ({
|
|||||||
...emailTemplateOptions,
|
...emailTemplateOptions,
|
||||||
});
|
});
|
||||||
|
|
||||||
await mailer.sendMail({
|
await queueJob({
|
||||||
to: email,
|
job: 'send-mail',
|
||||||
from: {
|
args: {
|
||||||
name: FROM_NAME,
|
to: email,
|
||||||
address: FROM_ADDRESS,
|
from: {
|
||||||
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: `You have been invited to join ${emailTemplateOptions.teamName} on Documenso`,
|
||||||
|
html: render(template),
|
||||||
|
text: render(template, { plainText: true }),
|
||||||
},
|
},
|
||||||
subject: `You have been invited to join ${emailTemplateOptions.teamName} on Documenso`,
|
|
||||||
html: render(template),
|
|
||||||
text: render(template, { plainText: true }),
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { render } from '@documenso/email/render';
|
import { render } from '@documenso/email/render';
|
||||||
import { TeamEmailRemovedTemplate } from '@documenso/email/templates/team-email-removed';
|
import { TeamEmailRemovedTemplate } from '@documenso/email/templates/team-email-removed';
|
||||||
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
||||||
@ -8,6 +7,8 @@ import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
|
|||||||
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
|
import { TEAM_MEMBER_ROLE_PERMISSIONS_MAP } from '@documenso/lib/constants/teams';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type DeleteTeamEmailOptions = {
|
export type DeleteTeamEmailOptions = {
|
||||||
userId: number;
|
userId: number;
|
||||||
userEmail: string;
|
userEmail: string;
|
||||||
@ -73,18 +74,21 @@ export const deleteTeamEmail = async ({ userId, userEmail, teamId }: DeleteTeamE
|
|||||||
teamUrl: team.url,
|
teamUrl: team.url,
|
||||||
});
|
});
|
||||||
|
|
||||||
await mailer.sendMail({
|
await queueJob({
|
||||||
to: {
|
job: 'create-document-audit-log',
|
||||||
address: team.owner.email,
|
args: {
|
||||||
name: team.owner.name ?? '',
|
to: {
|
||||||
|
address: team.owner.email,
|
||||||
|
name: team.owner.name ?? '',
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: `Team email has been revoked for ${team.name}`,
|
||||||
|
html: render(template),
|
||||||
|
text: render(template, { plainText: true }),
|
||||||
},
|
},
|
||||||
from: {
|
|
||||||
name: FROM_NAME,
|
|
||||||
address: FROM_ADDRESS,
|
|
||||||
},
|
|
||||||
subject: `Team email has been revoked for ${team.name}`,
|
|
||||||
html: render(template),
|
|
||||||
text: render(template, { plainText: true }),
|
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Todo: Teams - Alert us.
|
// Todo: Teams - Alert us.
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { createElement } from 'react';
|
import { createElement } from 'react';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { render } from '@documenso/email/render';
|
import { render } from '@documenso/email/render';
|
||||||
import { TeamTransferRequestTemplate } from '@documenso/email/templates/team-transfer-request';
|
import { TeamTransferRequestTemplate } from '@documenso/email/templates/team-transfer-request';
|
||||||
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
import { WEBAPP_BASE_URL } from '@documenso/lib/constants/app';
|
||||||
@ -8,6 +7,8 @@ import { FROM_ADDRESS, FROM_NAME } from '@documenso/lib/constants/email';
|
|||||||
import { createTokenVerification } from '@documenso/lib/utils/token-verification';
|
import { createTokenVerification } from '@documenso/lib/utils/token-verification';
|
||||||
import { prisma } from '@documenso/prisma';
|
import { prisma } from '@documenso/prisma';
|
||||||
|
|
||||||
|
import { queueJob } from '../queue/job';
|
||||||
|
|
||||||
export type RequestTeamOwnershipTransferOptions = {
|
export type RequestTeamOwnershipTransferOptions = {
|
||||||
/**
|
/**
|
||||||
* The ID of the user initiating the transfer.
|
* The ID of the user initiating the transfer.
|
||||||
@ -93,15 +94,18 @@ export const requestTeamOwnershipTransfer = async ({
|
|||||||
token,
|
token,
|
||||||
});
|
});
|
||||||
|
|
||||||
await mailer.sendMail({
|
await queueJob({
|
||||||
to: newOwnerUser.email,
|
job: 'create-document-audit-log',
|
||||||
from: {
|
args: {
|
||||||
name: FROM_NAME,
|
to: newOwnerUser.email,
|
||||||
address: FROM_ADDRESS,
|
from: {
|
||||||
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: `You have been requested to take ownership of team ${team.name} on Documenso`,
|
||||||
|
html: render(template),
|
||||||
|
text: render(template, { plainText: true }),
|
||||||
},
|
},
|
||||||
subject: `You have been requested to take ownership of team ${team.name} on Documenso`,
|
|
||||||
html: render(template),
|
|
||||||
text: render(template, { plainText: true }),
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
{ timeout: 30_000 },
|
{ timeout: 30_000 },
|
||||||
|
|||||||
@ -25,7 +25,7 @@ import {
|
|||||||
import { ZRecipientAuthOptionsSchema } from '../types/document-auth';
|
import { ZRecipientAuthOptionsSchema } from '../types/document-auth';
|
||||||
import type { RequestMetadata } from '../universal/extract-request-metadata';
|
import type { RequestMetadata } from '../universal/extract-request-metadata';
|
||||||
|
|
||||||
type CreateDocumentAuditLogDataOptions<T = TDocumentAuditLog['type']> = {
|
export type CreateDocumentAuditLogDataOptions<T = TDocumentAuditLog['type']> = {
|
||||||
documentId: number;
|
documentId: number;
|
||||||
type: T;
|
type: T;
|
||||||
data: Extract<TDocumentAuditLog, { type: T }>['data'];
|
data: Extract<TDocumentAuditLog, { type: T }>['data'];
|
||||||
|
|||||||
@ -2,12 +2,12 @@ import { createElement } from 'react';
|
|||||||
|
|
||||||
import { PDFDocument } from 'pdf-lib';
|
import { PDFDocument } from 'pdf-lib';
|
||||||
|
|
||||||
import { mailer } from '@documenso/email/mailer';
|
|
||||||
import { renderAsync } from '@documenso/email/render';
|
import { renderAsync } from '@documenso/email/render';
|
||||||
import { DocumentSelfSignedEmailTemplate } from '@documenso/email/templates/document-self-signed';
|
import { DocumentSelfSignedEmailTemplate } from '@documenso/email/templates/document-self-signed';
|
||||||
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
import { NEXT_PUBLIC_WEBAPP_URL } from '@documenso/lib/constants/app';
|
||||||
import { FROM_ADDRESS, FROM_NAME, SERVICE_USER_EMAIL } from '@documenso/lib/constants/email';
|
import { FROM_ADDRESS, FROM_NAME, SERVICE_USER_EMAIL } from '@documenso/lib/constants/email';
|
||||||
import { insertFieldInPDF } from '@documenso/lib/server-only/pdf/insert-field-in-pdf';
|
import { insertFieldInPDF } from '@documenso/lib/server-only/pdf/insert-field-in-pdf';
|
||||||
|
import { queueJob } from '@documenso/lib/server-only/queue/job';
|
||||||
import { alphaid } from '@documenso/lib/universal/id';
|
import { alphaid } from '@documenso/lib/universal/id';
|
||||||
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
import { getFile } from '@documenso/lib/universal/upload/get-file';
|
||||||
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
import { putFile } from '@documenso/lib/universal/upload/put-file';
|
||||||
@ -160,19 +160,22 @@ export const singleplayerRouter = router({
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
// Send email to signer.
|
// Send email to signer.
|
||||||
await mailer.sendMail({
|
await queueJob({
|
||||||
to: {
|
job: 'send-mail',
|
||||||
address: signer.email,
|
args: {
|
||||||
name: signer.name,
|
to: {
|
||||||
|
address: signer.email,
|
||||||
|
name: signer.name,
|
||||||
|
},
|
||||||
|
from: {
|
||||||
|
name: FROM_NAME,
|
||||||
|
address: FROM_ADDRESS,
|
||||||
|
},
|
||||||
|
subject: 'Document signed',
|
||||||
|
html,
|
||||||
|
text,
|
||||||
|
attachments: [{ content: signedPdfBuffer, filename: documentName }],
|
||||||
},
|
},
|
||||||
from: {
|
|
||||||
name: FROM_NAME,
|
|
||||||
address: FROM_ADDRESS,
|
|
||||||
},
|
|
||||||
subject: 'Document signed',
|
|
||||||
html,
|
|
||||||
text,
|
|
||||||
attachments: [{ content: signedPdfBuffer, filename: documentName }],
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return token;
|
return token;
|
||||||
|
|||||||
@ -93,6 +93,7 @@
|
|||||||
"NEXT_PRIVATE_STRIPE_API_KEY",
|
"NEXT_PRIVATE_STRIPE_API_KEY",
|
||||||
"NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET",
|
"NEXT_PRIVATE_STRIPE_WEBHOOK_SECRET",
|
||||||
"NEXT_PRIVATE_GITHUB_TOKEN",
|
"NEXT_PRIVATE_GITHUB_TOKEN",
|
||||||
|
"NEXT_RUNTIME",
|
||||||
"CI",
|
"CI",
|
||||||
"VERCEL",
|
"VERCEL",
|
||||||
"VERCEL_ENV",
|
"VERCEL_ENV",
|
||||||
|
|||||||
Reference in New Issue
Block a user