Skip to content

Commit

Permalink
Add app.ts and interactions.ts files
Browse files Browse the repository at this point in the history
  • Loading branch information
agadzik committed Jan 31, 2024
1 parent ff28d5d commit 03f3a32
Show file tree
Hide file tree
Showing 10 changed files with 4,374 additions and 46 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ node_modules
# CDK asset staging directory
.cdk.staging
cdk.out
cdk.context.json
18 changes: 18 additions & 0 deletions bin/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/usr/bin/env node
import "source-map-support/register";
import * as cdk from "aws-cdk-lib";
import { PalworldServerStack } from "../lib/palworld-server-stack";

const awsRegion = process.env.AWS_DEFAULT_REGION || "us-east-1";
const awsAccount = process.env.AWS_ACCOUNT_ID || "";

const app = new cdk.App();
new PalworldServerStack(app, "PalworldServerStack", {
env: { region: awsRegion, account: awsAccount },
tags: {
project: "Palworld",
environment: "Production",
},
});

app.synth();
21 changes: 0 additions & 21 deletions bin/cdk-palworld.ts

This file was deleted.

11 changes: 3 additions & 8 deletions cdk.json
Original file line number Diff line number Diff line change
@@ -1,9 +1,7 @@
{
"app": "npx ts-node --prefer-ts-exts bin/cdk-palworld.ts",
"app": "npx ts-node --prefer-ts-exts bin/app.ts",
"watch": {
"include": [
"**"
],
"include": ["**"],
"exclude": [
"README.md",
"cdk*.json",
Expand All @@ -19,10 +17,7 @@
"context": {
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@aws-cdk/core:target-partitions": [
"aws",
"aws-cn"
],
"@aws-cdk/core:target-partitions": ["aws", "aws-cn"],
"@aws-cdk-containers/ecs-service-extensions:enableDefaultLogDriver": true,
"@aws-cdk/aws-ec2:uniqueImdsv2TemplateName": true,
"@aws-cdk/aws-ecs:arnFormatIncludesClusterName": true,
Expand Down
16 changes: 0 additions & 16 deletions lib/cdk-palworld-stack.ts

This file was deleted.

167 changes: 167 additions & 0 deletions lib/palworld-server-stack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
import * as cdk from "aws-cdk-lib/core";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as ecs from "aws-cdk-lib/aws-ecs";
import * as efs from "aws-cdk-lib/aws-efs";
import * as backup from "aws-cdk-lib/aws-backup";
import * as events from "aws-cdk-lib/aws-events";
import * as iam from "aws-cdk-lib/aws-iam";
import * as lambda from "aws-cdk-lib/aws-lambda";
import * as nodejs from "aws-cdk-lib/aws-lambda-nodejs";
import * as apig from "aws-cdk-lib/aws-apigatewayv2";
import * as apig_integrations from "aws-cdk-lib/aws-apigatewayv2-integrations";
import { Construct } from "constructs";

export class PalworldServerStack extends cdk.Stack {
constructor(scope: Construct, id: string, props?: cdk.StackProps) {
super(scope, id, props);

const volumeName = "PalworldSaveDataEFS";

const vpc = new ec2.Vpc(this, "Vpc", {
maxAzs: 2,
});

const cluster = new ecs.Cluster(this, "PalworldCluster", {
vpc,
});

const fileSystem = new efs.FileSystem(this, "PalworldSaveDataEFS", {
vpc,
encrypted: false,
removalPolicy: cdk.RemovalPolicy.DESTROY,
lifecyclePolicy: efs.LifecyclePolicy.AFTER_14_DAYS,
performanceMode: efs.PerformanceMode.GENERAL_PURPOSE,
throughputMode: efs.ThroughputMode.BURSTING,
});

fileSystem.addToResourcePolicy(
new iam.PolicyStatement({
actions: ["elasticfilesystem:ClientMount"],
principals: [new iam.AnyPrincipal()],
conditions: {
Bool: {
"elasticfilesystem:AccessedViaMountTarget": "true",
},
},
})
);

const taskDefinition = new ecs.FargateTaskDefinition(
this,
"PalworldServerTaskDef",
{
memoryLimitMiB: 16384,
cpu: 4096,
}
);

taskDefinition.addVolume({
name: volumeName,
efsVolumeConfiguration: {
fileSystemId: fileSystem.fileSystemId,
},
});

const container = taskDefinition.addContainer("PalworldServer", {
image: ecs.ContainerImage.fromRegistry(
"thijsvanloef/palworld-server-docker"
),
environment: {
TZ: "UTC",
PLAYERS: "16",
PORT: "8211",
PUID: "1000",
PGID: "1000",
MULTITHREADING: "true",
COMMUNITY: "false",
UPDATE_ON_BOOT: "true",
RCON_ENABLED: "true",
RCON_PORT: "25575",
QUERY_PORT: "27015",
},
logging: ecs.LogDrivers.awsLogs({ streamPrefix: "PalworldServer" }),
portMappings: [
{ containerPort: 8211, protocol: ecs.Protocol.UDP },
{ containerPort: 27015, protocol: ecs.Protocol.UDP },
],
});

container.addMountPoints({
sourceVolume: volumeName,
containerPath: "/palworld",
readOnly: false,
});

const service = new ecs.FargateService(this, "PalworldServerService", {
cluster,
taskDefinition,
assignPublicIp: true,
});

// Allow access to Palworld from the internet
service.connections.allowFromAnyIpv4(ec2.Port.udp(8211));

// Allow access to RCON from the internet
service.connections.allowFromAnyIpv4(ec2.Port.udp(25575));

// Allow access to EFS from Fargate ECS
fileSystem.grantRootAccess(service.taskDefinition.taskRole.grantPrincipal);
fileSystem.connections.allowDefaultPortFrom(service.connections);

const plan = new backup.BackupPlan(this, "PalworldServerBackupPlan", {
backupVault: new backup.BackupVault(this, "PalworldServerBackupVault", {
removalPolicy: cdk.RemovalPolicy.DESTROY,
}),
});

plan.addRule(
new backup.BackupPlanRule({
deleteAfter: cdk.Duration.days(3),
scheduleExpression: events.Schedule.cron({ hour: "*", minute: "0" }),
})
);

plan.addSelection("PalworldServerEfsSelection", {
resources: [backup.BackupResource.fromEfsFileSystem(fileSystem)],
});

// API Gateway for controlling discord interactions
const expressAppLambda = new nodejs.NodejsFunction(
this,
"ExpressAppLambda",
{
runtime: lambda.Runtime.NODEJS_LATEST,
entry: "src/functions/discord/interactions.ts",
functionName: "express-app-handler",
handler: "handler",
timeout: cdk.Duration.seconds(60),
environment: {
APPLICATION_PUBLIC_KEY: process.env.APPLICATION_PUBLIC_KEY || "",
ECS_SERVICE_NAME: service.serviceName,
ECS_CLUSTER_ARN: service.cluster.clusterArn,
},
}
);

expressAppLambda.role?.addManagedPolicy(
iam.ManagedPolicy.fromManagedPolicyArn(
this,
"ECS_FullAccessPolicy",
"arn:aws:iam::aws:policy/AmazonECS_FullAccess"
)
);

const discordInteractionsIntegration =
new apig_integrations.HttpLambdaIntegration(
"DiscordInteractionsIntegration",
expressAppLambda
);
const httpApi = new apig.HttpApi(this, "ExpressAppEndpoint");

httpApi.addRoutes({
integration: discordInteractionsIntegration,
path: "/discord",
methods: [apig.HttpMethod.ANY],
});
}
}
8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,17 +11,23 @@
"cdk": "cdk"
},
"devDependencies": {
"@types/express": "^4.17.21",
"@types/jest": "^29.5.11",
"@types/node": "20.11.6",
"aws-cdk": "2.124.0",
"esbuild": "^0.20.0",
"jest": "^29.7.0",
"ts-jest": "^29.1.2",
"aws-cdk": "2.124.0",
"ts-node": "^10.9.2",
"typescript": "~5.3.3"
},
"dependencies": {
"@aws-sdk/client-ecs": "^3.502.0",
"aws-cdk-lib": "2.124.0",
"constructs": "^10.0.0",
"discord-interactions": "^3.4.0",
"express": "^4.18.2",
"serverless-http": "^3.2.0",
"source-map-support": "^0.5.21"
}
}
Loading

0 comments on commit 03f3a32

Please sign in to comment.