Finalize self hosted with self certificate, resolve webhook issues.
This commit is contained in:
@@ -67,11 +67,11 @@ export function EsignatureModalContainer({ esignatureModal, toggleModalVisible,
|
||||
destroyOnHidden
|
||||
width={"80%"}
|
||||
>
|
||||
<div style={{ height: "600px", width: "100%" }}>
|
||||
<div style={{ height: "80vh", width: "100%" }}>
|
||||
{token ? (
|
||||
<EmbedUpdateDocumentV1
|
||||
presignToken={token}
|
||||
host="https://stg-app.documenso.com"
|
||||
host="https://sign.imex.online"
|
||||
documentId={documentId}
|
||||
externalId={`${jobid}|${currentUser?.email}`}
|
||||
className="esignature-embed"
|
||||
|
||||
@@ -313,6 +313,7 @@ export function JobAuditTrail({ bodyshop, jobId }) {
|
||||
columns={esigColumns}
|
||||
mobileColumnKeys={["title", "status"]}
|
||||
rowKey="id"
|
||||
scroll={{ x: true }}
|
||||
dataSource={data ? data.esignature_documents : []}
|
||||
/>
|
||||
</Card>
|
||||
|
||||
24
documenso/cert/certificate.crt
Normal file
24
documenso/cert/certificate.crt
Normal file
@@ -0,0 +1,24 @@
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIID9zCCAt+gAwIBAgIUTB4OhIqfXvT0mBKHwYAwDPq79ygwDQYJKoZIhvcNAQEL
|
||||
BQAwgYoxCzAJBgNVBAYTAkNBMQswCQYDVQQIDAJCQzESMBAGA1UEBwwJVmFuY291
|
||||
dmVyMRowGAYDVQQKDBFJbUVYIFN5c3RlbXMgSW5jLjEXMBUGA1UEAwwOaW1leHN5
|
||||
c3RlbXMuY2ExJTAjBgkqhkiG9w0BCQEWFmNvbnRhY3RAaW1leHN5c3RlbXMuY2Ew
|
||||
HhcNMjYwNDEzMjAxMDIzWhcNMzYwNDEwMjAxMDIzWjCBijELMAkGA1UEBhMCQ0Ex
|
||||
CzAJBgNVBAgMAkJDMRIwEAYDVQQHDAlWYW5jb3V2ZXIxGjAYBgNVBAoMEUltRVgg
|
||||
U3lzdGVtcyBJbmMuMRcwFQYDVQQDDA5pbWV4c3lzdGVtcy5jYTElMCMGCSqGSIb3
|
||||
DQEJARYWY29udGFjdEBpbWV4c3lzdGVtcy5jYTCCASIwDQYJKoZIhvcNAQEBBQAD
|
||||
ggEPADCCAQoCggEBAPE+5bcnfYsMyLzJr50bzpHHP8I+cdSkvu7lwGysPZCCxi4Z
|
||||
vkIDq4Q5xDa3ZZCeNZ9feELqm9ZjWpnaZj4CMbXMDpIucZHQJC9USCGavYhzNYu2
|
||||
G3IU7D834jd8GkwGMQuXkGiuQmQssIZIKfX+MaZ0KKrh8gJbxXZOfCp3fdYOnFPq
|
||||
BFCR0N/gTbeRboq36dG4vo1FanDLGroMS7FycGjyUTQv3CTWkGAOAPGQVrGZgvYM
|
||||
DtFr+7M2J/KCbUMobK0uc1scAjLgetXknzVPU3qA66F3Hi7oWykoFX8m9oX/OJnK
|
||||
/Gt8rIjRMOyQSK7dKT7qXCxgQVQnqHbyUCX4WUkCAwEAAaNTMFEwHQYDVR0OBBYE
|
||||
FIRKLjeI+adC7yNg6cSDj72Kej11MB8GA1UdIwQYMBaAFIRKLjeI+adC7yNg6cSD
|
||||
j72Kej11MA8GA1UdEwEB/wQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAAHCSjlG
|
||||
bo5miEfisKffPyfzufBIhOLLORasuFQ3gVKBU32JytuoflABfcqy3prgZxbFLMB2
|
||||
fDcSImKuOtt79OMeMlA+ptfkWuOpFMqL2j6BilzjJ/MAlPAZlZmmuLh/fPj3lbMD
|
||||
QQds/YhSmZcTdRX8seQslnYq1AT7629BDbpCjjL3pRkntnePR7u8tgb28Pm8Vl3S
|
||||
uCnGS/mMxrS/7z+QnaDi1N/nyIwa2bQtGmsoMn+CzuUUjyMD4TYbdUJv+fca8/tR
|
||||
zezNEHcpBCKGGgZRowhifJwEoel0M1iEo8UYy5eFPDF8CoRGRIH7QSaduCfnej06
|
||||
KLtevL/vyhUpTMA=
|
||||
-----END CERTIFICATE-----
|
||||
BIN
documenso/cert/certificate.p12
Normal file
BIN
documenso/cert/certificate.p12
Normal file
Binary file not shown.
@@ -9,7 +9,7 @@ This Terraform stack deploys Documenso to AWS in `ca-central-1` using:
|
||||
- Route53 DNS for `esignature.imex.online`
|
||||
- Optional SES domain identity and DKIM management for outbound email
|
||||
- Secrets Manager for generated application secrets, SMTP credentials, and the optional Documenso signing certificate
|
||||
- AWS WAF with a basic managed rule set and rate limiting
|
||||
- AWS WAF with a basic managed rule set, rate limiting, and an allowlist for trusted IPv4 CIDRs
|
||||
- CloudWatch alarms for ALB, ECS, and RDS health indicators
|
||||
|
||||
## Why this shape
|
||||
@@ -47,9 +47,10 @@ This is the most practical fit for your Docker Compose workload if you want a ba
|
||||
2. If you want Documenso signing enabled, add `signing_certificate_base64` and `signing_certificate_passphrase`.
|
||||
3. Optionally set `upload_bucket_name` if you want a specific S3 bucket name.
|
||||
4. Set `manage_ses_resources = true` only if you want this stack to own SES identity verification and DKIM records.
|
||||
5. Run `terraform init`.
|
||||
6. Run `terraform plan`.
|
||||
7. Run `terraform apply`.
|
||||
5. Set `waf_bypass_ipv4_cidrs` with any public `/32` addresses that should bypass WAF inspection. The VPC CIDR is already allowlisted automatically.
|
||||
6. Run `terraform init`.
|
||||
7. Run `terraform plan`.
|
||||
8. Run `terraform apply`.
|
||||
|
||||
## Recommended first production adjustments
|
||||
|
||||
|
||||
@@ -26,6 +26,10 @@ locals {
|
||||
smtp_host = "email-smtp.${var.aws_region}.amazonaws.com"
|
||||
s3_bucket_name = coalesce(var.upload_bucket_name, "${local.name_prefix}-${data.aws_caller_identity.current.account_id}-${var.aws_region}")
|
||||
app_secret_name = coalesce(var.app_secret_name, "${local.name_prefix}/${replace(var.domain_name, ".", "-")}/app")
|
||||
waf_bypass_ipv4_cidrs = distinct(concat(
|
||||
[var.vpc_cidr],
|
||||
var.waf_bypass_ipv4_cidrs
|
||||
))
|
||||
common_tags = merge(var.tags, {
|
||||
Application = var.project_name
|
||||
ManagedBy = "Terraform"
|
||||
@@ -44,6 +48,7 @@ locals {
|
||||
NEXT_PRIVATE_ALLOWED_SIGNUP_DOMAINS = var.allowed_signup_domains
|
||||
NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID = aws_iam_access_key.documenso_upload.id
|
||||
NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY = aws_iam_access_key.documenso_upload.secret
|
||||
NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY = var.documenso_license_key
|
||||
},
|
||||
trimspace(var.signing_certificate_base64) != "" ? {
|
||||
NEXT_PRIVATE_SIGNING_LOCAL_FILE_CONTENTS = var.signing_certificate_base64
|
||||
@@ -66,7 +71,8 @@ locals {
|
||||
"NEXT_PRIVATE_SMTP_FROM_ADDRESS",
|
||||
"NEXT_PRIVATE_ALLOWED_SIGNUP_DOMAINS",
|
||||
"NEXT_PRIVATE_UPLOAD_ACCESS_KEY_ID",
|
||||
"NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY"
|
||||
"NEXT_PRIVATE_UPLOAD_SECRET_ACCESS_KEY",
|
||||
"NEXT_PRIVATE_DOCUMENSO_LICENSE_KEY"
|
||||
] : {
|
||||
name = secret_name
|
||||
valueFrom = "${aws_secretsmanager_secret.app.arn}:${secret_name}::"
|
||||
@@ -634,6 +640,16 @@ resource "aws_lb_listener" "https" {
|
||||
depends_on = [aws_acm_certificate_validation.this]
|
||||
}
|
||||
|
||||
resource "aws_wafv2_ip_set" "trusted_ipv4" {
|
||||
name = "${local.name_prefix}-trusted-ipv4"
|
||||
description = "IPv4 CIDRs that bypass the Documenso WAF rules"
|
||||
scope = "REGIONAL"
|
||||
ip_address_version = "IPV4"
|
||||
addresses = local.waf_bypass_ipv4_cidrs
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
|
||||
resource "aws_wafv2_web_acl" "this" {
|
||||
name = "${local.name_prefix}-web-acl"
|
||||
description = "WAF protection for Documenso"
|
||||
@@ -643,6 +659,27 @@ resource "aws_wafv2_web_acl" "this" {
|
||||
allow {}
|
||||
}
|
||||
|
||||
rule {
|
||||
name = "AllowTrustedIpv4"
|
||||
priority = 0
|
||||
|
||||
action {
|
||||
allow {}
|
||||
}
|
||||
|
||||
statement {
|
||||
ip_set_reference_statement {
|
||||
arn = aws_wafv2_ip_set.trusted_ipv4.arn
|
||||
}
|
||||
}
|
||||
|
||||
visibility_config {
|
||||
cloudwatch_metrics_enabled = true
|
||||
metric_name = "AllowTrustedIpv4"
|
||||
sampled_requests_enabled = true
|
||||
}
|
||||
}
|
||||
|
||||
rule {
|
||||
name = "AWSManagedRulesCommonRuleSet"
|
||||
priority = 1
|
||||
@@ -1004,4 +1041,4 @@ resource "aws_cloudwatch_metric_alarm" "rds_free_storage_low" {
|
||||
}
|
||||
|
||||
tags = local.common_tags
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -229,7 +229,7 @@ variable "smtp_password" {
|
||||
variable "smtp_from_name" {
|
||||
description = "Display name used in outbound email."
|
||||
type = string
|
||||
default = "ImEX E-Signature"
|
||||
default = "ImEX Sign"
|
||||
}
|
||||
|
||||
variable "smtp_from_address" {
|
||||
@@ -269,6 +269,12 @@ variable "waf_rate_limit" {
|
||||
default = 2000
|
||||
}
|
||||
|
||||
variable "waf_bypass_ipv4_cidrs" {
|
||||
description = "Additional IPv4 CIDR blocks that bypass the WAF. The VPC CIDR is always included automatically."
|
||||
type = list(string)
|
||||
default = []
|
||||
}
|
||||
|
||||
variable "alarm_actions" {
|
||||
description = "Optional list of SNS topic ARNs or other alarm actions to invoke when CloudWatch alarms fire."
|
||||
type = list(string)
|
||||
@@ -303,4 +309,10 @@ variable "rds_free_storage_alarm_threshold_bytes" {
|
||||
description = "Alarm threshold for low RDS free storage, in bytes."
|
||||
type = number
|
||||
default = 5368709120
|
||||
}
|
||||
|
||||
variable "documenso_license_key" {
|
||||
description = "Documenso license key. Not required for the free community edition, but required for enterprise features and support."
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
@@ -24,6 +24,15 @@
|
||||
- name: x-imex-auth
|
||||
value_from_env: DATAPUMP_AUTH
|
||||
comment: Project Mexico
|
||||
- name: Chatter API Data Pump
|
||||
webhook: '{{HASURA_API_URL}}/data/chatter-api'
|
||||
schedule: 45 4 * * *
|
||||
include_in_metadata: true
|
||||
payload: {}
|
||||
headers:
|
||||
- name: x-imex-auth
|
||||
value_from_env: DATAPUMP_AUTH
|
||||
comment: ""
|
||||
- name: Chatter Data Pump
|
||||
webhook: '{{HASURA_API_URL}}/data/chatter'
|
||||
schedule: 45 5 * * *
|
||||
|
||||
@@ -1891,6 +1891,14 @@
|
||||
- name: job
|
||||
using:
|
||||
foreign_key_constraint_on: jobid
|
||||
array_relationships:
|
||||
- name: esignature_documents
|
||||
using:
|
||||
foreign_key_constraint_on:
|
||||
column: documentid
|
||||
table:
|
||||
name: esignature_documents
|
||||
schema: public
|
||||
insert_permissions:
|
||||
- role: user
|
||||
permission:
|
||||
|
||||
@@ -3,10 +3,11 @@ const { Documenso } = require("@documenso/sdk-typescript");
|
||||
const axios = require("axios");
|
||||
const { jsrAuthString } = require("../utils/utils");
|
||||
const logger = require("../utils/logger");
|
||||
const DOCUMENSO_API_KEY = "api_asojim0czruv13ud";//Done on a by team basis,
|
||||
//Need to pull the key dynamically to send documents.
|
||||
const DOCUMENSO_API_KEY = "api_io2lssosg9v4p2mb";//Done on a by team basis,
|
||||
const documenso = new Documenso({
|
||||
apiKey: DOCUMENSO_API_KEY,//Done on a by team basis,
|
||||
serverURL: "https://stg-app.documenso.com/api/v2",
|
||||
serverURL: "https://sign.imex.online/api/v2",
|
||||
});
|
||||
const JSR_SERVER = "https://reports.test.imex.online";
|
||||
const jsreport = require("@jsreport/nodejs-client");
|
||||
@@ -216,9 +217,9 @@ async function newEsignDocument(req, res) {
|
||||
catch (error) {
|
||||
logger.log(`esig-new-error`, "ERROR", "esig", "api", {
|
||||
message: error.message, stack: error.stack,
|
||||
body: req.body
|
||||
body: _.omit(req.body, ["bodyshop"]) // bodyshop can be large, so we omit it from the logs
|
||||
});
|
||||
res.status(500).json({ error: "An error occurred while creating the e-sign document." });
|
||||
res.status(500).json({ error: "An error occurred while creating the e-sign document.", message: error.message });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,8 +7,8 @@ const { log } = require("node-persist");
|
||||
|
||||
const client = require('../graphql-client/graphql-client').client;
|
||||
const documenso = new Documenso({
|
||||
apiKey: "api_asojim0czruv13ud",//Done on a by team basis,
|
||||
serverURL: "https://stg-app.documenso.com/api/v2",
|
||||
apiKey: "api_io2lssosg9v4p2mb",// Centralize key and pull dynamically.
|
||||
serverURL: "https://sign.imex.online/api/v2",
|
||||
});
|
||||
|
||||
const webhookTypeEnums = {
|
||||
@@ -22,7 +22,6 @@ const webhookTypeEnums = {
|
||||
}
|
||||
|
||||
async function esignWebhook(req, res) {
|
||||
console.log("Esign Webhook Received:", req.body);
|
||||
try {
|
||||
const message = req.body
|
||||
logger.log(`esig-webhook-received`, "DEBUG", "redis", "api", {
|
||||
@@ -30,11 +29,12 @@ async function esignWebhook(req, res) {
|
||||
body: message
|
||||
});
|
||||
|
||||
const documentId = (message.payload?.id || message.payload?.payload?.id)?.toString()
|
||||
//TODO: Implement checks to prevent this from going backwards in status? If a request fails, it retries, which could cause a document marked as completed to be marked as rejected if the rejection event is processed after the completion event.
|
||||
switch (message.event) {
|
||||
case webhookTypeEnums.DOCUMENT_OPENED:
|
||||
await client.request(UPDATE_ESIGNATURE_DOCUMENT, {
|
||||
external_document_id: message.payload?.payload?.id?.toString(),
|
||||
external_document_id: documentId,
|
||||
esig_update: {
|
||||
status: "OPENED",
|
||||
opened: true,
|
||||
@@ -43,7 +43,7 @@ async function esignWebhook(req, res) {
|
||||
break;
|
||||
case webhookTypeEnums.DOCUMENT_REJECTED:
|
||||
await client.request(UPDATE_ESIGNATURE_DOCUMENT, {
|
||||
external_document_id: message.payload?.payload?.id?.toString(),
|
||||
external_document_id: documentId,
|
||||
esig_update: {
|
||||
status: "REJECTED",
|
||||
rejected: true,
|
||||
@@ -52,19 +52,19 @@ async function esignWebhook(req, res) {
|
||||
break;
|
||||
case webhookTypeEnums.DOCUMENT_CREATED:
|
||||
//This is largely a throwaway event we know it was created.
|
||||
console.log("Document created event received. Document ID:", message.payload?.payload?.documentId);
|
||||
console.log("Document created event received. Document ID:", documentId);
|
||||
// Here you can add any additional processing you want to do when a document is created
|
||||
break;
|
||||
case webhookTypeEnums.DOCUMENT_COMPLETED:
|
||||
console.log("Document completed event received. Document ID:", message.payload?.payload?.documentId);
|
||||
console.log("Document completed event received. Document ID:", documentId);
|
||||
await handleDocumentCompleted(message.payload);
|
||||
// Here you can add any additional processing you want to do when a document is completed
|
||||
break;
|
||||
case webhookTypeEnums.DOCUMENT_SIGNED:
|
||||
console.log("Document signed event received. Document ID:", message.payload?.payload?.documentId);
|
||||
console.log("Document signed event received. Document ID:", documentId);
|
||||
// Here you can add any additional processing you want to do when a document is signed
|
||||
await client.request(UPDATE_ESIGNATURE_DOCUMENT, {
|
||||
external_document_id: message.payload?.payload?.id?.toString(),
|
||||
external_document_id: documentId,
|
||||
esig_update: {
|
||||
status: "SIGNED",
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user