@reventlessdev/reventless-aws 3.0.0-alpha.239 → 3.0.0-alpha.241
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/CHANGELOG.md +14 -0
- package/package.json +7 -7
- package/src/Platform.res +292 -15
- package/src/Platform.res.mjs +362 -48
- package/src/adapter/Upload/Upload_Presign_S3.res +19 -3
- package/src/adapter/Upload/Upload_Presign_S3.res.mjs +9 -9
- package/src/capability/Capability_ObjectStore_S3.res +69 -7
- package/src/capability/Capability_ObjectStore_S3.res.mjs +32 -6
- package/src/plugin/stack/Plugin_Stack.res +209 -6
- package/src/plugin/stack/Plugin_Stack.res.mjs +122 -6
- package/src/util/Util_HostUiDomain.res +17 -0
- package/src/util/Util_HostUiDomain.res.mjs +8 -1
- package/src/util/Util_StoreLayout.res +136 -0
- package/src/util/Util_StoreLayout.res.mjs +55 -0
- package/tests/Util_StoreLayoutTest.res +155 -0
- package/tests/Util_StoreLayoutTest.res.mjs +87 -0
package/src/Platform.res.mjs
CHANGED
|
@@ -36,6 +36,7 @@ import * as PluginBehavior$ReventlessCore from "@reventlessdev/reventless-core/s
|
|
|
36
36
|
import * as Plugin_Helpers$ReventlessCore from "@reventlessdev/reventless-core/src/plugin/component/Plugin_Helpers.res.mjs";
|
|
37
37
|
import * as PgProjectionFeed$ReventlessAws from "./adapter/Postgres/PgProjectionFeed.res.mjs";
|
|
38
38
|
import * as Util_LocalConfig$ReventlessAws from "./util/Util_LocalConfig.res.mjs";
|
|
39
|
+
import * as Util_StoreLayout$ReventlessAws from "./util/Util_StoreLayout.res.mjs";
|
|
39
40
|
import * as IndexJs from "@pulumi/pulumi/runtime/index.js";
|
|
40
41
|
import * as AppSync_EventsApi$ReventlessAws from "./adapter/Api/AppSync_EventsApi.res.mjs";
|
|
41
42
|
import * as AppSync_MergedApi$ReventlessAws from "./components/Api/AppSync_MergedApi.res.mjs";
|
|
@@ -68,6 +69,7 @@ import * as UiFragments_Projection$ReventlessCore from "@reventlessdev/reventles
|
|
|
68
69
|
import * as Aggregate_Builder_Single$ReventlessAws from "./components/Aggregate_Builder_Single.res.mjs";
|
|
69
70
|
import * as ReadModel_Builder_Single$ReventlessAws from "./components/ReadModel_Builder_Single.res.mjs";
|
|
70
71
|
import * as StateChangeSlice_Builder$ReventlessAws from "./components/StateChangeSlice_Builder.res.mjs";
|
|
72
|
+
import * as Capability_ObjectStore_S3$ReventlessAws from "./capability/Capability_ObjectStore_S3.res.mjs";
|
|
71
73
|
import * as EventCollectorChannel_SQS$ReventlessAws from "./adapter/EventCollector/EventCollectorChannel_SQS.res.mjs";
|
|
72
74
|
import * as PgChangeFeedRelay_Builder$ReventlessAws from "./adapter/Postgres/PgChangeFeedRelay_Builder.res.mjs";
|
|
73
75
|
import * as RuntimeEnvironment_Lambda$ReventlessAws from "./adapter/Runtime/RuntimeEnvironment_Lambda.res.mjs";
|
|
@@ -114,6 +116,14 @@ function getSplitApiOutputs() {
|
|
|
114
116
|
return splitApiOutputsRef.contents;
|
|
115
117
|
}
|
|
116
118
|
|
|
119
|
+
let objectStoreEndpointsRef = {
|
|
120
|
+
contents: []
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
function getObjectStoreEndpoints() {
|
|
124
|
+
return objectStoreEndpointsRef.contents;
|
|
125
|
+
}
|
|
126
|
+
|
|
117
127
|
function MakeWithConfig(Config) {
|
|
118
128
|
Stdlib_Option.forEach(Config.commandHandlerConfig.aggregates, param => {
|
|
119
129
|
Stdlib_Option.forEach(param.sync, AggregateRuntime_Builder_Single$ReventlessAws.setConfig);
|
|
@@ -708,7 +718,8 @@ function MakeWithConfig(Config) {
|
|
|
708
718
|
log.info("Platform", undefined, `v` + version);
|
|
709
719
|
return Pervasives.failwith("makePlatform is not supported on AWS — deploy the platform with deployPlatform and each plugin with deployPlugin (merged-API composition).");
|
|
710
720
|
};
|
|
711
|
-
let deployPlatform = (version, hostUiBundle) => {
|
|
721
|
+
let deployPlatform = (version, hostUiBundle, capabilitiesOpt) => {
|
|
722
|
+
let capabilities = capabilitiesOpt !== undefined ? capabilitiesOpt : [];
|
|
712
723
|
log.info("Platform:deployPlatform", undefined, `v` + version);
|
|
713
724
|
let scheduler = makeScheduler();
|
|
714
725
|
hooks_scheduler.contents = scheduler;
|
|
@@ -868,41 +879,179 @@ function MakeWithConfig(Config) {
|
|
|
868
879
|
adminResources: param[4]
|
|
869
880
|
});
|
|
870
881
|
});
|
|
882
|
+
let stackName = Pulumi.getStack();
|
|
883
|
+
let storeLayout = Util_StoreLayout$ReventlessAws.layoutFor(stackName, Util_HostUiDomain$ReventlessAws.resolveProdStacks());
|
|
884
|
+
let storeProtection = Util_StoreLayout$ReventlessAws.protectionFor(stackName, undefined);
|
|
885
|
+
let declaredStores = Stdlib_Array.reduce(Stdlib_Array.filterMap(capabilities, c => {
|
|
886
|
+
if (typeof c !== "object") {
|
|
887
|
+
return;
|
|
888
|
+
} else {
|
|
889
|
+
return [
|
|
890
|
+
c.plugin,
|
|
891
|
+
c.store
|
|
892
|
+
];
|
|
893
|
+
}
|
|
894
|
+
}), [], (acc, param) => {
|
|
895
|
+
let store = param[1];
|
|
896
|
+
let plugin = param[0];
|
|
897
|
+
if (acc.some(param => param[0] === plugin ? param[1] === store : false)) {
|
|
898
|
+
return acc;
|
|
899
|
+
} else {
|
|
900
|
+
return acc.concat([[
|
|
901
|
+
plugin,
|
|
902
|
+
store
|
|
903
|
+
]]);
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
if (declaredStores.length !== 0) {
|
|
907
|
+
let tmp;
|
|
908
|
+
tmp = storeLayout === "PerStore" ? "bucket per store" : "shared bucket, {store}/… prefixes";
|
|
909
|
+
let tmp$1;
|
|
910
|
+
tmp$1 = storeProtection === "Protected" ? "protected" : "unprotected (disposable stack)";
|
|
911
|
+
log.info("Platform:deployPlatform", undefined, `object stores: ` + tmp + `, ` + tmp$1 + ` — stack ` + stackName);
|
|
912
|
+
}
|
|
913
|
+
let storeBuckets = {};
|
|
914
|
+
let storeBucketOrder = [];
|
|
915
|
+
let declaredStoreServices = declaredStores.map(param => {
|
|
916
|
+
let store = param[1];
|
|
917
|
+
let plugin = param[0];
|
|
918
|
+
let bucketName = Util_StoreLayout$ReventlessAws.bucketNameFor(storeLayout, stackName, plugin, store);
|
|
919
|
+
let keyPrefix = Util_StoreLayout$ReventlessAws.keyPrefixFor(store);
|
|
920
|
+
let b = storeBuckets[bucketName];
|
|
921
|
+
let bucket;
|
|
922
|
+
if (b !== undefined) {
|
|
923
|
+
bucket = b;
|
|
924
|
+
} else {
|
|
925
|
+
let tmp;
|
|
926
|
+
tmp = storeLayout === "PerStore" ? plugin : undefined;
|
|
927
|
+
let b$1 = Capability_ObjectStore_S3$ReventlessAws.make(bucketName, keyPrefix, tmp, storeProtection === "Protected", storeProtection === "Unprotected", undefined, undefined);
|
|
928
|
+
storeBuckets[bucketName] = b$1;
|
|
929
|
+
storeBucketOrder.push(bucketName);
|
|
930
|
+
bucket = b$1;
|
|
931
|
+
}
|
|
932
|
+
let storeHandle = Capability_ObjectStore_S3$ReventlessAws.underPrefix(bucket, keyPrefix);
|
|
933
|
+
let presign = Upload_Presign_S3$ReventlessAws.make(storeHandle.bucketName, undefined, storeHandle.keyPrefix, `UploadPresign-` + plugin + `-` + store, undefined);
|
|
934
|
+
return [
|
|
935
|
+
plugin + `.` + store,
|
|
936
|
+
keyPrefix,
|
|
937
|
+
bucketName,
|
|
938
|
+
presign.url
|
|
939
|
+
];
|
|
940
|
+
});
|
|
941
|
+
let declaredServedBuckets = Stdlib_Array.filterMap(storeBucketOrder, bucketName => Stdlib_Option.map(storeBuckets[bucketName], b => ({
|
|
942
|
+
id: bucketName,
|
|
943
|
+
prefixes: Stdlib_Array.filterMap(declaredStoreServices, param => {
|
|
944
|
+
if (param[2] === bucketName) {
|
|
945
|
+
return param[1];
|
|
946
|
+
}
|
|
947
|
+
}),
|
|
948
|
+
bucketId: b.bucketId,
|
|
949
|
+
bucketArn: b.bucketArn,
|
|
950
|
+
bucketRegionalDomainName: b.bucketRegionalDomainName
|
|
951
|
+
})));
|
|
952
|
+
let match$1 = Util_StoreLayout$ReventlessAws.servingFor(Stdlib_Option.isSome(hostUiBundle), declaredServedBuckets.length);
|
|
953
|
+
let storeServingBaseUrl;
|
|
954
|
+
switch (match$1) {
|
|
955
|
+
case "NoStores" :
|
|
956
|
+
case "HostShell" :
|
|
957
|
+
storeServingBaseUrl = undefined;
|
|
958
|
+
break;
|
|
959
|
+
case "PlatformOwned" :
|
|
960
|
+
storeServingBaseUrl = Plugin_Stack$ReventlessAws.makeServedBucketDistribution("object-stores", declaredServedBuckets, undefined);
|
|
961
|
+
break;
|
|
962
|
+
}
|
|
963
|
+
let declaredStoreEndpoints = declaredStoreServices.map(param => ({
|
|
964
|
+
store: param[0],
|
|
965
|
+
keyPrefix: param[1],
|
|
966
|
+
bucketName: param[2],
|
|
967
|
+
uploadUrl: param[3],
|
|
968
|
+
baseUrl: storeServingBaseUrl
|
|
969
|
+
}));
|
|
970
|
+
objectStoreEndpointsRef.contents = declaredStoreEndpoints;
|
|
971
|
+
if (declaredStoreEndpoints.length !== 0) {
|
|
972
|
+
Pulumi$Pulumi.$$export("uploadEndpoints", Pulumi.all(declaredStoreEndpoints.map(e => e.uploadUrl.apply(u => [
|
|
973
|
+
e.store,
|
|
974
|
+
u
|
|
975
|
+
]))).apply(pairs => Object.fromEntries(pairs)));
|
|
976
|
+
Pulumi$Pulumi.$$export("objectStores", Pulumi.all(declaredStoreEndpoints.map(e => {
|
|
977
|
+
let u = e.baseUrl;
|
|
978
|
+
if (u !== undefined) {
|
|
979
|
+
return u.apply(b => [
|
|
980
|
+
e,
|
|
981
|
+
b
|
|
982
|
+
]);
|
|
983
|
+
} else {
|
|
984
|
+
return Pulumi.output([
|
|
985
|
+
e,
|
|
986
|
+
undefined
|
|
987
|
+
]);
|
|
988
|
+
}
|
|
989
|
+
})).apply(pairs => Object.fromEntries(pairs.map(param => {
|
|
990
|
+
let baseUrl = param[1];
|
|
991
|
+
let e = param[0];
|
|
992
|
+
return [
|
|
993
|
+
e.store,
|
|
994
|
+
Object.fromEntries([
|
|
995
|
+
[
|
|
996
|
+
"bucketName",
|
|
997
|
+
e.bucketName
|
|
998
|
+
],
|
|
999
|
+
[
|
|
1000
|
+
"keyPrefix",
|
|
1001
|
+
e.keyPrefix
|
|
1002
|
+
]
|
|
1003
|
+
].concat(baseUrl !== undefined ? [[
|
|
1004
|
+
"baseUrl",
|
|
1005
|
+
baseUrl
|
|
1006
|
+
]] : []))
|
|
1007
|
+
];
|
|
1008
|
+
}))));
|
|
1009
|
+
}
|
|
871
1010
|
if (hostUiBundle !== undefined) {
|
|
872
|
-
let match$
|
|
873
|
-
let match$
|
|
1011
|
+
let match$2 = Util_LocalConfig$ReventlessAws.get("hostUiBaseDomain");
|
|
1012
|
+
let match$3 = Util_LocalConfig$ReventlessAws.get("hostUiHostedZoneId");
|
|
874
1013
|
let customDomain;
|
|
875
|
-
if (match$
|
|
1014
|
+
if (match$2 !== undefined && match$3 !== undefined) {
|
|
876
1015
|
let stack = Pulumi.getStack();
|
|
877
1016
|
let baseName = Stdlib_Option.getOr(Util_LocalConfig$ReventlessAws.get("hostUiBaseName"), Pulumi.getProject());
|
|
878
1017
|
let prodStacks = Stdlib_Option.getOr(Stdlib_Option.map(Util_LocalConfig$ReventlessAws.get("hostUiProdStacks"), Util_HostUiDomain$ReventlessAws.parseProdStacks), Util_HostUiDomain$ReventlessAws.defaultProdStacks);
|
|
879
|
-
let fqdn = Util_HostUiDomain$ReventlessAws.deriveFqdn(baseName, stack, match$
|
|
1018
|
+
let fqdn = Util_HostUiDomain$ReventlessAws.deriveFqdn(baseName, stack, match$2, prodStacks);
|
|
880
1019
|
customDomain = {
|
|
881
1020
|
fqdn: fqdn,
|
|
882
|
-
hostedZoneId: match$
|
|
1021
|
+
hostedZoneId: match$3
|
|
883
1022
|
};
|
|
884
1023
|
} else {
|
|
885
1024
|
customDomain = undefined;
|
|
886
1025
|
}
|
|
887
1026
|
let store = hostUiBundle.uploadBucket;
|
|
888
|
-
let servedBuckets =
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
1027
|
+
let servedBuckets = (
|
|
1028
|
+
store !== undefined ? [{
|
|
1029
|
+
id: store.keyPrefix,
|
|
1030
|
+
prefixes: [store.keyPrefix],
|
|
1031
|
+
bucketId: store.bucketId,
|
|
1032
|
+
bucketArn: store.bucketArn,
|
|
1033
|
+
bucketRegionalDomainName: store.bucketRegionalDomainName
|
|
1034
|
+
}] : []
|
|
1035
|
+
).concat(declaredServedBuckets);
|
|
1036
|
+
let match$4 = Plugin_Stack$ReventlessAws.makeUiBundleDistribution("host-ui", Stdlib_Option.getOr(hostUiBundle.bundleVersion, version), Stdlib_Option.getOr(hostUiBundle.assetsDir, Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-host-shell") + "/dist"), true, undefined, true, [
|
|
895
1037
|
"config.json",
|
|
896
1038
|
"ui-hints.json"
|
|
897
1039
|
], customDomain, servedBuckets);
|
|
898
|
-
let bucketName = match$
|
|
1040
|
+
let bucketName = match$4.bucketName;
|
|
899
1041
|
let regionStr = Stdlib_Option.getOr(new Pulumi.Config("aws").get("region"), "unknown");
|
|
900
1042
|
let cognitoPool = Platform_Stack$ReventlessAws.resolveCognitoUserPool();
|
|
901
1043
|
let domainEventsEndpointOutput = domainEventsApiOpt !== undefined ? AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt).apply(ep => ep + "/event") : Pulumi.output(undefined);
|
|
902
1044
|
let index = hostUiBundle.geocoderPlaceIndex;
|
|
903
1045
|
let geocoderEndpointOutput = index !== undefined ? Geocoder_AwsLocation$ReventlessAws.make(index.indexName, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
|
|
904
1046
|
let store$1 = hostUiBundle.uploadBucket;
|
|
905
|
-
let uploadEndpointOutput = store$1 !== undefined ? Upload_Presign_S3$ReventlessAws.make(store$1.bucketName, undefined, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
|
|
1047
|
+
let uploadEndpointOutput = store$1 !== undefined ? Upload_Presign_S3$ReventlessAws.make(store$1.bucketName, undefined, undefined, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
|
|
1048
|
+
let storeUploadEndpointsOutput = Pulumi.all(declaredStoreServices.map(param => {
|
|
1049
|
+
let qualified = param[0];
|
|
1050
|
+
return param[3].apply(u => [
|
|
1051
|
+
qualified,
|
|
1052
|
+
u
|
|
1053
|
+
]);
|
|
1054
|
+
}));
|
|
906
1055
|
let configJsonContent = Pulumi.all([
|
|
907
1056
|
Pulumi.all([
|
|
908
1057
|
resolvedDomainApiEndpoint,
|
|
@@ -912,20 +1061,25 @@ function MakeWithConfig(Config) {
|
|
|
912
1061
|
]),
|
|
913
1062
|
domainEventsEndpointOutput,
|
|
914
1063
|
geocoderEndpointOutput,
|
|
915
|
-
|
|
1064
|
+
Pulumi.all([
|
|
1065
|
+
uploadEndpointOutput,
|
|
1066
|
+
storeUploadEndpointsOutput
|
|
1067
|
+
])
|
|
916
1068
|
]).apply(param => {
|
|
917
|
-
let
|
|
1069
|
+
let match = param[3];
|
|
1070
|
+
let storeUploadEps = match[1];
|
|
1071
|
+
let uploadEpOpt = match[0];
|
|
918
1072
|
let geocoderEpOpt = param[2];
|
|
919
1073
|
let eventsEpOpt = param[1];
|
|
920
|
-
let match = param[0];
|
|
1074
|
+
let match$1 = param[0];
|
|
921
1075
|
let fields = [
|
|
922
1076
|
[
|
|
923
1077
|
"apiEndpoint",
|
|
924
|
-
match[0]
|
|
1078
|
+
match$1[0]
|
|
925
1079
|
],
|
|
926
1080
|
[
|
|
927
1081
|
"platformApiEndpoint",
|
|
928
|
-
match[1]
|
|
1082
|
+
match$1[1]
|
|
929
1083
|
],
|
|
930
1084
|
[
|
|
931
1085
|
"region",
|
|
@@ -937,11 +1091,11 @@ function MakeWithConfig(Config) {
|
|
|
937
1091
|
],
|
|
938
1092
|
[
|
|
939
1093
|
"cognitoUserPoolId",
|
|
940
|
-
match[2]
|
|
1094
|
+
match$1[2]
|
|
941
1095
|
],
|
|
942
1096
|
[
|
|
943
1097
|
"cognitoClientId",
|
|
944
|
-
match[3]
|
|
1098
|
+
match$1[3]
|
|
945
1099
|
],
|
|
946
1100
|
[
|
|
947
1101
|
"liveUpdates",
|
|
@@ -970,7 +1124,14 @@ function MakeWithConfig(Config) {
|
|
|
970
1124
|
"uploadEndpoint",
|
|
971
1125
|
uploadEpOpt
|
|
972
1126
|
]]) : withGeocoder;
|
|
973
|
-
|
|
1127
|
+
let withStoreUploads = storeUploadEps.length !== 0 ? withUpload.concat([[
|
|
1128
|
+
"uploadEndpoints",
|
|
1129
|
+
Object.fromEntries(storeUploadEps.map(param => [
|
|
1130
|
+
param[0],
|
|
1131
|
+
param[1]
|
|
1132
|
+
]))
|
|
1133
|
+
]]) : withUpload;
|
|
1134
|
+
return JSON.stringify(Object.fromEntries(withStoreUploads));
|
|
974
1135
|
});
|
|
975
1136
|
new (Aws.s3.BucketObject)("host-ui-config-json", {
|
|
976
1137
|
bucket: bucketName,
|
|
@@ -988,7 +1149,7 @@ function MakeWithConfig(Config) {
|
|
|
988
1149
|
contentType: "application/json"
|
|
989
1150
|
});
|
|
990
1151
|
}
|
|
991
|
-
Pulumi$Pulumi.$$export("hostShellUrl", match$
|
|
1152
|
+
Pulumi$Pulumi.$$export("hostShellUrl", match$4.distributionUrl);
|
|
992
1153
|
}
|
|
993
1154
|
return Pulumi$Pulumi.getOutputs();
|
|
994
1155
|
};
|
|
@@ -1724,7 +1885,8 @@ function Make($star) {
|
|
|
1724
1885
|
log.info("Platform", undefined, `v` + version);
|
|
1725
1886
|
return Pervasives.failwith("makePlatform is not supported on AWS — deploy the platform with deployPlatform and each plugin with deployPlugin (merged-API composition).");
|
|
1726
1887
|
};
|
|
1727
|
-
let deployPlatform = (version, hostUiBundle) => {
|
|
1888
|
+
let deployPlatform = (version, hostUiBundle, capabilitiesOpt) => {
|
|
1889
|
+
let capabilities = capabilitiesOpt !== undefined ? capabilitiesOpt : [];
|
|
1728
1890
|
log.info("Platform:deployPlatform", undefined, `v` + version);
|
|
1729
1891
|
let scheduler = makeScheduler();
|
|
1730
1892
|
hooks_scheduler.contents = scheduler;
|
|
@@ -1861,41 +2023,179 @@ function Make($star) {
|
|
|
1861
2023
|
adminResources: param[4]
|
|
1862
2024
|
});
|
|
1863
2025
|
});
|
|
2026
|
+
let stackName = Pulumi.getStack();
|
|
2027
|
+
let storeLayout = Util_StoreLayout$ReventlessAws.layoutFor(stackName, Util_HostUiDomain$ReventlessAws.resolveProdStacks());
|
|
2028
|
+
let storeProtection = Util_StoreLayout$ReventlessAws.protectionFor(stackName, undefined);
|
|
2029
|
+
let declaredStores = Stdlib_Array.reduce(Stdlib_Array.filterMap(capabilities, c => {
|
|
2030
|
+
if (typeof c !== "object") {
|
|
2031
|
+
return;
|
|
2032
|
+
} else {
|
|
2033
|
+
return [
|
|
2034
|
+
c.plugin,
|
|
2035
|
+
c.store
|
|
2036
|
+
];
|
|
2037
|
+
}
|
|
2038
|
+
}), [], (acc, param) => {
|
|
2039
|
+
let store = param[1];
|
|
2040
|
+
let plugin = param[0];
|
|
2041
|
+
if (acc.some(param => param[0] === plugin ? param[1] === store : false)) {
|
|
2042
|
+
return acc;
|
|
2043
|
+
} else {
|
|
2044
|
+
return acc.concat([[
|
|
2045
|
+
plugin,
|
|
2046
|
+
store
|
|
2047
|
+
]]);
|
|
2048
|
+
}
|
|
2049
|
+
});
|
|
2050
|
+
if (declaredStores.length !== 0) {
|
|
2051
|
+
let tmp;
|
|
2052
|
+
tmp = storeLayout === "PerStore" ? "bucket per store" : "shared bucket, {store}/… prefixes";
|
|
2053
|
+
let tmp$1;
|
|
2054
|
+
tmp$1 = storeProtection === "Protected" ? "protected" : "unprotected (disposable stack)";
|
|
2055
|
+
log.info("Platform:deployPlatform", undefined, `object stores: ` + tmp + `, ` + tmp$1 + ` — stack ` + stackName);
|
|
2056
|
+
}
|
|
2057
|
+
let storeBuckets = {};
|
|
2058
|
+
let storeBucketOrder = [];
|
|
2059
|
+
let declaredStoreServices = declaredStores.map(param => {
|
|
2060
|
+
let store = param[1];
|
|
2061
|
+
let plugin = param[0];
|
|
2062
|
+
let bucketName = Util_StoreLayout$ReventlessAws.bucketNameFor(storeLayout, stackName, plugin, store);
|
|
2063
|
+
let keyPrefix = Util_StoreLayout$ReventlessAws.keyPrefixFor(store);
|
|
2064
|
+
let b = storeBuckets[bucketName];
|
|
2065
|
+
let bucket;
|
|
2066
|
+
if (b !== undefined) {
|
|
2067
|
+
bucket = b;
|
|
2068
|
+
} else {
|
|
2069
|
+
let tmp;
|
|
2070
|
+
tmp = storeLayout === "PerStore" ? plugin : undefined;
|
|
2071
|
+
let b$1 = Capability_ObjectStore_S3$ReventlessAws.make(bucketName, keyPrefix, tmp, storeProtection === "Protected", storeProtection === "Unprotected", undefined, undefined);
|
|
2072
|
+
storeBuckets[bucketName] = b$1;
|
|
2073
|
+
storeBucketOrder.push(bucketName);
|
|
2074
|
+
bucket = b$1;
|
|
2075
|
+
}
|
|
2076
|
+
let storeHandle = Capability_ObjectStore_S3$ReventlessAws.underPrefix(bucket, keyPrefix);
|
|
2077
|
+
let presign = Upload_Presign_S3$ReventlessAws.make(storeHandle.bucketName, undefined, storeHandle.keyPrefix, `UploadPresign-` + plugin + `-` + store, undefined);
|
|
2078
|
+
return [
|
|
2079
|
+
plugin + `.` + store,
|
|
2080
|
+
keyPrefix,
|
|
2081
|
+
bucketName,
|
|
2082
|
+
presign.url
|
|
2083
|
+
];
|
|
2084
|
+
});
|
|
2085
|
+
let declaredServedBuckets = Stdlib_Array.filterMap(storeBucketOrder, bucketName => Stdlib_Option.map(storeBuckets[bucketName], b => ({
|
|
2086
|
+
id: bucketName,
|
|
2087
|
+
prefixes: Stdlib_Array.filterMap(declaredStoreServices, param => {
|
|
2088
|
+
if (param[2] === bucketName) {
|
|
2089
|
+
return param[1];
|
|
2090
|
+
}
|
|
2091
|
+
}),
|
|
2092
|
+
bucketId: b.bucketId,
|
|
2093
|
+
bucketArn: b.bucketArn,
|
|
2094
|
+
bucketRegionalDomainName: b.bucketRegionalDomainName
|
|
2095
|
+
})));
|
|
2096
|
+
let match$1 = Util_StoreLayout$ReventlessAws.servingFor(Stdlib_Option.isSome(hostUiBundle), declaredServedBuckets.length);
|
|
2097
|
+
let storeServingBaseUrl;
|
|
2098
|
+
switch (match$1) {
|
|
2099
|
+
case "NoStores" :
|
|
2100
|
+
case "HostShell" :
|
|
2101
|
+
storeServingBaseUrl = undefined;
|
|
2102
|
+
break;
|
|
2103
|
+
case "PlatformOwned" :
|
|
2104
|
+
storeServingBaseUrl = Plugin_Stack$ReventlessAws.makeServedBucketDistribution("object-stores", declaredServedBuckets, undefined);
|
|
2105
|
+
break;
|
|
2106
|
+
}
|
|
2107
|
+
let declaredStoreEndpoints = declaredStoreServices.map(param => ({
|
|
2108
|
+
store: param[0],
|
|
2109
|
+
keyPrefix: param[1],
|
|
2110
|
+
bucketName: param[2],
|
|
2111
|
+
uploadUrl: param[3],
|
|
2112
|
+
baseUrl: storeServingBaseUrl
|
|
2113
|
+
}));
|
|
2114
|
+
objectStoreEndpointsRef.contents = declaredStoreEndpoints;
|
|
2115
|
+
if (declaredStoreEndpoints.length !== 0) {
|
|
2116
|
+
Pulumi$Pulumi.$$export("uploadEndpoints", Pulumi.all(declaredStoreEndpoints.map(e => e.uploadUrl.apply(u => [
|
|
2117
|
+
e.store,
|
|
2118
|
+
u
|
|
2119
|
+
]))).apply(pairs => Object.fromEntries(pairs)));
|
|
2120
|
+
Pulumi$Pulumi.$$export("objectStores", Pulumi.all(declaredStoreEndpoints.map(e => {
|
|
2121
|
+
let u = e.baseUrl;
|
|
2122
|
+
if (u !== undefined) {
|
|
2123
|
+
return u.apply(b => [
|
|
2124
|
+
e,
|
|
2125
|
+
b
|
|
2126
|
+
]);
|
|
2127
|
+
} else {
|
|
2128
|
+
return Pulumi.output([
|
|
2129
|
+
e,
|
|
2130
|
+
undefined
|
|
2131
|
+
]);
|
|
2132
|
+
}
|
|
2133
|
+
})).apply(pairs => Object.fromEntries(pairs.map(param => {
|
|
2134
|
+
let baseUrl = param[1];
|
|
2135
|
+
let e = param[0];
|
|
2136
|
+
return [
|
|
2137
|
+
e.store,
|
|
2138
|
+
Object.fromEntries([
|
|
2139
|
+
[
|
|
2140
|
+
"bucketName",
|
|
2141
|
+
e.bucketName
|
|
2142
|
+
],
|
|
2143
|
+
[
|
|
2144
|
+
"keyPrefix",
|
|
2145
|
+
e.keyPrefix
|
|
2146
|
+
]
|
|
2147
|
+
].concat(baseUrl !== undefined ? [[
|
|
2148
|
+
"baseUrl",
|
|
2149
|
+
baseUrl
|
|
2150
|
+
]] : []))
|
|
2151
|
+
];
|
|
2152
|
+
}))));
|
|
2153
|
+
}
|
|
1864
2154
|
if (hostUiBundle !== undefined) {
|
|
1865
|
-
let match$
|
|
1866
|
-
let match$
|
|
2155
|
+
let match$2 = Util_LocalConfig$ReventlessAws.get("hostUiBaseDomain");
|
|
2156
|
+
let match$3 = Util_LocalConfig$ReventlessAws.get("hostUiHostedZoneId");
|
|
1867
2157
|
let customDomain;
|
|
1868
|
-
if (match$
|
|
2158
|
+
if (match$2 !== undefined && match$3 !== undefined) {
|
|
1869
2159
|
let stack = Pulumi.getStack();
|
|
1870
2160
|
let baseName = Stdlib_Option.getOr(Util_LocalConfig$ReventlessAws.get("hostUiBaseName"), Pulumi.getProject());
|
|
1871
2161
|
let prodStacks = Stdlib_Option.getOr(Stdlib_Option.map(Util_LocalConfig$ReventlessAws.get("hostUiProdStacks"), Util_HostUiDomain$ReventlessAws.parseProdStacks), Util_HostUiDomain$ReventlessAws.defaultProdStacks);
|
|
1872
|
-
let fqdn = Util_HostUiDomain$ReventlessAws.deriveFqdn(baseName, stack, match$
|
|
2162
|
+
let fqdn = Util_HostUiDomain$ReventlessAws.deriveFqdn(baseName, stack, match$2, prodStacks);
|
|
1873
2163
|
customDomain = {
|
|
1874
2164
|
fqdn: fqdn,
|
|
1875
|
-
hostedZoneId: match$
|
|
2165
|
+
hostedZoneId: match$3
|
|
1876
2166
|
};
|
|
1877
2167
|
} else {
|
|
1878
2168
|
customDomain = undefined;
|
|
1879
2169
|
}
|
|
1880
2170
|
let store = hostUiBundle.uploadBucket;
|
|
1881
|
-
let servedBuckets =
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
2171
|
+
let servedBuckets = (
|
|
2172
|
+
store !== undefined ? [{
|
|
2173
|
+
id: store.keyPrefix,
|
|
2174
|
+
prefixes: [store.keyPrefix],
|
|
2175
|
+
bucketId: store.bucketId,
|
|
2176
|
+
bucketArn: store.bucketArn,
|
|
2177
|
+
bucketRegionalDomainName: store.bucketRegionalDomainName
|
|
2178
|
+
}] : []
|
|
2179
|
+
).concat(declaredServedBuckets);
|
|
2180
|
+
let match$4 = Plugin_Stack$ReventlessAws.makeUiBundleDistribution("host-ui", Stdlib_Option.getOr(hostUiBundle.bundleVersion, version), Stdlib_Option.getOr(hostUiBundle.assetsDir, Util_Bundle$ReventlessAws.resolvePackageRoot("@reventlessdev/reventless-host-shell") + "/dist"), true, undefined, true, [
|
|
1888
2181
|
"config.json",
|
|
1889
2182
|
"ui-hints.json"
|
|
1890
2183
|
], customDomain, servedBuckets);
|
|
1891
|
-
let bucketName = match$
|
|
2184
|
+
let bucketName = match$4.bucketName;
|
|
1892
2185
|
let regionStr = Stdlib_Option.getOr(new Pulumi.Config("aws").get("region"), "unknown");
|
|
1893
2186
|
let cognitoPool = Platform_Stack$ReventlessAws.resolveCognitoUserPool();
|
|
1894
2187
|
let domainEventsEndpointOutput = domainEventsApiOpt !== undefined ? AppSync_EventsApi$ReventlessAws.httpEndpoint(domainEventsApiOpt).apply(ep => ep + "/event") : Pulumi.output(undefined);
|
|
1895
2188
|
let index = hostUiBundle.geocoderPlaceIndex;
|
|
1896
2189
|
let geocoderEndpointOutput = index !== undefined ? Geocoder_AwsLocation$ReventlessAws.make(index.indexName, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
|
|
1897
2190
|
let store$1 = hostUiBundle.uploadBucket;
|
|
1898
|
-
let uploadEndpointOutput = store$1 !== undefined ? Upload_Presign_S3$ReventlessAws.make(store$1.bucketName, undefined, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
|
|
2191
|
+
let uploadEndpointOutput = store$1 !== undefined ? Upload_Presign_S3$ReventlessAws.make(store$1.bucketName, undefined, undefined, undefined, undefined).url.apply(u => u) : Pulumi.output(undefined);
|
|
2192
|
+
let storeUploadEndpointsOutput = Pulumi.all(declaredStoreServices.map(param => {
|
|
2193
|
+
let qualified = param[0];
|
|
2194
|
+
return param[3].apply(u => [
|
|
2195
|
+
qualified,
|
|
2196
|
+
u
|
|
2197
|
+
]);
|
|
2198
|
+
}));
|
|
1899
2199
|
let configJsonContent = Pulumi.all([
|
|
1900
2200
|
Pulumi.all([
|
|
1901
2201
|
resolvedDomainApiEndpoint,
|
|
@@ -1905,20 +2205,25 @@ function Make($star) {
|
|
|
1905
2205
|
]),
|
|
1906
2206
|
domainEventsEndpointOutput,
|
|
1907
2207
|
geocoderEndpointOutput,
|
|
1908
|
-
|
|
2208
|
+
Pulumi.all([
|
|
2209
|
+
uploadEndpointOutput,
|
|
2210
|
+
storeUploadEndpointsOutput
|
|
2211
|
+
])
|
|
1909
2212
|
]).apply(param => {
|
|
1910
|
-
let
|
|
2213
|
+
let match = param[3];
|
|
2214
|
+
let storeUploadEps = match[1];
|
|
2215
|
+
let uploadEpOpt = match[0];
|
|
1911
2216
|
let geocoderEpOpt = param[2];
|
|
1912
2217
|
let eventsEpOpt = param[1];
|
|
1913
|
-
let match = param[0];
|
|
2218
|
+
let match$1 = param[0];
|
|
1914
2219
|
let fields = [
|
|
1915
2220
|
[
|
|
1916
2221
|
"apiEndpoint",
|
|
1917
|
-
match[0]
|
|
2222
|
+
match$1[0]
|
|
1918
2223
|
],
|
|
1919
2224
|
[
|
|
1920
2225
|
"platformApiEndpoint",
|
|
1921
|
-
match[1]
|
|
2226
|
+
match$1[1]
|
|
1922
2227
|
],
|
|
1923
2228
|
[
|
|
1924
2229
|
"region",
|
|
@@ -1930,11 +2235,11 @@ function Make($star) {
|
|
|
1930
2235
|
],
|
|
1931
2236
|
[
|
|
1932
2237
|
"cognitoUserPoolId",
|
|
1933
|
-
match[2]
|
|
2238
|
+
match$1[2]
|
|
1934
2239
|
],
|
|
1935
2240
|
[
|
|
1936
2241
|
"cognitoClientId",
|
|
1937
|
-
match[3]
|
|
2242
|
+
match$1[3]
|
|
1938
2243
|
],
|
|
1939
2244
|
[
|
|
1940
2245
|
"liveUpdates",
|
|
@@ -1963,7 +2268,14 @@ function Make($star) {
|
|
|
1963
2268
|
"uploadEndpoint",
|
|
1964
2269
|
uploadEpOpt
|
|
1965
2270
|
]]) : withGeocoder;
|
|
1966
|
-
|
|
2271
|
+
let withStoreUploads = storeUploadEps.length !== 0 ? withUpload.concat([[
|
|
2272
|
+
"uploadEndpoints",
|
|
2273
|
+
Object.fromEntries(storeUploadEps.map(param => [
|
|
2274
|
+
param[0],
|
|
2275
|
+
param[1]
|
|
2276
|
+
]))
|
|
2277
|
+
]]) : withUpload;
|
|
2278
|
+
return JSON.stringify(Object.fromEntries(withStoreUploads));
|
|
1967
2279
|
});
|
|
1968
2280
|
new (Aws.s3.BucketObject)("host-ui-config-json", {
|
|
1969
2281
|
bucket: bucketName,
|
|
@@ -1981,7 +2293,7 @@ function Make($star) {
|
|
|
1981
2293
|
contentType: "application/json"
|
|
1982
2294
|
});
|
|
1983
2295
|
}
|
|
1984
|
-
Pulumi$Pulumi.$$export("hostShellUrl", match$
|
|
2296
|
+
Pulumi$Pulumi.$$export("hostShellUrl", match$4.distributionUrl);
|
|
1985
2297
|
}
|
|
1986
2298
|
return Pulumi$Pulumi.getOutputs();
|
|
1987
2299
|
};
|
|
@@ -2133,6 +2445,8 @@ export {
|
|
|
2133
2445
|
getApiConfig,
|
|
2134
2446
|
splitApiOutputsRef,
|
|
2135
2447
|
getSplitApiOutputs,
|
|
2448
|
+
objectStoreEndpointsRef,
|
|
2449
|
+
getObjectStoreEndpoints,
|
|
2136
2450
|
MakeWithConfig,
|
|
2137
2451
|
Make,
|
|
2138
2452
|
}
|
|
@@ -42,9 +42,19 @@ let make = (
|
|
|
42
42
|
// bucket's CloudFront `{prefix}/*` behavior so the returned `/{key}` ref
|
|
43
43
|
// resolves.
|
|
44
44
|
~servedPrefix: string=defaultServedPrefix,
|
|
45
|
+
// Resource-name stem. Defaults to the single-service name this adapter had
|
|
46
|
+
// when a deployment could only have one store, so the existing service keeps
|
|
47
|
+
// its identity; declared stores each pass their own, because there is one
|
|
48
|
+
// service per store.
|
|
49
|
+
//
|
|
50
|
+
// One service per store rather than one service with wildcard write across
|
|
51
|
+
// stores: a single wide-scoped presigner reintroduces exactly the blast radius
|
|
52
|
+
// the per-store split exists to remove, and it does so invisibly — every
|
|
53
|
+
// upload still works.
|
|
54
|
+
~name: string="UploadPresignService",
|
|
45
55
|
~opts=?,
|
|
46
56
|
): serviceOutputs => {
|
|
47
|
-
let serviceName =
|
|
57
|
+
let serviceName = name
|
|
48
58
|
let opts =
|
|
49
59
|
opts->Option.map(ReventlessCore.Util.Pulumi.ComponentResourceOptions.toCustomResourceOptions)
|
|
50
60
|
|
|
@@ -61,12 +71,18 @@ let make = (
|
|
|
61
71
|
)
|
|
62
72
|
|
|
63
73
|
// CloudWatch Logs (so failures are observable) plus least-privilege
|
|
64
|
-
// `s3:PutObject` on
|
|
74
|
+
// `s3:PutObject` on this store's own keys.
|
|
75
|
+
//
|
|
76
|
+
// Scoped to `{bucket}/{servedPrefix}/*`, not to the whole bucket. Because keys
|
|
77
|
+
// are rooted at the store's prefix in *both* layouts, this one expression is
|
|
78
|
+
// least-privilege in both: a dedicated bucket's service still cannot write
|
|
79
|
+
// outside its prefix, and a shared bucket's services cannot write into each
|
|
80
|
+
// other's stores. The layouts stay one model — even the policy does not fork.
|
|
65
81
|
let _policy =
|
|
66
82
|
bucketName
|
|
67
83
|
->Pulumi.Output.fromInput
|
|
68
84
|
->Pulumi.Output.apply(b => {
|
|
69
|
-
let arn = `arn:aws:s3:::${b}/*`
|
|
85
|
+
let arn = `arn:aws:s3:::${b}/${servedPrefix}/*`
|
|
70
86
|
let _ = IAM.RolePolicy.make(
|
|
71
87
|
~name=`${serviceName}Policy`,
|
|
72
88
|
~args={
|
|
@@ -14,16 +14,16 @@ import * as Util_Pulumi$ReventlessCore from "@reventlessdev/reventless-core/src/
|
|
|
14
14
|
|
|
15
15
|
let defaultServedPrefix = "uploads";
|
|
16
16
|
|
|
17
|
-
function make(bucketName, corsOriginsOpt, servedPrefixOpt, opts) {
|
|
17
|
+
function make(bucketName, corsOriginsOpt, servedPrefixOpt, nameOpt, opts) {
|
|
18
18
|
let corsOrigins = corsOriginsOpt !== undefined ? corsOriginsOpt : ["*"];
|
|
19
19
|
let servedPrefix = servedPrefixOpt !== undefined ? servedPrefixOpt : defaultServedPrefix;
|
|
20
|
-
let
|
|
20
|
+
let name = nameOpt !== undefined ? nameOpt : "UploadPresignService";
|
|
21
21
|
let opts$1 = Stdlib_Option.map(opts, Util_Pulumi$ReventlessCore.ComponentResourceOptions.toCustomResourceOptions);
|
|
22
|
-
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(
|
|
22
|
+
let lambdaRole = IAM$PulumiAws.Role.makeWithDefaultPolicy(name, Pulumi.output(AWS$ReventlessAws.Lambda.principal), AWS_Tags$ReventlessAws.make(name, "Platform", "Identity", "Platform", undefined, undefined, undefined, undefined), opts$1);
|
|
23
23
|
bucketName.apply(b => {
|
|
24
|
-
let arn = `arn:aws:s3:::` + b + `/*`;
|
|
25
|
-
new (Aws.iam.RolePolicy)(
|
|
26
|
-
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined,
|
|
24
|
+
let arn = `arn:aws:s3:::` + b + `/` + servedPrefix + `/*`;
|
|
25
|
+
new (Aws.iam.RolePolicy)(name + `Policy`, {
|
|
26
|
+
policy: PolicyDocument$PulumiAws.toJsonString(PolicyDocument$PulumiAws.make(undefined, name + `Policy`, [
|
|
27
27
|
{
|
|
28
28
|
Sid: "AllowLambdaLogging",
|
|
29
29
|
Effect: "Allow",
|
|
@@ -50,7 +50,7 @@ function make(bucketName, corsOriginsOpt, servedPrefixOpt, opts) {
|
|
|
50
50
|
]]);
|
|
51
51
|
let match = Util_Bundle$ReventlessAws.buildCodeArchive("@reventlessdev/reventless-aws/src/adapter/Upload/Upload_Presign_S3_Ops.res.mjs", packageDirs, undefined);
|
|
52
52
|
let layers = Stdlib_Option.getOr(Stdlib_Option.map(Lambda$PulumiAws.reventlessLayerArn, arn => [arn]), []);
|
|
53
|
-
let lambda = new (Aws.lambda.Function)(
|
|
53
|
+
let lambda = new (Aws.lambda.Function)(name, {
|
|
54
54
|
handler: "index.handler",
|
|
55
55
|
runtime: "nodejs22.x",
|
|
56
56
|
code: match.code,
|
|
@@ -58,7 +58,7 @@ function make(bucketName, corsOriginsOpt, servedPrefixOpt, opts) {
|
|
|
58
58
|
memorySize: 256,
|
|
59
59
|
timeout: 30,
|
|
60
60
|
layers: layers,
|
|
61
|
-
tags: AWS_Tags$ReventlessAws.make(
|
|
61
|
+
tags: AWS_Tags$ReventlessAws.make(name, "Platform", "Runtime", "Platform", undefined, undefined, undefined, undefined),
|
|
62
62
|
environment: {
|
|
63
63
|
variables: Object.fromEntries([
|
|
64
64
|
[
|
|
@@ -85,7 +85,7 @@ function make(bucketName, corsOriginsOpt, servedPrefixOpt, opts) {
|
|
|
85
85
|
},
|
|
86
86
|
sourceCodeHash: match.sourceCodeHash
|
|
87
87
|
}, opts$1 !== undefined ? Primitive_option.valFromOption(opts$1) : undefined);
|
|
88
|
-
let functionUrl = new (Aws.lambda.FunctionUrl)(
|
|
88
|
+
let functionUrl = new (Aws.lambda.FunctionUrl)(name + `Url`, {
|
|
89
89
|
authorizationType: "NONE",
|
|
90
90
|
functionName: lambda.name,
|
|
91
91
|
cors: {
|