2021-08-05 01:01:56 +03:00
|
|
|
import * as pulumi from '@pulumi/pulumi'
|
|
|
|
import * as aws from '@pulumi/aws'
|
2021-08-08 15:52:05 +03:00
|
|
|
import * as awsx from '@pulumi/awsx'
|
2021-08-05 01:01:56 +03:00
|
|
|
import * as cloud from '@pulumi/cloud'
|
|
|
|
|
|
|
|
import { getType } from 'mime'
|
|
|
|
|
|
|
|
import { readdirSync, lstatSync } from 'fs'
|
|
|
|
import { join } from 'path'
|
|
|
|
|
2021-08-08 15:52:05 +03:00
|
|
|
import { handle } from '@anticrm/dev-account'
|
|
|
|
|
2021-08-05 01:01:56 +03:00
|
|
|
const siteBucket = new aws.s3.Bucket('anticrm-app', {
|
|
|
|
acl: "public-read",
|
|
|
|
website: {
|
|
|
|
indexDocument: 'index.html'
|
|
|
|
}
|
|
|
|
})
|
|
|
|
|
2021-08-18 12:00:40 +03:00
|
|
|
const uploadBucket = new aws.s3.Bucket('anticrm-upload', {
|
|
|
|
acl: "public-read"
|
|
|
|
})
|
|
|
|
|
2021-08-05 01:01:56 +03:00
|
|
|
const buildDir = "../../dev/prod"
|
|
|
|
|
|
|
|
function createObjects(root: string, path: string): void {
|
|
|
|
const dir = join(root, path)
|
|
|
|
for (const file of readdirSync(dir)) {
|
|
|
|
const relative = join(path, file)
|
|
|
|
const absolute = join(root, relative)
|
|
|
|
if (lstatSync(absolute).isDirectory()) {
|
|
|
|
createObjects(root, relative)
|
|
|
|
} else {
|
|
|
|
new aws.s3.BucketObject(relative, {
|
|
|
|
acl: 'public-read',
|
|
|
|
bucket: siteBucket,
|
|
|
|
source: new pulumi.asset.FileAsset(absolute), // use FileAsset to point to a file
|
|
|
|
contentType: getType(absolute) || undefined, // set the MIME type of the file
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-08-07 20:44:29 +03:00
|
|
|
createObjects(buildDir + '/public', '')
|
|
|
|
createObjects(buildDir + '/dist', '')
|
2021-08-05 01:01:56 +03:00
|
|
|
|
|
|
|
export const bucketName = siteBucket.bucket // create a stack export for bucket name
|
|
|
|
export const websiteUrl = siteBucket.websiteEndpoint
|
|
|
|
|
2021-08-18 12:00:40 +03:00
|
|
|
export const uploadBucketName = uploadBucket.bucket
|
|
|
|
|
2021-08-05 01:01:56 +03:00
|
|
|
// // Create an S3 Bucket Policy to allow public read of all objects in bucket
|
|
|
|
// // This reusable function can be pulled out into its own module
|
|
|
|
// function publicReadPolicyForBucket(bucketName: string) {
|
|
|
|
// return JSON.stringify({
|
|
|
|
// Version: "2012-10-17",
|
|
|
|
// Statement: [{
|
|
|
|
// Effect: "Allow",
|
|
|
|
// Principal: "*",
|
|
|
|
// Action: [
|
|
|
|
// "s3:GetObject"
|
|
|
|
// ],
|
|
|
|
// Resource: [
|
|
|
|
// `arn:aws:s3:::${bucketName}/*` // policy refers to bucket name explicitly
|
|
|
|
// ]
|
|
|
|
// }]
|
|
|
|
// })
|
|
|
|
// }
|
|
|
|
|
|
|
|
// // Set the access policy for the bucket so all objects are readable
|
|
|
|
// new aws.s3.BucketPolicy('bucketPolicy', {
|
|
|
|
// bucket: siteBucket.bucket, // depends on siteBucket -- see explanation below
|
|
|
|
// policy: siteBucket.bucket.apply(publicReadPolicyForBucket)
|
|
|
|
// // transform the siteBucket.bucket output property -- see explanation below
|
|
|
|
// });
|
|
|
|
|
2021-08-08 15:52:05 +03:00
|
|
|
// D O C K E R
|
|
|
|
|
2021-08-09 15:36:44 +03:00
|
|
|
// const service = new cloud.Service("dev-server", {
|
|
|
|
// containers: {
|
|
|
|
// server: {
|
|
|
|
// build: "./dev-server",
|
|
|
|
// memory: 128,
|
|
|
|
// ports: [{ port: 3333 }],
|
|
|
|
// },
|
|
|
|
// },
|
|
|
|
// replicas: 1,
|
|
|
|
// })
|
|
|
|
|
|
|
|
// export const serverEndpoint = service.defaultEndpoint.hostname
|
2021-08-08 15:52:05 +03:00
|
|
|
|
|
|
|
//
|
|
|
|
// L O G I N
|
|
|
|
//
|
|
|
|
|
|
|
|
// Define a new GET endpoint that just returns a 200 and "hello" in the body.
|
|
|
|
const api = new awsx.apigateway.API("login", {
|
|
|
|
routes: [
|
|
|
|
{
|
|
|
|
path: "/",
|
|
|
|
method: "POST",
|
|
|
|
eventHandler: async (event) => {
|
|
|
|
let body = event.body;
|
|
|
|
if (event.isBase64Encoded) {
|
|
|
|
body = Buffer.from(body as string, 'base64').toString()
|
|
|
|
}
|
|
|
|
|
2021-08-29 10:20:14 +03:00
|
|
|
// const result = handle(body, 'wss://pacific-refuge-43514.herokuapp.com/')
|
|
|
|
const result = handle(body, 'wss://transactor.hc.engineering/')
|
2021-08-08 15:52:05 +03:00
|
|
|
return {
|
|
|
|
statusCode: result.statusCode,
|
2021-08-08 18:10:36 +03:00
|
|
|
headers: {
|
|
|
|
"Access-Control-Allow-Headers" : "Content-Type",
|
|
|
|
"Access-Control-Allow-Origin": "*",
|
|
|
|
"Access-Control-Allow-Methods": "OPTIONS,POST"
|
|
|
|
},
|
2021-08-08 15:52:05 +03:00
|
|
|
body: result.body
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
path: "/",
|
|
|
|
method: "OPTIONS",
|
|
|
|
eventHandler: async (event) => {
|
|
|
|
console.log('OPTIONS call')
|
|
|
|
return {
|
|
|
|
statusCode: 200,
|
|
|
|
headers: {
|
|
|
|
"Access-Control-Allow-Headers" : "Content-Type",
|
|
|
|
"Access-Control-Allow-Origin": "*",
|
|
|
|
"Access-Control-Allow-Methods": "OPTIONS,POST"
|
|
|
|
},
|
|
|
|
body: ''
|
|
|
|
}
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
|
|
|
|
// Export the auto-generated API Gateway base URL.
|
|
|
|
export const url = api.url
|
|
|
|
|
|
|
|
//
|
|
|
|
// D N S
|
|
|
|
//
|
|
|
|
|
2021-08-05 01:01:56 +03:00
|
|
|
// Get the hosted zone by domain name
|
|
|
|
const zoneId = aws.route53.getZone({ name: "hc.engineering." }).then(zone => zone.zoneId)
|
|
|
|
|
|
|
|
const tenMinutes = 60 * 10
|
|
|
|
|
|
|
|
// Per AWS, ACM certificate must be in the us-east-1 region.
|
|
|
|
const eastRegion = new aws.Provider("east", {
|
|
|
|
profile: aws.config.profile,
|
|
|
|
region: "us-east-1",
|
|
|
|
})
|
|
|
|
|
|
|
|
const certificate = new aws.acm.Certificate(
|
|
|
|
"certificate",
|
|
|
|
{
|
|
|
|
domainName: "app.hc.engineering",
|
|
|
|
validationMethod: "DNS",
|
|
|
|
},
|
|
|
|
{ provider: eastRegion }
|
|
|
|
);
|
|
|
|
|
|
|
|
const certificateValidationDomain = new aws.route53.Record(
|
|
|
|
"app.hc.engineering-validation",
|
|
|
|
{
|
|
|
|
name: certificate.domainValidationOptions[0].resourceRecordName,
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: certificate.domainValidationOptions[0].resourceRecordType,
|
|
|
|
records: [certificate.domainValidationOptions[0].resourceRecordValue],
|
|
|
|
ttl: tenMinutes,
|
|
|
|
}
|
|
|
|
);
|
|
|
|
|
|
|
|
const certificateValidation = new aws.acm.CertificateValidation(
|
|
|
|
"certificateValidation",
|
|
|
|
{
|
|
|
|
certificateArn: certificate.arn,
|
|
|
|
validationRecordFqdns: [certificateValidationDomain.fqdn],
|
|
|
|
},
|
|
|
|
{ provider: eastRegion }
|
|
|
|
)
|
|
|
|
|
|
|
|
// distributionArgs configures the CloudFront distribution. Relevant documentation:
|
|
|
|
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html
|
|
|
|
// https://www.terraform.io/docs/providers/aws/r/cloudfront_distribution.html
|
|
|
|
const distributionArgs: aws.cloudfront.DistributionArgs = {
|
|
|
|
enabled: true,
|
|
|
|
// Alternate aliases the CloudFront distribution can be reached at, in addition to https://xxxx.cloudfront.net.
|
|
|
|
// Required if you want to access the distribution via config.targetDomain as well.
|
|
|
|
aliases: ["app.hc.engineering"],
|
|
|
|
|
|
|
|
// We only specify one origin for this distribution, the S3 content bucket.
|
|
|
|
origins: [
|
|
|
|
{
|
|
|
|
originId: siteBucket.arn,
|
|
|
|
domainName: siteBucket.websiteEndpoint,
|
|
|
|
customOriginConfig: {
|
|
|
|
// Amazon S3 doesn't support HTTPS connections when using an S3 bucket configured as a website endpoint.
|
|
|
|
// https://docs.aws.amazon.com/AmazonCloudFront/latest/DeveloperGuide/distribution-web-values-specify.html#DownloadDistValuesOriginProtocolPolicy
|
|
|
|
originProtocolPolicy: "http-only",
|
|
|
|
httpPort: 80,
|
|
|
|
httpsPort: 443,
|
|
|
|
originSslProtocols: ["TLSv1.2"],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
|
|
|
|
defaultRootObject: "index.html",
|
|
|
|
|
|
|
|
// A CloudFront distribution can configure different cache behaviors based on the request path.
|
|
|
|
// Here we just specify a single, default cache behavior which is just read-only requests to S3.
|
|
|
|
defaultCacheBehavior: {
|
|
|
|
targetOriginId: siteBucket.arn,
|
|
|
|
|
|
|
|
viewerProtocolPolicy: "redirect-to-https",
|
|
|
|
allowedMethods: ["GET", "HEAD", "OPTIONS"],
|
|
|
|
cachedMethods: ["GET", "HEAD", "OPTIONS"],
|
|
|
|
|
|
|
|
forwardedValues: {
|
|
|
|
cookies: { forward: "none" },
|
|
|
|
queryString: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
minTtl: 0,
|
|
|
|
defaultTtl: tenMinutes,
|
|
|
|
maxTtl: tenMinutes,
|
|
|
|
},
|
|
|
|
|
|
|
|
// "All" is the most broad distribution, and also the most expensive.
|
|
|
|
// "100" is the least broad, and also the least expensive.
|
|
|
|
priceClass: "PriceClass_100",
|
|
|
|
|
|
|
|
// You can customize error responses. When CloudFront receives an error from the origin (e.g. S3 or some other
|
|
|
|
// web service) it can return a different error code, and return the response for a different resource.
|
|
|
|
customErrorResponses: [
|
|
|
|
{ errorCode: 404, responseCode: 200, responsePagePath: "/index.html" },
|
|
|
|
],
|
|
|
|
|
|
|
|
restrictions: {
|
|
|
|
geoRestriction: {
|
|
|
|
restrictionType: "none",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
|
|
|
viewerCertificate: {
|
|
|
|
acmCertificateArn: certificateValidation.certificateArn, // Per AWS, ACM certificate must be in the us-east-1 region.
|
|
|
|
sslSupportMethod: "sni-only",
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
const cdn = new aws.cloudfront.Distribution("cdn", distributionArgs)
|
|
|
|
|
|
|
|
// Create a Route53 A-record
|
|
|
|
new aws.route53.Record("targetDomain", {
|
|
|
|
name: "app.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "A",
|
|
|
|
aliases: [{
|
|
|
|
zoneId: cdn.hostedZoneId,
|
|
|
|
name: cdn.domainName,
|
|
|
|
evaluateTargetHealth: true,
|
|
|
|
}],
|
|
|
|
})
|
2021-08-19 10:36:12 +03:00
|
|
|
|
|
|
|
new aws.route53.Record("databaseRecord", {
|
|
|
|
name: "db.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "CNAME",
|
|
|
|
ttl: 300,
|
|
|
|
records: [
|
|
|
|
"xored.com"
|
|
|
|
]
|
|
|
|
})
|
2021-08-28 23:31:45 +03:00
|
|
|
|
|
|
|
new aws.route53.Record("frontRecord", {
|
|
|
|
name: "front.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "A",
|
|
|
|
ttl: 300,
|
|
|
|
records: [
|
2021-10-23 03:05:45 +03:00
|
|
|
"107.191.38.121"
|
2021-08-28 23:31:45 +03:00
|
|
|
]
|
|
|
|
})
|
2021-08-29 10:20:14 +03:00
|
|
|
|
|
|
|
new aws.route53.Record("transactorRecord", {
|
|
|
|
name: "transactor.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "A",
|
|
|
|
ttl: 300,
|
|
|
|
records: [
|
2021-10-23 03:05:45 +03:00
|
|
|
"107.191.38.121"
|
2021-08-29 10:20:14 +03:00
|
|
|
]
|
|
|
|
})
|
2021-08-29 12:39:33 +03:00
|
|
|
|
|
|
|
new aws.route53.Record("uploadRecord", {
|
|
|
|
name: "upload.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "A",
|
|
|
|
ttl: 300,
|
|
|
|
records: [
|
2021-10-23 03:05:45 +03:00
|
|
|
"107.191.38.121"
|
2021-08-29 12:39:33 +03:00
|
|
|
]
|
|
|
|
})
|
2021-09-10 13:26:11 +03:00
|
|
|
|
|
|
|
new aws.route53.Record("accountRecord", {
|
|
|
|
name: "account.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "A",
|
|
|
|
ttl: 300,
|
|
|
|
records: [
|
2021-10-23 03:05:45 +03:00
|
|
|
"107.191.38.121"
|
2021-09-10 13:26:11 +03:00
|
|
|
]
|
|
|
|
})
|
2021-11-29 20:13:04 +03:00
|
|
|
|
|
|
|
new aws.route53.Record("telegramRecord", {
|
|
|
|
name: "telegram.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "A",
|
|
|
|
ttl: 300,
|
|
|
|
records: [
|
|
|
|
"107.191.38.121"
|
|
|
|
]
|
|
|
|
})
|
2022-02-03 12:02:05 +03:00
|
|
|
|
|
|
|
new aws.route53.Record("rekoniRecord", {
|
|
|
|
name: "rekoni.hc.engineering",
|
|
|
|
zoneId: zoneId,
|
|
|
|
type: "A",
|
|
|
|
ttl: 300,
|
|
|
|
records: [
|
|
|
|
"107.191.38.121"
|
|
|
|
]
|
|
|
|
})
|