@ts-cloud/core 0.7.47 → 0.7.49
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/dist/index.js +190 -1
- package/dist/modules/index.d.ts +1 -0
- package/dist/modules/sftp.d.ts +12 -0
- package/dist/modules/sftp.test.d.ts +1 -0
- package/dist/types.d.ts +38 -0
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -10836,6 +10836,136 @@ class SMS {
|
|
|
10836
10836
|
return { resources, outputs };
|
|
10837
10837
|
}
|
|
10838
10838
|
}
|
|
10839
|
+
// src/modules/sftp.ts
|
|
10840
|
+
function logicalPart(value) {
|
|
10841
|
+
const clean = value.replace(/[^a-zA-Z0-9]/g, " ").trim();
|
|
10842
|
+
const part = clean.split(/\s+/).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
|
|
10843
|
+
return part || "User";
|
|
10844
|
+
}
|
|
10845
|
+
function normalizeHomeDirectory(value, username) {
|
|
10846
|
+
const path = (value || username).replace(/^\/+|\/+$/g, "");
|
|
10847
|
+
if (!path || path.split("/").includes(".."))
|
|
10848
|
+
throw new Error(`sftp: invalid homeDirectory for user ${username}`);
|
|
10849
|
+
return path;
|
|
10850
|
+
}
|
|
10851
|
+
|
|
10852
|
+
class Sftp {
|
|
10853
|
+
static create(options) {
|
|
10854
|
+
if (!options.bucket.trim())
|
|
10855
|
+
throw new Error("sftp: bucket is required");
|
|
10856
|
+
const endpointType = options.endpointType ?? "PUBLIC";
|
|
10857
|
+
if (endpointType === "VPC" && (!options.endpointDetails?.vpcId || !options.endpointDetails.subnetIds.length))
|
|
10858
|
+
throw new Error("sftp: VPC endpoints require endpointDetails.vpcId and at least one subnet");
|
|
10859
|
+
const prefix = `${logicalPart(options.slug)}${logicalPart(options.environment)}Sftp`;
|
|
10860
|
+
const serverLogicalId = `${prefix}Server`;
|
|
10861
|
+
const resources = {};
|
|
10862
|
+
let loggingRoleArn;
|
|
10863
|
+
if (options.logging !== false) {
|
|
10864
|
+
const loggingRoleLogicalId = `${prefix}LoggingRole`;
|
|
10865
|
+
resources[loggingRoleLogicalId] = {
|
|
10866
|
+
Type: "AWS::IAM::Role",
|
|
10867
|
+
Properties: {
|
|
10868
|
+
AssumeRolePolicyDocument: {
|
|
10869
|
+
Version: "2012-10-17",
|
|
10870
|
+
Statement: [{ Effect: "Allow", Principal: { Service: "transfer.amazonaws.com" }, Action: "sts:AssumeRole" }]
|
|
10871
|
+
},
|
|
10872
|
+
Policies: [{
|
|
10873
|
+
PolicyName: "TransferLogging",
|
|
10874
|
+
PolicyDocument: {
|
|
10875
|
+
Version: "2012-10-17",
|
|
10876
|
+
Statement: [{
|
|
10877
|
+
Effect: "Allow",
|
|
10878
|
+
Action: ["logs:CreateLogGroup", "logs:CreateLogStream", "logs:DescribeLogStreams", "logs:PutLogEvents"],
|
|
10879
|
+
Resource: "arn:aws:logs:*:*:log-group:/aws/transfer/*"
|
|
10880
|
+
}]
|
|
10881
|
+
}
|
|
10882
|
+
}]
|
|
10883
|
+
}
|
|
10884
|
+
};
|
|
10885
|
+
loggingRoleArn = { "Fn::GetAtt": [loggingRoleLogicalId, "Arn"] };
|
|
10886
|
+
}
|
|
10887
|
+
resources[serverLogicalId] = {
|
|
10888
|
+
Type: "AWS::Transfer::Server",
|
|
10889
|
+
Properties: {
|
|
10890
|
+
Domain: "S3",
|
|
10891
|
+
EndpointType: endpointType,
|
|
10892
|
+
IdentityProviderType: "SERVICE_MANAGED",
|
|
10893
|
+
Protocols: ["SFTP"],
|
|
10894
|
+
...endpointType === "VPC" ? {
|
|
10895
|
+
EndpointDetails: {
|
|
10896
|
+
VpcId: options.endpointDetails.vpcId,
|
|
10897
|
+
SubnetIds: options.endpointDetails.subnetIds,
|
|
10898
|
+
...options.endpointDetails.securityGroupIds?.length ? { SecurityGroupIds: options.endpointDetails.securityGroupIds } : {},
|
|
10899
|
+
...options.endpointDetails.addressAllocationIds?.length ? { AddressAllocationIds: options.endpointDetails.addressAllocationIds } : {}
|
|
10900
|
+
}
|
|
10901
|
+
} : {},
|
|
10902
|
+
...options.securityPolicyName ? { SecurityPolicyName: options.securityPolicyName } : {},
|
|
10903
|
+
...loggingRoleArn ? { LoggingRole: loggingRoleArn } : {},
|
|
10904
|
+
Tags: [
|
|
10905
|
+
{ Key: "Project", Value: options.slug },
|
|
10906
|
+
{ Key: "Environment", Value: options.environment },
|
|
10907
|
+
{ Key: "ManagedBy", Value: "ts-cloud" }
|
|
10908
|
+
]
|
|
10909
|
+
}
|
|
10910
|
+
};
|
|
10911
|
+
for (const [username, user] of Object.entries(options.users)) {
|
|
10912
|
+
if (!/^[a-zA-Z0-9_.@-]{3,100}$/.test(username))
|
|
10913
|
+
throw new Error(`sftp: invalid username ${username}`);
|
|
10914
|
+
if (!user.sshPublicKeys.length)
|
|
10915
|
+
throw new Error(`sftp: user ${username} requires at least one SSH public key`);
|
|
10916
|
+
const userPart = logicalPart(username);
|
|
10917
|
+
const home = normalizeHomeDirectory(user.homeDirectory, username);
|
|
10918
|
+
const userLogicalId = `${prefix}${userPart}User`;
|
|
10919
|
+
let roleArn = user.roleArn;
|
|
10920
|
+
if (!roleArn) {
|
|
10921
|
+
const roleLogicalId = `${prefix}${userPart}Role`;
|
|
10922
|
+
const bucketArn = `arn:aws:s3:::${options.bucket}`;
|
|
10923
|
+
resources[roleLogicalId] = {
|
|
10924
|
+
Type: "AWS::IAM::Role",
|
|
10925
|
+
Properties: {
|
|
10926
|
+
AssumeRolePolicyDocument: {
|
|
10927
|
+
Version: "2012-10-17",
|
|
10928
|
+
Statement: [{ Effect: "Allow", Principal: { Service: "transfer.amazonaws.com" }, Action: "sts:AssumeRole" }]
|
|
10929
|
+
},
|
|
10930
|
+
Policies: [{
|
|
10931
|
+
PolicyName: "SftpHomeDirectory",
|
|
10932
|
+
PolicyDocument: {
|
|
10933
|
+
Version: "2012-10-17",
|
|
10934
|
+
Statement: [
|
|
10935
|
+
{
|
|
10936
|
+
Effect: "Allow",
|
|
10937
|
+
Action: ["s3:ListBucket", "s3:GetBucketLocation"],
|
|
10938
|
+
Resource: bucketArn,
|
|
10939
|
+
Condition: { StringLike: { "s3:prefix": [home, `${home}/*`] } }
|
|
10940
|
+
},
|
|
10941
|
+
{
|
|
10942
|
+
Effect: "Allow",
|
|
10943
|
+
Action: ["s3:GetObject", "s3:PutObject", "s3:DeleteObject", "s3:GetObjectVersion"],
|
|
10944
|
+
Resource: `${bucketArn}/${home}/*`
|
|
10945
|
+
}
|
|
10946
|
+
]
|
|
10947
|
+
}
|
|
10948
|
+
}]
|
|
10949
|
+
}
|
|
10950
|
+
};
|
|
10951
|
+
roleArn = { "Fn::GetAtt": [roleLogicalId, "Arn"] };
|
|
10952
|
+
}
|
|
10953
|
+
resources[userLogicalId] = {
|
|
10954
|
+
Type: "AWS::Transfer::User",
|
|
10955
|
+
DependsOn: [serverLogicalId],
|
|
10956
|
+
Properties: {
|
|
10957
|
+
ServerId: { Ref: serverLogicalId },
|
|
10958
|
+
UserName: username,
|
|
10959
|
+
Role: roleArn,
|
|
10960
|
+
HomeDirectoryType: "PATH",
|
|
10961
|
+
HomeDirectory: `/${options.bucket}/${home}`,
|
|
10962
|
+
SshPublicKeys: user.sshPublicKeys
|
|
10963
|
+
}
|
|
10964
|
+
};
|
|
10965
|
+
}
|
|
10966
|
+
return { resources, serverLogicalId };
|
|
10967
|
+
}
|
|
10968
|
+
}
|
|
10839
10969
|
// src/modules/ai.ts
|
|
10840
10970
|
class AI {
|
|
10841
10971
|
static createBedrockRole(servicePrincipal, options) {
|
|
@@ -25298,6 +25428,9 @@ var cloud_config_schema_default = {
|
|
|
25298
25428
|
cdn: {
|
|
25299
25429
|
$ref: "#/definitions/cdn"
|
|
25300
25430
|
},
|
|
25431
|
+
sftp: {
|
|
25432
|
+
$ref: "#/definitions/sftp"
|
|
25433
|
+
},
|
|
25301
25434
|
functions: {
|
|
25302
25435
|
type: "object",
|
|
25303
25436
|
description: "Lambda functions configuration",
|
|
@@ -25348,6 +25481,14 @@ var cloud_config_schema_default = {
|
|
|
25348
25481
|
timeout: { type: "number", description: "HTTP request timeout in seconds" },
|
|
25349
25482
|
gatewayVersion: { type: "number", enum: [1, 2], description: "API Gateway version (2 = HTTP API, 1 = REST)" },
|
|
25350
25483
|
warm: { type: "number", description: "Keep-warm / provisioned concurrency count" },
|
|
25484
|
+
lambdaInsights: {
|
|
25485
|
+
type: "object",
|
|
25486
|
+
description: "CloudWatch Lambda Insights extension configuration for zip deployments",
|
|
25487
|
+
required: ["layerArn"],
|
|
25488
|
+
properties: {
|
|
25489
|
+
layerArn: { type: "string", description: "Region- and architecture-specific Lambda Insights extension layer ARN" }
|
|
25490
|
+
}
|
|
25491
|
+
},
|
|
25351
25492
|
queues: { description: "Queue names (true = single default queue, false = disabled)" },
|
|
25352
25493
|
scheduler: { type: "string", enum: ["off", "on", "sub-minute"], description: "Task scheduler mode" },
|
|
25353
25494
|
build: { type: "array", items: { type: "string" }, description: "Commands run locally before packaging" },
|
|
@@ -25621,6 +25762,15 @@ var cloud_config_schema_default = {
|
|
|
25621
25762
|
type: "boolean",
|
|
25622
25763
|
default: true
|
|
25623
25764
|
},
|
|
25765
|
+
originShield: {
|
|
25766
|
+
type: "boolean",
|
|
25767
|
+
description: "Enable CloudFront Origin Shield",
|
|
25768
|
+
default: false
|
|
25769
|
+
},
|
|
25770
|
+
originShieldRegion: {
|
|
25771
|
+
type: "string",
|
|
25772
|
+
description: "AWS region used by CloudFront Origin Shield"
|
|
25773
|
+
},
|
|
25624
25774
|
compress: {
|
|
25625
25775
|
type: "boolean",
|
|
25626
25776
|
description: "Enable automatic compression",
|
|
@@ -25661,6 +25811,38 @@ var cloud_config_schema_default = {
|
|
|
25661
25811
|
}
|
|
25662
25812
|
}
|
|
25663
25813
|
},
|
|
25814
|
+
sftp: {
|
|
25815
|
+
type: "object",
|
|
25816
|
+
required: ["bucket", "users"],
|
|
25817
|
+
properties: {
|
|
25818
|
+
bucket: { type: "string", description: "S3 bucket backing the SFTP server" },
|
|
25819
|
+
endpointType: { type: "string", enum: ["PUBLIC", "VPC"], default: "PUBLIC" },
|
|
25820
|
+
endpointDetails: {
|
|
25821
|
+
type: "object",
|
|
25822
|
+
required: ["vpcId", "subnetIds"],
|
|
25823
|
+
properties: {
|
|
25824
|
+
vpcId: { type: "string" },
|
|
25825
|
+
subnetIds: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
25826
|
+
securityGroupIds: { type: "array", items: { type: "string" } },
|
|
25827
|
+
addressAllocationIds: { type: "array", items: { type: "string" } }
|
|
25828
|
+
}
|
|
25829
|
+
},
|
|
25830
|
+
securityPolicyName: { type: "string" },
|
|
25831
|
+
logging: { type: "boolean", default: true },
|
|
25832
|
+
users: {
|
|
25833
|
+
type: "object",
|
|
25834
|
+
additionalProperties: {
|
|
25835
|
+
type: "object",
|
|
25836
|
+
required: ["sshPublicKeys"],
|
|
25837
|
+
properties: {
|
|
25838
|
+
sshPublicKeys: { type: "array", items: { type: "string" }, minItems: 1 },
|
|
25839
|
+
homeDirectory: { type: "string" },
|
|
25840
|
+
roleArn: { type: "string" }
|
|
25841
|
+
}
|
|
25842
|
+
}
|
|
25843
|
+
}
|
|
25844
|
+
}
|
|
25845
|
+
},
|
|
25664
25846
|
function: {
|
|
25665
25847
|
type: "object",
|
|
25666
25848
|
required: ["name", "runtime"],
|
|
@@ -45568,6 +45750,9 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45568
45750
|
const hasQueue = queueNames.length > 0;
|
|
45569
45751
|
const logRetention = app.logRetention ?? 14;
|
|
45570
45752
|
const imageMode = app.packaging === "image";
|
|
45753
|
+
if (imageMode && app.lambdaInsights) {
|
|
45754
|
+
throw new Error("serverless app: `lambdaInsights` layer attachment is only supported for zip packaging. Container images must install the Lambda Insights extension in the image.");
|
|
45755
|
+
}
|
|
45571
45756
|
const schedulerEnabled = (app.scheduler ?? "on") !== "off";
|
|
45572
45757
|
const cacheEnabled = (app.cache?.driver ?? "dynamodb") === "dynamodb";
|
|
45573
45758
|
const assetsEnabled = Boolean(app.assets);
|
|
@@ -45629,6 +45814,8 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45629
45814
|
const managedPolicies = ["arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole"];
|
|
45630
45815
|
if (app.vpc?.subnets?.length)
|
|
45631
45816
|
managedPolicies.push("arn:aws:iam::aws:policy/service-role/AWSLambdaVPCAccessExecutionRole");
|
|
45817
|
+
if (app.lambdaInsights)
|
|
45818
|
+
managedPolicies.push("arn:aws:iam::aws:policy/CloudWatchLambdaInsightsExecutionRolePolicy");
|
|
45632
45819
|
resources.AppRole = {
|
|
45633
45820
|
Type: "AWS::IAM::Role",
|
|
45634
45821
|
Properties: {
|
|
@@ -45684,6 +45871,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45684
45871
|
Type: "AWS::Logs::LogGroup",
|
|
45685
45872
|
Properties: { LogGroupName: `/aws/lambda/${name}`, RetentionInDays: logRetention }
|
|
45686
45873
|
};
|
|
45874
|
+
const layers = [...opts.runtimeLayers ?? [], ...app.lambdaInsights ? [app.lambdaInsights.layerArn] : []];
|
|
45687
45875
|
const codeProps = imageMode ? {
|
|
45688
45876
|
PackageType: "Image",
|
|
45689
45877
|
Code: { ImageUri: Fn2.ref("ImageUri") },
|
|
@@ -45692,7 +45880,7 @@ function composeServerlessAppTemplate(opts) {
|
|
|
45692
45880
|
Runtime: runtime,
|
|
45693
45881
|
Handler: handler8,
|
|
45694
45882
|
Code: { S3Bucket: Fn2.ref("ArtifactBucket"), S3Key: Fn2.ref("ArtifactKey") },
|
|
45695
|
-
...
|
|
45883
|
+
...layers.length ? { Layers: [...new Set(layers)] } : {}
|
|
45696
45884
|
};
|
|
45697
45885
|
resources[logicalId] = {
|
|
45698
45886
|
Type: "AWS::Lambda::Function",
|
|
@@ -47494,6 +47682,7 @@ export {
|
|
|
47494
47682
|
Spinner,
|
|
47495
47683
|
exports_send as SmsHandlers,
|
|
47496
47684
|
exports_advanced3 as SmsAdvanced,
|
|
47685
|
+
Sftp,
|
|
47497
47686
|
ServiceMeshManager,
|
|
47498
47687
|
SenderReputationManager,
|
|
47499
47688
|
SecurityScanningManager,
|
package/dist/modules/index.d.ts
CHANGED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { EnvironmentType, SftpConfig } from '../types';
|
|
2
|
+
export interface SftpResources {
|
|
3
|
+
resources: Record<string, any>;
|
|
4
|
+
serverLogicalId: string;
|
|
5
|
+
}
|
|
6
|
+
/** Build an AWS Transfer Family SFTP server with service-managed users. */
|
|
7
|
+
export declare class Sftp {
|
|
8
|
+
static create(options: SftpConfig & {
|
|
9
|
+
slug: string;
|
|
10
|
+
environment: EnvironmentType;
|
|
11
|
+
}): SftpResources;
|
|
12
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/types.d.ts
CHANGED
|
@@ -354,6 +354,8 @@ export interface InfrastructureConfig {
|
|
|
354
354
|
appDatabase?: DatabaseConfig;
|
|
355
355
|
cache?: CacheConfig;
|
|
356
356
|
cdn?: Record<string, CdnItemConfig & ResourceConditions> | CdnItemConfig;
|
|
357
|
+
/** AWS Transfer Family SFTP server backed by S3. */
|
|
358
|
+
sftp?: SftpConfig;
|
|
357
359
|
/**
|
|
358
360
|
* Elastic File System (EFS) configuration
|
|
359
361
|
* For shared file storage across multiple instances
|
|
@@ -1378,6 +1380,23 @@ export interface CdnConfig {
|
|
|
1378
1380
|
customDomain?: string;
|
|
1379
1381
|
certificateArn?: string;
|
|
1380
1382
|
}
|
|
1383
|
+
export interface SftpConfig {
|
|
1384
|
+
bucket: string;
|
|
1385
|
+
users: Record<string, {
|
|
1386
|
+
sshPublicKeys: string[];
|
|
1387
|
+
homeDirectory?: string;
|
|
1388
|
+
roleArn?: string;
|
|
1389
|
+
}>;
|
|
1390
|
+
endpointType?: 'PUBLIC' | 'VPC';
|
|
1391
|
+
endpointDetails?: {
|
|
1392
|
+
vpcId: string;
|
|
1393
|
+
subnetIds: string[];
|
|
1394
|
+
securityGroupIds?: string[];
|
|
1395
|
+
addressAllocationIds?: string[];
|
|
1396
|
+
};
|
|
1397
|
+
securityPolicyName?: string;
|
|
1398
|
+
logging?: boolean;
|
|
1399
|
+
}
|
|
1381
1400
|
export interface DnsConfig {
|
|
1382
1401
|
domain?: string;
|
|
1383
1402
|
hostedZoneId?: string;
|
|
@@ -1717,6 +1736,17 @@ export interface ServerlessAppConfig {
|
|
|
1717
1736
|
provisionedConcurrency?: number;
|
|
1718
1737
|
/** CloudWatch log retention (days) for all function log groups. @default 14 */
|
|
1719
1738
|
logRetention?: number;
|
|
1739
|
+
/**
|
|
1740
|
+
* Enable CloudWatch Lambda Insights for every function in the app.
|
|
1741
|
+
*
|
|
1742
|
+
* AWS publishes architecture- and region-specific extension layers, so the
|
|
1743
|
+
* layer ARN stays explicit instead of ts-cloud pinning a version that can go
|
|
1744
|
+
* stale. Zip deployments attach the layer and the required AWS managed role
|
|
1745
|
+
* policy. Container-image deployments must bake the extension into the image.
|
|
1746
|
+
*/
|
|
1747
|
+
lambdaInsights?: {
|
|
1748
|
+
layerArn: string;
|
|
1749
|
+
};
|
|
1720
1750
|
/** CLI function memory in MB. @default 1024 */
|
|
1721
1751
|
cliMemory?: number;
|
|
1722
1752
|
/** CLI command timeout in seconds (allow room for migrations). @default 900 */
|
|
@@ -2684,6 +2714,10 @@ export interface CdnFrontConfig {
|
|
|
2684
2714
|
secret?: string;
|
|
2685
2715
|
/** Header name carrying {@link secret}. @default 'X-Origin-Verify' */
|
|
2686
2716
|
secretHeader?: string;
|
|
2717
|
+
/** Enable CloudFront Origin Shield for the gateway origin. @default false */
|
|
2718
|
+
originShield?: boolean;
|
|
2719
|
+
/** AWS region used by Origin Shield. Required when {@link originShield} is enabled. */
|
|
2720
|
+
originShieldRegion?: string;
|
|
2687
2721
|
}
|
|
2688
2722
|
export interface DatabaseItemConfig {
|
|
2689
2723
|
engine?: 'dynamodb' | 'postgres' | 'mysql';
|
|
@@ -2757,6 +2791,10 @@ export interface CdnItemConfig {
|
|
|
2757
2791
|
* Enable CDN
|
|
2758
2792
|
*/
|
|
2759
2793
|
enabled?: boolean;
|
|
2794
|
+
/** Enable CloudFront Origin Shield for this origin. @default false */
|
|
2795
|
+
originShield?: boolean;
|
|
2796
|
+
/** AWS region used by Origin Shield. Defaults to the deployment region. */
|
|
2797
|
+
originShieldRegion?: string;
|
|
2760
2798
|
/**
|
|
2761
2799
|
* Cache policy configuration
|
|
2762
2800
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ts-cloud/core",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.49",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Core CloudFormation generation library for ts-cloud",
|
|
6
6
|
"author": "Chris Breuer <chris@stacksjs.com>",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"typecheck": "tsc --noEmit"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@ts-cloud/aws-types": "0.7.
|
|
34
|
+
"@ts-cloud/aws-types": "0.7.49"
|
|
35
35
|
},
|
|
36
36
|
"devDependencies": {
|
|
37
37
|
"typescript": "^7.0.2"
|