@openhi/constructs 0.0.137 → 0.0.138
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/lib/index.d.mts +51 -1
- package/lib/index.d.ts +51 -1
- package/lib/index.js +921 -824
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +943 -848
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.js
CHANGED
|
@@ -798,6 +798,8 @@ __export(src_exports, {
|
|
|
798
798
|
DataStorePostgresReplica: () => DataStorePostgresReplica,
|
|
799
799
|
DiscoverableStringParameter: () => DiscoverableStringParameter,
|
|
800
800
|
DynamoDbDataStore: () => DynamoDbDataStore,
|
|
801
|
+
LOCALHOST_OAUTH_CALLBACK_URLS: () => LOCALHOST_OAUTH_CALLBACK_URLS,
|
|
802
|
+
LOCALHOST_OAUTH_LOGOUT_URLS: () => LOCALHOST_OAUTH_LOGOUT_URLS,
|
|
801
803
|
OPENHI_REPO_TAG_KEY_ENV_VAR: () => OPENHI_REPO_TAG_KEY_ENV_VAR,
|
|
802
804
|
OPENHI_RESOURCE_URN_SYSTEM: () => OPENHI_RESOURCE_URN_SYSTEM,
|
|
803
805
|
OPENHI_TAG_KEY_PREFIX_ENV_VAR: () => OPENHI_TAG_KEY_PREFIX_ENV_VAR,
|
|
@@ -1473,23 +1475,10 @@ var import_aws_cognito2 = require("aws-cdk-lib/aws-cognito");
|
|
|
1473
1475
|
var CognitoUserPoolClient = class extends import_aws_cognito2.UserPoolClient {
|
|
1474
1476
|
constructor(scope, props) {
|
|
1475
1477
|
super(scope, "user-pool-client", {
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1478
|
+
// Default: SPA client (no secret). OAuth flow + callback/logout URL
|
|
1479
|
+
// composition is the owning service's responsibility — pass via
|
|
1480
|
+
// `props.oAuth` (see `OpenHiAuthService.resolveOAuthRedirectUrls`).
|
|
1479
1481
|
generateSecret: false,
|
|
1480
|
-
oAuth: {
|
|
1481
|
-
flows: {
|
|
1482
|
-
authorizationCodeGrant: true,
|
|
1483
|
-
implicitCodeGrant: true
|
|
1484
|
-
},
|
|
1485
|
-
callbackUrls: [
|
|
1486
|
-
`http://localhost:3000/oauth/callback`,
|
|
1487
|
-
`https://localhost:3000/oauth/callback`
|
|
1488
|
-
]
|
|
1489
|
-
},
|
|
1490
|
-
/**
|
|
1491
|
-
* Overrideable props
|
|
1492
|
-
*/
|
|
1493
1482
|
...props
|
|
1494
1483
|
});
|
|
1495
1484
|
}
|
|
@@ -2816,10 +2805,11 @@ var StaticContent = class extends import_constructs10.Construct {
|
|
|
2816
2805
|
};
|
|
2817
2806
|
|
|
2818
2807
|
// src/services/open-hi-auth-service.ts
|
|
2808
|
+
var import_config7 = __toESM(require_lib());
|
|
2819
2809
|
var import_aws_cognito4 = require("aws-cdk-lib/aws-cognito");
|
|
2820
|
-
var
|
|
2810
|
+
var import_aws_iam7 = require("aws-cdk-lib/aws-iam");
|
|
2821
2811
|
var import_aws_kms2 = require("aws-cdk-lib/aws-kms");
|
|
2822
|
-
var
|
|
2812
|
+
var import_core2 = require("aws-cdk-lib/core");
|
|
2823
2813
|
|
|
2824
2814
|
// src/services/open-hi-data-service.ts
|
|
2825
2815
|
var import_config4 = __toESM(require_lib());
|
|
@@ -6792,620 +6782,247 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
6792
6782
|
_OpenHiDataService.SERVICE_TYPE = "data";
|
|
6793
6783
|
var OpenHiDataService = _OpenHiDataService;
|
|
6794
6784
|
|
|
6795
|
-
// src/
|
|
6796
|
-
var
|
|
6797
|
-
var
|
|
6798
|
-
var buildProvisionDefaultWorkspaceRequestedDetail = (event) => {
|
|
6799
|
-
const attrs = event.request?.userAttributes ?? {};
|
|
6800
|
-
const cognitoSub = attrs.sub?.trim();
|
|
6801
|
-
if (!cognitoSub) {
|
|
6802
|
-
return void 0;
|
|
6803
|
-
}
|
|
6804
|
-
const email = attrs.email?.trim();
|
|
6805
|
-
const displayName = email || event.userName || cognitoSub;
|
|
6806
|
-
return {
|
|
6807
|
-
cognitoSub,
|
|
6808
|
-
...email ? { email } : {},
|
|
6809
|
-
displayName,
|
|
6810
|
-
trigger: {
|
|
6811
|
-
source: "cognito.post-confirmation",
|
|
6812
|
-
triggerSource: event.triggerSource,
|
|
6813
|
-
userPoolId: event.userPoolId,
|
|
6814
|
-
userName: event.userName,
|
|
6815
|
-
clientId: event.callerContext?.clientId
|
|
6816
|
-
}
|
|
6817
|
-
};
|
|
6818
|
-
};
|
|
6785
|
+
// src/services/open-hi-website-service.ts
|
|
6786
|
+
var import_config6 = __toESM(require_lib());
|
|
6787
|
+
var import_aws_s32 = require("aws-cdk-lib/aws-s3");
|
|
6819
6788
|
|
|
6820
|
-
// src/
|
|
6789
|
+
// src/services/open-hi-rest-api-service.ts
|
|
6790
|
+
var import_config5 = __toESM(require_lib());
|
|
6791
|
+
var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
|
|
6792
|
+
var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
|
|
6793
|
+
var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
|
|
6794
|
+
var import_aws_iam5 = require("aws-cdk-lib/aws-iam");
|
|
6795
|
+
var import_aws_route535 = require("aws-cdk-lib/aws-route53");
|
|
6796
|
+
var import_aws_route53_targets3 = require("aws-cdk-lib/aws-route53-targets");
|
|
6797
|
+
var import_core = require("aws-cdk-lib/core");
|
|
6798
|
+
var import_constructs19 = require("constructs");
|
|
6799
|
+
|
|
6800
|
+
// src/data/lambda/cors-options-lambda.ts
|
|
6821
6801
|
var import_node_fs9 = __toESM(require("fs"));
|
|
6822
6802
|
var import_node_path9 = __toESM(require("path"));
|
|
6823
|
-
var import_aws_cdk_lib15 = require("aws-cdk-lib");
|
|
6824
|
-
var import_aws_events8 = require("aws-cdk-lib/aws-events");
|
|
6825
|
-
var import_aws_events_targets4 = require("aws-cdk-lib/aws-events-targets");
|
|
6826
|
-
var import_aws_iam5 = require("aws-cdk-lib/aws-iam");
|
|
6827
6803
|
var import_aws_lambda10 = require("aws-cdk-lib/aws-lambda");
|
|
6828
6804
|
var import_aws_lambda_nodejs10 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
6829
6805
|
var import_constructs17 = require("constructs");
|
|
6830
|
-
var HANDLER_NAME9 = "
|
|
6806
|
+
var HANDLER_NAME9 = "cors-options-lambda.handler.js";
|
|
6831
6807
|
function resolveHandlerEntry9(dirname) {
|
|
6832
6808
|
const sameDir = import_node_path9.default.join(dirname, HANDLER_NAME9);
|
|
6833
6809
|
if (import_node_fs9.default.existsSync(sameDir)) {
|
|
6834
6810
|
return sameDir;
|
|
6835
6811
|
}
|
|
6836
|
-
|
|
6812
|
+
const fromLib = import_node_path9.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME9);
|
|
6813
|
+
return fromLib;
|
|
6837
6814
|
}
|
|
6838
|
-
var
|
|
6839
|
-
constructor(scope,
|
|
6840
|
-
super(scope,
|
|
6815
|
+
var CorsOptionsLambda = class extends import_constructs17.Construct {
|
|
6816
|
+
constructor(scope, id = "cors-options-lambda") {
|
|
6817
|
+
super(scope, id);
|
|
6841
6818
|
this.lambda = new import_aws_lambda_nodejs10.NodejsFunction(this, "handler", {
|
|
6842
6819
|
entry: resolveHandlerEntry9(__dirname),
|
|
6843
6820
|
runtime: import_aws_lambda10.Runtime.NODEJS_LATEST,
|
|
6844
|
-
memorySize:
|
|
6845
|
-
environment: {
|
|
6846
|
-
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName
|
|
6847
|
-
}
|
|
6848
|
-
});
|
|
6849
|
-
props.dataStoreTable.grant(
|
|
6850
|
-
this.lambda,
|
|
6851
|
-
"dynamodb:PutItem",
|
|
6852
|
-
"dynamodb:UpdateItem"
|
|
6853
|
-
);
|
|
6854
|
-
this.lambda.addToRolePolicy(
|
|
6855
|
-
new import_aws_iam5.PolicyStatement({
|
|
6856
|
-
effect: import_aws_iam5.Effect.ALLOW,
|
|
6857
|
-
actions: ["dynamodb:Query"],
|
|
6858
|
-
resources: [`${props.dataStoreTable.tableArn}/index/*`]
|
|
6859
|
-
})
|
|
6860
|
-
);
|
|
6861
|
-
this.rule = new import_aws_events8.Rule(this, "rule", {
|
|
6862
|
-
eventBus: props.controlEventBus,
|
|
6863
|
-
eventPattern: {
|
|
6864
|
-
source: [USER_ONBOARDING_EVENT_SOURCE],
|
|
6865
|
-
detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE]
|
|
6866
|
-
},
|
|
6867
|
-
targets: [
|
|
6868
|
-
new import_aws_events_targets4.LambdaFunction(this.lambda, {
|
|
6869
|
-
retryAttempts: 2,
|
|
6870
|
-
maxEventAge: import_aws_cdk_lib15.Duration.hours(2)
|
|
6871
|
-
})
|
|
6872
|
-
]
|
|
6821
|
+
memorySize: 128
|
|
6873
6822
|
});
|
|
6874
6823
|
}
|
|
6875
6824
|
};
|
|
6876
6825
|
|
|
6877
|
-
// src/
|
|
6826
|
+
// src/data/lambda/rest-api-lambda.ts
|
|
6827
|
+
var import_node_fs10 = __toESM(require("fs"));
|
|
6828
|
+
var import_node_path10 = __toESM(require("path"));
|
|
6829
|
+
var import_aws_lambda11 = require("aws-cdk-lib/aws-lambda");
|
|
6830
|
+
var import_aws_lambda_nodejs11 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
6878
6831
|
var import_constructs18 = require("constructs");
|
|
6879
|
-
var
|
|
6832
|
+
var HANDLER_NAME10 = "rest-api-lambda.handler.js";
|
|
6833
|
+
function resolveHandlerEntry10(dirname) {
|
|
6834
|
+
const sameDir = import_node_path10.default.join(dirname, HANDLER_NAME10);
|
|
6835
|
+
if (import_node_fs10.default.existsSync(sameDir)) {
|
|
6836
|
+
return sameDir;
|
|
6837
|
+
}
|
|
6838
|
+
const fromLib = import_node_path10.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME10);
|
|
6839
|
+
return fromLib;
|
|
6840
|
+
}
|
|
6841
|
+
var RestApiLambda = class extends import_constructs18.Construct {
|
|
6880
6842
|
constructor(scope, props) {
|
|
6881
|
-
super(scope, "
|
|
6882
|
-
this.
|
|
6883
|
-
|
|
6884
|
-
|
|
6843
|
+
super(scope, "rest-api-lambda");
|
|
6844
|
+
this.lambda = new import_aws_lambda_nodejs11.NodejsFunction(this, "handler", {
|
|
6845
|
+
entry: resolveHandlerEntry10(__dirname),
|
|
6846
|
+
runtime: import_aws_lambda11.Runtime.NODEJS_LATEST,
|
|
6847
|
+
memorySize: 1024,
|
|
6848
|
+
environment: {
|
|
6849
|
+
DYNAMO_TABLE_NAME: props.dynamoTableName,
|
|
6850
|
+
BRANCH_TAG_VALUE: props.branchTagValue,
|
|
6851
|
+
HTTP_API_TAG_VALUE: props.httpApiTagValue,
|
|
6852
|
+
OPENHI_PG_CLUSTER_ARN: props.postgresClusterArn,
|
|
6853
|
+
OPENHI_PG_SECRET_ARN: props.postgresSecretArn,
|
|
6854
|
+
OPENHI_PG_DATABASE: props.postgresDatabase,
|
|
6855
|
+
OPENHI_PG_SCHEMA: props.postgresSchema,
|
|
6856
|
+
...props.extraEnvironment
|
|
6857
|
+
},
|
|
6858
|
+
bundling: {
|
|
6859
|
+
minify: true,
|
|
6860
|
+
sourceMap: false
|
|
6861
|
+
}
|
|
6885
6862
|
});
|
|
6886
6863
|
}
|
|
6887
6864
|
};
|
|
6888
6865
|
|
|
6889
|
-
// src/services/open-hi-
|
|
6890
|
-
var
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
this.preTokenGenerationLambda = this.createPreTokenGenerationLambda();
|
|
6904
|
-
this.postAuthenticationLambda = this.createPostAuthenticationLambda();
|
|
6905
|
-
this.postConfirmationLambda = this.createPostConfirmationLambda();
|
|
6906
|
-
this.userOnboardingWorkflow = this.createUserOnboardingWorkflow();
|
|
6907
|
-
this.userPool = this.createUserPool();
|
|
6908
|
-
this.grantPreTokenGenerationPermissions();
|
|
6909
|
-
this.grantPostAuthenticationPermissions();
|
|
6910
|
-
this.grantPostConfirmationPermissions();
|
|
6911
|
-
this.userPoolClient = this.createUserPoolClient();
|
|
6912
|
-
this.userPoolDomain = this.createUserPoolDomain();
|
|
6913
|
-
}
|
|
6866
|
+
// src/services/open-hi-rest-api-service.ts
|
|
6867
|
+
var REST_API_BASE_URL_SSM_NAME = "REST_API_BASE_URL";
|
|
6868
|
+
var REST_API_DOMAIN_NAME_SSM_NAME = "REST_API_DOMAIN_NAME";
|
|
6869
|
+
var DEV_CORS_ALLOW_ORIGINS = [
|
|
6870
|
+
"http://localhost:3000",
|
|
6871
|
+
"https://localhost:3000",
|
|
6872
|
+
"http://localhost:5173",
|
|
6873
|
+
"https://localhost:5173",
|
|
6874
|
+
"http://127.0.0.1:3000",
|
|
6875
|
+
"https://127.0.0.1:3000",
|
|
6876
|
+
"http://127.0.0.1:5173",
|
|
6877
|
+
"https://127.0.0.1:5173"
|
|
6878
|
+
];
|
|
6879
|
+
var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
6914
6880
|
/**
|
|
6915
|
-
*
|
|
6881
|
+
* Compose the REST API's full per-deploy domain. Thin wrapper over
|
|
6882
|
+
* {@link OpenHiService.composeServiceDomain} that pins `domainPrefix`
|
|
6883
|
+
* to {@link API_DOMAIN_PREFIX}.
|
|
6884
|
+
*
|
|
6885
|
+
* Use from sibling stacks that need to predict the API's hostname
|
|
6886
|
+
* before the REST API stack is synthesised.
|
|
6916
6887
|
*/
|
|
6917
|
-
static
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6888
|
+
static composeFullDomain(opts) {
|
|
6889
|
+
return OpenHiService.composeServiceDomain({
|
|
6890
|
+
...opts,
|
|
6891
|
+
domainPrefix: _OpenHiRestApiService.API_DOMAIN_PREFIX
|
|
6921
6892
|
});
|
|
6922
|
-
return import_aws_cognito4.UserPool.fromUserPoolId(scope, "user-pool", userPoolId);
|
|
6923
|
-
}
|
|
6924
|
-
/**
|
|
6925
|
-
* Returns an IUserPoolClient by looking up the Auth stack's User Pool Client ID from SSM.
|
|
6926
|
-
*/
|
|
6927
|
-
static userPoolClientFromConstruct(scope) {
|
|
6928
|
-
const userPoolClientId = DiscoverableStringParameter.valueForLookupName(
|
|
6929
|
-
scope,
|
|
6930
|
-
{
|
|
6931
|
-
ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,
|
|
6932
|
-
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
6933
|
-
}
|
|
6934
|
-
);
|
|
6935
|
-
return import_aws_cognito4.UserPoolClient.fromUserPoolClientId(
|
|
6936
|
-
scope,
|
|
6937
|
-
"user-pool-client",
|
|
6938
|
-
userPoolClientId
|
|
6939
|
-
);
|
|
6940
6893
|
}
|
|
6941
6894
|
/**
|
|
6942
|
-
* Returns an
|
|
6895
|
+
* Returns an IHttpApi by looking up the REST API stack's HTTP API ID from SSM.
|
|
6943
6896
|
*/
|
|
6944
|
-
static
|
|
6945
|
-
const
|
|
6946
|
-
ssmParamName:
|
|
6947
|
-
serviceType:
|
|
6897
|
+
static rootHttpApiFromConstruct(scope) {
|
|
6898
|
+
const httpApiId = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
6899
|
+
ssmParamName: RootHttpApi.SSM_PARAM_NAME,
|
|
6900
|
+
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
6948
6901
|
});
|
|
6949
|
-
return
|
|
6902
|
+
return import_aws_apigatewayv22.HttpApi.fromHttpApiAttributes(scope, "http-api", { httpApiId });
|
|
6950
6903
|
}
|
|
6951
6904
|
/**
|
|
6952
|
-
* Returns the
|
|
6953
|
-
*
|
|
6954
|
-
* the Auth stack's User Pool Domain from SSM and composing it with the
|
|
6955
|
-
* calling stack's region.
|
|
6956
|
-
*
|
|
6957
|
-
* Equivalent to `UserPoolDomain.baseUrl()` on the concrete construct,
|
|
6958
|
-
* but works across stacks where the looked-up `IUserPoolDomain` is an
|
|
6959
|
-
* `Import` and does not carry the `baseUrl()` method. Assumes the
|
|
6960
|
-
* domain was created as a Cognito-managed prefix domain (the only
|
|
6961
|
-
* variant `OpenHiAuthService.createUserPoolDomain` produces).
|
|
6905
|
+
* Returns the REST API base URL (e.g. https://api.example.com) by looking it up from SSM.
|
|
6906
|
+
* Use in other stacks for E2E, scripts, or config.
|
|
6962
6907
|
*/
|
|
6963
|
-
static
|
|
6964
|
-
|
|
6965
|
-
ssmParamName:
|
|
6966
|
-
serviceType:
|
|
6908
|
+
static restApiBaseUrlFromConstruct(scope) {
|
|
6909
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
6910
|
+
ssmParamName: REST_API_BASE_URL_SSM_NAME,
|
|
6911
|
+
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
6967
6912
|
});
|
|
6968
|
-
const region = import_core.Stack.of(scope).region;
|
|
6969
|
-
return `https://${domainName}.auth.${region}.amazoncognito.com`;
|
|
6970
6913
|
}
|
|
6971
6914
|
/**
|
|
6972
|
-
* Returns
|
|
6915
|
+
* Returns the REST API's custom domain name (bare hostname, no scheme — e.g.
|
|
6916
|
+
* `api.example.com`) by looking it up from SSM. Use as the host for a
|
|
6917
|
+
* CloudFront `HttpOrigin` so the website's distribution can proxy `/api/*`
|
|
6918
|
+
* to this stack's API Gateway without per-branch DNS knowledge.
|
|
6973
6919
|
*/
|
|
6974
|
-
static
|
|
6975
|
-
|
|
6976
|
-
ssmParamName:
|
|
6977
|
-
serviceType:
|
|
6920
|
+
static restApiDomainNameFromConstruct(scope) {
|
|
6921
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
6922
|
+
ssmParamName: REST_API_DOMAIN_NAME_SSM_NAME,
|
|
6923
|
+
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
6978
6924
|
});
|
|
6979
|
-
return import_aws_kms2.Key.fromKeyArn(scope, "kms-key", keyArn);
|
|
6980
6925
|
}
|
|
6981
6926
|
get serviceType() {
|
|
6982
|
-
return
|
|
6927
|
+
return _OpenHiRestApiService.SERVICE_TYPE;
|
|
6983
6928
|
}
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
|
|
6996
|
-
return key;
|
|
6997
|
-
}
|
|
6998
|
-
/**
|
|
6999
|
-
* Creates the Pre Token Generation Lambda (Cognito trigger). On every
|
|
7000
|
-
* sign-in and token refresh the Lambda resolves the User by Cognito `sub`
|
|
7001
|
-
* (GSI2) and injects `ohi_tid`, `ohi_wid`, `ohi_uid`, `ohi_uname` into
|
|
7002
|
-
* both the ID token and the access token (ADR 2026-03-17-01).
|
|
7003
|
-
*/
|
|
7004
|
-
createPreTokenGenerationLambda() {
|
|
7005
|
-
const construct = new PreTokenGenerationLambda(this, {
|
|
7006
|
-
dynamoTableName: this.dataStoreTable().tableName
|
|
7007
|
-
});
|
|
7008
|
-
return construct.lambda;
|
|
7009
|
-
}
|
|
7010
|
-
/**
|
|
7011
|
-
* Creates the Post Authentication Lambda (Cognito trigger). Calls
|
|
7012
|
-
* AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user
|
|
7013
|
-
* sessions per ADR 2026-03-17-01.
|
|
7014
|
-
*/
|
|
7015
|
-
createPostAuthenticationLambda() {
|
|
7016
|
-
const construct = new PostAuthenticationLambda(this);
|
|
7017
|
-
return construct.lambda;
|
|
6929
|
+
constructor(ohEnv, props = {}) {
|
|
6930
|
+
super(ohEnv, _OpenHiRestApiService.SERVICE_TYPE, props);
|
|
6931
|
+
this.props = props;
|
|
6932
|
+
this.validateConfig(props);
|
|
6933
|
+
const hostedZone = this.createHostedZone();
|
|
6934
|
+
const certificate = this.createCertificate();
|
|
6935
|
+
this.apiDomainName = this.createApiDomainNameString(hostedZone);
|
|
6936
|
+
this.createRestApiBaseUrlParameter(this.apiDomainName);
|
|
6937
|
+
this.createRestApiDomainNameParameter(this.apiDomainName);
|
|
6938
|
+
const domainName = this.createDomainName(hostedZone, certificate);
|
|
6939
|
+
this.rootHttpApi = this.createRootHttpApi(domainName);
|
|
6940
|
+
this.createRestApiLambdaAndRoutes(hostedZone, domainName);
|
|
7018
6941
|
}
|
|
7019
6942
|
/**
|
|
7020
|
-
*
|
|
7021
|
-
* confirmation, publishes a control-plane workflow event; provisioning lives
|
|
7022
|
-
* behind EventBridge.
|
|
6943
|
+
* Validates that config required for the REST API stack is present.
|
|
7023
6944
|
*/
|
|
7024
|
-
|
|
7025
|
-
const
|
|
7026
|
-
|
|
7027
|
-
|
|
7028
|
-
return construct.lambda;
|
|
7029
|
-
}
|
|
7030
|
-
createUserOnboardingWorkflow() {
|
|
7031
|
-
return new UserOnboardingWorkflow(this, {
|
|
7032
|
-
controlEventBus: this.controlEventBus(),
|
|
7033
|
-
dataStoreTable: this.dataStoreTable()
|
|
7034
|
-
});
|
|
7035
|
-
}
|
|
7036
|
-
dataStoreTable() {
|
|
7037
|
-
if (this._dataStoreTable === null) {
|
|
7038
|
-
this._dataStoreTable = OpenHiDataService.dynamoDbDataStoreFromConstruct(this);
|
|
6945
|
+
validateConfig(props) {
|
|
6946
|
+
const { config } = props;
|
|
6947
|
+
if (!config) {
|
|
6948
|
+
throw new Error("Config is required");
|
|
7039
6949
|
}
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
|
|
7043
|
-
if (
|
|
7044
|
-
|
|
6950
|
+
if (!config.hostedZoneId) {
|
|
6951
|
+
throw new Error("Hosted zone ID is required");
|
|
6952
|
+
}
|
|
6953
|
+
if (!config.zoneName) {
|
|
6954
|
+
throw new Error("Zone name is required");
|
|
7045
6955
|
}
|
|
7046
|
-
return this._controlEventBus;
|
|
7047
6956
|
}
|
|
7048
6957
|
/**
|
|
7049
|
-
* Creates the
|
|
7050
|
-
* Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
|
|
6958
|
+
* Creates the hosted zone reference (imported from config).
|
|
7051
6959
|
* Override to customize.
|
|
7052
6960
|
*/
|
|
7053
|
-
|
|
7054
|
-
const
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
|
|
7058
|
-
userPool.addTrigger(
|
|
7059
|
-
import_aws_cognito4.UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,
|
|
7060
|
-
this.preTokenGenerationLambda,
|
|
7061
|
-
import_aws_cognito4.LambdaVersion.V2_0
|
|
7062
|
-
);
|
|
7063
|
-
userPool.addTrigger(
|
|
7064
|
-
import_aws_cognito4.UserPoolOperation.POST_AUTHENTICATION,
|
|
7065
|
-
this.postAuthenticationLambda
|
|
7066
|
-
);
|
|
7067
|
-
userPool.addTrigger(
|
|
7068
|
-
import_aws_cognito4.UserPoolOperation.POST_CONFIRMATION,
|
|
7069
|
-
this.postConfirmationLambda
|
|
7070
|
-
);
|
|
7071
|
-
new DiscoverableStringParameter(this, "user-pool-param", {
|
|
7072
|
-
ssmParamName: CognitoUserPool.SSM_PARAM_NAME,
|
|
7073
|
-
stringValue: userPool.userPoolId,
|
|
7074
|
-
description: "Cognito User Pool ID for this Auth stack; cross-stack reference"
|
|
6961
|
+
createHostedZone() {
|
|
6962
|
+
const { config } = this.props;
|
|
6963
|
+
return import_aws_route535.HostedZone.fromHostedZoneAttributes(this, "root-zone", {
|
|
6964
|
+
hostedZoneId: config.hostedZoneId,
|
|
6965
|
+
zoneName: config.zoneName
|
|
7075
6966
|
});
|
|
7076
|
-
return userPool;
|
|
7077
6967
|
}
|
|
7078
6968
|
/**
|
|
7079
|
-
*
|
|
7080
|
-
*
|
|
7081
|
-
* - `Query` on GSI2 to resolve a User by Cognito `sub`
|
|
7082
|
-
* - `GetItem` on the base table for direct User reads
|
|
7083
|
-
*
|
|
7084
|
-
* No write or scan access: a User missing `currentTenant`/`currentWorkspace`
|
|
7085
|
-
* falls into the absent-claims path; repair belongs in a separate backfill.
|
|
6969
|
+
* Creates the wildcard certificate (imported from Global stack via SSM).
|
|
6970
|
+
* Override to customize.
|
|
7086
6971
|
*/
|
|
7087
|
-
|
|
7088
|
-
|
|
7089
|
-
const dynamoActions = ["dynamodb:GetItem", "dynamodb:Query"];
|
|
7090
|
-
dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);
|
|
7091
|
-
this.preTokenGenerationLambda.addToRolePolicy(
|
|
7092
|
-
new import_aws_iam6.PolicyStatement({
|
|
7093
|
-
effect: import_aws_iam6.Effect.ALLOW,
|
|
7094
|
-
actions: [...dynamoActions],
|
|
7095
|
-
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
7096
|
-
})
|
|
7097
|
-
);
|
|
6972
|
+
createCertificate() {
|
|
6973
|
+
return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);
|
|
7098
6974
|
}
|
|
7099
6975
|
/**
|
|
7100
|
-
*
|
|
7101
|
-
*
|
|
7102
|
-
*
|
|
7103
|
-
*
|
|
7104
|
-
*
|
|
7105
|
-
* trigger, creating the cycle:
|
|
7106
|
-
* userPool → lambda (trigger ARN) → role policy → userPool ARN.
|
|
7107
|
-
* Using `formatArn` avoids referencing the User Pool resource directly
|
|
7108
|
-
* while still scoping to user pools in this account+region. The Lambda
|
|
7109
|
-
* is invoked only by Cognito with a Cognito-provided `event.userPoolId`,
|
|
7110
|
-
* so the runtime target is constrained by the trigger contract.
|
|
6976
|
+
* Returns the API domain name string (e.g. api.example.com or api-\{prefix\}.example.com).
|
|
6977
|
+
* Delegates to {@link OpenHiRestApiService.composeFullDomain} so the
|
|
6978
|
+
* release-vs-feature composition stays in one place; picks up
|
|
6979
|
+
* `this.defaultReleaseBranch` (not a hard-coded `"main"`).
|
|
6980
|
+
* Override to customize.
|
|
7111
6981
|
*/
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
|
|
7119
|
-
resource: "userpool",
|
|
7120
|
-
resourceName: "*"
|
|
7121
|
-
})
|
|
7122
|
-
]
|
|
7123
|
-
})
|
|
7124
|
-
);
|
|
6982
|
+
createApiDomainNameString(hostedZone) {
|
|
6983
|
+
return _OpenHiRestApiService.composeFullDomain({
|
|
6984
|
+
branchName: this.branchName,
|
|
6985
|
+
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
6986
|
+
childZonePrefix: this.childZonePrefix,
|
|
6987
|
+
zoneName: hostedZone.zoneName
|
|
6988
|
+
});
|
|
7125
6989
|
}
|
|
7126
6990
|
/**
|
|
7127
|
-
*
|
|
7128
|
-
*
|
|
6991
|
+
* Creates the SSM parameter for the REST API base URL.
|
|
6992
|
+
* Look up via {@link OpenHiRestApiService.restApiBaseUrlFromConstruct}.
|
|
6993
|
+
* Override to customize.
|
|
7129
6994
|
*/
|
|
7130
|
-
|
|
7131
|
-
|
|
6995
|
+
createRestApiBaseUrlParameter(apiDomainName) {
|
|
6996
|
+
const restApiBaseUrl = `https://${apiDomainName}`;
|
|
6997
|
+
new DiscoverableStringParameter(this, "rest-api-base-url-param", {
|
|
6998
|
+
ssmParamName: REST_API_BASE_URL_SSM_NAME,
|
|
6999
|
+
stringValue: restApiBaseUrl,
|
|
7000
|
+
description: "REST API base URL for this deployment (E2E, scripts)"
|
|
7001
|
+
});
|
|
7132
7002
|
}
|
|
7133
7003
|
/**
|
|
7134
|
-
* Creates the
|
|
7135
|
-
*
|
|
7004
|
+
* Creates the SSM parameter exposing the REST API's custom domain (bare
|
|
7005
|
+
* hostname, no scheme). Consumed by the website service as the CloudFront
|
|
7006
|
+
* `/api/*` origin host.
|
|
7007
|
+
* Look up via {@link OpenHiRestApiService.restApiDomainNameFromConstruct}.
|
|
7136
7008
|
* Override to customize.
|
|
7137
7009
|
*/
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
|
|
7143
|
-
ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,
|
|
7144
|
-
stringValue: client.userPoolClientId,
|
|
7145
|
-
description: "Cognito User Pool Client ID for this Auth stack; cross-stack reference"
|
|
7010
|
+
createRestApiDomainNameParameter(apiDomainName) {
|
|
7011
|
+
new DiscoverableStringParameter(this, "rest-api-domain-name-param", {
|
|
7012
|
+
ssmParamName: REST_API_DOMAIN_NAME_SSM_NAME,
|
|
7013
|
+
stringValue: apiDomainName,
|
|
7014
|
+
description: "REST API custom domain name (bare hostname) for cross-stack CloudFront origin lookup"
|
|
7146
7015
|
});
|
|
7147
|
-
return client;
|
|
7148
7016
|
}
|
|
7149
7017
|
/**
|
|
7150
|
-
* Creates the
|
|
7151
|
-
* Look up via {@link OpenHiAuthService.userPoolDomainFromConstruct}.
|
|
7018
|
+
* Creates the API Gateway custom domain name resource.
|
|
7152
7019
|
* Override to customize.
|
|
7153
7020
|
*/
|
|
7154
|
-
|
|
7155
|
-
const
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
|
|
7159
|
-
}
|
|
7160
|
-
});
|
|
7161
|
-
new DiscoverableStringParameter(this, "user-pool-domain-param", {
|
|
7162
|
-
ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,
|
|
7163
|
-
stringValue: domain.domainName,
|
|
7164
|
-
description: "Cognito User Pool Domain (hosted UI) for this Auth stack; cross-stack reference"
|
|
7165
|
-
});
|
|
7166
|
-
return domain;
|
|
7167
|
-
}
|
|
7168
|
-
};
|
|
7169
|
-
_OpenHiAuthService.SERVICE_TYPE = "auth";
|
|
7170
|
-
var OpenHiAuthService = _OpenHiAuthService;
|
|
7171
|
-
|
|
7172
|
-
// src/services/open-hi-rest-api-service.ts
|
|
7173
|
-
var import_config5 = __toESM(require_lib());
|
|
7174
|
-
var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
|
|
7175
|
-
var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
|
|
7176
|
-
var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
|
|
7177
|
-
var import_aws_iam7 = require("aws-cdk-lib/aws-iam");
|
|
7178
|
-
var import_aws_route535 = require("aws-cdk-lib/aws-route53");
|
|
7179
|
-
var import_aws_route53_targets3 = require("aws-cdk-lib/aws-route53-targets");
|
|
7180
|
-
var import_core2 = require("aws-cdk-lib/core");
|
|
7181
|
-
var import_constructs21 = require("constructs");
|
|
7182
|
-
|
|
7183
|
-
// src/data/lambda/cors-options-lambda.ts
|
|
7184
|
-
var import_node_fs10 = __toESM(require("fs"));
|
|
7185
|
-
var import_node_path10 = __toESM(require("path"));
|
|
7186
|
-
var import_aws_lambda11 = require("aws-cdk-lib/aws-lambda");
|
|
7187
|
-
var import_aws_lambda_nodejs11 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
7188
|
-
var import_constructs19 = require("constructs");
|
|
7189
|
-
var HANDLER_NAME10 = "cors-options-lambda.handler.js";
|
|
7190
|
-
function resolveHandlerEntry10(dirname) {
|
|
7191
|
-
const sameDir = import_node_path10.default.join(dirname, HANDLER_NAME10);
|
|
7192
|
-
if (import_node_fs10.default.existsSync(sameDir)) {
|
|
7193
|
-
return sameDir;
|
|
7194
|
-
}
|
|
7195
|
-
const fromLib = import_node_path10.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME10);
|
|
7196
|
-
return fromLib;
|
|
7197
|
-
}
|
|
7198
|
-
var CorsOptionsLambda = class extends import_constructs19.Construct {
|
|
7199
|
-
constructor(scope, id = "cors-options-lambda") {
|
|
7200
|
-
super(scope, id);
|
|
7201
|
-
this.lambda = new import_aws_lambda_nodejs11.NodejsFunction(this, "handler", {
|
|
7202
|
-
entry: resolveHandlerEntry10(__dirname),
|
|
7203
|
-
runtime: import_aws_lambda11.Runtime.NODEJS_LATEST,
|
|
7204
|
-
memorySize: 128
|
|
7205
|
-
});
|
|
7206
|
-
}
|
|
7207
|
-
};
|
|
7208
|
-
|
|
7209
|
-
// src/data/lambda/rest-api-lambda.ts
|
|
7210
|
-
var import_node_fs11 = __toESM(require("fs"));
|
|
7211
|
-
var import_node_path11 = __toESM(require("path"));
|
|
7212
|
-
var import_aws_lambda12 = require("aws-cdk-lib/aws-lambda");
|
|
7213
|
-
var import_aws_lambda_nodejs12 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
7214
|
-
var import_constructs20 = require("constructs");
|
|
7215
|
-
var HANDLER_NAME11 = "rest-api-lambda.handler.js";
|
|
7216
|
-
function resolveHandlerEntry11(dirname) {
|
|
7217
|
-
const sameDir = import_node_path11.default.join(dirname, HANDLER_NAME11);
|
|
7218
|
-
if (import_node_fs11.default.existsSync(sameDir)) {
|
|
7219
|
-
return sameDir;
|
|
7220
|
-
}
|
|
7221
|
-
const fromLib = import_node_path11.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME11);
|
|
7222
|
-
return fromLib;
|
|
7223
|
-
}
|
|
7224
|
-
var RestApiLambda = class extends import_constructs20.Construct {
|
|
7225
|
-
constructor(scope, props) {
|
|
7226
|
-
super(scope, "rest-api-lambda");
|
|
7227
|
-
this.lambda = new import_aws_lambda_nodejs12.NodejsFunction(this, "handler", {
|
|
7228
|
-
entry: resolveHandlerEntry11(__dirname),
|
|
7229
|
-
runtime: import_aws_lambda12.Runtime.NODEJS_LATEST,
|
|
7230
|
-
memorySize: 1024,
|
|
7231
|
-
environment: {
|
|
7232
|
-
DYNAMO_TABLE_NAME: props.dynamoTableName,
|
|
7233
|
-
BRANCH_TAG_VALUE: props.branchTagValue,
|
|
7234
|
-
HTTP_API_TAG_VALUE: props.httpApiTagValue,
|
|
7235
|
-
OPENHI_PG_CLUSTER_ARN: props.postgresClusterArn,
|
|
7236
|
-
OPENHI_PG_SECRET_ARN: props.postgresSecretArn,
|
|
7237
|
-
OPENHI_PG_DATABASE: props.postgresDatabase,
|
|
7238
|
-
OPENHI_PG_SCHEMA: props.postgresSchema,
|
|
7239
|
-
...props.extraEnvironment
|
|
7240
|
-
},
|
|
7241
|
-
bundling: {
|
|
7242
|
-
minify: true,
|
|
7243
|
-
sourceMap: false
|
|
7244
|
-
}
|
|
7245
|
-
});
|
|
7246
|
-
}
|
|
7247
|
-
};
|
|
7248
|
-
|
|
7249
|
-
// src/services/open-hi-rest-api-service.ts
|
|
7250
|
-
var REST_API_BASE_URL_SSM_NAME = "REST_API_BASE_URL";
|
|
7251
|
-
var REST_API_DOMAIN_NAME_SSM_NAME = "REST_API_DOMAIN_NAME";
|
|
7252
|
-
var DEV_CORS_ALLOW_ORIGINS = [
|
|
7253
|
-
"http://localhost:3000",
|
|
7254
|
-
"https://localhost:3000",
|
|
7255
|
-
"http://localhost:5173",
|
|
7256
|
-
"https://localhost:5173",
|
|
7257
|
-
"http://127.0.0.1:3000",
|
|
7258
|
-
"https://127.0.0.1:3000",
|
|
7259
|
-
"http://127.0.0.1:5173",
|
|
7260
|
-
"https://127.0.0.1:5173"
|
|
7261
|
-
];
|
|
7262
|
-
var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
7263
|
-
/**
|
|
7264
|
-
* Compose the REST API's full per-deploy domain. Thin wrapper over
|
|
7265
|
-
* {@link OpenHiService.composeServiceDomain} that pins `domainPrefix`
|
|
7266
|
-
* to {@link API_DOMAIN_PREFIX}.
|
|
7267
|
-
*
|
|
7268
|
-
* Use from sibling stacks that need to predict the API's hostname
|
|
7269
|
-
* before the REST API stack is synthesised.
|
|
7270
|
-
*/
|
|
7271
|
-
static composeFullDomain(opts) {
|
|
7272
|
-
return OpenHiService.composeServiceDomain({
|
|
7273
|
-
...opts,
|
|
7274
|
-
domainPrefix: _OpenHiRestApiService.API_DOMAIN_PREFIX
|
|
7275
|
-
});
|
|
7276
|
-
}
|
|
7277
|
-
/**
|
|
7278
|
-
* Returns an IHttpApi by looking up the REST API stack's HTTP API ID from SSM.
|
|
7279
|
-
*/
|
|
7280
|
-
static rootHttpApiFromConstruct(scope) {
|
|
7281
|
-
const httpApiId = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7282
|
-
ssmParamName: RootHttpApi.SSM_PARAM_NAME,
|
|
7283
|
-
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
7284
|
-
});
|
|
7285
|
-
return import_aws_apigatewayv22.HttpApi.fromHttpApiAttributes(scope, "http-api", { httpApiId });
|
|
7286
|
-
}
|
|
7287
|
-
/**
|
|
7288
|
-
* Returns the REST API base URL (e.g. https://api.example.com) by looking it up from SSM.
|
|
7289
|
-
* Use in other stacks for E2E, scripts, or config.
|
|
7290
|
-
*/
|
|
7291
|
-
static restApiBaseUrlFromConstruct(scope) {
|
|
7292
|
-
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7293
|
-
ssmParamName: REST_API_BASE_URL_SSM_NAME,
|
|
7294
|
-
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
7295
|
-
});
|
|
7296
|
-
}
|
|
7297
|
-
/**
|
|
7298
|
-
* Returns the REST API's custom domain name (bare hostname, no scheme — e.g.
|
|
7299
|
-
* `api.example.com`) by looking it up from SSM. Use as the host for a
|
|
7300
|
-
* CloudFront `HttpOrigin` so the website's distribution can proxy `/api/*`
|
|
7301
|
-
* to this stack's API Gateway without per-branch DNS knowledge.
|
|
7302
|
-
*/
|
|
7303
|
-
static restApiDomainNameFromConstruct(scope) {
|
|
7304
|
-
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7305
|
-
ssmParamName: REST_API_DOMAIN_NAME_SSM_NAME,
|
|
7306
|
-
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
7307
|
-
});
|
|
7308
|
-
}
|
|
7309
|
-
get serviceType() {
|
|
7310
|
-
return _OpenHiRestApiService.SERVICE_TYPE;
|
|
7311
|
-
}
|
|
7312
|
-
constructor(ohEnv, props = {}) {
|
|
7313
|
-
super(ohEnv, _OpenHiRestApiService.SERVICE_TYPE, props);
|
|
7314
|
-
this.props = props;
|
|
7315
|
-
this.validateConfig(props);
|
|
7316
|
-
const hostedZone = this.createHostedZone();
|
|
7317
|
-
const certificate = this.createCertificate();
|
|
7318
|
-
this.apiDomainName = this.createApiDomainNameString(hostedZone);
|
|
7319
|
-
this.createRestApiBaseUrlParameter(this.apiDomainName);
|
|
7320
|
-
this.createRestApiDomainNameParameter(this.apiDomainName);
|
|
7321
|
-
const domainName = this.createDomainName(hostedZone, certificate);
|
|
7322
|
-
this.rootHttpApi = this.createRootHttpApi(domainName);
|
|
7323
|
-
this.createRestApiLambdaAndRoutes(hostedZone, domainName);
|
|
7324
|
-
}
|
|
7325
|
-
/**
|
|
7326
|
-
* Validates that config required for the REST API stack is present.
|
|
7327
|
-
*/
|
|
7328
|
-
validateConfig(props) {
|
|
7329
|
-
const { config } = props;
|
|
7330
|
-
if (!config) {
|
|
7331
|
-
throw new Error("Config is required");
|
|
7332
|
-
}
|
|
7333
|
-
if (!config.hostedZoneId) {
|
|
7334
|
-
throw new Error("Hosted zone ID is required");
|
|
7335
|
-
}
|
|
7336
|
-
if (!config.zoneName) {
|
|
7337
|
-
throw new Error("Zone name is required");
|
|
7338
|
-
}
|
|
7339
|
-
}
|
|
7340
|
-
/**
|
|
7341
|
-
* Creates the hosted zone reference (imported from config).
|
|
7342
|
-
* Override to customize.
|
|
7343
|
-
*/
|
|
7344
|
-
createHostedZone() {
|
|
7345
|
-
const { config } = this.props;
|
|
7346
|
-
return import_aws_route535.HostedZone.fromHostedZoneAttributes(this, "root-zone", {
|
|
7347
|
-
hostedZoneId: config.hostedZoneId,
|
|
7348
|
-
zoneName: config.zoneName
|
|
7349
|
-
});
|
|
7350
|
-
}
|
|
7351
|
-
/**
|
|
7352
|
-
* Creates the wildcard certificate (imported from Global stack via SSM).
|
|
7353
|
-
* Override to customize.
|
|
7354
|
-
*/
|
|
7355
|
-
createCertificate() {
|
|
7356
|
-
return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);
|
|
7357
|
-
}
|
|
7358
|
-
/**
|
|
7359
|
-
* Returns the API domain name string (e.g. api.example.com or api-\{prefix\}.example.com).
|
|
7360
|
-
* Delegates to {@link OpenHiRestApiService.composeFullDomain} so the
|
|
7361
|
-
* release-vs-feature composition stays in one place; picks up
|
|
7362
|
-
* `this.defaultReleaseBranch` (not a hard-coded `"main"`).
|
|
7363
|
-
* Override to customize.
|
|
7364
|
-
*/
|
|
7365
|
-
createApiDomainNameString(hostedZone) {
|
|
7366
|
-
return _OpenHiRestApiService.composeFullDomain({
|
|
7367
|
-
branchName: this.branchName,
|
|
7368
|
-
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
7369
|
-
childZonePrefix: this.childZonePrefix,
|
|
7370
|
-
zoneName: hostedZone.zoneName
|
|
7371
|
-
});
|
|
7372
|
-
}
|
|
7373
|
-
/**
|
|
7374
|
-
* Creates the SSM parameter for the REST API base URL.
|
|
7375
|
-
* Look up via {@link OpenHiRestApiService.restApiBaseUrlFromConstruct}.
|
|
7376
|
-
* Override to customize.
|
|
7377
|
-
*/
|
|
7378
|
-
createRestApiBaseUrlParameter(apiDomainName) {
|
|
7379
|
-
const restApiBaseUrl = `https://${apiDomainName}`;
|
|
7380
|
-
new DiscoverableStringParameter(this, "rest-api-base-url-param", {
|
|
7381
|
-
ssmParamName: REST_API_BASE_URL_SSM_NAME,
|
|
7382
|
-
stringValue: restApiBaseUrl,
|
|
7383
|
-
description: "REST API base URL for this deployment (E2E, scripts)"
|
|
7384
|
-
});
|
|
7385
|
-
}
|
|
7386
|
-
/**
|
|
7387
|
-
* Creates the SSM parameter exposing the REST API's custom domain (bare
|
|
7388
|
-
* hostname, no scheme). Consumed by the website service as the CloudFront
|
|
7389
|
-
* `/api/*` origin host.
|
|
7390
|
-
* Look up via {@link OpenHiRestApiService.restApiDomainNameFromConstruct}.
|
|
7391
|
-
* Override to customize.
|
|
7392
|
-
*/
|
|
7393
|
-
createRestApiDomainNameParameter(apiDomainName) {
|
|
7394
|
-
new DiscoverableStringParameter(this, "rest-api-domain-name-param", {
|
|
7395
|
-
ssmParamName: REST_API_DOMAIN_NAME_SSM_NAME,
|
|
7396
|
-
stringValue: apiDomainName,
|
|
7397
|
-
description: "REST API custom domain name (bare hostname) for cross-stack CloudFront origin lookup"
|
|
7398
|
-
});
|
|
7399
|
-
}
|
|
7400
|
-
/**
|
|
7401
|
-
* Creates the API Gateway custom domain name resource.
|
|
7402
|
-
* Override to customize.
|
|
7403
|
-
*/
|
|
7404
|
-
createDomainName(_hostedZone, certificate) {
|
|
7405
|
-
const apiDomainName = this.createApiDomainNameString(_hostedZone);
|
|
7406
|
-
return new import_aws_apigatewayv22.DomainName(this, "domain", {
|
|
7407
|
-
domainName: apiDomainName,
|
|
7408
|
-
certificate
|
|
7021
|
+
createDomainName(_hostedZone, certificate) {
|
|
7022
|
+
const apiDomainName = this.createApiDomainNameString(_hostedZone);
|
|
7023
|
+
return new import_aws_apigatewayv22.DomainName(this, "domain", {
|
|
7024
|
+
domainName: apiDomainName,
|
|
7025
|
+
certificate
|
|
7409
7026
|
});
|
|
7410
7027
|
}
|
|
7411
7028
|
/**
|
|
@@ -7430,8 +7047,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7430
7047
|
extraEnvironment
|
|
7431
7048
|
});
|
|
7432
7049
|
lambda.addToRolePolicy(
|
|
7433
|
-
new
|
|
7434
|
-
effect:
|
|
7050
|
+
new import_aws_iam5.PolicyStatement({
|
|
7051
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7435
7052
|
actions: [
|
|
7436
7053
|
"rds-data:ExecuteStatement",
|
|
7437
7054
|
"rds-data:BatchExecuteStatement"
|
|
@@ -7440,8 +7057,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7440
7057
|
})
|
|
7441
7058
|
);
|
|
7442
7059
|
lambda.addToRolePolicy(
|
|
7443
|
-
new
|
|
7444
|
-
effect:
|
|
7060
|
+
new import_aws_iam5.PolicyStatement({
|
|
7061
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7445
7062
|
actions: ["secretsmanager:GetSecretValue"],
|
|
7446
7063
|
resources: [postgresSecretArn]
|
|
7447
7064
|
})
|
|
@@ -7459,15 +7076,15 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7459
7076
|
];
|
|
7460
7077
|
dataStoreTable.grant(lambda, ...dynamoActions);
|
|
7461
7078
|
lambda.addToRolePolicy(
|
|
7462
|
-
new
|
|
7463
|
-
effect:
|
|
7079
|
+
new import_aws_iam5.PolicyStatement({
|
|
7080
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7464
7081
|
actions: [...dynamoActions],
|
|
7465
7082
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
7466
7083
|
})
|
|
7467
7084
|
);
|
|
7468
7085
|
lambda.addToRolePolicy(
|
|
7469
|
-
new
|
|
7470
|
-
effect:
|
|
7086
|
+
new import_aws_iam5.PolicyStatement({
|
|
7087
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7471
7088
|
actions: [
|
|
7472
7089
|
"ssm:GetParameter",
|
|
7473
7090
|
"ssm:GetParameters",
|
|
@@ -7548,11 +7165,18 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7548
7165
|
const { corsPreflight: cors, ...restRootHttpApiProps } = this.props.rootHttpApiProps ?? {};
|
|
7549
7166
|
const isNonProd = this.ohEnv.ohStage.stageType !== import_config5.OPEN_HI_STAGE.PROD;
|
|
7550
7167
|
const callerOrigins = cors?.allowOrigins ?? [];
|
|
7551
|
-
const
|
|
7552
|
-
const
|
|
7168
|
+
const autoOrigins = this.resolveAutoInjectedCorsOrigins();
|
|
7169
|
+
const mergedOrigins = Array.from(
|
|
7170
|
+
/* @__PURE__ */ new Set([
|
|
7171
|
+
...callerOrigins,
|
|
7172
|
+
...autoOrigins,
|
|
7173
|
+
...isNonProd ? DEV_CORS_ALLOW_ORIGINS : []
|
|
7174
|
+
])
|
|
7175
|
+
);
|
|
7176
|
+
const corsPreflight = this.buildCorsPreflightOptions(mergedOrigins, cors);
|
|
7553
7177
|
const rootHttpApi = new RootHttpApi(this, {
|
|
7554
7178
|
...restRootHttpApiProps,
|
|
7555
|
-
|
|
7179
|
+
corsPreflight,
|
|
7556
7180
|
defaultDomainMapping: {
|
|
7557
7181
|
domainName,
|
|
7558
7182
|
mappingKey: void 0
|
|
@@ -7566,6 +7190,33 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7566
7190
|
});
|
|
7567
7191
|
return rootHttpApi;
|
|
7568
7192
|
}
|
|
7193
|
+
/**
|
|
7194
|
+
* Returns the admin-console and marketing-website origins this REST API
|
|
7195
|
+
* stack should accept by default, composed from the same branch context
|
|
7196
|
+
* the website service will see at synth time. Both hostnames are
|
|
7197
|
+
* `https://`-only — they always resolve to real DNS records.
|
|
7198
|
+
*
|
|
7199
|
+
* Auto-injected on every stage (no `isNonProd` gate) so the admin SPA can
|
|
7200
|
+
* call the API cross-origin without the caller having to predict the
|
|
7201
|
+
* per-deploy hostname. Override to customize the auto-injected set.
|
|
7202
|
+
*/
|
|
7203
|
+
resolveAutoInjectedCorsOrigins() {
|
|
7204
|
+
const zoneName = this.props.config.zoneName;
|
|
7205
|
+
const adminHost = OpenHiWebsiteService.composeFullDomain({
|
|
7206
|
+
domainPrefix: ADMIN_DOMAIN_PREFIX,
|
|
7207
|
+
branchName: this.branchName,
|
|
7208
|
+
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
7209
|
+
childZonePrefix: this.childZonePrefix,
|
|
7210
|
+
zoneName
|
|
7211
|
+
});
|
|
7212
|
+
const websiteHost = OpenHiWebsiteService.composeFullDomain({
|
|
7213
|
+
branchName: this.branchName,
|
|
7214
|
+
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
7215
|
+
childZonePrefix: this.childZonePrefix,
|
|
7216
|
+
zoneName
|
|
7217
|
+
});
|
|
7218
|
+
return [`https://${adminHost}`, `https://${websiteHost}`];
|
|
7219
|
+
}
|
|
7569
7220
|
/**
|
|
7570
7221
|
* Builds the full `CorsPreflightOptions` from a merged origins array,
|
|
7571
7222
|
* filling defaults for `allowMethods`/`allowHeaders`/`allowCredentials`/
|
|
@@ -7585,7 +7236,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7585
7236
|
],
|
|
7586
7237
|
allowHeaders: cors?.allowHeaders ?? ["Content-Type", "Authorization"],
|
|
7587
7238
|
allowCredentials: cors?.allowCredentials ?? true,
|
|
7588
|
-
maxAge: cors?.maxAge ??
|
|
7239
|
+
maxAge: cors?.maxAge ?? import_core.Duration.days(1),
|
|
7589
7240
|
...cors?.exposeHeaders !== void 0 && {
|
|
7590
7241
|
exposeHeaders: cors.exposeHeaders
|
|
7591
7242
|
}
|
|
@@ -7603,7 +7254,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7603
7254
|
* client-side from `window.location.origin`.
|
|
7604
7255
|
*/
|
|
7605
7256
|
resolveRuntimeConfigEnvVars() {
|
|
7606
|
-
const cognitoScope = new
|
|
7257
|
+
const cognitoScope = new import_constructs19.Construct(this, "runtime-config");
|
|
7607
7258
|
const userPool = OpenHiAuthService.userPoolFromConstruct(cognitoScope);
|
|
7608
7259
|
const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(cognitoScope);
|
|
7609
7260
|
const cognitoDomainUrl = OpenHiAuthService.userPoolDomainBaseUrlFromConstruct(cognitoScope);
|
|
@@ -7623,330 +7274,774 @@ _OpenHiRestApiService.SERVICE_TYPE = "rest-api";
|
|
|
7623
7274
|
_OpenHiRestApiService.API_DOMAIN_PREFIX = "api";
|
|
7624
7275
|
var OpenHiRestApiService = _OpenHiRestApiService;
|
|
7625
7276
|
|
|
7626
|
-
// src/services/open-hi-
|
|
7627
|
-
var
|
|
7628
|
-
var
|
|
7277
|
+
// src/services/open-hi-website-service.ts
|
|
7278
|
+
var SSM_PARAM_NAME_FULL_DOMAIN = "WEBSITE_FULL_DOMAIN";
|
|
7279
|
+
var ADMIN_DOMAIN_PREFIX = "admin";
|
|
7280
|
+
var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
|
|
7629
7281
|
/**
|
|
7630
|
-
*
|
|
7631
|
-
*
|
|
7282
|
+
* Compose the website's full per-deploy domain. Thin wrapper over
|
|
7283
|
+
* {@link OpenHiService.composeServiceDomain} that fills in
|
|
7284
|
+
* {@link DEFAULT_DOMAIN_PREFIX} when `domainPrefix` is omitted.
|
|
7285
|
+
*
|
|
7286
|
+
* Use from sibling stacks that need to predict the website's hostname
|
|
7287
|
+
* before the website stack is synthesised — e.g. the REST API stack
|
|
7288
|
+
* computing its CORS `allowOrigins` for the admin-console.
|
|
7632
7289
|
*/
|
|
7633
|
-
static
|
|
7634
|
-
return
|
|
7290
|
+
static composeFullDomain(opts) {
|
|
7291
|
+
return OpenHiService.composeServiceDomain({
|
|
7292
|
+
...opts,
|
|
7293
|
+
domainPrefix: opts.domainPrefix ?? _OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX
|
|
7294
|
+
});
|
|
7635
7295
|
}
|
|
7636
|
-
|
|
7637
|
-
|
|
7296
|
+
/**
|
|
7297
|
+
* Looks up the static-hosting bucket ARN published by the release-branch
|
|
7298
|
+
* deploy of this service.
|
|
7299
|
+
*/
|
|
7300
|
+
static bucketArnFromConstruct(scope) {
|
|
7301
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7302
|
+
ssmParamName: StaticHosting.SSM_PARAM_NAME_BUCKET_ARN,
|
|
7303
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7304
|
+
});
|
|
7305
|
+
}
|
|
7306
|
+
/**
|
|
7307
|
+
* Looks up the CloudFront distribution ARN published by the release-branch
|
|
7308
|
+
* deploy of this service.
|
|
7309
|
+
*/
|
|
7310
|
+
static distributionArnFromConstruct(scope) {
|
|
7311
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7312
|
+
ssmParamName: StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_ARN,
|
|
7313
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7314
|
+
});
|
|
7315
|
+
}
|
|
7316
|
+
/**
|
|
7317
|
+
* Looks up the CloudFront distribution domain
|
|
7318
|
+
* (e.g. dXXXXX.cloudfront.net) published by the release-branch deploy.
|
|
7319
|
+
*/
|
|
7320
|
+
static distributionDomainFromConstruct(scope) {
|
|
7321
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7322
|
+
ssmParamName: StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_DOMAIN,
|
|
7323
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7324
|
+
});
|
|
7325
|
+
}
|
|
7326
|
+
/**
|
|
7327
|
+
* Looks up the CloudFront distribution ID published by the release-branch
|
|
7328
|
+
* deploy of this service.
|
|
7329
|
+
*/
|
|
7330
|
+
static distributionIdFromConstruct(scope) {
|
|
7331
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7332
|
+
ssmParamName: StaticHosting.SSM_PARAM_NAME_DISTRIBUTION_ID,
|
|
7333
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7334
|
+
});
|
|
7335
|
+
}
|
|
7336
|
+
/**
|
|
7337
|
+
* Looks up the website's full domain (e.g. www.example.com) published by
|
|
7338
|
+
* the release-branch deploy of this service.
|
|
7339
|
+
*/
|
|
7340
|
+
static fullDomainFromConstruct(scope) {
|
|
7341
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7342
|
+
ssmParamName: SSM_PARAM_NAME_FULL_DOMAIN,
|
|
7343
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7344
|
+
});
|
|
7345
|
+
}
|
|
7346
|
+
get serviceType() {
|
|
7347
|
+
return _OpenHiWebsiteService.SERVICE_TYPE;
|
|
7348
|
+
}
|
|
7349
|
+
constructor(ohEnv, props) {
|
|
7350
|
+
super(ohEnv, _OpenHiWebsiteService.SERVICE_TYPE, props);
|
|
7351
|
+
this.props = props;
|
|
7352
|
+
this.validateConfig(props);
|
|
7353
|
+
const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
|
|
7354
|
+
const hostedZone = this.createHostedZone();
|
|
7355
|
+
this.fullDomain = this.computeFullDomain(hostedZone);
|
|
7356
|
+
const shouldCreateHostingInfra = props.createHostingInfrastructure ?? isReleaseBranch;
|
|
7357
|
+
if (shouldCreateHostingInfra) {
|
|
7358
|
+
const certificate = this.createCertificate();
|
|
7359
|
+
this.staticHosting = this.createStaticHosting({
|
|
7360
|
+
certificate,
|
|
7361
|
+
hostedZone
|
|
7362
|
+
});
|
|
7363
|
+
this.createFullDomainParameter();
|
|
7364
|
+
} else if (!isReleaseBranch) {
|
|
7365
|
+
this.perBranchHostname = this.createPerBranchHostname(hostedZone);
|
|
7366
|
+
}
|
|
7367
|
+
if (props.createStaticContent !== false) {
|
|
7368
|
+
const bucket = this.resolveStaticHostingBucket();
|
|
7369
|
+
this.staticContent = this.createStaticContent(bucket);
|
|
7370
|
+
}
|
|
7371
|
+
}
|
|
7372
|
+
/**
|
|
7373
|
+
* Validates that config required for the website stack is present.
|
|
7374
|
+
*/
|
|
7375
|
+
validateConfig(props) {
|
|
7376
|
+
const { config } = props;
|
|
7377
|
+
if (!config) {
|
|
7378
|
+
throw new Error("Config is required");
|
|
7379
|
+
}
|
|
7380
|
+
if (!config.zoneName) {
|
|
7381
|
+
throw new Error("Zone name is required");
|
|
7382
|
+
}
|
|
7383
|
+
if (!config.hostedZoneId) {
|
|
7384
|
+
throw new Error("Hosted zone ID is required to import the website zone");
|
|
7385
|
+
}
|
|
7386
|
+
}
|
|
7387
|
+
/**
|
|
7388
|
+
* Imports the website's hosted zone from config attributes (no SSM lookup).
|
|
7389
|
+
* The website attaches DNS records here on the release-branch deploy and
|
|
7390
|
+
* the same zone is imported on feature-branch deploys for any sub-domain
|
|
7391
|
+
* routing.
|
|
7392
|
+
* Override to customize.
|
|
7393
|
+
*/
|
|
7394
|
+
createHostedZone() {
|
|
7395
|
+
return OpenHiGlobalService.rootHostedZoneFromConstruct(this, {
|
|
7396
|
+
zoneName: this.config.zoneName,
|
|
7397
|
+
hostedZoneId: this.config.hostedZoneId
|
|
7398
|
+
});
|
|
7399
|
+
}
|
|
7400
|
+
/**
|
|
7401
|
+
* Returns the wildcard certificate looked up from the Global service.
|
|
7402
|
+
* Override to customize.
|
|
7403
|
+
*/
|
|
7404
|
+
createCertificate() {
|
|
7405
|
+
return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);
|
|
7406
|
+
}
|
|
7407
|
+
/**
|
|
7408
|
+
* Computes the full website domain from `domainPrefix`,
|
|
7409
|
+
* `childZonePrefix`, and the child zone name. Release-branch deploys
|
|
7410
|
+
* serve at `\<domainPrefix\>.\<zone\>` (e.g. `admin.dev.openhi.org`);
|
|
7411
|
+
* every other deploy serves a per-PR preview at
|
|
7412
|
+
* `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>`
|
|
7413
|
+
* (e.g. `admin-feat-1093-patient-migration.dev.openhi.org`).
|
|
7414
|
+
*
|
|
7415
|
+
* Delegates to {@link OpenHiWebsiteService.composeFullDomain} so the
|
|
7416
|
+
* release-vs-feature composition stays in one place.
|
|
7417
|
+
*/
|
|
7418
|
+
computeFullDomain(hostedZone) {
|
|
7419
|
+
return _OpenHiWebsiteService.composeFullDomain({
|
|
7420
|
+
domainPrefix: this.props.domainPrefix,
|
|
7421
|
+
branchName: this.branchName,
|
|
7422
|
+
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
7423
|
+
childZonePrefix: this.childZonePrefix,
|
|
7424
|
+
zoneName: hostedZone.zoneName
|
|
7425
|
+
});
|
|
7426
|
+
}
|
|
7427
|
+
/**
|
|
7428
|
+
* Returns the sub-domain label (left of the zone) for the current
|
|
7429
|
+
* deploy. Used for the per-branch S3 key prefix passed to
|
|
7430
|
+
* {@link StaticContent} so the upload prefix always matches the
|
|
7431
|
+
* served hostname.
|
|
7432
|
+
*
|
|
7433
|
+
* Non-release deploys compose the per-PR slug as
|
|
7434
|
+
* `\<domainPrefix\>-\<childZonePrefix\>`, mirroring the REST API's
|
|
7435
|
+
* `api-\<childZonePrefix\>` convention. When `domainPrefix` is `admin`
|
|
7436
|
+
* (the only consumer today), the resulting sub-domain starts with
|
|
7437
|
+
* {@link PER_BRANCH_PREVIEW_PREFIX}, so the per-PR S3 key prefix
|
|
7438
|
+
* matches what `StaticHosting`'s lifecycle rule expires.
|
|
7439
|
+
*/
|
|
7440
|
+
computeSubDomain() {
|
|
7441
|
+
const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
|
|
7442
|
+
const domainPrefix = this.props.domainPrefix ?? _OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX;
|
|
7443
|
+
if (isReleaseBranch) {
|
|
7444
|
+
return domainPrefix;
|
|
7445
|
+
}
|
|
7446
|
+
return `${domainPrefix}-${this.childZonePrefix}`;
|
|
7447
|
+
}
|
|
7448
|
+
/**
|
|
7449
|
+
* Creates the StaticHosting infrastructure (bucket + distribution +
|
|
7450
|
+
* Lambda@Edge + 4 SSM params + DNS). The release-branch distribution
|
|
7451
|
+
* adds `*.\<zone\>` as a wildcard alt-name on top of the canonical
|
|
7452
|
+
* hostname so per-PR previews resolve via the same distribution.
|
|
7453
|
+
*
|
|
7454
|
+
* The bucket carries an S3 lifecycle rule that expires per-PR
|
|
7455
|
+
* preview content (keys under {@link PER_BRANCH_PREVIEW_PREFIX})
|
|
7456
|
+
* on non-production stages. PROD never gets the rule — see
|
|
7457
|
+
* `enablePreviewLifecycle`.
|
|
7458
|
+
*/
|
|
7459
|
+
createStaticHosting(deps) {
|
|
7460
|
+
const restApi = this.props.restApi === true ? this.resolveRestApi() : void 0;
|
|
7461
|
+
const wildcardSan = `*.${deps.hostedZone.zoneName}`;
|
|
7462
|
+
return new StaticHosting(this, "static-hosting", {
|
|
7463
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE,
|
|
7464
|
+
certificate: deps.certificate,
|
|
7465
|
+
hostedZone: deps.hostedZone,
|
|
7466
|
+
domainNames: [this.fullDomain, wildcardSan],
|
|
7467
|
+
description: `OpenHI website (${this.fullDomain})`,
|
|
7468
|
+
prefixPattern: PER_BRANCH_PREVIEW_PREFIX,
|
|
7469
|
+
enablePreviewLifecycle: this.ohEnv.ohStage.stageType !== import_config6.OPEN_HI_STAGE.PROD,
|
|
7470
|
+
...restApi !== void 0 && { restApi }
|
|
7471
|
+
});
|
|
7472
|
+
}
|
|
7473
|
+
/**
|
|
7474
|
+
* Resolves the REST API custom-domain hostname from the rest-api stack's
|
|
7475
|
+
* `REST_API_DOMAIN_NAME` SSM parameter. Wrapped in a private method so
|
|
7476
|
+
* it can be overridden / stubbed in subclasses and tests.
|
|
7477
|
+
*/
|
|
7478
|
+
resolveRestApi() {
|
|
7479
|
+
return {
|
|
7480
|
+
domainName: OpenHiRestApiService.restApiDomainNameFromConstruct(this)
|
|
7481
|
+
};
|
|
7482
|
+
}
|
|
7483
|
+
/**
|
|
7484
|
+
* Creates the SSM parameter that publishes the website's full domain.
|
|
7485
|
+
* Look up via {@link OpenHiWebsiteService.fullDomainFromConstruct}.
|
|
7486
|
+
*/
|
|
7487
|
+
createFullDomainParameter() {
|
|
7488
|
+
new DiscoverableStringParameter(this, "full-domain-param", {
|
|
7489
|
+
ssmParamName: SSM_PARAM_NAME_FULL_DOMAIN,
|
|
7490
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE,
|
|
7491
|
+
stringValue: this.fullDomain,
|
|
7492
|
+
description: "Full website domain (e.g. www.example.com)"
|
|
7493
|
+
});
|
|
7494
|
+
}
|
|
7495
|
+
/**
|
|
7496
|
+
* Creates the StaticContent uploader. Receives the resolved static-hosting
|
|
7497
|
+
* bucket from the constructor — on the release-branch deploy this is the
|
|
7498
|
+
* just-created {@link staticHosting} bucket (no SSM round-trip within a
|
|
7499
|
+
* single stack); on every other deploy it is imported from the bucket ARN
|
|
7500
|
+
* the release-branch deploy publishes to SSM, addressed against
|
|
7501
|
+
* {@link OpenHiService.releaseBranchHash}. See
|
|
7502
|
+
* {@link resolveStaticHostingBucket}.
|
|
7503
|
+
*
|
|
7504
|
+
* The S3 key prefix is `\<sub-domain\>.\<zone\>/\<contentDest\>` so the
|
|
7505
|
+
* upload location matches the Host-header-derived folder the Lambda@Edge
|
|
7506
|
+
* viewer-request handler prepends. Passing the zone name (rather than
|
|
7507
|
+
* `this.fullDomain`) for the `fullDomain` prop keeps the prefix flat —
|
|
7508
|
+
* `admin-feat-foo.dev.openhi.org/`, not
|
|
7509
|
+
* `admin-feat-foo.admin.dev.openhi.org/`.
|
|
7510
|
+
*/
|
|
7511
|
+
createStaticContent(bucket) {
|
|
7512
|
+
const { contentSourceDirectory, contentDestinationDirectory } = this.props;
|
|
7513
|
+
return new StaticContent(this, "static-content", {
|
|
7514
|
+
bucket,
|
|
7515
|
+
contentSourceDirectory,
|
|
7516
|
+
contentDestinationDirectory,
|
|
7517
|
+
subDomain: this.computeSubDomain(),
|
|
7518
|
+
fullDomain: this.config.zoneName
|
|
7519
|
+
});
|
|
7520
|
+
}
|
|
7521
|
+
/**
|
|
7522
|
+
* Creates the per-PR `PerBranchHostname` alias record on non-release
|
|
7523
|
+
* branch deploys. The record points
|
|
7524
|
+
* `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>` at the release-branch
|
|
7525
|
+
* CloudFront distribution (resolved from SSM against
|
|
7526
|
+
* {@link OpenHiService.releaseBranchHash}).
|
|
7527
|
+
*/
|
|
7528
|
+
createPerBranchHostname(hostedZone) {
|
|
7529
|
+
return new PerBranchHostname(this, "per-branch-hostname", {
|
|
7530
|
+
hostname: this.fullDomain,
|
|
7531
|
+
hostedZone,
|
|
7532
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7533
|
+
});
|
|
7534
|
+
}
|
|
7535
|
+
/**
|
|
7536
|
+
* Returns an {@link IBucket} pointing at the static-hosting bucket the
|
|
7537
|
+
* uploaders write to. On the release-branch deploy this is the bucket
|
|
7538
|
+
* just provisioned by {@link staticHosting}; on every other deploy it's
|
|
7539
|
+
* imported from the bucket ARN the release-branch deploy publishes to
|
|
7540
|
+
* SSM, addressed against {@link OpenHiService.releaseBranchHash}.
|
|
7541
|
+
*/
|
|
7542
|
+
resolveStaticHostingBucket() {
|
|
7543
|
+
if (this.staticHosting) {
|
|
7544
|
+
return this.staticHosting.bucket;
|
|
7545
|
+
}
|
|
7546
|
+
const bucketArn = DiscoverableStringParameter.valueForLookupName(this, {
|
|
7547
|
+
ssmParamName: StaticHosting.SSM_PARAM_NAME_BUCKET_ARN,
|
|
7548
|
+
serviceType: _OpenHiWebsiteService.SERVICE_TYPE,
|
|
7549
|
+
branchHash: this.releaseBranchHash
|
|
7550
|
+
});
|
|
7551
|
+
return import_aws_s32.Bucket.fromBucketArn(this, "shared-bucket", bucketArn);
|
|
7552
|
+
}
|
|
7553
|
+
};
|
|
7554
|
+
_OpenHiWebsiteService.SERVICE_TYPE = "website";
|
|
7555
|
+
/**
|
|
7556
|
+
* Default `domainPrefix` for this service when none is supplied.
|
|
7557
|
+
* Release-branch hostname is `www.<zone>`; per-PR preview hostname is
|
|
7558
|
+
* `www-<childZonePrefix>.<zone>`.
|
|
7559
|
+
*/
|
|
7560
|
+
_OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX = "www";
|
|
7561
|
+
var OpenHiWebsiteService = _OpenHiWebsiteService;
|
|
7562
|
+
|
|
7563
|
+
// src/workflows/control-plane/user-onboarding/events.ts
|
|
7564
|
+
var USER_ONBOARDING_EVENT_SOURCE = "openhi.control.user-onboarding";
|
|
7565
|
+
var PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE = "ProvisionDefaultWorkspaceRequested";
|
|
7566
|
+
var buildProvisionDefaultWorkspaceRequestedDetail = (event) => {
|
|
7567
|
+
const attrs = event.request?.userAttributes ?? {};
|
|
7568
|
+
const cognitoSub = attrs.sub?.trim();
|
|
7569
|
+
if (!cognitoSub) {
|
|
7570
|
+
return void 0;
|
|
7571
|
+
}
|
|
7572
|
+
const email = attrs.email?.trim();
|
|
7573
|
+
const displayName = email || event.userName || cognitoSub;
|
|
7574
|
+
return {
|
|
7575
|
+
cognitoSub,
|
|
7576
|
+
...email ? { email } : {},
|
|
7577
|
+
displayName,
|
|
7578
|
+
trigger: {
|
|
7579
|
+
source: "cognito.post-confirmation",
|
|
7580
|
+
triggerSource: event.triggerSource,
|
|
7581
|
+
userPoolId: event.userPoolId,
|
|
7582
|
+
userName: event.userName,
|
|
7583
|
+
clientId: event.callerContext?.clientId
|
|
7584
|
+
}
|
|
7585
|
+
};
|
|
7586
|
+
};
|
|
7587
|
+
|
|
7588
|
+
// src/workflows/control-plane/user-onboarding/provision-default-workspace-lambda.ts
|
|
7589
|
+
var import_node_fs11 = __toESM(require("fs"));
|
|
7590
|
+
var import_node_path11 = __toESM(require("path"));
|
|
7591
|
+
var import_aws_cdk_lib15 = require("aws-cdk-lib");
|
|
7592
|
+
var import_aws_events8 = require("aws-cdk-lib/aws-events");
|
|
7593
|
+
var import_aws_events_targets4 = require("aws-cdk-lib/aws-events-targets");
|
|
7594
|
+
var import_aws_iam6 = require("aws-cdk-lib/aws-iam");
|
|
7595
|
+
var import_aws_lambda12 = require("aws-cdk-lib/aws-lambda");
|
|
7596
|
+
var import_aws_lambda_nodejs12 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
7597
|
+
var import_constructs20 = require("constructs");
|
|
7598
|
+
var HANDLER_NAME11 = "provision-default-workspace.handler.js";
|
|
7599
|
+
function resolveHandlerEntry11(dirname) {
|
|
7600
|
+
const sameDir = import_node_path11.default.join(dirname, HANDLER_NAME11);
|
|
7601
|
+
if (import_node_fs11.default.existsSync(sameDir)) {
|
|
7602
|
+
return sameDir;
|
|
7603
|
+
}
|
|
7604
|
+
return import_node_path11.default.join(dirname, "..", "..", "..", "..", "lib", HANDLER_NAME11);
|
|
7605
|
+
}
|
|
7606
|
+
var ProvisionDefaultWorkspaceLambda = class extends import_constructs20.Construct {
|
|
7607
|
+
constructor(scope, props) {
|
|
7608
|
+
super(scope, "provision-default-workspace-lambda");
|
|
7609
|
+
this.lambda = new import_aws_lambda_nodejs12.NodejsFunction(this, "handler", {
|
|
7610
|
+
entry: resolveHandlerEntry11(__dirname),
|
|
7611
|
+
runtime: import_aws_lambda12.Runtime.NODEJS_LATEST,
|
|
7612
|
+
memorySize: 1024,
|
|
7613
|
+
environment: {
|
|
7614
|
+
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName
|
|
7615
|
+
}
|
|
7616
|
+
});
|
|
7617
|
+
props.dataStoreTable.grant(
|
|
7618
|
+
this.lambda,
|
|
7619
|
+
"dynamodb:PutItem",
|
|
7620
|
+
"dynamodb:UpdateItem"
|
|
7621
|
+
);
|
|
7622
|
+
this.lambda.addToRolePolicy(
|
|
7623
|
+
new import_aws_iam6.PolicyStatement({
|
|
7624
|
+
effect: import_aws_iam6.Effect.ALLOW,
|
|
7625
|
+
actions: ["dynamodb:Query"],
|
|
7626
|
+
resources: [`${props.dataStoreTable.tableArn}/index/*`]
|
|
7627
|
+
})
|
|
7628
|
+
);
|
|
7629
|
+
this.rule = new import_aws_events8.Rule(this, "rule", {
|
|
7630
|
+
eventBus: props.controlEventBus,
|
|
7631
|
+
eventPattern: {
|
|
7632
|
+
source: [USER_ONBOARDING_EVENT_SOURCE],
|
|
7633
|
+
detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE]
|
|
7634
|
+
},
|
|
7635
|
+
targets: [
|
|
7636
|
+
new import_aws_events_targets4.LambdaFunction(this.lambda, {
|
|
7637
|
+
retryAttempts: 2,
|
|
7638
|
+
maxEventAge: import_aws_cdk_lib15.Duration.hours(2)
|
|
7639
|
+
})
|
|
7640
|
+
]
|
|
7641
|
+
});
|
|
7642
|
+
}
|
|
7643
|
+
};
|
|
7644
|
+
|
|
7645
|
+
// src/workflows/control-plane/user-onboarding/user-onboarding-workflow.ts
|
|
7646
|
+
var import_constructs21 = require("constructs");
|
|
7647
|
+
var UserOnboardingWorkflow = class extends import_constructs21.Construct {
|
|
7648
|
+
constructor(scope, props) {
|
|
7649
|
+
super(scope, "user-onboarding-workflow");
|
|
7650
|
+
this.provisionDefaultWorkspace = new ProvisionDefaultWorkspaceLambda(this, {
|
|
7651
|
+
dataStoreTable: props.dataStoreTable,
|
|
7652
|
+
controlEventBus: props.controlEventBus
|
|
7653
|
+
});
|
|
7638
7654
|
}
|
|
7655
|
+
};
|
|
7656
|
+
|
|
7657
|
+
// src/services/open-hi-auth-service.ts
|
|
7658
|
+
var LOCALHOST_OAUTH_CALLBACK_URLS = [
|
|
7659
|
+
"http://localhost:3000/oauth/callback",
|
|
7660
|
+
"https://localhost:3000/oauth/callback"
|
|
7661
|
+
];
|
|
7662
|
+
var LOCALHOST_OAUTH_LOGOUT_URLS = [
|
|
7663
|
+
"http://localhost:3000/oauth/logout",
|
|
7664
|
+
"https://localhost:3000/oauth/logout"
|
|
7665
|
+
];
|
|
7666
|
+
var _OpenHiAuthService = class _OpenHiAuthService extends OpenHiService {
|
|
7639
7667
|
constructor(ohEnv, props = {}) {
|
|
7640
|
-
super(ohEnv,
|
|
7668
|
+
super(ohEnv, _OpenHiAuthService.SERVICE_TYPE, props);
|
|
7669
|
+
/**
|
|
7670
|
+
* Cross-stack reference to the data store table. Cached so repeated
|
|
7671
|
+
* lookups share a single CDK construct id ("dynamo-db-data-store") in
|
|
7672
|
+
* this stack — a second `Table.fromTableName` call under the same scope
|
|
7673
|
+
* would collide.
|
|
7674
|
+
*/
|
|
7675
|
+
this._dataStoreTable = null;
|
|
7676
|
+
this._controlEventBus = null;
|
|
7641
7677
|
this.props = props;
|
|
7642
|
-
this.
|
|
7678
|
+
this.userPoolKmsKey = this.createUserPoolKmsKey();
|
|
7679
|
+
this.preTokenGenerationLambda = this.createPreTokenGenerationLambda();
|
|
7680
|
+
this.postAuthenticationLambda = this.createPostAuthenticationLambda();
|
|
7681
|
+
this.postConfirmationLambda = this.createPostConfirmationLambda();
|
|
7682
|
+
this.userOnboardingWorkflow = this.createUserOnboardingWorkflow();
|
|
7683
|
+
this.userPool = this.createUserPool();
|
|
7684
|
+
this.grantPreTokenGenerationPermissions();
|
|
7685
|
+
this.grantPostAuthenticationPermissions();
|
|
7686
|
+
this.grantPostConfirmationPermissions();
|
|
7687
|
+
this.userPoolClient = this.createUserPoolClient();
|
|
7688
|
+
this.userPoolDomain = this.createUserPoolDomain();
|
|
7643
7689
|
}
|
|
7644
|
-
/**
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
7655
|
-
|
|
7690
|
+
/**
|
|
7691
|
+
* Returns an IUserPool by looking up the Auth stack's User Pool ID from SSM.
|
|
7692
|
+
*/
|
|
7693
|
+
static userPoolFromConstruct(scope) {
|
|
7694
|
+
const userPoolId = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7695
|
+
ssmParamName: CognitoUserPool.SSM_PARAM_NAME,
|
|
7696
|
+
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
7697
|
+
});
|
|
7698
|
+
return import_aws_cognito4.UserPool.fromUserPoolId(scope, "user-pool", userPoolId);
|
|
7699
|
+
}
|
|
7700
|
+
/**
|
|
7701
|
+
* Returns an IUserPoolClient by looking up the Auth stack's User Pool Client ID from SSM.
|
|
7702
|
+
*/
|
|
7703
|
+
static userPoolClientFromConstruct(scope) {
|
|
7704
|
+
const userPoolClientId = DiscoverableStringParameter.valueForLookupName(
|
|
7705
|
+
scope,
|
|
7706
|
+
{
|
|
7707
|
+
ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,
|
|
7708
|
+
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
7656
7709
|
}
|
|
7710
|
+
);
|
|
7711
|
+
return import_aws_cognito4.UserPoolClient.fromUserPoolClientId(
|
|
7712
|
+
scope,
|
|
7713
|
+
"user-pool-client",
|
|
7714
|
+
userPoolClientId
|
|
7715
|
+
);
|
|
7716
|
+
}
|
|
7717
|
+
/**
|
|
7718
|
+
* Returns an IUserPoolDomain by looking up the Auth stack's User Pool Domain from SSM.
|
|
7719
|
+
*/
|
|
7720
|
+
static userPoolDomainFromConstruct(scope) {
|
|
7721
|
+
const domainName = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7722
|
+
ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,
|
|
7723
|
+
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
7657
7724
|
});
|
|
7725
|
+
return import_aws_cognito4.UserPoolDomain.fromDomainName(scope, "user-pool-domain", domainName);
|
|
7658
7726
|
}
|
|
7659
|
-
};
|
|
7660
|
-
_OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
|
|
7661
|
-
var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
7662
|
-
|
|
7663
|
-
// src/services/open-hi-website-service.ts
|
|
7664
|
-
var import_config6 = __toESM(require_lib());
|
|
7665
|
-
var import_aws_s32 = require("aws-cdk-lib/aws-s3");
|
|
7666
|
-
var SSM_PARAM_NAME_FULL_DOMAIN = "WEBSITE_FULL_DOMAIN";
|
|
7667
|
-
var ADMIN_DOMAIN_PREFIX = "admin";
|
|
7668
|
-
var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
|
|
7669
7727
|
/**
|
|
7670
|
-
*
|
|
7671
|
-
*
|
|
7672
|
-
*
|
|
7728
|
+
* Returns the full Cognito Hosted UI base URL (e.g.
|
|
7729
|
+
* `https://auth-abc.auth.us-east-2.amazoncognito.com`) by looking up
|
|
7730
|
+
* the Auth stack's User Pool Domain from SSM and composing it with the
|
|
7731
|
+
* calling stack's region.
|
|
7673
7732
|
*
|
|
7674
|
-
*
|
|
7675
|
-
*
|
|
7676
|
-
*
|
|
7733
|
+
* Equivalent to `UserPoolDomain.baseUrl()` on the concrete construct,
|
|
7734
|
+
* but works across stacks where the looked-up `IUserPoolDomain` is an
|
|
7735
|
+
* `Import` and does not carry the `baseUrl()` method. Assumes the
|
|
7736
|
+
* domain was created as a Cognito-managed prefix domain (the only
|
|
7737
|
+
* variant `OpenHiAuthService.createUserPoolDomain` produces).
|
|
7677
7738
|
*/
|
|
7678
|
-
static
|
|
7679
|
-
|
|
7680
|
-
|
|
7681
|
-
|
|
7739
|
+
static userPoolDomainBaseUrlFromConstruct(scope) {
|
|
7740
|
+
const domainName = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7741
|
+
ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,
|
|
7742
|
+
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
7682
7743
|
});
|
|
7744
|
+
const region = import_core2.Stack.of(scope).region;
|
|
7745
|
+
return `https://${domainName}.auth.${region}.amazoncognito.com`;
|
|
7683
7746
|
}
|
|
7684
7747
|
/**
|
|
7685
|
-
*
|
|
7686
|
-
* deploy of this service.
|
|
7748
|
+
* Returns an IKey (KMS) by looking up the Auth stack's User Pool KMS Key ARN from SSM.
|
|
7687
7749
|
*/
|
|
7688
|
-
static
|
|
7689
|
-
|
|
7690
|
-
ssmParamName:
|
|
7691
|
-
serviceType:
|
|
7750
|
+
static userPoolKmsKeyFromConstruct(scope) {
|
|
7751
|
+
const keyArn = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7752
|
+
ssmParamName: CognitoUserPoolKmsKey.SSM_PARAM_NAME,
|
|
7753
|
+
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
7692
7754
|
});
|
|
7755
|
+
return import_aws_kms2.Key.fromKeyArn(scope, "kms-key", keyArn);
|
|
7756
|
+
}
|
|
7757
|
+
get serviceType() {
|
|
7758
|
+
return _OpenHiAuthService.SERVICE_TYPE;
|
|
7693
7759
|
}
|
|
7694
7760
|
/**
|
|
7695
|
-
*
|
|
7696
|
-
*
|
|
7761
|
+
* Creates the KMS key for the Cognito User Pool and exports its ARN to SSM.
|
|
7762
|
+
* Look up via {@link OpenHiAuthService.userPoolKmsKeyFromConstruct}.
|
|
7763
|
+
* Override to customize.
|
|
7697
7764
|
*/
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
7701
|
-
|
|
7765
|
+
createUserPoolKmsKey() {
|
|
7766
|
+
const key = new CognitoUserPoolKmsKey(this);
|
|
7767
|
+
new DiscoverableStringParameter(this, "kms-key-param", {
|
|
7768
|
+
ssmParamName: CognitoUserPoolKmsKey.SSM_PARAM_NAME,
|
|
7769
|
+
stringValue: key.keyArn,
|
|
7770
|
+
description: "KMS key ARN for Cognito User Pool (e.g. custom sender); cross-stack reference"
|
|
7702
7771
|
});
|
|
7772
|
+
return key;
|
|
7703
7773
|
}
|
|
7704
7774
|
/**
|
|
7705
|
-
*
|
|
7706
|
-
*
|
|
7775
|
+
* Creates the Pre Token Generation Lambda (Cognito trigger). On every
|
|
7776
|
+
* sign-in and token refresh the Lambda resolves the User by Cognito `sub`
|
|
7777
|
+
* (GSI2) and injects `ohi_tid`, `ohi_wid`, `ohi_uid`, `ohi_uname` into
|
|
7778
|
+
* both the ID token and the access token (ADR 2026-03-17-01).
|
|
7707
7779
|
*/
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
|
|
7711
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7780
|
+
createPreTokenGenerationLambda() {
|
|
7781
|
+
const construct = new PreTokenGenerationLambda(this, {
|
|
7782
|
+
dynamoTableName: this.dataStoreTable().tableName
|
|
7712
7783
|
});
|
|
7784
|
+
return construct.lambda;
|
|
7713
7785
|
}
|
|
7714
7786
|
/**
|
|
7715
|
-
*
|
|
7716
|
-
*
|
|
7787
|
+
* Creates the Post Authentication Lambda (Cognito trigger). Calls
|
|
7788
|
+
* AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user
|
|
7789
|
+
* sessions per ADR 2026-03-17-01.
|
|
7717
7790
|
*/
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7722
|
-
});
|
|
7791
|
+
createPostAuthenticationLambda() {
|
|
7792
|
+
const construct = new PostAuthenticationLambda(this);
|
|
7793
|
+
return construct.lambda;
|
|
7723
7794
|
}
|
|
7724
7795
|
/**
|
|
7725
|
-
*
|
|
7726
|
-
*
|
|
7796
|
+
* Creates the Post Confirmation Lambda (Cognito trigger). On sign-up
|
|
7797
|
+
* confirmation, publishes a control-plane workflow event; provisioning lives
|
|
7798
|
+
* behind EventBridge.
|
|
7727
7799
|
*/
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
|
|
7731
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7800
|
+
createPostConfirmationLambda() {
|
|
7801
|
+
const construct = new PostConfirmationLambda(this, {
|
|
7802
|
+
controlEventBusName: this.controlEventBus().eventBusName
|
|
7732
7803
|
});
|
|
7804
|
+
return construct.lambda;
|
|
7733
7805
|
}
|
|
7734
|
-
|
|
7735
|
-
return
|
|
7806
|
+
createUserOnboardingWorkflow() {
|
|
7807
|
+
return new UserOnboardingWorkflow(this, {
|
|
7808
|
+
controlEventBus: this.controlEventBus(),
|
|
7809
|
+
dataStoreTable: this.dataStoreTable()
|
|
7810
|
+
});
|
|
7736
7811
|
}
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
|
|
7740
|
-
this.validateConfig(props);
|
|
7741
|
-
const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
|
|
7742
|
-
const hostedZone = this.createHostedZone();
|
|
7743
|
-
this.fullDomain = this.computeFullDomain(hostedZone);
|
|
7744
|
-
const shouldCreateHostingInfra = props.createHostingInfrastructure ?? isReleaseBranch;
|
|
7745
|
-
if (shouldCreateHostingInfra) {
|
|
7746
|
-
const certificate = this.createCertificate();
|
|
7747
|
-
this.staticHosting = this.createStaticHosting({
|
|
7748
|
-
certificate,
|
|
7749
|
-
hostedZone
|
|
7750
|
-
});
|
|
7751
|
-
this.createFullDomainParameter();
|
|
7752
|
-
} else if (!isReleaseBranch) {
|
|
7753
|
-
this.perBranchHostname = this.createPerBranchHostname(hostedZone);
|
|
7754
|
-
}
|
|
7755
|
-
if (props.createStaticContent !== false) {
|
|
7756
|
-
const bucket = this.resolveStaticHostingBucket();
|
|
7757
|
-
this.staticContent = this.createStaticContent(bucket);
|
|
7812
|
+
dataStoreTable() {
|
|
7813
|
+
if (this._dataStoreTable === null) {
|
|
7814
|
+
this._dataStoreTable = OpenHiDataService.dynamoDbDataStoreFromConstruct(this);
|
|
7758
7815
|
}
|
|
7816
|
+
return this._dataStoreTable;
|
|
7759
7817
|
}
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
|
|
7763
|
-
validateConfig(props) {
|
|
7764
|
-
const { config } = props;
|
|
7765
|
-
if (!config) {
|
|
7766
|
-
throw new Error("Config is required");
|
|
7767
|
-
}
|
|
7768
|
-
if (!config.zoneName) {
|
|
7769
|
-
throw new Error("Zone name is required");
|
|
7770
|
-
}
|
|
7771
|
-
if (!config.hostedZoneId) {
|
|
7772
|
-
throw new Error("Hosted zone ID is required to import the website zone");
|
|
7818
|
+
controlEventBus() {
|
|
7819
|
+
if (this._controlEventBus === null) {
|
|
7820
|
+
this._controlEventBus = OpenHiGlobalService.controlEventBusFromConstruct(this);
|
|
7773
7821
|
}
|
|
7822
|
+
return this._controlEventBus;
|
|
7774
7823
|
}
|
|
7775
7824
|
/**
|
|
7776
|
-
*
|
|
7777
|
-
*
|
|
7778
|
-
* the same zone is imported on feature-branch deploys for any sub-domain
|
|
7779
|
-
* routing.
|
|
7825
|
+
* Creates the Cognito User Pool and exports its ID to SSM.
|
|
7826
|
+
* Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
|
|
7780
7827
|
* Override to customize.
|
|
7781
7828
|
*/
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
7785
|
-
|
|
7829
|
+
createUserPool() {
|
|
7830
|
+
const userPool = new CognitoUserPool(this, {
|
|
7831
|
+
...this.props.userPoolProps,
|
|
7832
|
+
customSenderKmsKey: this.userPoolKmsKey
|
|
7833
|
+
});
|
|
7834
|
+
userPool.addTrigger(
|
|
7835
|
+
import_aws_cognito4.UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,
|
|
7836
|
+
this.preTokenGenerationLambda,
|
|
7837
|
+
import_aws_cognito4.LambdaVersion.V2_0
|
|
7838
|
+
);
|
|
7839
|
+
userPool.addTrigger(
|
|
7840
|
+
import_aws_cognito4.UserPoolOperation.POST_AUTHENTICATION,
|
|
7841
|
+
this.postAuthenticationLambda
|
|
7842
|
+
);
|
|
7843
|
+
userPool.addTrigger(
|
|
7844
|
+
import_aws_cognito4.UserPoolOperation.POST_CONFIRMATION,
|
|
7845
|
+
this.postConfirmationLambda
|
|
7846
|
+
);
|
|
7847
|
+
new DiscoverableStringParameter(this, "user-pool-param", {
|
|
7848
|
+
ssmParamName: CognitoUserPool.SSM_PARAM_NAME,
|
|
7849
|
+
stringValue: userPool.userPoolId,
|
|
7850
|
+
description: "Cognito User Pool ID for this Auth stack; cross-stack reference"
|
|
7786
7851
|
});
|
|
7852
|
+
return userPool;
|
|
7787
7853
|
}
|
|
7788
7854
|
/**
|
|
7789
|
-
*
|
|
7790
|
-
*
|
|
7855
|
+
* Grants the Pre Token Generation Lambda read-only access on the data
|
|
7856
|
+
* store table and its GSIs. The Lambda only needs:
|
|
7857
|
+
* - `Query` on GSI2 to resolve a User by Cognito `sub`
|
|
7858
|
+
* - `GetItem` on the base table for direct User reads
|
|
7859
|
+
*
|
|
7860
|
+
* No write or scan access: a User missing `currentTenant`/`currentWorkspace`
|
|
7861
|
+
* falls into the absent-claims path; repair belongs in a separate backfill.
|
|
7791
7862
|
*/
|
|
7792
|
-
|
|
7793
|
-
|
|
7863
|
+
grantPreTokenGenerationPermissions() {
|
|
7864
|
+
const dataStoreTable = this.dataStoreTable();
|
|
7865
|
+
const dynamoActions = ["dynamodb:GetItem", "dynamodb:Query"];
|
|
7866
|
+
dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);
|
|
7867
|
+
this.preTokenGenerationLambda.addToRolePolicy(
|
|
7868
|
+
new import_aws_iam7.PolicyStatement({
|
|
7869
|
+
effect: import_aws_iam7.Effect.ALLOW,
|
|
7870
|
+
actions: [...dynamoActions],
|
|
7871
|
+
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
7872
|
+
})
|
|
7873
|
+
);
|
|
7794
7874
|
}
|
|
7795
7875
|
/**
|
|
7796
|
-
*
|
|
7797
|
-
* `
|
|
7798
|
-
* serve at `\<domainPrefix\>.\<zone\>` (e.g. `admin.dev.openhi.org`);
|
|
7799
|
-
* every other deploy serves a per-PR preview at
|
|
7800
|
-
* `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>`
|
|
7801
|
-
* (e.g. `admin-feat-1093-patient-migration.dev.openhi.org`).
|
|
7876
|
+
* Grants the Post Authentication Lambda permission to call
|
|
7877
|
+
* `cognito-idp:AdminUserGlobalSignOut`.
|
|
7802
7878
|
*
|
|
7803
|
-
*
|
|
7804
|
-
*
|
|
7879
|
+
* Scoped via `Stack.of(this).formatArn` rather than `userPool.userPoolArn`
|
|
7880
|
+
* because the User Pool registers this Lambda as a Post Authentication
|
|
7881
|
+
* trigger, creating the cycle:
|
|
7882
|
+
* userPool → lambda (trigger ARN) → role policy → userPool ARN.
|
|
7883
|
+
* Using `formatArn` avoids referencing the User Pool resource directly
|
|
7884
|
+
* while still scoping to user pools in this account+region. The Lambda
|
|
7885
|
+
* is invoked only by Cognito with a Cognito-provided `event.userPoolId`,
|
|
7886
|
+
* so the runtime target is constrained by the trigger contract.
|
|
7805
7887
|
*/
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
7813
|
-
|
|
7888
|
+
grantPostAuthenticationPermissions() {
|
|
7889
|
+
this.postAuthenticationLambda.addToRolePolicy(
|
|
7890
|
+
new import_aws_iam7.PolicyStatement({
|
|
7891
|
+
actions: ["cognito-idp:AdminUserGlobalSignOut"],
|
|
7892
|
+
resources: [
|
|
7893
|
+
import_core2.Stack.of(this).formatArn({
|
|
7894
|
+
service: "cognito-idp",
|
|
7895
|
+
resource: "userpool",
|
|
7896
|
+
resourceName: "*"
|
|
7897
|
+
})
|
|
7898
|
+
]
|
|
7899
|
+
})
|
|
7900
|
+
);
|
|
7814
7901
|
}
|
|
7815
7902
|
/**
|
|
7816
|
-
*
|
|
7817
|
-
*
|
|
7818
|
-
* {@link StaticContent} so the upload prefix always matches the
|
|
7819
|
-
* served hostname.
|
|
7820
|
-
*
|
|
7821
|
-
* Non-release deploys compose the per-PR slug as
|
|
7822
|
-
* `\<domainPrefix\>-\<childZonePrefix\>`, mirroring the REST API's
|
|
7823
|
-
* `api-\<childZonePrefix\>` convention. When `domainPrefix` is `admin`
|
|
7824
|
-
* (the only consumer today), the resulting sub-domain starts with
|
|
7825
|
-
* {@link PER_BRANCH_PREVIEW_PREFIX}, so the per-PR S3 key prefix
|
|
7826
|
-
* matches what `StaticHosting`'s lifecycle rule expires.
|
|
7903
|
+
* Grants the Post Confirmation Lambda publish-only access to the
|
|
7904
|
+
* control-plane event bus. Workflow Lambdas own DynamoDB writes.
|
|
7827
7905
|
*/
|
|
7828
|
-
|
|
7829
|
-
|
|
7830
|
-
const domainPrefix = this.props.domainPrefix ?? _OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX;
|
|
7831
|
-
if (isReleaseBranch) {
|
|
7832
|
-
return domainPrefix;
|
|
7833
|
-
}
|
|
7834
|
-
return `${domainPrefix}-${this.childZonePrefix}`;
|
|
7906
|
+
grantPostConfirmationPermissions() {
|
|
7907
|
+
this.controlEventBus().grantPutEventsTo(this.postConfirmationLambda);
|
|
7835
7908
|
}
|
|
7836
7909
|
/**
|
|
7837
|
-
* Creates the
|
|
7838
|
-
*
|
|
7839
|
-
*
|
|
7840
|
-
*
|
|
7841
|
-
*
|
|
7842
|
-
* The bucket carries an S3 lifecycle rule that expires per-PR
|
|
7843
|
-
* preview content (keys under {@link PER_BRANCH_PREVIEW_PREFIX})
|
|
7844
|
-
* on non-production stages. PROD never gets the rule — see
|
|
7845
|
-
* `enablePreviewLifecycle`.
|
|
7910
|
+
* Creates the User Pool Client and exports its ID to SSM (AUTH service type).
|
|
7911
|
+
* OAuth flows are enabled with auto-injected callback/logout URLs derived
|
|
7912
|
+
* from this stack's branch context (see {@link resolveOAuthRedirectUrls}).
|
|
7913
|
+
* Look up via {@link OpenHiAuthService.userPoolClientFromConstruct}.
|
|
7914
|
+
* Override to customize.
|
|
7846
7915
|
*/
|
|
7847
|
-
|
|
7848
|
-
const
|
|
7849
|
-
const
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
7858
|
-
|
|
7916
|
+
createUserPoolClient() {
|
|
7917
|
+
const { callbackUrls, logoutUrls } = this.resolveOAuthRedirectUrls();
|
|
7918
|
+
const client = new CognitoUserPoolClient(this, {
|
|
7919
|
+
userPool: this.userPool,
|
|
7920
|
+
oAuth: {
|
|
7921
|
+
flows: {
|
|
7922
|
+
authorizationCodeGrant: true,
|
|
7923
|
+
implicitCodeGrant: true
|
|
7924
|
+
},
|
|
7925
|
+
callbackUrls,
|
|
7926
|
+
logoutUrls
|
|
7927
|
+
}
|
|
7928
|
+
});
|
|
7929
|
+
new DiscoverableStringParameter(this, "user-pool-client-param", {
|
|
7930
|
+
ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,
|
|
7931
|
+
stringValue: client.userPoolClientId,
|
|
7932
|
+
description: "Cognito User Pool Client ID for this Auth stack; cross-stack reference"
|
|
7859
7933
|
});
|
|
7934
|
+
return client;
|
|
7860
7935
|
}
|
|
7861
7936
|
/**
|
|
7862
|
-
*
|
|
7863
|
-
*
|
|
7864
|
-
*
|
|
7937
|
+
* Returns the OAuth `callbackUrls` and `logoutUrls` the Cognito User Pool
|
|
7938
|
+
* Client should accept. Composed from the same branch context the website
|
|
7939
|
+
* service will see at synth time:
|
|
7940
|
+
*
|
|
7941
|
+
* - `https://admin{,-<childZonePrefix>}.<zone>/oauth/{callback,logout}`
|
|
7942
|
+
* - `https://www{,-<childZonePrefix>}.<zone>/oauth/{callback,logout}`
|
|
7943
|
+
*
|
|
7944
|
+
* Both deployed-host pairs are auto-injected on every stage. On non-prod
|
|
7945
|
+
* stages the localhost dev URLs from {@link LOCALHOST_OAUTH_CALLBACK_URLS}
|
|
7946
|
+
* / {@link LOCALHOST_OAUTH_LOGOUT_URLS} join the merge; on prod they are
|
|
7947
|
+
* deliberately excluded.
|
|
7948
|
+
*
|
|
7949
|
+
* If `zoneName` is absent (no-DNS test configurations), the deployed-host
|
|
7950
|
+
* pairs are skipped — only the localhost set survives, and only on
|
|
7951
|
+
* non-prod. Override to customize.
|
|
7865
7952
|
*/
|
|
7866
|
-
|
|
7953
|
+
resolveOAuthRedirectUrls() {
|
|
7954
|
+
const isNonProd = this.ohEnv.ohStage.stageType !== import_config7.OPEN_HI_STAGE.PROD;
|
|
7955
|
+
const zoneName = this.props.config?.zoneName;
|
|
7956
|
+
const deployedOrigins = [];
|
|
7957
|
+
if (zoneName !== void 0) {
|
|
7958
|
+
const adminHost = OpenHiWebsiteService.composeFullDomain({
|
|
7959
|
+
domainPrefix: ADMIN_DOMAIN_PREFIX,
|
|
7960
|
+
branchName: this.branchName,
|
|
7961
|
+
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
7962
|
+
childZonePrefix: this.childZonePrefix,
|
|
7963
|
+
zoneName
|
|
7964
|
+
});
|
|
7965
|
+
const websiteHost = OpenHiWebsiteService.composeFullDomain({
|
|
7966
|
+
branchName: this.branchName,
|
|
7967
|
+
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
7968
|
+
childZonePrefix: this.childZonePrefix,
|
|
7969
|
+
zoneName
|
|
7970
|
+
});
|
|
7971
|
+
deployedOrigins.push(`https://${adminHost}`, `https://${websiteHost}`);
|
|
7972
|
+
}
|
|
7973
|
+
const localhostCallbacks = isNonProd ? LOCALHOST_OAUTH_CALLBACK_URLS : [];
|
|
7974
|
+
const localhostLogouts = isNonProd ? LOCALHOST_OAUTH_LOGOUT_URLS : [];
|
|
7867
7975
|
return {
|
|
7868
|
-
|
|
7976
|
+
callbackUrls: [
|
|
7977
|
+
...deployedOrigins.map((o) => `${o}/oauth/callback`),
|
|
7978
|
+
...localhostCallbacks
|
|
7979
|
+
],
|
|
7980
|
+
logoutUrls: [
|
|
7981
|
+
...deployedOrigins.map((o) => `${o}/oauth/logout`),
|
|
7982
|
+
...localhostLogouts
|
|
7983
|
+
]
|
|
7869
7984
|
};
|
|
7870
7985
|
}
|
|
7871
7986
|
/**
|
|
7872
|
-
* Creates the
|
|
7873
|
-
* Look up via {@link
|
|
7987
|
+
* Creates the User Pool Domain (Cognito hosted UI) and exports domain name to SSM.
|
|
7988
|
+
* Look up via {@link OpenHiAuthService.userPoolDomainFromConstruct}.
|
|
7989
|
+
* Override to customize.
|
|
7874
7990
|
*/
|
|
7875
|
-
|
|
7876
|
-
new
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7880
|
-
|
|
7991
|
+
createUserPoolDomain() {
|
|
7992
|
+
const domain = new CognitoUserPoolDomain(this, {
|
|
7993
|
+
userPool: this.userPool,
|
|
7994
|
+
cognitoDomain: {
|
|
7995
|
+
domainPrefix: `auth-${this.branchHash}`
|
|
7996
|
+
}
|
|
7881
7997
|
});
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
|
|
7886
|
-
* just-created {@link staticHosting} bucket (no SSM round-trip within a
|
|
7887
|
-
* single stack); on every other deploy it is imported from the bucket ARN
|
|
7888
|
-
* the release-branch deploy publishes to SSM, addressed against
|
|
7889
|
-
* {@link OpenHiService.releaseBranchHash}. See
|
|
7890
|
-
* {@link resolveStaticHostingBucket}.
|
|
7891
|
-
*
|
|
7892
|
-
* The S3 key prefix is `\<sub-domain\>.\<zone\>/\<contentDest\>` so the
|
|
7893
|
-
* upload location matches the Host-header-derived folder the Lambda@Edge
|
|
7894
|
-
* viewer-request handler prepends. Passing the zone name (rather than
|
|
7895
|
-
* `this.fullDomain`) for the `fullDomain` prop keeps the prefix flat —
|
|
7896
|
-
* `admin-feat-foo.dev.openhi.org/`, not
|
|
7897
|
-
* `admin-feat-foo.admin.dev.openhi.org/`.
|
|
7898
|
-
*/
|
|
7899
|
-
createStaticContent(bucket) {
|
|
7900
|
-
const { contentSourceDirectory, contentDestinationDirectory } = this.props;
|
|
7901
|
-
return new StaticContent(this, "static-content", {
|
|
7902
|
-
bucket,
|
|
7903
|
-
contentSourceDirectory,
|
|
7904
|
-
contentDestinationDirectory,
|
|
7905
|
-
subDomain: this.computeSubDomain(),
|
|
7906
|
-
fullDomain: this.config.zoneName
|
|
7998
|
+
new DiscoverableStringParameter(this, "user-pool-domain-param", {
|
|
7999
|
+
ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,
|
|
8000
|
+
stringValue: domain.domainName,
|
|
8001
|
+
description: "Cognito User Pool Domain (hosted UI) for this Auth stack; cross-stack reference"
|
|
7907
8002
|
});
|
|
8003
|
+
return domain;
|
|
7908
8004
|
}
|
|
8005
|
+
};
|
|
8006
|
+
_OpenHiAuthService.SERVICE_TYPE = "auth";
|
|
8007
|
+
var OpenHiAuthService = _OpenHiAuthService;
|
|
8008
|
+
|
|
8009
|
+
// src/services/open-hi-graphql-service.ts
|
|
8010
|
+
var import_aws_appsync2 = require("aws-cdk-lib/aws-appsync");
|
|
8011
|
+
var _OpenHiGraphqlService = class _OpenHiGraphqlService extends OpenHiService {
|
|
7909
8012
|
/**
|
|
7910
|
-
*
|
|
7911
|
-
*
|
|
7912
|
-
* `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>` at the release-branch
|
|
7913
|
-
* CloudFront distribution (resolved from SSM against
|
|
7914
|
-
* {@link OpenHiService.releaseBranchHash}).
|
|
8013
|
+
* Returns the GraphQL API by looking up the GraphQL stack's API ID from SSM.
|
|
8014
|
+
* Use from other stacks to obtain an IGraphqlApi reference.
|
|
7915
8015
|
*/
|
|
7916
|
-
|
|
7917
|
-
return
|
|
7918
|
-
hostname: this.fullDomain,
|
|
7919
|
-
hostedZone,
|
|
7920
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7921
|
-
});
|
|
8016
|
+
static graphqlApiFromConstruct(scope) {
|
|
8017
|
+
return RootGraphqlApi.fromConstruct(scope);
|
|
7922
8018
|
}
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
7937
|
-
|
|
8019
|
+
get serviceType() {
|
|
8020
|
+
return _OpenHiGraphqlService.SERVICE_TYPE;
|
|
8021
|
+
}
|
|
8022
|
+
constructor(ohEnv, props = {}) {
|
|
8023
|
+
super(ohEnv, _OpenHiGraphqlService.SERVICE_TYPE, props);
|
|
8024
|
+
this.props = props;
|
|
8025
|
+
this.rootGraphqlApi = this.createRootGraphqlApi();
|
|
8026
|
+
}
|
|
8027
|
+
/** Creates the root GraphQL API with Cognito user pool. */
|
|
8028
|
+
createRootGraphqlApi() {
|
|
8029
|
+
const userPool = OpenHiAuthService.userPoolFromConstruct(this);
|
|
8030
|
+
return new RootGraphqlApi(this, {
|
|
8031
|
+
authorizationConfig: {
|
|
8032
|
+
defaultAuthorization: {
|
|
8033
|
+
authorizationType: import_aws_appsync2.AuthorizationType.USER_POOL,
|
|
8034
|
+
userPoolConfig: {
|
|
8035
|
+
userPool,
|
|
8036
|
+
defaultAction: import_aws_appsync2.UserPoolDefaultAction.ALLOW
|
|
8037
|
+
}
|
|
8038
|
+
}
|
|
8039
|
+
}
|
|
7938
8040
|
});
|
|
7939
|
-
return import_aws_s32.Bucket.fromBucketArn(this, "shared-bucket", bucketArn);
|
|
7940
8041
|
}
|
|
7941
8042
|
};
|
|
7942
|
-
|
|
7943
|
-
|
|
7944
|
-
* Default `domainPrefix` for this service when none is supplied.
|
|
7945
|
-
* Release-branch hostname is `www.<zone>`; per-PR preview hostname is
|
|
7946
|
-
* `www-<childZonePrefix>.<zone>`.
|
|
7947
|
-
*/
|
|
7948
|
-
_OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX = "www";
|
|
7949
|
-
var OpenHiWebsiteService = _OpenHiWebsiteService;
|
|
8043
|
+
_OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
|
|
8044
|
+
var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
7950
8045
|
|
|
7951
8046
|
// src/workflows/control-plane/owning-delete-cascade/events.ts
|
|
7952
8047
|
var import_workflows5 = __toESM(require_lib2());
|
|
@@ -8482,6 +8577,8 @@ var RenameCascadeWorkflow = class extends import_constructs25.Construct {
|
|
|
8482
8577
|
DataStorePostgresReplica,
|
|
8483
8578
|
DiscoverableStringParameter,
|
|
8484
8579
|
DynamoDbDataStore,
|
|
8580
|
+
LOCALHOST_OAUTH_CALLBACK_URLS,
|
|
8581
|
+
LOCALHOST_OAUTH_LOGOUT_URLS,
|
|
8485
8582
|
OPENHI_REPO_TAG_KEY_ENV_VAR,
|
|
8486
8583
|
OPENHI_RESOURCE_URN_SYSTEM,
|
|
8487
8584
|
OPENHI_TAG_KEY_PREFIX_ENV_VAR,
|