Skip to content
Merged
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
4 changes: 2 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ npm run check # format:checkとlintの両方を実行

### CDK操作

すべてのCDKコマンドには`--context stage=<stage>`が必要です。stageは`dev`、`staging`、`prd-v0292`、`dev2`のいずれかです。
すべてのCDKコマンドには`--context stage=<stage>`が必要です。stageは`dev`、`staging`、`prd-v0292`のいずれかです。

```bash
# デプロイ前の差分を確認
Expand Down Expand Up @@ -184,7 +184,7 @@ ECSサービスはステージごとに設定されたFargate/Fargate Spotの混
### ステージ設定

- すべてのCDKコマンドには`--context stage=<stage>`パラメータが必要
- 有効なステージ: `dev`、`staging`、`prd-v0292`、`dev2`
- 有効なステージ: `dev`、`staging`、`prd-v0292`
- `tag`コンテキストパラメータはデプロイ時に必要で、ECRのDecidim Dockerイメージタグを指定

### リソース命名規則
Expand Down
2 changes: 1 addition & 1 deletion bin/decidim-cfj-cdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { Tags } from 'aws-cdk-lib';

const app = new cdk.App();

const stages = ['dev', 'staging', 'prd-v0292', 'dev2'] as const;
const stages = ['dev', 'staging', 'prd-v0292'] as const;
const stage = app.node.tryGetContext('stage') as string;
const tag = app.node.tryGetContext('tag') as string;
if (!stages.includes(stage as (typeof stages)[number])) {
Expand Down
6 changes: 3 additions & 3 deletions config/dev.json
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
{
"rds": {
"snapshot": true,
"snapshotIdentifier": "decidim-master-2025-08-11",
"instanceType": "t3.micro",
"snapshotIdentifier": "decidim-master-2025-12-25",
"instanceType": "t4g.micro",
"deletionProtection": false,
"allocatedStorage": 20,
"maxAllocatedStorage": 40,
Expand All @@ -17,7 +17,7 @@

"s3Bucket": "dev-v300-decidim",

"cacheNodeType": "cache.t3.medium",
"cacheNodeType": "cache.t3.micro",
"engineVersion": "6.x",
"numCacheNodes": 1,
"automaticFailoverEnabled": false,
Expand Down
43 changes: 0 additions & 43 deletions config/dev2.json

This file was deleted.

12 changes: 10 additions & 2 deletions config/prd-v0292.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"rds": {
"snapshot": true,
"snapshotIdentifier": "decidim-master-2025-07-14",
"instanceType": "t3.medium",
"instanceType": "t4g.medium",
"deletionProtection": true,
"allocatedStorage": 20,
"maxAllocatedStorage": 40,
Expand All @@ -19,7 +19,7 @@

"cacheNodeType": "cache.t3.medium",
"engineVersion": "6.x",
"numCacheNodes": 3,
"numCacheNodes": 2,
"automaticFailoverEnabled": true,

"ecs": {
Expand All @@ -35,6 +35,14 @@
"fargateCapacityProvider": {
"base": 1,
"weight": 2
},
"mainApp": {
"cpu": 2048,
"memory": 4096
},
"sidekiq": {
"cpu": 512,
"memory": 2048
}
},

Expand Down
4 changes: 2 additions & 2 deletions config/staging.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"rds": {
"snapshot": false,
"snapshotIdentifier": "",
"instanceType": "t2.micro",
"instanceType": "t4g.micro",
"deletionProtection": false,
"allocatedStorage": 20,
"maxAllocatedStorage": 20,
Expand All @@ -17,7 +17,7 @@

"s3Bucket": "staging-decidim",

"cacheNodeType": "cache.t2.micro",
"cacheNodeType": "cache.t3.micro",
"engineVersion": "6.x",
"numCacheNodes": 1,
"automaticFailoverEnabled": false,
Expand Down
134 changes: 46 additions & 88 deletions lib/cloudfront.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,12 @@ export class CloudFrontStack extends Stack {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesCommonRuleSet',
// Decidimはリッチテキストエディタ(Quill)でHTMLボディを送信するため、
// 以下のルールが正規リクエストを誤検知でブロックする
excludedRules: [
{ name: 'CrossSiteScripting_BODY' },
{ name: 'SizeRestrictions_BODY' },
{ name: 'GenericRFI_BODY' },
{ name: 'CrossSiteScripting_BODY' }, // リッチテキストのHTML投稿が誤検知される
{ name: 'SizeRestrictions_BODY' }, // 大きなフォーム投稿(画像添付等)がブロックされる
{ name: 'GenericRFI_BODY' }, // リッチテキスト内のURL記述が誤検知される
],
},
},
Expand Down Expand Up @@ -156,6 +158,7 @@ export class CloudFrontStack extends Stack {
managedRuleGroupStatement: {
vendorName: 'AWS',
name: 'AWSManagedRulesSQLiRuleSet',
// リッチテキストのHTML投稿がSQLインジェクションとして誤検知される
excludedRules: [{ name: 'SQLi_BODY' }],
},
},
Expand Down Expand Up @@ -364,7 +367,7 @@ export class CloudFrontStack extends Stack {
});
}

let distribution: cloudfront.Distribution;
const isPrd = props.stage === 'prd-v0292';

const stripS3PrefixFn = new cloudfront.Function(this, 'StripS3PrefixFn', {
code: cloudfront.FunctionCode.fromInline(`
Expand Down Expand Up @@ -392,93 +395,48 @@ export class CloudFrontStack extends Stack {
lifecycleRules: [{ expiration: Duration.days(180) }],
});

if (props.stage === 'prd-v0292') {
distribution = new cloudfront.Distribution(this, 'Distribution', {
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
defaultBehavior: {
origin: albOrigin,
allowedMethods: AllowedMethods.ALLOW_ALL,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: CachePolicy.CACHING_DISABLED,
originRequestPolicy: OriginRequestPolicy.ALL_VIEWER,
},
comment: `${props.stage}-${props.serviceName}-cloudfront`,
domainNames: [endpoint, `*.${props.domain}`],
certificate: aws_certificatemanager.Certificate.fromCertificateArn(
this,
'cloudFrontCertificate',
props.certificateArn
),
webAclId: waf.attrArn,
enableLogging: true,
logBucket: logBucket,
logFilePrefix: 'cloudfront-logs/',
});

// 既存のビヘイビア
distribution.addBehavior('decidim-packs/*', albOrigin, {
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
const distribution = new cloudfront.Distribution(this, 'Distribution', {
priceClass: isPrd
? cloudfront.PriceClass.PRICE_CLASS_ALL
: cloudfront.PriceClass.PRICE_CLASS_200,
defaultBehavior: {
origin: albOrigin,
allowedMethods: AllowedMethods.ALLOW_ALL,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
});

// S3画像アクセス用のビヘイビア
distribution.addBehavior('/s3/*', s3Origin, {
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
originRequestPolicy: OriginRequestPolicy.CORS_S3_ORIGIN,
functionAssociations: [
{
function: stripS3PrefixFn,
eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
},
],
});
} else {
distribution = new cloudfront.Distribution(this, 'Distribution', {
priceClass: cloudfront.PriceClass.PRICE_CLASS_ALL,
defaultBehavior: {
origin: albOrigin,
allowedMethods: AllowedMethods.ALLOW_ALL,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: CachePolicy.CACHING_DISABLED,
originRequestPolicy: OriginRequestPolicy.ALL_VIEWER,
},
comment: `${props.stage}-${props.serviceName}-cloudfront`,
domainNames: [endpoint],
certificate: aws_certificatemanager.Certificate.fromCertificateArn(
this,
'cloudFrontCertificate',
props.certificateArn
),
webAclId: waf.attrArn,
enableLogging: true,
logBucket: logBucket,
logFilePrefix: 'cloudfront-logs/',
});
cachePolicy: CachePolicy.CACHING_DISABLED,
originRequestPolicy: OriginRequestPolicy.ALL_VIEWER,
},
comment: `${props.stage}-${props.serviceName}-cloudfront`,
domainNames: isPrd ? [endpoint, `*.${props.domain}`] : [endpoint],
certificate: aws_certificatemanager.Certificate.fromCertificateArn(
this,
'cloudFrontCertificate',
props.certificateArn
),
webAclId: waf.attrArn,
enableLogging: true,
logBucket: logBucket,
logFilePrefix: 'cloudfront-logs/',
});

// 既存のビヘイビア
distribution.addBehavior('decidim-packs/*', albOrigin, {
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
});
distribution.addBehavior('decidim-packs/*', albOrigin, {
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
viewerProtocolPolicy: ViewerProtocolPolicy.REDIRECT_TO_HTTPS,
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
});

// S3画像アクセス用のビヘイビア
distribution.addBehavior('/s3/*', s3Origin, {
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
originRequestPolicy: OriginRequestPolicy.CORS_S3_ORIGIN,
functionAssociations: [
{
function: stripS3PrefixFn,
eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
},
],
});
}
distribution.addBehavior('/s3/*', s3Origin, {
allowedMethods: AllowedMethods.ALLOW_GET_HEAD_OPTIONS,
viewerProtocolPolicy: ViewerProtocolPolicy.HTTPS_ONLY,
cachePolicy: CachePolicy.CACHING_OPTIMIZED,
originRequestPolicy: OriginRequestPolicy.CORS_S3_ORIGIN,
functionAssociations: [
{
function: stripS3PrefixFn,
eventType: cloudfront.FunctionEventType.VIEWER_REQUEST,
},
],
});

// CloudFrontディストリビューションのドメイン名を出力
new CfnOutput(this, 'CloudFrontDomainName', {
Expand Down
2 changes: 1 addition & 1 deletion lib/decidim-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ export class DecidimStack extends cdk.Stack {
command: [
'sh',
'-c',
'bundle exec rails db:create; bundle exec rake db:migrate && rails s -b 0.0.0.0',
'bundle exec rails db:create && bundle exec rake db:migrate && bundle exec rails s -b 0.0.0.0',
],
healthCheck: {
command: ['CMD-SHELL', `curl --fail -s http://localhost:3000 || exit 1`],
Expand Down
9 changes: 9 additions & 0 deletions lib/elasticache-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,14 @@ export class ElasticacheStack extends Stack {
constructor(scope: Construct, id: string, props: ElastiCacheStackProps) {
super(scope, id, props);

const parameterGroup = new elasticache.CfnParameterGroup(this, 'RedisParameterGroup', {
cacheParameterGroupFamily: 'redis6.x',
description: `${props.stage}-${props.serviceName} Redis parameter group`,
properties: {
'maxmemory-policy': 'volatile-lru',
},
});

const elastiCacheProps: CfnReplicationGroupProps = {
replicationGroupDescription: `${props.stage}-${props.serviceName}-cache`,
engine: 'redis',
Expand All @@ -29,6 +37,7 @@ export class ElasticacheStack extends Stack {
automaticFailoverEnabled: props.automaticFailoverEnabled,
securityGroupIds: [props.securityGroup],
cacheSubnetGroupName: props.ecSubnetGroup.cacheSubnetGroupName,
cacheParameterGroupName: parameterGroup.ref,
};

if (props.stage === 'prd-v0292') {
Expand Down
5 changes: 3 additions & 2 deletions lib/rds-stack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ export class RdsStack extends Stack {
const config = props.rds;

const rdsProps: DatabaseInstanceSourceProps = {
engine: DatabaseInstanceEngine.postgres({ version: rds.PostgresEngineVersion.VER_14_17 }),
engine: DatabaseInstanceEngine.postgres({ version: rds.PostgresEngineVersion.VER_16_11 }),
instanceType: config.instanceType,
instanceIdentifier: `${props.stage}-${props.serviceName}-postgresql`,
vpc: props.vpc,
securityGroups: [props.securityGroup],
multiAz: config.multiAz,
removalPolicy: RemovalPolicy.DESTROY,
deletionProtection: config.deletionProtection,
storageType: StorageType.GP2,
storageType: StorageType.GP3,
allocatedStorage: config.allocatedStorage,
maxAllocatedStorage: config.maxAllocatedStorage,
autoMinorVersionUpgrade: true,
Expand Down Expand Up @@ -69,6 +69,7 @@ export class RdsStack extends Stack {
this,
`/decidim-cfj/${props.stage}/RDS_USERNAME`
),
// TODO: AWS Secrets Managerへの移行を検討(自動ローテーション対応)
password: SecretValue.unsafePlainText(
ssm.StringParameter.valueForTypedStringParameterV2(
this,
Expand Down
2 changes: 1 addition & 1 deletion test/__snapshots__/cloudfront.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ exports[`Cloudfront Stack Created 1`] = `
},
},
],
"PriceClass": "PriceClass_All",
"PriceClass": "PriceClass_200",
"ViewerCertificate": {
"AcmCertificateArn": "arn:aws:acm:us-east-1:887442827229:certificate/8aec1d57-e068-47f3-a180-e0c1fbb782de",
"MinimumProtocolVersion": "TLSv1.2_2021",
Expand Down
2 changes: 1 addition & 1 deletion test/__snapshots__/decidim-cfj-cdk.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -1490,7 +1490,7 @@ exports[`DecidimStack Created 1`] = `
"Command": [
"sh",
"-c",
"bundle exec rails db:create; bundle exec rake db:migrate && rails s -b 0.0.0.0",
"bundle exec rails db:create && bundle exec rake db:migrate && bundle exec rails s -b 0.0.0.0",
],
"Environment": [
{
Expand Down
Loading