From 4691e2f084990b4987d39cfc158b53f417280616 Mon Sep 17 00:00:00 2001 From: Yaroslav Pshenichnikov Date: Tue, 30 Jul 2024 13:24:40 +0300 Subject: [PATCH] Add first version of zoom reverse proxy --- develop-template.yaml | 23 +++++++++++ zoom-reverse-proxy/index.mjs | 80 ++++++++++++++++++++++++++++++++++++ 2 files changed, 103 insertions(+) create mode 100644 zoom-reverse-proxy/index.mjs diff --git a/develop-template.yaml b/develop-template.yaml index 0ce978d..30959d5 100644 --- a/develop-template.yaml +++ b/develop-template.yaml @@ -53,6 +53,29 @@ Resources: ApplyOn: None PackageType: Zip Role: !GetAtt DocspaceReverseProxyRole.Arn + + zoomreverseproxy: + Type: AWS::Serverless::Function + Properties: + CodeUri: zoom-reverse-proxy/ + Description: "" + MemorySize: 128 + Timeout: 3 + Handler: index.handler + Runtime: nodejs18.x + Architectures: + - x86_64 + EventInvokeConfig: + MaximumEventAgeInSeconds: 21600 + MaximumRetryAttempts: 2 + EphemeralStorage: + Size: 512 + RuntimeManagementConfig: + UpdateRuntimeOn: Auto + SnapStart: + ApplyOn: None + PackageType: Zip + Role: !GetAtt DocspaceReverseProxyRole.Arn GlobalDynamoDBTable: Type: AWS::DynamoDB::GlobalTable diff --git a/zoom-reverse-proxy/index.mjs b/zoom-reverse-proxy/index.mjs new file mode 100644 index 0000000..88964eb --- /dev/null +++ b/zoom-reverse-proxy/index.mjs @@ -0,0 +1,80 @@ +'use strict'; + +const AWS = require('aws-sdk'); +const cachedItem = {}; +const regionsMap = { + 'us-west-2': 'oforms.onlyoffice.com', + 'eu-central-1': 'blog.onlyoffice.com', + 'default': 'onlyoffice.com' +}; + +const ddbRegionsMap = { + ddbRegionsMap_placeholder +}; + +const dynamodbTableName = "dynamodb_table_name_placeholder"; + +const execRegionCode = process.env.AWS_REGION; +var ddbClientRegion = ddbRegionsMap[execRegionCode] || ddbRegionsMap["default"]; + +console.log("DynamoDB client region set to: %s", ddbClientRegion); + +const { DynamoDBClient, GetItemCommand } = require("@aws-sdk/client-dynamodb"); + +async function getTenantRegion(ddbRegion, tenantDomain) { + console.log("Fetching tenant region for domain: %s from DynamoDB region: %s", tenantDomain, ddbRegion); + const ddbClient = new DynamoDBClient({ region: ddbRegion }); + const getItemParams = { + Key: { 'tenant_domain': { S: tenantDomain } }, + ProjectionExpression: "tenant_region", + TableName: dynamodbTableName + }; + + try { + const response = await ddbClient.send(new GetItemCommand(getItemParams)); + if (response.Item) { + const tenantRegion = regionsMap[response.Item["tenant_region"]["S"]]; + console.log("Tenant region fetched: %s", tenantRegion); + return tenantRegion; + } else { + console.warn("No data found for domain: %s", tenantDomain); + return null; + } + } catch (err) { + console.error("Error fetching from DynamoDB: %s", err); + return null; + } +} + +exports.handler = async (event) => { + const request = event.Records[0].cf.request; + const headers = request.headers; + const fullDomain = headers.host[0].value.toLowerCase(); + const domainParts = fullDomain.split('.'); + + if (domainParts.length === 4 && domainParts[1] === 'devops' && domainParts[2] === 'onlyoffice' && domainParts[3] === 'io') { + const tenantDomain = fullDomain; + + // Check the cache first + if (cachedItem[tenantDomain] && cachedItem[tenantDomain].expiresOn > Date.now()) { + request.origin.custom.domainName = cachedItem[tenantDomain].value; + console.log("Origin fetched from cache for domain: %s", tenantDomain); + } else { + // Fetch the region from DynamoDB and update the origin + const tenantRegion = await getTenantRegion(ddbClientRegion, tenantDomain); + if (tenantRegion) { + request.origin.custom.domainName = tenantRegion; + cachedItem[tenantDomain] = { + value: tenantRegion, + expiresOn: Date.now() + 15 * 60 * 1000 // Cache for 15 minutes + }; + console.log("Origin updated to %s for domain: %s", tenantRegion, tenantDomain); + } else { + request.origin.custom.domainName = regionsMap["default"]; + console.log("Default origin used for domain: %s", tenantDomain); + } + } + } + + return request; +};