Skip to content

Commit

Permalink
Merge branch 'main' into feature/DO-1607-feature-environments
Browse files Browse the repository at this point in the history
  • Loading branch information
TheOrangePuff authored May 7, 2024
2 parents a9b69ba + c0d4867 commit 00a889d
Show file tree
Hide file tree
Showing 29 changed files with 3,020 additions and 1,302 deletions.
2,754 changes: 1,882 additions & 872 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion packages/graphql-mesh-server/.npmignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
*.ts
!assets/handlers/*.ts
!assets/handlers/**/*.ts
!*.d.ts
!*.js

Expand Down
34 changes: 3 additions & 31 deletions packages/graphql-mesh-server/README.md
Original file line number Diff line number Diff line change
@@ -1,35 +1,7 @@
# GraphQL Mesh in Fargate
A construct host [GraphQL Mesh](https://the-guild.dev/graphql/mesh) server in Fargate.
A construct to host a [GraphQL Mesh](https://the-guild.dev/graphql/mesh) server in Fargate.

![graphql mesh server hosting diagram](docs/graphql_mesh_server_hosting.png)

## Deployment notifications
If notificationArn is set this construct creates a CodeStar notification rule, SNS topic and Lambda function to receive notifications for codepipeline executions and forward them to another SNS topic. This is so that you can setup AWS Chatbot either in this account OR another account and forward the notifications there.

## Props

- `vpc?`: VPC to attach Redis and Fargate instances to (default: create a vpc)
- `vpcName?`: If no VPC is provided create one with this name (default: 'graphql-server-vpc')
- `cacheNodeType?`: Cache node type (default: 'cache.t2.micro')
- `repository?`: Repository to pull the container image from
- `certificateArn:` ARN of the certificate to add to the load balancer
- `minCapacity?`: Minimum number of Fargate instances
- `maxCapacity?`: Maximum number of Fargate instances
- `cpu?`: Amount of vCPU per Fargate instance (default: 512)
- `memory?`: Amount of memory per Fargate instance (default: 1024)
- `redis?`: Redis instance to use for mesh caching
- `secrets?`: SSM values to pass through to the container as secrets
- `cpuScalingSteps?`: Pass custom CPU scaling steps (default: [{ upper: 30, change: -1 }, { lower: 50, change: +1 }, { lower: 85, change: +3 }])
- `notificationArn?`: SNS Topic ARN to publish deployment notifications to
- `notificationRegion?`: Region of the SNS Topic that deployment notifications are sent to
- `blockedIps?`: List of IPv4 addresses to block
- `blockedIpPriority?`: The WAF rule priority (defaults to 2)
- `blockedIpv6s?`: List of IPv6 addresses to block
- `blockedIpv6Priority?`: The WAF rule priority (defaults to 3)
- `wafManagedRules?`: List of AWS Managed rules to add to the WAF
- `wafRules?`: List of custom rules
- `rateLimit?`: The limit on requests per 5-minute period. If provided, rate limiting will be enabled
- `rateLimitPriority?`: The WAF rule priority. Only used when a rateLimit value is provided (defaults to 10)
- `rateLimitBypassList?`: List of IPv4 addresses that can bypass rate limiting
- `containerInsights?`: Enable/disable container insights (defaults to true)
- `logStreamPrefix?`: Log stream prefix (defaults to 'graphql-server')
- `snsTopic?`: Optional SNS topic to subscribe all alarms to
- `additionalAlarms?`: Any additional custom alarms
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import {
CloudFrontClient,
CreateInvalidationCommand,
CreateInvalidationCommandInput,
CreateInvalidationCommandOutput,
} from "@aws-sdk/client-cloudfront";

const cfClient = new CloudFrontClient();

const PATHS = process.env.PATHS as string;
const DISTRIBUTION_ID = process.env.DISTRIBUTION_ID as string;

export const handler = async (): Promise<CreateInvalidationCommandOutput> => {
const paths = PATHS.split(",");

const invalidationInput: CreateInvalidationCommandInput = {
DistributionId: DISTRIBUTION_ID,
InvalidationBatch: {
Paths: {
Quantity: paths.length,
Items: paths,
},
CallerReference: "lambda",
},
};

const command = new CreateInvalidationCommand(invalidationInput);
return cfClient.send(command);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { existsSync, readFileSync, renameSync, writeFileSync } from "node:fs";

const IP_REGEX =
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\/([1-9]|[1-2][0-9]|3[1-2]))?$/;

// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const MAINTENANCE_FILE_PATH = process.env.MAINTENANCE_FILE_PATH!;
const FILE_NAME = "maintenance";
const PATHS = [
`${MAINTENANCE_FILE_PATH}/${FILE_NAME}.disabled`,
`${MAINTENANCE_FILE_PATH}/${FILE_NAME}.enabled`,
];

export const getFilePath = (): string => {
for (const path of PATHS) {
if (existsSync(path)) {
return path;
}
}

// If the maintenance file wasn't found, create one
writeFileSync(PATHS[0], "");
return PATHS[0];
};

export const getFileContents = (): string => {
return readFileSync(getFilePath(), "utf-8");
};

export const setFileContents = (input: string): void => {
if (!validateIps(input)) throw new Error("List of IP addresses not valid");
const uniqueIps = [...new Set(input.split(","))].join(",");

writeFileSync(getFilePath(), uniqueIps, { encoding: "utf-8" });
};

export const updateFileContents = (input: string): void => {
if (!input) throw new Error("Nothing to update.");
if (!validateIps(input)) throw new Error("List of IP addresses not valid");

setFileContents(`${getFileContents()},${input}`);
};

export const getCurrentStatus = (): "disabled" | "enabled" => {
return inMaintenanceMode() ? "enabled" : "disabled";
};

export const inMaintenanceMode = (): boolean => {
return getFilePath().includes("enabled");
};

export const toggleMaintenanceStatus = () => {
const desiredStatus = inMaintenanceMode() ? "disabled" : "enabled";

renameSync(
getFilePath(),
`${MAINTENANCE_FILE_PATH}/${FILE_NAME}.${desiredStatus}`
);
};

const validateIps = (ipList: string) => {
const ips = ipList.split(",");
return ips.find(ip => !IP_REGEX.test(ip)) === undefined;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
import { getCurrentStatus, toggleMaintenanceStatus } from "./lib/file";

const MAINTENANCE_FILE_PATH = process.env.MAINTENANCE_FILE_PATH;

export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
if (!MAINTENANCE_FILE_PATH) throw new Error("a");
let body = "Method not implemented";
let status = 200;

switch (event.httpMethod) {
case "GET":
body = getCurrentStatus();
break;
case "POST":
body = changeMaintenanceStatus(extractDesiredStatusFromEvent(event));
break;
default:
status = 501;
}

return {
body: body,
statusCode: status,
};
};
const extractDesiredStatusFromEvent = (
event: APIGatewayProxyEvent
): "enabled" | "disabled" | undefined => {
if (event.resource.includes("enable")) return "enabled";
if (event.resource.includes("disable")) return "disabled";
return undefined;
};

const changeMaintenanceStatus = (
desiredStatus?: "enabled" | "disabled"
): string => {
// If no status is provided then toggle
if (desiredStatus === undefined) {
toggleMaintenanceStatus();
return getCurrentStatus();
}

const currentStatus = getCurrentStatus();
if (currentStatus === desiredStatus) return currentStatus;

toggleMaintenanceStatus();
return getCurrentStatus();
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { APIGatewayProxyEvent, APIGatewayProxyResult } from "aws-lambda";
import {
getFileContents,
setFileContents,
updateFileContents,
} from "./lib/file";

const MAINTENANCE_FILE_PATH = process.env.MAINTENANCE_FILE_PATH;

export const handler = async (
event: APIGatewayProxyEvent
): Promise<APIGatewayProxyResult> => {
if (!MAINTENANCE_FILE_PATH) throw new Error("a");
let body = "Method not implemented";
let status = 200;

try {
switch (event.httpMethod) {
case "GET":
body = getFileContents();
break;
case "PUT":
setFileContents(event.body || "");
body = "Successfully updated whitelist";
break;
case "PATCH":
updateFileContents(event.body || "");
body = "Successfully updated whitelist";
break;
default:
status = 501;
}
} catch (error) {
let statusCode = 500;
const errorMsg = error.errorMessage;

if (!errorMsg) {
return {
body: "Unknown Error",
statusCode: statusCode,
};
}

if (errorMsg === "List of IP addresses not valid") statusCode = 403;

return {
body: errorMsg,
statusCode: statusCode,
};
}

return {
body: body,
statusCode: status,
};
};
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit 00a889d

Please sign in to comment.