Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions cdk/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
*.js
!jest.config.js
*.d.ts
node_modules

# CDK asset staging directory
.cdk.staging
cdk.out
6 changes: 6 additions & 0 deletions cdk/.npmignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
*.ts
!*.d.ts

# CDK asset staging directory
.cdk.staging
cdk.out
43 changes: 43 additions & 0 deletions cdk/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# CDK-Deployment PmdM
Im Zuge der App-Erneuerung wurde das alte Deployment mit dem Serverless Framework im Mai 2026 auf AWS-CDK umgestellt. Ziel war, das Deployment auf eine moderne und standardisierte Basis zu setzen, mit der das Entwicklungsteam schon Erfahrung hat.

## Stages
Das Deployment ist in die Stages 'dev', 'staging' und 'prod' aufgeteilt. Die aktive Stage wird über die Umgebungsvariable `STAGE` oder den CDK-Kontext-Parameter `--context stage=<stage>` gesetzt. Ohne Angabe wird `dev` verwendet.

## Komponenten
### S3-Buckets
Das Projekt beinhaltet zwei S3-Buckets:
- pmdm-appbucket-{stage} beinhaltet die statische Applikation, um sie auszuliefern. Nur CloudFront hat Lesezugriff.
- pmdm-projectbucket-{stage} beinhaltet gespeicherte Projekte der User und einige statische Maus-Assets. Nur CloudFront und die Lambdas haben Zugriff. CORS ist für PUT-Requests von der jeweiligen Stage-Domain konfiguriert.

### Lambda-Funktionen
Die Lambdafunktionen laufen mit NodeJS, das im Rahmen des Updates auf die aktuellste Runtime-Version 24 aktualisiert wurde. Die Handler liegen unter `../src/backend/` und werden beim CDK-Build mit esbuild gebundelt.

- `prepareAssetUpload`: Prüft, ob ein Medien-Asset (Maus-Kostüm, Hintergrund, Sound) bereits im Projekt-Bucket existiert, und liefert andernfalls eine Pre-Signed-URL, damit der Browser es direkt in S3 hochladen kann.
- `saveProject`: Speichert das aktuelle Scratch-Projekt als JSON in S3 und pflegt einen Pro-User-Index mit Projektname und Zeitstempeln.
- `prepareShareResult`: Generiert einen eindeutigen 5-stelligen Share-Key und eine Pre-Signed-URL, über die die App einen teilbaren Snapshot des Projektergebnisses in S3 ablegen kann.

### API Gateway
Das API Gateway stellt eine einfache API bereit, mit denen die Applikation die Lambas ansprechen kann. Die drei Endpoints mappen direkt auf die Namen der Lambdafunktionen.

### Cloudfront CDN
Cloudfront stellt den Einstiegspunkt für Aufrufe dar und verteilt Requests an die Buckets und die API. Es ist derzeit in der niedrigsten Preisklasse konfiguriert, die hauptsächlich Europa und die USA einschließt.

### DNS
Hier ist noch zu klären, ob die DNS-Zone tatsächlich in Route53 gehostet wird oder extern. Derzeit ist der Code für externes DNS konfiguriert, eine Route53-Integration ist aber schon vorbereitet.

## Zertifikate
Derzeit müssen die TLS-Zertifikate separat gemanagt werden, damit mit einer externen DNS-Zone zusammengearbeitet werden kann.

## Benutzung
### Vorbereitung
Mit externer DNS-Zone:
- `createDnsRecord` in der bin/cdk.ts auf false setzen
- gewünschte Domains in lib/config.ts eintragen
- aws-cli wie benötigt einrichten inkl. Access Keys o.ä.
- in ACM (AWS Certificate Manager) händisch die Zertifikate anlegen und per DNS-01-Challenge bestätigen
- Zertifikat-ARNs in lib/config.ts eintragen
### Deployment
- im Rootverzeichnis mit `yarn build` die App bauen
- im CDK-Verzeichnis `npx cdk diff` die Änderungen anzeigen lassen, ggf. mit `npx cdk synth` das CloudFormation-Template prüfen
- und dann mit `npx cdk deploy` deployen.
29 changes: 29 additions & 0 deletions cdk/bin/cdk.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
#!/usr/bin/env node
import * as cdk from 'aws-cdk-lib/core'
import { MausAppStack } from '../lib/cdk-stack'
import { resolveStage, STAGES } from '../lib/config'

const app = new cdk.App()

const stage = resolveStage(process.env.STAGE ?? app.node.tryGetContext('stage'))
const config = STAGES[stage]

// tbd: arbeiten wir überhaupt mit hosted zones oder nur externen DNS-Records?
/*
const createDnsRecord =
(process.env.CREATE_DNS_RECORD ??
app.node.tryGetContext('createDnsRecord') ??
'true') !== 'false'
*/
const createDnsRecord = false // für Entwicklungsphase und Testing

new MausAppStack(app, `MausApp-${stage}`, {
stage,
config,
createDnsRecord,
env: {
account: config.account || process.env.CDK_DEFAULT_ACCOUNT,
region: config.region || process.env.CDK_DEFAULT_REGION,
},
description: `PmdM stack (${stage})`,
})
105 changes: 105 additions & 0 deletions cdk/cdk.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
{
"app": "npx ts-node --prefer-ts-exts bin/cdk.ts",
"watch": {
"include": [
"**"
],
"exclude": [
"README.md",
"cdk*.json",
"**/*.d.ts",
"**/*.js",
"tsconfig.json",
"package*.json",
"yarn.lock",
"node_modules",
"test"
]
},
"context": {
"@aws-cdk/aws-signer:signingProfileNamePassedToCfn": true,
"@aws-cdk/aws-ecs-patterns:secGroupsDisablesImplicitOpenListener": true,
"@aws-cdk/aws-lambda:recognizeLayerVersion": true,
"@aws-cdk/core:checkSecretUsage": true,
"@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,
"@aws-cdk/aws-iam:minimizePolicies": true,
"@aws-cdk/core:validateSnapshotRemovalPolicy": true,
"@aws-cdk/aws-codepipeline:crossAccountKeyAliasStackSafeResourceName": true,
"@aws-cdk/aws-s3:createDefaultLoggingPolicy": true,
"@aws-cdk/aws-sns-subscriptions:restrictSqsDescryption": true,
"@aws-cdk/aws-apigateway:disableCloudWatchRole": true,
"@aws-cdk/core:enablePartitionLiterals": true,
"@aws-cdk/aws-events:eventsTargetQueueSameAccount": true,
"@aws-cdk/aws-ecs:disableExplicitDeploymentControllerForCircuitBreaker": true,
"@aws-cdk/aws-iam:importedRoleStackSafeDefaultPolicyName": true,
"@aws-cdk/aws-s3:serverAccessLogsUseBucketPolicy": true,
"@aws-cdk/aws-route53-patters:useCertificate": true,
"@aws-cdk/customresources:installLatestAwsSdkDefault": false,
"@aws-cdk/aws-rds:databaseProxyUniqueResourceName": true,
"@aws-cdk/aws-codedeploy:removeAlarmsFromDeploymentGroup": true,
"@aws-cdk/aws-apigateway:authorizerChangeDeploymentLogicalId": true,
"@aws-cdk/aws-ec2:launchTemplateDefaultUserData": true,
"@aws-cdk/aws-secretsmanager:useAttachedSecretResourcePolicyForSecretTargetAttachments": true,
"@aws-cdk/aws-redshift:columnId": true,
"@aws-cdk/aws-stepfunctions-tasks:enableEmrServicePolicyV2": true,
"@aws-cdk/aws-ec2:restrictDefaultSecurityGroup": true,
"@aws-cdk/aws-apigateway:requestValidatorUniqueId": true,
"@aws-cdk/aws-kms:aliasNameRef": true,
"@aws-cdk/aws-kms:applyImportedAliasPermissionsToPrincipal": true,
"@aws-cdk/aws-autoscaling:generateLaunchTemplateInsteadOfLaunchConfig": true,
"@aws-cdk/core:includePrefixInUniqueNameGeneration": true,
"@aws-cdk/aws-efs:denyAnonymousAccess": true,
"@aws-cdk/aws-opensearchservice:enableOpensearchMultiAzWithStandby": true,
"@aws-cdk/aws-lambda-nodejs:useLatestRuntimeVersion": true,
"@aws-cdk/aws-efs:mountTargetOrderInsensitiveLogicalId": true,
"@aws-cdk/aws-rds:auroraClusterChangeScopeOfInstanceParameterGroupWithEachParameters": true,
"@aws-cdk/aws-appsync:useArnForSourceApiAssociationIdentifier": true,
"@aws-cdk/aws-rds:preventRenderingDeprecatedCredentials": true,
"@aws-cdk/aws-codepipeline-actions:useNewDefaultBranchForCodeCommitSource": true,
"@aws-cdk/aws-cloudwatch-actions:changeLambdaPermissionLogicalIdForLambdaAction": true,
"@aws-cdk/aws-codepipeline:crossAccountKeysDefaultValueToFalse": true,
"@aws-cdk/aws-codepipeline:defaultPipelineTypeToV2": true,
"@aws-cdk/aws-kms:reduceCrossAccountRegionPolicyScope": true,
"@aws-cdk/aws-eks:nodegroupNameAttribute": true,
"@aws-cdk/aws-eks:useNativeOidcProvider": true,
"@aws-cdk/aws-ec2:ebsDefaultGp3Volume": true,
"@aws-cdk/aws-ecs:removeDefaultDeploymentAlarm": true,
"@aws-cdk/custom-resources:logApiResponseDataPropertyTrueDefault": false,
"@aws-cdk/aws-s3:keepNotificationInImportedBucket": false,
"@aws-cdk/core:explicitStackTags": true,
"@aws-cdk/aws-ecs:reduceEc2FargateCloudWatchPermissions": true,
"@aws-cdk/aws-dynamodb:resourcePolicyPerReplica": true,
"@aws-cdk/aws-ec2:ec2SumTImeoutEnabled": true,
"@aws-cdk/aws-appsync:appSyncGraphQLAPIScopeLambdaPermission": true,
"@aws-cdk/aws-rds:setCorrectValueForDatabaseInstanceReadReplicaInstanceResourceId": true,
"@aws-cdk/core:cfnIncludeRejectComplexResourceUpdateCreatePolicyIntrinsics": true,
"@aws-cdk/aws-lambda-nodejs:sdkV3ExcludeSmithyPackages": true,
"@aws-cdk/aws-stepfunctions-tasks:fixRunEcsTaskPolicy": true,
"@aws-cdk/aws-ec2:bastionHostUseAmazonLinux2023ByDefault": true,
"@aws-cdk/aws-route53-targets:userPoolDomainNameMethodWithoutCustomResource": true,
"@aws-cdk/aws-elasticloadbalancingV2:albDualstackWithoutPublicIpv4SecurityGroupRulesDefault": true,
"@aws-cdk/aws-iam:oidcRejectUnauthorizedConnections": true,
"@aws-cdk/core:enableAdditionalMetadataCollection": true,
"@aws-cdk/aws-lambda:createNewPoliciesWithAddToRolePolicy": false,
"@aws-cdk/aws-s3:setUniqueReplicationRoleName": true,
"@aws-cdk/aws-events:requireEventBusPolicySid": true,
"@aws-cdk/core:aspectPrioritiesMutating": true,
"@aws-cdk/aws-dynamodb:retainTableReplica": true,
"@aws-cdk/aws-stepfunctions:useDistributedMapResultWriterV2": true,
"@aws-cdk/s3-notifications:addS3TrustKeyPolicyForSnsSubscriptions": true,
"@aws-cdk/aws-ec2:requirePrivateSubnetsForEgressOnlyInternetGateway": true,
"@aws-cdk/aws-s3:publicAccessBlockedByDefault": true,
"@aws-cdk/aws-lambda:useCdkManagedLogGroup": true,
"@aws-cdk/aws-elasticloadbalancingv2:networkLoadBalancerWithSecurityGroupByDefault": true,
"@aws-cdk/aws-ecs-patterns:uniqueTargetGroupId": true,
"@aws-cdk/aws-route53-patterns:useDistribution": true,
"@aws-cdk/aws-cloudfront:defaultFunctionRuntimeV2_0": true,
"@aws-cdk/aws-elasticloadbalancingv2:usePostQuantumTlsPolicy": true
}
}
9 changes: 9 additions & 0 deletions cdk/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
module.exports = {
testEnvironment: 'node',
roots: ['<rootDir>/test'],
testMatch: ['**/*.test.ts'],
transform: {
'^.+\\.tsx?$': 'ts-jest'
},
setupFilesAfterEnv: ['aws-cdk-lib/testhelpers/jest-autoclean'],
};
Loading
Loading