webspresso 0.0.105 → 0.0.106
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/AGENTS.md +15 -2
- package/.agents/CLI.md +219 -7
- package/.agents/FLAGS.md +118 -0
- package/.agents/LOGGING.md +93 -0
- package/.agents/METRICS.md +127 -0
- package/.agents/MIGRATION_JOBS.md +95 -0
- package/.agents/PLUGINS.md +10 -3
- package/.agents/QUEUE.md +42 -0
- package/.agents/RATE_LIMIT.md +147 -0
- package/.agents/SCALE_OUT.md +109 -0
- package/.agents/SECRETS.md +117 -0
- package/AGENTS.md +10 -0
- package/bin/commands/add-aws.js +396 -0
- package/bin/commands/add-ci.js +523 -0
- package/bin/commands/add-cloudflare.js +240 -0
- package/bin/commands/add-cloudrun.js +381 -0
- package/bin/commands/add-migration-job.js +396 -0
- package/bin/commands/assets-sync.js +209 -0
- package/bin/commands/client-generate.js +52 -0
- package/bin/commands/console.js +53 -0
- package/bin/commands/db-migrate.js +47 -2
- package/bin/commands/db-studio.js +84 -0
- package/bin/commands/deploy-check.js +242 -13
- package/bin/commands/export.js +87 -0
- package/bin/commands/secrets-pull.js +208 -0
- package/bin/utils/db.js +7 -1
- package/bin/webspresso.js +22 -0
- package/core/app.js +421 -0
- package/core/auth/authenticate.js +93 -0
- package/core/auth/index.js +34 -4
- package/core/auth/manager.js +50 -5
- package/core/auth/middleware.js +16 -68
- package/core/auth/redis-session.js +1008 -0
- package/core/auth/sessions.js +156 -0
- package/core/cache/cdn.js +296 -0
- package/core/cache/index.js +12 -0
- package/core/cache/tagged-lru.js +9 -1
- package/core/console/commands.js +148 -0
- package/core/console/context.js +260 -0
- package/core/console/eval.js +70 -0
- package/core/console/index.js +22 -0
- package/core/console/repl.js +102 -0
- package/core/errors/domain.js +1 -0
- package/core/events/dispatcher.js +7 -0
- package/core/events/signal.js +7 -0
- package/core/export/discovery.js +191 -0
- package/core/export/exporter.js +277 -0
- package/core/export/index.js +24 -0
- package/core/flags/evaluator.js +252 -0
- package/core/flags/hasher.js +37 -0
- package/core/flags/index.js +21 -0
- package/core/flags/manager.js +263 -0
- package/core/flags/store.js +126 -0
- package/core/health/middleware.js +3 -3
- package/core/http/adapter.js +74 -0
- package/core/http/express-adapter.js +46 -0
- package/core/http/hono-adapter.js +531 -0
- package/core/http/index.js +41 -0
- package/core/http/pipeline.js +485 -0
- package/core/index.js +336 -0
- package/core/limits/middleware.js +9 -1
- package/core/logger/formatters.js +264 -0
- package/core/logger/index.js +260 -0
- package/core/logger/trace.js +137 -0
- package/core/metrics/collectors.js +125 -0
- package/core/metrics/index.js +45 -0
- package/core/metrics/middleware.js +230 -0
- package/core/metrics/registry.js +366 -0
- package/core/orm/d1.js +265 -0
- package/core/orm/index.js +105 -5
- package/core/orm/migrate-cloud.js +194 -0
- package/core/orm/pool.js +97 -0
- package/core/orm/query-builder.js +9 -0
- package/core/queue/adapters/cloudflare.js +131 -0
- package/core/queue/adapters/pubsub.js +400 -0
- package/core/queue/adapters/sqs.js +419 -0
- package/core/queue/adapters/wait-until.js +77 -0
- package/core/queue/index.js +8 -0
- package/core/queue/manager.js +11 -0
- package/core/rate-limit/index.js +22 -0
- package/core/rate-limit/limiter.js +219 -0
- package/core/rate-limit/stores/memory.js +129 -0
- package/core/rate-limit/stores/redis.js +151 -0
- package/core/rate-limit/utils.js +112 -0
- package/core/resilience/circuit-breaker.js +31 -0
- package/core/secrets/index.js +113 -0
- package/core/secrets/manager.js +308 -0
- package/core/secrets/providers/aws.js +288 -0
- package/core/secrets/providers/env.js +44 -0
- package/core/secrets/providers/gcp.js +307 -0
- package/core/secrets/providers/memory.js +76 -0
- package/core/shutdown/shutdown-manager.js +9 -0
- package/core/stream/index.js +19 -0
- package/core/stream/text-stream.js +326 -0
- package/core/studio/index.js +43 -0
- package/core/studio/inspector.js +258 -0
- package/core/studio/server.js +325 -0
- package/core/studio/ui.js +681 -0
- package/index.d.ts +934 -2
- package/index.js +125 -3
- package/package.json +3 -1
- package/plugins/admin-panel/index.js +12 -5
- package/plugins/api-key/index.js +3 -2
- package/plugins/audit-log/index.js +3 -1
- package/plugins/basic-auth/index.js +3 -2
- package/plugins/cors.js +3 -2
- package/plugins/csrf/index.js +4 -3
- package/plugins/csv/index.js +8 -5
- package/plugins/email/index.js +11 -9
- package/plugins/index.js +3 -0
- package/plugins/queue/index.js +18 -2
- package/plugins/redirect/index.js +7 -2
- package/plugins/seo-checker/index.js +2 -2
- package/plugins/site-analytics/index.js +3 -1
- package/plugins/swagger.js +8 -2
- package/plugins/upload/gcs-provider.js +148 -0
- package/plugins/upload/index.js +132 -74
- package/plugins/upload/s3-provider.js +257 -0
- package/plugins/webhooks/index.js +27 -23
- package/plugins/xlsx/index.js +7 -5
- package/src/api/api-loader.js +12 -95
- package/src/api/define-api.js +1 -0
- package/src/client-runtime/constants.js +10 -0
- package/src/client-runtime/mount.js +2 -3
- package/src/dev/client-gen.js +458 -0
- package/src/discovery/scan-routes.js +5 -2
- package/src/file-router.js +2 -0
- package/src/helpers.js +53 -0
- package/src/pages/page-loader.js +12 -205
- package/src/plugin-manager.js +9 -1
- package/src/server.js +231 -140
- package/src/services/executor.js +9 -0
- package/src/services/registry.js +9 -1
- package/src/services/validator.js +38 -20
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Add AWS Command
|
|
3
|
+
* Generate production-ready AWS configuration (apprunner.yaml, ecs-task-definition.json, deploy-aws.sh)
|
|
4
|
+
* Supports AWS App Runner and AWS ECS (Fargate) container deployments
|
|
5
|
+
* @module bin/commands/add-aws
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use strict';
|
|
9
|
+
|
|
10
|
+
const fs = require('fs');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { generateDockerfile, generateDockerignore } = require('./add-docker');
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Normalize CPU value to ECS Fargate units (256, 512, 1024, 2048, 4096)
|
|
16
|
+
* @param {string|number} cpu
|
|
17
|
+
* @returns {string}
|
|
18
|
+
*/
|
|
19
|
+
function normalizeEcsCpu(cpu) {
|
|
20
|
+
if (!cpu) return '512';
|
|
21
|
+
const str = String(cpu).trim().toLowerCase();
|
|
22
|
+
if (str === '0.25' || str === '0.25 vcpu') return '256';
|
|
23
|
+
if (str === '0.5' || str === '0.5 vcpu') return '512';
|
|
24
|
+
if (str === '1' || str === '1 vcpu') return '1024';
|
|
25
|
+
if (str === '2' || str === '2 vcpu') return '2048';
|
|
26
|
+
if (str === '4' || str === '4 vcpu') return '4096';
|
|
27
|
+
return str.replace(/[^0-9]/g, '') || '512';
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Normalize Memory value to ECS Fargate MiB (512, 1024, 2048, etc.)
|
|
32
|
+
* @param {string|number} memory
|
|
33
|
+
* @returns {string}
|
|
34
|
+
*/
|
|
35
|
+
function normalizeEcsMemory(memory) {
|
|
36
|
+
if (!memory) return '1024';
|
|
37
|
+
const str = String(memory).trim().toLowerCase();
|
|
38
|
+
if (str.endsWith('gb') || str.endsWith('g')) {
|
|
39
|
+
const num = parseFloat(str);
|
|
40
|
+
return String(Math.round(num * 1024));
|
|
41
|
+
}
|
|
42
|
+
if (str.endsWith('mib') || str.endsWith('mb') || str.endsWith('mi')) {
|
|
43
|
+
return str.replace(/[^0-9]/g, '') || '1024';
|
|
44
|
+
}
|
|
45
|
+
return str.replace(/[^0-9]/g, '') || '1024';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Generate apprunner.yaml for AWS App Runner
|
|
50
|
+
* @param {Object} options
|
|
51
|
+
* @param {string} options.appName
|
|
52
|
+
* @param {number} [options.port=7331]
|
|
53
|
+
* @param {string} [options.nodeVersion='nodejs20']
|
|
54
|
+
* @returns {string}
|
|
55
|
+
*/
|
|
56
|
+
function generateAppRunnerYaml(options = {}) {
|
|
57
|
+
const {
|
|
58
|
+
port = 7331,
|
|
59
|
+
nodeVersion = 'nodejs20',
|
|
60
|
+
} = options;
|
|
61
|
+
|
|
62
|
+
return `# ==============================================================================
|
|
63
|
+
# AWS App Runner Configuration for Webspresso
|
|
64
|
+
# Fully-managed container & code runtime with auto-scaling and zero-ops TLS
|
|
65
|
+
# Docs: https://docs.aws.amazon.com/apprunner/latest/dg/config-file.html
|
|
66
|
+
# ==============================================================================
|
|
67
|
+
|
|
68
|
+
version: 1.0
|
|
69
|
+
runtime: ${nodeVersion}
|
|
70
|
+
|
|
71
|
+
build:
|
|
72
|
+
commands:
|
|
73
|
+
build:
|
|
74
|
+
- npm ci --omit=dev
|
|
75
|
+
- npm run build --if-present
|
|
76
|
+
env:
|
|
77
|
+
- name: NODE_ENV
|
|
78
|
+
value: production
|
|
79
|
+
|
|
80
|
+
run:
|
|
81
|
+
command: npm start
|
|
82
|
+
network:
|
|
83
|
+
port: "${port}"
|
|
84
|
+
env: PORT
|
|
85
|
+
env:
|
|
86
|
+
- name: NODE_ENV
|
|
87
|
+
value: production
|
|
88
|
+
- name: PORT
|
|
89
|
+
value: "${port}"
|
|
90
|
+
`;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Generate AWS ECS Task Definition JSON for AWS Fargate
|
|
95
|
+
* @param {Object} options
|
|
96
|
+
* @param {string} options.appName
|
|
97
|
+
* @param {string} [options.region='us-east-1']
|
|
98
|
+
* @param {string|number} [options.cpu='512']
|
|
99
|
+
* @param {string|number} [options.memory='1024']
|
|
100
|
+
* @param {number} [options.port=7331]
|
|
101
|
+
* @returns {string}
|
|
102
|
+
*/
|
|
103
|
+
function generateEcsTaskDefinition(options = {}) {
|
|
104
|
+
const {
|
|
105
|
+
appName = 'webspresso-app',
|
|
106
|
+
region = 'us-east-1',
|
|
107
|
+
cpu = '512',
|
|
108
|
+
memory = '1024',
|
|
109
|
+
port = 7331,
|
|
110
|
+
} = options;
|
|
111
|
+
|
|
112
|
+
const normalizedCpu = normalizeEcsCpu(cpu);
|
|
113
|
+
const normalizedMemory = normalizeEcsMemory(memory);
|
|
114
|
+
|
|
115
|
+
const taskDef = {
|
|
116
|
+
family: appName,
|
|
117
|
+
networkMode: 'awsvpc',
|
|
118
|
+
requiresCompatibilities: ['FARGATE'],
|
|
119
|
+
cpu: normalizedCpu,
|
|
120
|
+
memory: normalizedMemory,
|
|
121
|
+
executionRoleArn: 'arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/ecsTaskExecutionRole',
|
|
122
|
+
taskRoleArn: 'arn:aws:iam::YOUR_AWS_ACCOUNT_ID:role/ecsTaskRole',
|
|
123
|
+
containerDefinitions: [
|
|
124
|
+
{
|
|
125
|
+
name: appName,
|
|
126
|
+
image: `YOUR_AWS_ACCOUNT_ID.dkr.ecr.${region}.amazonaws.com/${appName}:latest`,
|
|
127
|
+
essential: true,
|
|
128
|
+
portMappings: [
|
|
129
|
+
{
|
|
130
|
+
containerPort: port,
|
|
131
|
+
hostPort: port,
|
|
132
|
+
protocol: 'tcp',
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
environment: [
|
|
136
|
+
{ name: 'NODE_ENV', value: 'production' },
|
|
137
|
+
{ name: 'PORT', value: String(port) },
|
|
138
|
+
],
|
|
139
|
+
healthCheck: {
|
|
140
|
+
command: [
|
|
141
|
+
'CMD-SHELL',
|
|
142
|
+
`node -e 'require("http").get("http://localhost:${port}/healthz", (r) => process.exit(r.statusCode === 200 ? 0 : 1))' || exit 1`,
|
|
143
|
+
],
|
|
144
|
+
interval: 30,
|
|
145
|
+
timeout: 5,
|
|
146
|
+
retries: 3,
|
|
147
|
+
startPeriod: 15,
|
|
148
|
+
},
|
|
149
|
+
logConfiguration: {
|
|
150
|
+
logDriver: 'awslogs',
|
|
151
|
+
options: {
|
|
152
|
+
'awslogs-group': `/ecs/${appName}`,
|
|
153
|
+
'awslogs-region': region,
|
|
154
|
+
'awslogs-stream-prefix': 'ecs',
|
|
155
|
+
'awslogs-create-group': 'true',
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
};
|
|
161
|
+
|
|
162
|
+
return JSON.stringify(taskDef, null, 2) + '\n';
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Generate deploy-aws.sh script for automated deployment to ECR, App Runner, or ECS
|
|
167
|
+
* @param {Object} options
|
|
168
|
+
* @param {string} options.appName
|
|
169
|
+
* @param {string} [options.region='us-east-1']
|
|
170
|
+
* @param {number} [options.port=7331]
|
|
171
|
+
* @returns {string}
|
|
172
|
+
*/
|
|
173
|
+
function generateAwsDeployScript(options = {}) {
|
|
174
|
+
const {
|
|
175
|
+
appName = 'webspresso-app',
|
|
176
|
+
region = 'us-east-1',
|
|
177
|
+
port = 7331,
|
|
178
|
+
} = options;
|
|
179
|
+
|
|
180
|
+
return `#!/usr/bin/env bash
|
|
181
|
+
set -euo pipefail
|
|
182
|
+
|
|
183
|
+
# ==============================================================================
|
|
184
|
+
# Webspresso AWS Deployment Automation Script
|
|
185
|
+
# Builds hardened Docker image, pushes to Amazon ECR, and updates App Runner / ECS
|
|
186
|
+
# Usage: ./deploy-aws.sh [APP_NAME] [REGION]
|
|
187
|
+
# ==============================================================================
|
|
188
|
+
|
|
189
|
+
APP_NAME="\${1:-${appName}}"
|
|
190
|
+
REGION="\${2:-\${AWS_REGION:-${region}}}"
|
|
191
|
+
PORT="\${PORT:-${port}}"
|
|
192
|
+
|
|
193
|
+
echo "☁️ Webspresso AWS Deployment Target: '\$APP_NAME' in '\$REGION'"
|
|
194
|
+
|
|
195
|
+
# 1. Verify AWS CLI
|
|
196
|
+
if ! command -v aws &> /dev/null; then
|
|
197
|
+
echo "❌ Error: 'aws' CLI is not installed or not in PATH."
|
|
198
|
+
echo "Please install AWS CLI: https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html"
|
|
199
|
+
exit 1
|
|
200
|
+
fi
|
|
201
|
+
|
|
202
|
+
# 2. Verify Docker
|
|
203
|
+
if ! command -v docker &> /dev/null; then
|
|
204
|
+
echo "❌ Error: 'docker' is not installed or not running."
|
|
205
|
+
exit 1
|
|
206
|
+
fi
|
|
207
|
+
|
|
208
|
+
# 3. Retrieve AWS Account ID
|
|
209
|
+
AWS_ACCOUNT_ID="\$(aws sts get-caller-identity --query Account --output text 2>/dev/null || true)"
|
|
210
|
+
if [ -z "\$AWS_ACCOUNT_ID" ]; then
|
|
211
|
+
echo "❌ Error: Could not determine AWS Account ID. Are your AWS credentials configured?"
|
|
212
|
+
echo "Run: aws configure"
|
|
213
|
+
exit 1
|
|
214
|
+
fi
|
|
215
|
+
|
|
216
|
+
ECR_URI="\$AWS_ACCOUNT_ID.dkr.ecr.\$REGION.amazonaws.com/\$APP_NAME"
|
|
217
|
+
IMAGE_TAG="\$(git rev-parse --short HEAD 2>/dev/null || echo 'latest')"
|
|
218
|
+
|
|
219
|
+
echo "🔑 Authenticated as AWS Account: \$AWS_ACCOUNT_ID"
|
|
220
|
+
echo "📦 Target ECR Repository: \$ECR_URI"
|
|
221
|
+
|
|
222
|
+
# 4. Ensure ECR Repository exists
|
|
223
|
+
aws ecr describe-repositories --repository-names "\$APP_NAME" --region "\$REGION" &>/dev/null || {
|
|
224
|
+
echo "📁 Creating ECR repository '\$APP_NAME'..."
|
|
225
|
+
aws ecr create-repository \\
|
|
226
|
+
--repository-name "\$APP_NAME" \\
|
|
227
|
+
--image-scanning-configuration scanOnPush=true \\
|
|
228
|
+
--region "\$REGION" >/dev/null
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
# 5. Authenticate Docker with Amazon ECR
|
|
232
|
+
echo "🔐 Logging in to Amazon ECR..."
|
|
233
|
+
aws ecr get-login-password --region "\$REGION" | docker login --username AWS --password-stdin "\$AWS_ACCOUNT_ID.dkr.ecr.\$REGION.amazonaws.com"
|
|
234
|
+
|
|
235
|
+
# 6. Build production multi-stage Docker image
|
|
236
|
+
echo "🔨 Building Docker image..."
|
|
237
|
+
docker build -t "\$APP_NAME:\$IMAGE_TAG" -t "\$ECR_URI:\$IMAGE_TAG" -t "\$ECR_URI:latest" .
|
|
238
|
+
|
|
239
|
+
# 7. Push to ECR
|
|
240
|
+
echo "🚀 Pushing image to Amazon ECR..."
|
|
241
|
+
docker push "\$ECR_URI:\$IMAGE_TAG"
|
|
242
|
+
docker push "\$ECR_URI:latest"
|
|
243
|
+
|
|
244
|
+
echo ""
|
|
245
|
+
echo "✅ Image successfully built and pushed to Amazon ECR:"
|
|
246
|
+
echo " \$ECR_URI:\$IMAGE_TAG"
|
|
247
|
+
echo " \$ECR_URI:latest"
|
|
248
|
+
echo ""
|
|
249
|
+
echo "Next steps to complete deployment:"
|
|
250
|
+
echo " Option A (AWS App Runner):"
|
|
251
|
+
echo " aws apprunner create-service --service-name \$APP_NAME \\"
|
|
252
|
+
echo " --source-configuration ImageRepository='{ImageIdentifier=\"'\$ECR_URI:latest'\",ImageRepositoryType=\"ECR\",ImageConfiguration={Port=\"'\$PORT'\"}}'"
|
|
253
|
+
echo ""
|
|
254
|
+
echo " Option B (AWS ECS Fargate):"
|
|
255
|
+
echo " aws ecs register-task-definition --cli-input-json file://ecs-task-definition.json"
|
|
256
|
+
echo " aws ecs update-service --cluster \$APP_NAME-cluster --service \$APP_NAME-service --force-new-deployment"
|
|
257
|
+
echo ""
|
|
258
|
+
`;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Register command with Commander
|
|
263
|
+
* @param {import('commander').Command} program
|
|
264
|
+
*/
|
|
265
|
+
function registerCommand(program) {
|
|
266
|
+
program
|
|
267
|
+
.command('add:aws')
|
|
268
|
+
.alias('aws')
|
|
269
|
+
.alias('add-aws')
|
|
270
|
+
.description('Generate production-ready AWS deployment configuration (App Runner & ECS Fargate)')
|
|
271
|
+
.option('-r, --region <region>', 'AWS region', 'us-east-1')
|
|
272
|
+
.option('--port <port>', 'Container port (Webspresso default is 7331)', (v) => parseInt(v, 10), 7331)
|
|
273
|
+
.option('--cpu <cpu>', 'CPU allocation for ECS/App Runner (e.g. 512, 1024 or 1 vCPU)', '512')
|
|
274
|
+
.option('--memory <memory>', 'Memory allocation for ECS/App Runner (e.g. 1024, 1GB)', '1024')
|
|
275
|
+
.option('-f, --force', 'Overwrite existing files without prompting', false)
|
|
276
|
+
.action(async (...actionArgs) => {
|
|
277
|
+
const cmd = actionArgs[actionArgs.length - 1];
|
|
278
|
+
const options = (cmd && typeof cmd.opts === 'function')
|
|
279
|
+
? cmd.opts()
|
|
280
|
+
: (actionArgs.find((a) => a && typeof a === 'object') || {});
|
|
281
|
+
|
|
282
|
+
const cwd = process.cwd();
|
|
283
|
+
const pkgPath = path.join(cwd, 'package.json');
|
|
284
|
+
|
|
285
|
+
if (!fs.existsSync(pkgPath)) {
|
|
286
|
+
console.error('❌ Not a Webspresso project! package.json was not found in current directory.');
|
|
287
|
+
process.exit(1);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
let pkg = {};
|
|
291
|
+
try {
|
|
292
|
+
pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
293
|
+
} catch (err) {
|
|
294
|
+
console.error(`❌ Failed to parse package.json: ${err.message}`);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const appName = pkg.name ? pkg.name.replace(/[^a-zA-Z0-9_-]/g, '-') : 'webspresso-app';
|
|
299
|
+
const force = Boolean(options.force);
|
|
300
|
+
const region = options.region || 'us-east-1';
|
|
301
|
+
const port = options.port || 7331;
|
|
302
|
+
const cpu = options.cpu || '512';
|
|
303
|
+
const memory = options.memory || '1024';
|
|
304
|
+
|
|
305
|
+
const appRunnerPath = path.join(cwd, 'apprunner.yaml');
|
|
306
|
+
const ecsTaskDefPath = path.join(cwd, 'ecs-task-definition.json');
|
|
307
|
+
const scriptPath = path.join(cwd, 'deploy-aws.sh');
|
|
308
|
+
const dockerfilePath = path.join(cwd, 'Dockerfile');
|
|
309
|
+
const dockerignorePath = path.join(cwd, '.dockerignore');
|
|
310
|
+
|
|
311
|
+
console.log(`\n☁️ Scaffolding AWS deployment configuration for "${appName}"...`);
|
|
312
|
+
|
|
313
|
+
// 1. Ensure Dockerfile exists (needed by ECR & container deploy)
|
|
314
|
+
if (!fs.existsSync(dockerfilePath)) {
|
|
315
|
+
const dockerfileContent = generateDockerfile({ port, node: '22-alpine' });
|
|
316
|
+
fs.writeFileSync(dockerfilePath, dockerfileContent, 'utf8');
|
|
317
|
+
console.log(`✅ Created Dockerfile (configured for port ${port})`);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
if (!fs.existsSync(dockerignorePath)) {
|
|
321
|
+
const dockerignoreContent = generateDockerignore();
|
|
322
|
+
fs.writeFileSync(dockerignorePath, dockerignoreContent, 'utf8');
|
|
323
|
+
console.log(`✅ Created .dockerignore`);
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// 2. apprunner.yaml
|
|
327
|
+
if (fs.existsSync(appRunnerPath) && !force) {
|
|
328
|
+
console.log(`⚠️ ${path.basename(appRunnerPath)} already exists. Skipping (use --force to overwrite).`);
|
|
329
|
+
} else {
|
|
330
|
+
const appRunnerContent = generateAppRunnerYaml({
|
|
331
|
+
appName,
|
|
332
|
+
port,
|
|
333
|
+
});
|
|
334
|
+
fs.writeFileSync(appRunnerPath, appRunnerContent, 'utf8');
|
|
335
|
+
console.log(`✅ Created ${path.basename(appRunnerPath)} (AWS App Runner)`);
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
// 3. ecs-task-definition.json
|
|
339
|
+
if (fs.existsSync(ecsTaskDefPath) && !force) {
|
|
340
|
+
console.log(`⚠️ ${path.basename(ecsTaskDefPath)} already exists. Skipping (use --force to overwrite).`);
|
|
341
|
+
} else {
|
|
342
|
+
const taskDefContent = generateEcsTaskDefinition({
|
|
343
|
+
appName,
|
|
344
|
+
region,
|
|
345
|
+
cpu,
|
|
346
|
+
memory,
|
|
347
|
+
port,
|
|
348
|
+
});
|
|
349
|
+
fs.writeFileSync(ecsTaskDefPath, taskDefContent, 'utf8');
|
|
350
|
+
console.log(`✅ Created ${path.basename(ecsTaskDefPath)} (AWS ECS Fargate Task Definition)`);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// 4. deploy-aws.sh
|
|
354
|
+
if (fs.existsSync(scriptPath) && !force) {
|
|
355
|
+
console.log(`⚠️ ${path.basename(scriptPath)} already exists. Skipping (use --force to overwrite).`);
|
|
356
|
+
} else {
|
|
357
|
+
const scriptContent = generateAwsDeployScript({
|
|
358
|
+
appName,
|
|
359
|
+
region,
|
|
360
|
+
port,
|
|
361
|
+
});
|
|
362
|
+
fs.writeFileSync(scriptPath, scriptContent, { encoding: 'utf8', mode: 0o755 });
|
|
363
|
+
console.log(`✅ Created ${path.basename(scriptPath)} (executable)`);
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
// 5. Update package.json scripts
|
|
367
|
+
let modifiedPkg = false;
|
|
368
|
+
if (!pkg.scripts) pkg.scripts = {};
|
|
369
|
+
if (!pkg.scripts['aws:deploy']) {
|
|
370
|
+
pkg.scripts['aws:deploy'] = './deploy-aws.sh';
|
|
371
|
+
modifiedPkg = true;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (modifiedPkg) {
|
|
375
|
+
try {
|
|
376
|
+
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n', 'utf8');
|
|
377
|
+
console.log(`✅ Added "aws:deploy" script to package.json`);
|
|
378
|
+
} catch (_) {}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
console.log('\n🎉 AWS deployment configuration complete!');
|
|
382
|
+
console.log('\nNext steps:');
|
|
383
|
+
console.log(' 1. Configure AWS CLI: `aws configure`');
|
|
384
|
+
console.log(' 2. Review ecs-task-definition.json and apprunner.yaml');
|
|
385
|
+
console.log(' 3. Deploy container via ECR: `npm run aws:deploy`\n');
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
module.exports = {
|
|
390
|
+
registerCommand,
|
|
391
|
+
generateAppRunnerYaml,
|
|
392
|
+
generateEcsTaskDefinition,
|
|
393
|
+
generateAwsDeployScript,
|
|
394
|
+
normalizeEcsCpu,
|
|
395
|
+
normalizeEcsMemory,
|
|
396
|
+
};
|