@hot-updater/aws 0.20.13 → 0.20.14
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/iac/index.cjs +43 -8
- package/dist/iac/index.js +42 -8
- package/dist/lambda/index.cjs +31 -3
- package/dist/lambda/index.d.cts +2 -1
- package/package.json +5 -4
package/dist/iac/index.cjs
CHANGED
|
@@ -70,6 +70,8 @@ let crypto = require("crypto");
|
|
|
70
70
|
crypto = __toESM(crypto);
|
|
71
71
|
let __aws_sdk_client_iam = require("@aws-sdk/client-iam");
|
|
72
72
|
__aws_sdk_client_iam = __toESM(__aws_sdk_client_iam);
|
|
73
|
+
let __aws_sdk_client_sts = require("@aws-sdk/client-sts");
|
|
74
|
+
__aws_sdk_client_sts = __toESM(__aws_sdk_client_sts);
|
|
73
75
|
let __aws_sdk_client_lambda = require("@aws-sdk/client-lambda");
|
|
74
76
|
__aws_sdk_client_lambda = __toESM(__aws_sdk_client_lambda);
|
|
75
77
|
let fs_promises = require("fs/promises");
|
|
@@ -8050,6 +8052,11 @@ var IAMManager = class {
|
|
|
8050
8052
|
region: this.region,
|
|
8051
8053
|
credentials: this.credentials
|
|
8052
8054
|
});
|
|
8055
|
+
const accountId = (await new __aws_sdk_client_sts.STS({
|
|
8056
|
+
region: this.region,
|
|
8057
|
+
credentials: this.credentials
|
|
8058
|
+
}).getCallerIdentity({})).Account;
|
|
8059
|
+
if (!accountId) throw new Error("Failed to get AWS account ID");
|
|
8053
8060
|
const assumeRolePolicyDocument = JSON.stringify({
|
|
8054
8061
|
Version: "2012-10-17",
|
|
8055
8062
|
Statement: [{
|
|
@@ -8059,9 +8066,27 @@ var IAMManager = class {
|
|
|
8059
8066
|
}]
|
|
8060
8067
|
});
|
|
8061
8068
|
const roleName = "hot-updater-edge-role";
|
|
8069
|
+
const ssmPolicyDocument = JSON.stringify({
|
|
8070
|
+
Version: "2012-10-17",
|
|
8071
|
+
Statement: [{
|
|
8072
|
+
Effect: "Allow",
|
|
8073
|
+
Action: ["ssm:GetParameter"],
|
|
8074
|
+
Resource: `arn:aws:ssm:*:${accountId}:parameter/hot-updater/*`
|
|
8075
|
+
}]
|
|
8076
|
+
});
|
|
8062
8077
|
try {
|
|
8063
8078
|
const { Role: existingRole } = await iamClient.getRole({ RoleName: roleName });
|
|
8064
8079
|
if (existingRole?.Arn) {
|
|
8080
|
+
try {
|
|
8081
|
+
await iamClient.putRolePolicy({
|
|
8082
|
+
RoleName: roleName,
|
|
8083
|
+
PolicyName: "HotUpdaterSSMAccess",
|
|
8084
|
+
PolicyDocument: ssmPolicyDocument
|
|
8085
|
+
});
|
|
8086
|
+
f.info("Updated SSM access policy for existing IAM role");
|
|
8087
|
+
} catch {
|
|
8088
|
+
f.warn("Failed to update SSM policy, continuing anyway");
|
|
8089
|
+
}
|
|
8065
8090
|
f.info(`Using existing IAM role: ${roleName} (${existingRole.Arn})`);
|
|
8066
8091
|
return existingRole.Arn;
|
|
8067
8092
|
}
|
|
@@ -8070,7 +8095,7 @@ var IAMManager = class {
|
|
|
8070
8095
|
const lambdaRoleArn = (await iamClient.createRole({
|
|
8071
8096
|
RoleName: roleName,
|
|
8072
8097
|
AssumeRolePolicyDocument: assumeRolePolicyDocument,
|
|
8073
|
-
Description: "Role for Lambda@Edge to access S3"
|
|
8098
|
+
Description: "Role for Lambda@Edge to access S3 and SSM"
|
|
8074
8099
|
})).Role?.Arn;
|
|
8075
8100
|
f.info(`Created IAM role: ${roleName} (${lambdaRoleArn})`);
|
|
8076
8101
|
await iamClient.attachRolePolicy({
|
|
@@ -8081,7 +8106,13 @@ var IAMManager = class {
|
|
|
8081
8106
|
RoleName: roleName,
|
|
8082
8107
|
PolicyArn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
|
|
8083
8108
|
});
|
|
8084
|
-
f.info(`Attached
|
|
8109
|
+
f.info(`Attached managed policies to ${roleName}`);
|
|
8110
|
+
await iamClient.putRolePolicy({
|
|
8111
|
+
RoleName: roleName,
|
|
8112
|
+
PolicyName: "HotUpdaterSSMAccess",
|
|
8113
|
+
PolicyDocument: ssmPolicyDocument
|
|
8114
|
+
});
|
|
8115
|
+
f.info(`Added SSM access inline policy to ${roleName}`);
|
|
8085
8116
|
return lambdaRoleArn;
|
|
8086
8117
|
} catch (createError) {
|
|
8087
8118
|
if (createError instanceof Error) f.error(`Error setting up IAM role: ${createError.message}`);
|
|
@@ -8099,7 +8130,7 @@ var LambdaEdgeDeployer = class {
|
|
|
8099
8130
|
constructor(credentials) {
|
|
8100
8131
|
this.credentials = credentials;
|
|
8101
8132
|
}
|
|
8102
|
-
async deploy(lambdaRoleArn,
|
|
8133
|
+
async deploy(lambdaRoleArn, config) {
|
|
8103
8134
|
const cwd = (0, __hot_updater_plugin_core.getCwd)();
|
|
8104
8135
|
const lambdaName = await he({
|
|
8105
8136
|
message: "Enter the name of the Lambda@Edge function",
|
|
@@ -8111,8 +8142,9 @@ var LambdaEdgeDeployer = class {
|
|
|
8111
8142
|
const { tmpDir, removeTmpDir } = await (0, __hot_updater_plugin_core.copyDirToTmp)(path.default.dirname(lambdaPath));
|
|
8112
8143
|
const indexPath = path.default.join(tmpDir, "index.cjs");
|
|
8113
8144
|
const code = (0, __hot_updater_plugin_core.transformEnv)(indexPath, {
|
|
8114
|
-
CLOUDFRONT_KEY_PAIR_ID:
|
|
8115
|
-
|
|
8145
|
+
CLOUDFRONT_KEY_PAIR_ID: config.publicKeyId,
|
|
8146
|
+
SSM_PARAMETER_NAME: config.ssmParameterName,
|
|
8147
|
+
SSM_REGION: config.ssmRegion
|
|
8116
8148
|
});
|
|
8117
8149
|
await fs_promises.default.writeFile(indexPath, code);
|
|
8118
8150
|
const lambdaClient = new __aws_sdk_client_lambda.Lambda({
|
|
@@ -8873,9 +8905,12 @@ const runInit = async ({ build }) => {
|
|
|
8873
8905
|
const keyPair = await new SSMKeyPairManager(bucketRegion, credentials).getOrCreateKeyPair(`/hot-updater/${bucketName}/keypair`);
|
|
8874
8906
|
const cloudFrontManager = new CloudFrontManager(bucketRegion, credentials);
|
|
8875
8907
|
const { publicKeyId, keyGroupId } = await cloudFrontManager.getOrCreateKeyGroup(keyPair.publicKey);
|
|
8876
|
-
const
|
|
8877
|
-
|
|
8878
|
-
|
|
8908
|
+
const lambdaEdgeDeployer = new LambdaEdgeDeployer(credentials);
|
|
8909
|
+
const ssmParameterName = `/hot-updater/${bucketName}/keypair`;
|
|
8910
|
+
const { functionArn } = await lambdaEdgeDeployer.deploy(lambdaRoleArn, {
|
|
8911
|
+
publicKeyId,
|
|
8912
|
+
ssmParameterName,
|
|
8913
|
+
ssmRegion: bucketRegion
|
|
8879
8914
|
});
|
|
8880
8915
|
const { distributionDomain, distributionId } = await cloudFrontManager.createOrUpdateDistribution({
|
|
8881
8916
|
keyGroupId,
|
package/dist/iac/index.js
CHANGED
|
@@ -23,6 +23,7 @@ import { Buffer as Buffer$1 } from "node:buffer";
|
|
|
23
23
|
import { CloudFront } from "@aws-sdk/client-cloudfront";
|
|
24
24
|
import crypto from "crypto";
|
|
25
25
|
import { IAM } from "@aws-sdk/client-iam";
|
|
26
|
+
import { STS } from "@aws-sdk/client-sts";
|
|
26
27
|
import { Lambda } from "@aws-sdk/client-lambda";
|
|
27
28
|
import fs$1 from "fs/promises";
|
|
28
29
|
import { CopyObjectCommand, DeleteObjectCommand, GetObjectCommand, ListObjectsV2Command, S3 } from "@aws-sdk/client-s3";
|
|
@@ -8025,6 +8026,11 @@ var IAMManager = class {
|
|
|
8025
8026
|
region: this.region,
|
|
8026
8027
|
credentials: this.credentials
|
|
8027
8028
|
});
|
|
8029
|
+
const accountId = (await new STS({
|
|
8030
|
+
region: this.region,
|
|
8031
|
+
credentials: this.credentials
|
|
8032
|
+
}).getCallerIdentity({})).Account;
|
|
8033
|
+
if (!accountId) throw new Error("Failed to get AWS account ID");
|
|
8028
8034
|
const assumeRolePolicyDocument = JSON.stringify({
|
|
8029
8035
|
Version: "2012-10-17",
|
|
8030
8036
|
Statement: [{
|
|
@@ -8034,9 +8040,27 @@ var IAMManager = class {
|
|
|
8034
8040
|
}]
|
|
8035
8041
|
});
|
|
8036
8042
|
const roleName = "hot-updater-edge-role";
|
|
8043
|
+
const ssmPolicyDocument = JSON.stringify({
|
|
8044
|
+
Version: "2012-10-17",
|
|
8045
|
+
Statement: [{
|
|
8046
|
+
Effect: "Allow",
|
|
8047
|
+
Action: ["ssm:GetParameter"],
|
|
8048
|
+
Resource: `arn:aws:ssm:*:${accountId}:parameter/hot-updater/*`
|
|
8049
|
+
}]
|
|
8050
|
+
});
|
|
8037
8051
|
try {
|
|
8038
8052
|
const { Role: existingRole } = await iamClient.getRole({ RoleName: roleName });
|
|
8039
8053
|
if (existingRole?.Arn) {
|
|
8054
|
+
try {
|
|
8055
|
+
await iamClient.putRolePolicy({
|
|
8056
|
+
RoleName: roleName,
|
|
8057
|
+
PolicyName: "HotUpdaterSSMAccess",
|
|
8058
|
+
PolicyDocument: ssmPolicyDocument
|
|
8059
|
+
});
|
|
8060
|
+
f.info("Updated SSM access policy for existing IAM role");
|
|
8061
|
+
} catch {
|
|
8062
|
+
f.warn("Failed to update SSM policy, continuing anyway");
|
|
8063
|
+
}
|
|
8040
8064
|
f.info(`Using existing IAM role: ${roleName} (${existingRole.Arn})`);
|
|
8041
8065
|
return existingRole.Arn;
|
|
8042
8066
|
}
|
|
@@ -8045,7 +8069,7 @@ var IAMManager = class {
|
|
|
8045
8069
|
const lambdaRoleArn = (await iamClient.createRole({
|
|
8046
8070
|
RoleName: roleName,
|
|
8047
8071
|
AssumeRolePolicyDocument: assumeRolePolicyDocument,
|
|
8048
|
-
Description: "Role for Lambda@Edge to access S3"
|
|
8072
|
+
Description: "Role for Lambda@Edge to access S3 and SSM"
|
|
8049
8073
|
})).Role?.Arn;
|
|
8050
8074
|
f.info(`Created IAM role: ${roleName} (${lambdaRoleArn})`);
|
|
8051
8075
|
await iamClient.attachRolePolicy({
|
|
@@ -8056,7 +8080,13 @@ var IAMManager = class {
|
|
|
8056
8080
|
RoleName: roleName,
|
|
8057
8081
|
PolicyArn: "arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess"
|
|
8058
8082
|
});
|
|
8059
|
-
f.info(`Attached
|
|
8083
|
+
f.info(`Attached managed policies to ${roleName}`);
|
|
8084
|
+
await iamClient.putRolePolicy({
|
|
8085
|
+
RoleName: roleName,
|
|
8086
|
+
PolicyName: "HotUpdaterSSMAccess",
|
|
8087
|
+
PolicyDocument: ssmPolicyDocument
|
|
8088
|
+
});
|
|
8089
|
+
f.info(`Added SSM access inline policy to ${roleName}`);
|
|
8060
8090
|
return lambdaRoleArn;
|
|
8061
8091
|
} catch (createError) {
|
|
8062
8092
|
if (createError instanceof Error) f.error(`Error setting up IAM role: ${createError.message}`);
|
|
@@ -8074,7 +8104,7 @@ var LambdaEdgeDeployer = class {
|
|
|
8074
8104
|
constructor(credentials) {
|
|
8075
8105
|
this.credentials = credentials;
|
|
8076
8106
|
}
|
|
8077
|
-
async deploy(lambdaRoleArn,
|
|
8107
|
+
async deploy(lambdaRoleArn, config) {
|
|
8078
8108
|
const cwd = getCwd();
|
|
8079
8109
|
const lambdaName = await he({
|
|
8080
8110
|
message: "Enter the name of the Lambda@Edge function",
|
|
@@ -8086,8 +8116,9 @@ var LambdaEdgeDeployer = class {
|
|
|
8086
8116
|
const { tmpDir, removeTmpDir } = await copyDirToTmp(path$1.dirname(lambdaPath));
|
|
8087
8117
|
const indexPath = path$1.join(tmpDir, "index.cjs");
|
|
8088
8118
|
const code = transformEnv(indexPath, {
|
|
8089
|
-
CLOUDFRONT_KEY_PAIR_ID:
|
|
8090
|
-
|
|
8119
|
+
CLOUDFRONT_KEY_PAIR_ID: config.publicKeyId,
|
|
8120
|
+
SSM_PARAMETER_NAME: config.ssmParameterName,
|
|
8121
|
+
SSM_REGION: config.ssmRegion
|
|
8091
8122
|
});
|
|
8092
8123
|
await fs$1.writeFile(indexPath, code);
|
|
8093
8124
|
const lambdaClient = new Lambda({
|
|
@@ -8848,9 +8879,12 @@ const runInit = async ({ build }) => {
|
|
|
8848
8879
|
const keyPair = await new SSMKeyPairManager(bucketRegion, credentials).getOrCreateKeyPair(`/hot-updater/${bucketName}/keypair`);
|
|
8849
8880
|
const cloudFrontManager = new CloudFrontManager(bucketRegion, credentials);
|
|
8850
8881
|
const { publicKeyId, keyGroupId } = await cloudFrontManager.getOrCreateKeyGroup(keyPair.publicKey);
|
|
8851
|
-
const
|
|
8852
|
-
|
|
8853
|
-
|
|
8882
|
+
const lambdaEdgeDeployer = new LambdaEdgeDeployer(credentials);
|
|
8883
|
+
const ssmParameterName = `/hot-updater/${bucketName}/keypair`;
|
|
8884
|
+
const { functionArn } = await lambdaEdgeDeployer.deploy(lambdaRoleArn, {
|
|
8885
|
+
publicKeyId,
|
|
8886
|
+
ssmParameterName,
|
|
8887
|
+
ssmRegion: bucketRegion
|
|
8854
8888
|
});
|
|
8855
8889
|
const { distributionDomain, distributionId } = await cloudFrontManager.createOrUpdateDistribution({
|
|
8856
8890
|
keyGroupId,
|
package/dist/lambda/index.cjs
CHANGED
|
@@ -24,6 +24,8 @@ var __toESM$1 = (mod, isNodeMode, target) => (target = mod != null ? __create$1(
|
|
|
24
24
|
}) : target, mod));
|
|
25
25
|
|
|
26
26
|
//#endregion
|
|
27
|
+
let __aws_sdk_client_ssm = require("@aws-sdk/client-ssm");
|
|
28
|
+
__aws_sdk_client_ssm = __toESM$1(__aws_sdk_client_ssm);
|
|
27
29
|
let node_crypto = require("node:crypto");
|
|
28
30
|
node_crypto = __toESM$1(node_crypto);
|
|
29
31
|
|
|
@@ -3122,7 +3124,32 @@ const withSignedUrl = async ({ data, reqUrl, keyPairId, privateKey, expiresSecon
|
|
|
3122
3124
|
//#endregion
|
|
3123
3125
|
//#region lambda/index.ts
|
|
3124
3126
|
const CLOUDFRONT_KEY_PAIR_ID = HotUpdater.CLOUDFRONT_KEY_PAIR_ID;
|
|
3125
|
-
const
|
|
3127
|
+
const SSM_PARAMETER_NAME = HotUpdater.SSM_PARAMETER_NAME;
|
|
3128
|
+
const SSM_REGION = HotUpdater.SSM_REGION;
|
|
3129
|
+
let cachedPrivateKey = null;
|
|
3130
|
+
/**
|
|
3131
|
+
* Retrieves CloudFront private key from SSM Parameter Store
|
|
3132
|
+
* Uses global cache to avoid repeated SSM calls on warm Lambda invocations
|
|
3133
|
+
*/
|
|
3134
|
+
async function getPrivateKey() {
|
|
3135
|
+
if (cachedPrivateKey !== null) return cachedPrivateKey;
|
|
3136
|
+
if (!SSM_REGION) throw new Error(`Invalid AWS region format: ${SSM_REGION}. Expected format like 'us-east-1' or 'ap-southeast-1'`);
|
|
3137
|
+
const response = await new __aws_sdk_client_ssm.SSM({ region: SSM_REGION }).getParameter({
|
|
3138
|
+
Name: SSM_PARAMETER_NAME,
|
|
3139
|
+
WithDecryption: true
|
|
3140
|
+
});
|
|
3141
|
+
if (!response.Parameter?.Value) throw new Error(`Failed to retrieve private key from SSM parameter: ${SSM_PARAMETER_NAME}`);
|
|
3142
|
+
let keyPair;
|
|
3143
|
+
try {
|
|
3144
|
+
keyPair = JSON.parse(response.Parameter.Value);
|
|
3145
|
+
} catch (error) {
|
|
3146
|
+
throw new Error(`Invalid JSON format in SSM parameter: ${SSM_PARAMETER_NAME}. ${error instanceof Error ? error.message : String(error)}`);
|
|
3147
|
+
}
|
|
3148
|
+
const privateKey = keyPair.privateKey;
|
|
3149
|
+
if (!privateKey || typeof privateKey !== "string") throw new Error(`Invalid private key format in SSM parameter: ${SSM_PARAMETER_NAME}`);
|
|
3150
|
+
cachedPrivateKey = privateKey;
|
|
3151
|
+
return privateKey;
|
|
3152
|
+
}
|
|
3126
3153
|
const app = new Hono();
|
|
3127
3154
|
const validatePlatform = (platform) => {
|
|
3128
3155
|
return ["ios", "android"].includes(platform);
|
|
@@ -3139,6 +3166,7 @@ const processDefaultValues = (channel, minBundleId) => ({
|
|
|
3139
3166
|
const handleUpdateRequest = async (c, params, strategy, expiresSeconds) => {
|
|
3140
3167
|
try {
|
|
3141
3168
|
if (!c.req.header("host")) return c.json({ error: "Missing host header." }, 500);
|
|
3169
|
+
const privateKey = await getPrivateKey();
|
|
3142
3170
|
const updateConfig = {
|
|
3143
3171
|
platform: params.platform,
|
|
3144
3172
|
bundleId: params.bundleId,
|
|
@@ -3155,14 +3183,14 @@ const handleUpdateRequest = async (c, params, strategy, expiresSeconds) => {
|
|
|
3155
3183
|
const updateInfo = await getUpdateInfo({
|
|
3156
3184
|
baseUrl: c.req.url,
|
|
3157
3185
|
keyPairId: CLOUDFRONT_KEY_PAIR_ID,
|
|
3158
|
-
privateKey
|
|
3186
|
+
privateKey
|
|
3159
3187
|
}, updateConfig);
|
|
3160
3188
|
if (!updateInfo) return c.json(null);
|
|
3161
3189
|
const appUpdateInfo = await withSignedUrl({
|
|
3162
3190
|
data: updateInfo,
|
|
3163
3191
|
reqUrl: c.req.url,
|
|
3164
3192
|
keyPairId: CLOUDFRONT_KEY_PAIR_ID,
|
|
3165
|
-
privateKey
|
|
3193
|
+
privateKey,
|
|
3166
3194
|
expiresSeconds
|
|
3167
3195
|
});
|
|
3168
3196
|
return c.json(appUpdateInfo);
|
package/dist/lambda/index.d.cts
CHANGED
|
@@ -4,7 +4,8 @@ import { CloudFrontRequestHandler } from "aws-lambda";
|
|
|
4
4
|
declare global {
|
|
5
5
|
var HotUpdater: {
|
|
6
6
|
CLOUDFRONT_KEY_PAIR_ID: string;
|
|
7
|
-
|
|
7
|
+
SSM_PARAMETER_NAME: string;
|
|
8
|
+
SSM_REGION: string;
|
|
8
9
|
};
|
|
9
10
|
}
|
|
10
11
|
declare const handler: CloudFrontRequestHandler;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hot-updater/aws",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.20.
|
|
4
|
+
"version": "0.20.14",
|
|
5
5
|
"description": "React Native OTA solution for self-hosted",
|
|
6
6
|
"main": "dist/index.cjs",
|
|
7
7
|
"module": "dist/index.js",
|
|
@@ -45,8 +45,8 @@
|
|
|
45
45
|
"mime": "^4.0.4",
|
|
46
46
|
"picocolors": "1.1.1",
|
|
47
47
|
"@clack/prompts": "0.10.0",
|
|
48
|
-
"@hot-updater/
|
|
49
|
-
"@hot-updater/
|
|
48
|
+
"@hot-updater/core": "0.20.14",
|
|
49
|
+
"@hot-updater/js": "0.20.14"
|
|
50
50
|
},
|
|
51
51
|
"dependencies": {
|
|
52
52
|
"@aws-sdk/client-cloudfront": "3.772.0",
|
|
@@ -54,10 +54,11 @@
|
|
|
54
54
|
"@aws-sdk/client-lambda": "3.772.0",
|
|
55
55
|
"@aws-sdk/client-s3": "3.772.0",
|
|
56
56
|
"@aws-sdk/client-ssm": "3.772.0",
|
|
57
|
+
"@aws-sdk/client-sts": "3.772.0",
|
|
57
58
|
"@aws-sdk/credential-providers": "3.772.0",
|
|
58
59
|
"@aws-sdk/lib-storage": "3.772.0",
|
|
59
60
|
"aws-lambda": "1.0.7",
|
|
60
|
-
"@hot-updater/plugin-core": "0.20.
|
|
61
|
+
"@hot-updater/plugin-core": "0.20.14"
|
|
61
62
|
},
|
|
62
63
|
"scripts": {
|
|
63
64
|
"build": "tsdown",
|