@openhi/constructs 0.0.136 → 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 +926 -828
- package/lib/index.js.map +1 -1
- package/lib/index.mjs +948 -852
- package/lib/index.mjs.map +1 -1
- package/package.json +3 -3
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
|
}
|
|
@@ -2322,10 +2311,11 @@ var DataStorePostgresReplica = class extends import_constructs6.Construct {
|
|
|
2322
2311
|
bundling: {
|
|
2323
2312
|
minify: true,
|
|
2324
2313
|
sourceMap: false,
|
|
2325
|
-
// pg
|
|
2326
|
-
//
|
|
2327
|
-
//
|
|
2328
|
-
|
|
2314
|
+
// pg's conditional optional deps (pg-native, pg-cloudflare) are
|
|
2315
|
+
// marked external so esbuild does not try to resolve them — pg's
|
|
2316
|
+
// runtime code wraps the requires in try/catch and falls back to
|
|
2317
|
+
// the pure-JS client when they are not present.
|
|
2318
|
+
externalModules: ["pg-native", "pg-cloudflare"]
|
|
2329
2319
|
}
|
|
2330
2320
|
});
|
|
2331
2321
|
this.cluster.secret.grantRead(this.replicationFunction);
|
|
@@ -2815,10 +2805,11 @@ var StaticContent = class extends import_constructs10.Construct {
|
|
|
2815
2805
|
};
|
|
2816
2806
|
|
|
2817
2807
|
// src/services/open-hi-auth-service.ts
|
|
2808
|
+
var import_config7 = __toESM(require_lib());
|
|
2818
2809
|
var import_aws_cognito4 = require("aws-cdk-lib/aws-cognito");
|
|
2819
|
-
var
|
|
2810
|
+
var import_aws_iam7 = require("aws-cdk-lib/aws-iam");
|
|
2820
2811
|
var import_aws_kms2 = require("aws-cdk-lib/aws-kms");
|
|
2821
|
-
var
|
|
2812
|
+
var import_core2 = require("aws-cdk-lib/core");
|
|
2822
2813
|
|
|
2823
2814
|
// src/services/open-hi-data-service.ts
|
|
2824
2815
|
var import_config4 = __toESM(require_lib());
|
|
@@ -6791,620 +6782,247 @@ var _OpenHiDataService = class _OpenHiDataService extends OpenHiService {
|
|
|
6791
6782
|
_OpenHiDataService.SERVICE_TYPE = "data";
|
|
6792
6783
|
var OpenHiDataService = _OpenHiDataService;
|
|
6793
6784
|
|
|
6794
|
-
// src/
|
|
6795
|
-
var
|
|
6796
|
-
var
|
|
6797
|
-
var buildProvisionDefaultWorkspaceRequestedDetail = (event) => {
|
|
6798
|
-
const attrs = event.request?.userAttributes ?? {};
|
|
6799
|
-
const cognitoSub = attrs.sub?.trim();
|
|
6800
|
-
if (!cognitoSub) {
|
|
6801
|
-
return void 0;
|
|
6802
|
-
}
|
|
6803
|
-
const email = attrs.email?.trim();
|
|
6804
|
-
const displayName = email || event.userName || cognitoSub;
|
|
6805
|
-
return {
|
|
6806
|
-
cognitoSub,
|
|
6807
|
-
...email ? { email } : {},
|
|
6808
|
-
displayName,
|
|
6809
|
-
trigger: {
|
|
6810
|
-
source: "cognito.post-confirmation",
|
|
6811
|
-
triggerSource: event.triggerSource,
|
|
6812
|
-
userPoolId: event.userPoolId,
|
|
6813
|
-
userName: event.userName,
|
|
6814
|
-
clientId: event.callerContext?.clientId
|
|
6815
|
-
}
|
|
6816
|
-
};
|
|
6817
|
-
};
|
|
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");
|
|
6818
6788
|
|
|
6819
|
-
// 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
|
|
6820
6801
|
var import_node_fs9 = __toESM(require("fs"));
|
|
6821
6802
|
var import_node_path9 = __toESM(require("path"));
|
|
6822
|
-
var import_aws_cdk_lib15 = require("aws-cdk-lib");
|
|
6823
|
-
var import_aws_events8 = require("aws-cdk-lib/aws-events");
|
|
6824
|
-
var import_aws_events_targets4 = require("aws-cdk-lib/aws-events-targets");
|
|
6825
|
-
var import_aws_iam5 = require("aws-cdk-lib/aws-iam");
|
|
6826
6803
|
var import_aws_lambda10 = require("aws-cdk-lib/aws-lambda");
|
|
6827
6804
|
var import_aws_lambda_nodejs10 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
6828
6805
|
var import_constructs17 = require("constructs");
|
|
6829
|
-
var HANDLER_NAME9 = "
|
|
6806
|
+
var HANDLER_NAME9 = "cors-options-lambda.handler.js";
|
|
6830
6807
|
function resolveHandlerEntry9(dirname) {
|
|
6831
6808
|
const sameDir = import_node_path9.default.join(dirname, HANDLER_NAME9);
|
|
6832
6809
|
if (import_node_fs9.default.existsSync(sameDir)) {
|
|
6833
6810
|
return sameDir;
|
|
6834
6811
|
}
|
|
6835
|
-
|
|
6812
|
+
const fromLib = import_node_path9.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME9);
|
|
6813
|
+
return fromLib;
|
|
6836
6814
|
}
|
|
6837
|
-
var
|
|
6838
|
-
constructor(scope,
|
|
6839
|
-
super(scope,
|
|
6815
|
+
var CorsOptionsLambda = class extends import_constructs17.Construct {
|
|
6816
|
+
constructor(scope, id = "cors-options-lambda") {
|
|
6817
|
+
super(scope, id);
|
|
6840
6818
|
this.lambda = new import_aws_lambda_nodejs10.NodejsFunction(this, "handler", {
|
|
6841
6819
|
entry: resolveHandlerEntry9(__dirname),
|
|
6842
6820
|
runtime: import_aws_lambda10.Runtime.NODEJS_LATEST,
|
|
6843
|
-
memorySize:
|
|
6844
|
-
environment: {
|
|
6845
|
-
DYNAMO_TABLE_NAME: props.dataStoreTable.tableName
|
|
6846
|
-
}
|
|
6847
|
-
});
|
|
6848
|
-
props.dataStoreTable.grant(
|
|
6849
|
-
this.lambda,
|
|
6850
|
-
"dynamodb:PutItem",
|
|
6851
|
-
"dynamodb:UpdateItem"
|
|
6852
|
-
);
|
|
6853
|
-
this.lambda.addToRolePolicy(
|
|
6854
|
-
new import_aws_iam5.PolicyStatement({
|
|
6855
|
-
effect: import_aws_iam5.Effect.ALLOW,
|
|
6856
|
-
actions: ["dynamodb:Query"],
|
|
6857
|
-
resources: [`${props.dataStoreTable.tableArn}/index/*`]
|
|
6858
|
-
})
|
|
6859
|
-
);
|
|
6860
|
-
this.rule = new import_aws_events8.Rule(this, "rule", {
|
|
6861
|
-
eventBus: props.controlEventBus,
|
|
6862
|
-
eventPattern: {
|
|
6863
|
-
source: [USER_ONBOARDING_EVENT_SOURCE],
|
|
6864
|
-
detailType: [PROVISION_DEFAULT_WORKSPACE_DETAIL_TYPE]
|
|
6865
|
-
},
|
|
6866
|
-
targets: [
|
|
6867
|
-
new import_aws_events_targets4.LambdaFunction(this.lambda, {
|
|
6868
|
-
retryAttempts: 2,
|
|
6869
|
-
maxEventAge: import_aws_cdk_lib15.Duration.hours(2)
|
|
6870
|
-
})
|
|
6871
|
-
]
|
|
6821
|
+
memorySize: 128
|
|
6872
6822
|
});
|
|
6873
6823
|
}
|
|
6874
6824
|
};
|
|
6875
6825
|
|
|
6876
|
-
// 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");
|
|
6877
6831
|
var import_constructs18 = require("constructs");
|
|
6878
|
-
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 {
|
|
6879
6842
|
constructor(scope, props) {
|
|
6880
|
-
super(scope, "
|
|
6881
|
-
this.
|
|
6882
|
-
|
|
6883
|
-
|
|
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
|
+
}
|
|
6884
6862
|
});
|
|
6885
6863
|
}
|
|
6886
6864
|
};
|
|
6887
6865
|
|
|
6888
|
-
// src/services/open-hi-
|
|
6889
|
-
var
|
|
6890
|
-
|
|
6891
|
-
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
this.preTokenGenerationLambda = this.createPreTokenGenerationLambda();
|
|
6903
|
-
this.postAuthenticationLambda = this.createPostAuthenticationLambda();
|
|
6904
|
-
this.postConfirmationLambda = this.createPostConfirmationLambda();
|
|
6905
|
-
this.userOnboardingWorkflow = this.createUserOnboardingWorkflow();
|
|
6906
|
-
this.userPool = this.createUserPool();
|
|
6907
|
-
this.grantPreTokenGenerationPermissions();
|
|
6908
|
-
this.grantPostAuthenticationPermissions();
|
|
6909
|
-
this.grantPostConfirmationPermissions();
|
|
6910
|
-
this.userPoolClient = this.createUserPoolClient();
|
|
6911
|
-
this.userPoolDomain = this.createUserPoolDomain();
|
|
6912
|
-
}
|
|
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 {
|
|
6913
6880
|
/**
|
|
6914
|
-
*
|
|
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.
|
|
6915
6887
|
*/
|
|
6916
|
-
static
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6888
|
+
static composeFullDomain(opts) {
|
|
6889
|
+
return OpenHiService.composeServiceDomain({
|
|
6890
|
+
...opts,
|
|
6891
|
+
domainPrefix: _OpenHiRestApiService.API_DOMAIN_PREFIX
|
|
6920
6892
|
});
|
|
6921
|
-
return import_aws_cognito4.UserPool.fromUserPoolId(scope, "user-pool", userPoolId);
|
|
6922
|
-
}
|
|
6923
|
-
/**
|
|
6924
|
-
* Returns an IUserPoolClient by looking up the Auth stack's User Pool Client ID from SSM.
|
|
6925
|
-
*/
|
|
6926
|
-
static userPoolClientFromConstruct(scope) {
|
|
6927
|
-
const userPoolClientId = DiscoverableStringParameter.valueForLookupName(
|
|
6928
|
-
scope,
|
|
6929
|
-
{
|
|
6930
|
-
ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,
|
|
6931
|
-
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
6932
|
-
}
|
|
6933
|
-
);
|
|
6934
|
-
return import_aws_cognito4.UserPoolClient.fromUserPoolClientId(
|
|
6935
|
-
scope,
|
|
6936
|
-
"user-pool-client",
|
|
6937
|
-
userPoolClientId
|
|
6938
|
-
);
|
|
6939
6893
|
}
|
|
6940
6894
|
/**
|
|
6941
|
-
* Returns an
|
|
6895
|
+
* Returns an IHttpApi by looking up the REST API stack's HTTP API ID from SSM.
|
|
6942
6896
|
*/
|
|
6943
|
-
static
|
|
6944
|
-
const
|
|
6945
|
-
ssmParamName:
|
|
6946
|
-
serviceType:
|
|
6897
|
+
static rootHttpApiFromConstruct(scope) {
|
|
6898
|
+
const httpApiId = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
6899
|
+
ssmParamName: RootHttpApi.SSM_PARAM_NAME,
|
|
6900
|
+
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
6947
6901
|
});
|
|
6948
|
-
return
|
|
6902
|
+
return import_aws_apigatewayv22.HttpApi.fromHttpApiAttributes(scope, "http-api", { httpApiId });
|
|
6949
6903
|
}
|
|
6950
6904
|
/**
|
|
6951
|
-
* Returns the
|
|
6952
|
-
*
|
|
6953
|
-
* the Auth stack's User Pool Domain from SSM and composing it with the
|
|
6954
|
-
* calling stack's region.
|
|
6955
|
-
*
|
|
6956
|
-
* Equivalent to `UserPoolDomain.baseUrl()` on the concrete construct,
|
|
6957
|
-
* but works across stacks where the looked-up `IUserPoolDomain` is an
|
|
6958
|
-
* `Import` and does not carry the `baseUrl()` method. Assumes the
|
|
6959
|
-
* domain was created as a Cognito-managed prefix domain (the only
|
|
6960
|
-
* 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.
|
|
6961
6907
|
*/
|
|
6962
|
-
static
|
|
6963
|
-
|
|
6964
|
-
ssmParamName:
|
|
6965
|
-
serviceType:
|
|
6908
|
+
static restApiBaseUrlFromConstruct(scope) {
|
|
6909
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
6910
|
+
ssmParamName: REST_API_BASE_URL_SSM_NAME,
|
|
6911
|
+
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
6966
6912
|
});
|
|
6967
|
-
const region = import_core.Stack.of(scope).region;
|
|
6968
|
-
return `https://${domainName}.auth.${region}.amazoncognito.com`;
|
|
6969
6913
|
}
|
|
6970
6914
|
/**
|
|
6971
|
-
* 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.
|
|
6972
6919
|
*/
|
|
6973
|
-
static
|
|
6974
|
-
|
|
6975
|
-
ssmParamName:
|
|
6976
|
-
serviceType:
|
|
6920
|
+
static restApiDomainNameFromConstruct(scope) {
|
|
6921
|
+
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
6922
|
+
ssmParamName: REST_API_DOMAIN_NAME_SSM_NAME,
|
|
6923
|
+
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
6977
6924
|
});
|
|
6978
|
-
return import_aws_kms2.Key.fromKeyArn(scope, "kms-key", keyArn);
|
|
6979
6925
|
}
|
|
6980
6926
|
get serviceType() {
|
|
6981
|
-
return
|
|
6927
|
+
return _OpenHiRestApiService.SERVICE_TYPE;
|
|
6982
6928
|
}
|
|
6983
|
-
|
|
6984
|
-
|
|
6985
|
-
|
|
6986
|
-
|
|
6987
|
-
|
|
6988
|
-
|
|
6989
|
-
|
|
6990
|
-
|
|
6991
|
-
|
|
6992
|
-
|
|
6993
|
-
|
|
6994
|
-
|
|
6995
|
-
return key;
|
|
6996
|
-
}
|
|
6997
|
-
/**
|
|
6998
|
-
* Creates the Pre Token Generation Lambda (Cognito trigger). On every
|
|
6999
|
-
* sign-in and token refresh the Lambda resolves the User by Cognito `sub`
|
|
7000
|
-
* (GSI2) and injects `ohi_tid`, `ohi_wid`, `ohi_uid`, `ohi_uname` into
|
|
7001
|
-
* both the ID token and the access token (ADR 2026-03-17-01).
|
|
7002
|
-
*/
|
|
7003
|
-
createPreTokenGenerationLambda() {
|
|
7004
|
-
const construct = new PreTokenGenerationLambda(this, {
|
|
7005
|
-
dynamoTableName: this.dataStoreTable().tableName
|
|
7006
|
-
});
|
|
7007
|
-
return construct.lambda;
|
|
7008
|
-
}
|
|
7009
|
-
/**
|
|
7010
|
-
* Creates the Post Authentication Lambda (Cognito trigger). Calls
|
|
7011
|
-
* AdminUserGlobalSignOut on every sign-in to enforce single-device-per-user
|
|
7012
|
-
* sessions per ADR 2026-03-17-01.
|
|
7013
|
-
*/
|
|
7014
|
-
createPostAuthenticationLambda() {
|
|
7015
|
-
const construct = new PostAuthenticationLambda(this);
|
|
7016
|
-
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);
|
|
7017
6941
|
}
|
|
7018
6942
|
/**
|
|
7019
|
-
*
|
|
7020
|
-
* confirmation, publishes a control-plane workflow event; provisioning lives
|
|
7021
|
-
* behind EventBridge.
|
|
6943
|
+
* Validates that config required for the REST API stack is present.
|
|
7022
6944
|
*/
|
|
7023
|
-
|
|
7024
|
-
const
|
|
7025
|
-
|
|
7026
|
-
|
|
7027
|
-
return construct.lambda;
|
|
7028
|
-
}
|
|
7029
|
-
createUserOnboardingWorkflow() {
|
|
7030
|
-
return new UserOnboardingWorkflow(this, {
|
|
7031
|
-
controlEventBus: this.controlEventBus(),
|
|
7032
|
-
dataStoreTable: this.dataStoreTable()
|
|
7033
|
-
});
|
|
7034
|
-
}
|
|
7035
|
-
dataStoreTable() {
|
|
7036
|
-
if (this._dataStoreTable === null) {
|
|
7037
|
-
this._dataStoreTable = OpenHiDataService.dynamoDbDataStoreFromConstruct(this);
|
|
6945
|
+
validateConfig(props) {
|
|
6946
|
+
const { config } = props;
|
|
6947
|
+
if (!config) {
|
|
6948
|
+
throw new Error("Config is required");
|
|
7038
6949
|
}
|
|
7039
|
-
|
|
7040
|
-
|
|
7041
|
-
|
|
7042
|
-
if (
|
|
7043
|
-
|
|
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");
|
|
7044
6955
|
}
|
|
7045
|
-
return this._controlEventBus;
|
|
7046
6956
|
}
|
|
7047
6957
|
/**
|
|
7048
|
-
* Creates the
|
|
7049
|
-
* Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
|
|
6958
|
+
* Creates the hosted zone reference (imported from config).
|
|
7050
6959
|
* Override to customize.
|
|
7051
6960
|
*/
|
|
7052
|
-
|
|
7053
|
-
const
|
|
7054
|
-
|
|
7055
|
-
|
|
7056
|
-
|
|
7057
|
-
userPool.addTrigger(
|
|
7058
|
-
import_aws_cognito4.UserPoolOperation.PRE_TOKEN_GENERATION_CONFIG,
|
|
7059
|
-
this.preTokenGenerationLambda,
|
|
7060
|
-
import_aws_cognito4.LambdaVersion.V2_0
|
|
7061
|
-
);
|
|
7062
|
-
userPool.addTrigger(
|
|
7063
|
-
import_aws_cognito4.UserPoolOperation.POST_AUTHENTICATION,
|
|
7064
|
-
this.postAuthenticationLambda
|
|
7065
|
-
);
|
|
7066
|
-
userPool.addTrigger(
|
|
7067
|
-
import_aws_cognito4.UserPoolOperation.POST_CONFIRMATION,
|
|
7068
|
-
this.postConfirmationLambda
|
|
7069
|
-
);
|
|
7070
|
-
new DiscoverableStringParameter(this, "user-pool-param", {
|
|
7071
|
-
ssmParamName: CognitoUserPool.SSM_PARAM_NAME,
|
|
7072
|
-
stringValue: userPool.userPoolId,
|
|
7073
|
-
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
|
|
7074
6966
|
});
|
|
7075
|
-
return userPool;
|
|
7076
6967
|
}
|
|
7077
6968
|
/**
|
|
7078
|
-
*
|
|
7079
|
-
*
|
|
7080
|
-
* - `Query` on GSI2 to resolve a User by Cognito `sub`
|
|
7081
|
-
* - `GetItem` on the base table for direct User reads
|
|
7082
|
-
*
|
|
7083
|
-
* No write or scan access: a User missing `currentTenant`/`currentWorkspace`
|
|
7084
|
-
* 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.
|
|
7085
6971
|
*/
|
|
7086
|
-
|
|
7087
|
-
|
|
7088
|
-
const dynamoActions = ["dynamodb:GetItem", "dynamodb:Query"];
|
|
7089
|
-
dataStoreTable.grant(this.preTokenGenerationLambda, ...dynamoActions);
|
|
7090
|
-
this.preTokenGenerationLambda.addToRolePolicy(
|
|
7091
|
-
new import_aws_iam6.PolicyStatement({
|
|
7092
|
-
effect: import_aws_iam6.Effect.ALLOW,
|
|
7093
|
-
actions: [...dynamoActions],
|
|
7094
|
-
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
7095
|
-
})
|
|
7096
|
-
);
|
|
6972
|
+
createCertificate() {
|
|
6973
|
+
return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);
|
|
7097
6974
|
}
|
|
7098
6975
|
/**
|
|
7099
|
-
*
|
|
7100
|
-
*
|
|
7101
|
-
*
|
|
7102
|
-
*
|
|
7103
|
-
*
|
|
7104
|
-
* trigger, creating the cycle:
|
|
7105
|
-
* userPool → lambda (trigger ARN) → role policy → userPool ARN.
|
|
7106
|
-
* Using `formatArn` avoids referencing the User Pool resource directly
|
|
7107
|
-
* while still scoping to user pools in this account+region. The Lambda
|
|
7108
|
-
* is invoked only by Cognito with a Cognito-provided `event.userPoolId`,
|
|
7109
|
-
* 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.
|
|
7110
6981
|
*/
|
|
7111
|
-
|
|
7112
|
-
|
|
7113
|
-
|
|
7114
|
-
|
|
7115
|
-
|
|
7116
|
-
|
|
7117
|
-
|
|
7118
|
-
resource: "userpool",
|
|
7119
|
-
resourceName: "*"
|
|
7120
|
-
})
|
|
7121
|
-
]
|
|
7122
|
-
})
|
|
7123
|
-
);
|
|
6982
|
+
createApiDomainNameString(hostedZone) {
|
|
6983
|
+
return _OpenHiRestApiService.composeFullDomain({
|
|
6984
|
+
branchName: this.branchName,
|
|
6985
|
+
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
6986
|
+
childZonePrefix: this.childZonePrefix,
|
|
6987
|
+
zoneName: hostedZone.zoneName
|
|
6988
|
+
});
|
|
7124
6989
|
}
|
|
7125
6990
|
/**
|
|
7126
|
-
*
|
|
7127
|
-
*
|
|
6991
|
+
* Creates the SSM parameter for the REST API base URL.
|
|
6992
|
+
* Look up via {@link OpenHiRestApiService.restApiBaseUrlFromConstruct}.
|
|
6993
|
+
* Override to customize.
|
|
7128
6994
|
*/
|
|
7129
|
-
|
|
7130
|
-
|
|
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
|
+
});
|
|
7131
7002
|
}
|
|
7132
7003
|
/**
|
|
7133
|
-
* Creates the
|
|
7134
|
-
*
|
|
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}.
|
|
7135
7008
|
* Override to customize.
|
|
7136
7009
|
*/
|
|
7137
|
-
|
|
7138
|
-
|
|
7139
|
-
|
|
7140
|
-
|
|
7141
|
-
|
|
7142
|
-
ssmParamName: CognitoUserPoolClient.SSM_PARAM_NAME,
|
|
7143
|
-
stringValue: client.userPoolClientId,
|
|
7144
|
-
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"
|
|
7145
7015
|
});
|
|
7146
|
-
return client;
|
|
7147
7016
|
}
|
|
7148
7017
|
/**
|
|
7149
|
-
* Creates the
|
|
7150
|
-
* Look up via {@link OpenHiAuthService.userPoolDomainFromConstruct}.
|
|
7018
|
+
* Creates the API Gateway custom domain name resource.
|
|
7151
7019
|
* Override to customize.
|
|
7152
7020
|
*/
|
|
7153
|
-
|
|
7154
|
-
const
|
|
7155
|
-
|
|
7156
|
-
|
|
7157
|
-
|
|
7158
|
-
}
|
|
7159
|
-
});
|
|
7160
|
-
new DiscoverableStringParameter(this, "user-pool-domain-param", {
|
|
7161
|
-
ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,
|
|
7162
|
-
stringValue: domain.domainName,
|
|
7163
|
-
description: "Cognito User Pool Domain (hosted UI) for this Auth stack; cross-stack reference"
|
|
7164
|
-
});
|
|
7165
|
-
return domain;
|
|
7166
|
-
}
|
|
7167
|
-
};
|
|
7168
|
-
_OpenHiAuthService.SERVICE_TYPE = "auth";
|
|
7169
|
-
var OpenHiAuthService = _OpenHiAuthService;
|
|
7170
|
-
|
|
7171
|
-
// src/services/open-hi-rest-api-service.ts
|
|
7172
|
-
var import_config5 = __toESM(require_lib());
|
|
7173
|
-
var import_aws_apigatewayv22 = require("aws-cdk-lib/aws-apigatewayv2");
|
|
7174
|
-
var import_aws_apigatewayv2_authorizers = require("aws-cdk-lib/aws-apigatewayv2-authorizers");
|
|
7175
|
-
var import_aws_apigatewayv2_integrations = require("aws-cdk-lib/aws-apigatewayv2-integrations");
|
|
7176
|
-
var import_aws_iam7 = require("aws-cdk-lib/aws-iam");
|
|
7177
|
-
var import_aws_route535 = require("aws-cdk-lib/aws-route53");
|
|
7178
|
-
var import_aws_route53_targets3 = require("aws-cdk-lib/aws-route53-targets");
|
|
7179
|
-
var import_core2 = require("aws-cdk-lib/core");
|
|
7180
|
-
var import_constructs21 = require("constructs");
|
|
7181
|
-
|
|
7182
|
-
// src/data/lambda/cors-options-lambda.ts
|
|
7183
|
-
var import_node_fs10 = __toESM(require("fs"));
|
|
7184
|
-
var import_node_path10 = __toESM(require("path"));
|
|
7185
|
-
var import_aws_lambda11 = require("aws-cdk-lib/aws-lambda");
|
|
7186
|
-
var import_aws_lambda_nodejs11 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
7187
|
-
var import_constructs19 = require("constructs");
|
|
7188
|
-
var HANDLER_NAME10 = "cors-options-lambda.handler.js";
|
|
7189
|
-
function resolveHandlerEntry10(dirname) {
|
|
7190
|
-
const sameDir = import_node_path10.default.join(dirname, HANDLER_NAME10);
|
|
7191
|
-
if (import_node_fs10.default.existsSync(sameDir)) {
|
|
7192
|
-
return sameDir;
|
|
7193
|
-
}
|
|
7194
|
-
const fromLib = import_node_path10.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME10);
|
|
7195
|
-
return fromLib;
|
|
7196
|
-
}
|
|
7197
|
-
var CorsOptionsLambda = class extends import_constructs19.Construct {
|
|
7198
|
-
constructor(scope, id = "cors-options-lambda") {
|
|
7199
|
-
super(scope, id);
|
|
7200
|
-
this.lambda = new import_aws_lambda_nodejs11.NodejsFunction(this, "handler", {
|
|
7201
|
-
entry: resolveHandlerEntry10(__dirname),
|
|
7202
|
-
runtime: import_aws_lambda11.Runtime.NODEJS_LATEST,
|
|
7203
|
-
memorySize: 128
|
|
7204
|
-
});
|
|
7205
|
-
}
|
|
7206
|
-
};
|
|
7207
|
-
|
|
7208
|
-
// src/data/lambda/rest-api-lambda.ts
|
|
7209
|
-
var import_node_fs11 = __toESM(require("fs"));
|
|
7210
|
-
var import_node_path11 = __toESM(require("path"));
|
|
7211
|
-
var import_aws_lambda12 = require("aws-cdk-lib/aws-lambda");
|
|
7212
|
-
var import_aws_lambda_nodejs12 = require("aws-cdk-lib/aws-lambda-nodejs");
|
|
7213
|
-
var import_constructs20 = require("constructs");
|
|
7214
|
-
var HANDLER_NAME11 = "rest-api-lambda.handler.js";
|
|
7215
|
-
function resolveHandlerEntry11(dirname) {
|
|
7216
|
-
const sameDir = import_node_path11.default.join(dirname, HANDLER_NAME11);
|
|
7217
|
-
if (import_node_fs11.default.existsSync(sameDir)) {
|
|
7218
|
-
return sameDir;
|
|
7219
|
-
}
|
|
7220
|
-
const fromLib = import_node_path11.default.join(dirname, "..", "..", "..", "lib", HANDLER_NAME11);
|
|
7221
|
-
return fromLib;
|
|
7222
|
-
}
|
|
7223
|
-
var RestApiLambda = class extends import_constructs20.Construct {
|
|
7224
|
-
constructor(scope, props) {
|
|
7225
|
-
super(scope, "rest-api-lambda");
|
|
7226
|
-
this.lambda = new import_aws_lambda_nodejs12.NodejsFunction(this, "handler", {
|
|
7227
|
-
entry: resolveHandlerEntry11(__dirname),
|
|
7228
|
-
runtime: import_aws_lambda12.Runtime.NODEJS_LATEST,
|
|
7229
|
-
memorySize: 1024,
|
|
7230
|
-
environment: {
|
|
7231
|
-
DYNAMO_TABLE_NAME: props.dynamoTableName,
|
|
7232
|
-
BRANCH_TAG_VALUE: props.branchTagValue,
|
|
7233
|
-
HTTP_API_TAG_VALUE: props.httpApiTagValue,
|
|
7234
|
-
OPENHI_PG_CLUSTER_ARN: props.postgresClusterArn,
|
|
7235
|
-
OPENHI_PG_SECRET_ARN: props.postgresSecretArn,
|
|
7236
|
-
OPENHI_PG_DATABASE: props.postgresDatabase,
|
|
7237
|
-
OPENHI_PG_SCHEMA: props.postgresSchema,
|
|
7238
|
-
...props.extraEnvironment
|
|
7239
|
-
},
|
|
7240
|
-
bundling: {
|
|
7241
|
-
minify: true,
|
|
7242
|
-
sourceMap: false
|
|
7243
|
-
}
|
|
7244
|
-
});
|
|
7245
|
-
}
|
|
7246
|
-
};
|
|
7247
|
-
|
|
7248
|
-
// src/services/open-hi-rest-api-service.ts
|
|
7249
|
-
var REST_API_BASE_URL_SSM_NAME = "REST_API_BASE_URL";
|
|
7250
|
-
var REST_API_DOMAIN_NAME_SSM_NAME = "REST_API_DOMAIN_NAME";
|
|
7251
|
-
var DEV_CORS_ALLOW_ORIGINS = [
|
|
7252
|
-
"http://localhost:3000",
|
|
7253
|
-
"https://localhost:3000",
|
|
7254
|
-
"http://localhost:5173",
|
|
7255
|
-
"https://localhost:5173",
|
|
7256
|
-
"http://127.0.0.1:3000",
|
|
7257
|
-
"https://127.0.0.1:3000",
|
|
7258
|
-
"http://127.0.0.1:5173",
|
|
7259
|
-
"https://127.0.0.1:5173"
|
|
7260
|
-
];
|
|
7261
|
-
var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
7262
|
-
/**
|
|
7263
|
-
* Compose the REST API's full per-deploy domain. Thin wrapper over
|
|
7264
|
-
* {@link OpenHiService.composeServiceDomain} that pins `domainPrefix`
|
|
7265
|
-
* to {@link API_DOMAIN_PREFIX}.
|
|
7266
|
-
*
|
|
7267
|
-
* Use from sibling stacks that need to predict the API's hostname
|
|
7268
|
-
* before the REST API stack is synthesised.
|
|
7269
|
-
*/
|
|
7270
|
-
static composeFullDomain(opts) {
|
|
7271
|
-
return OpenHiService.composeServiceDomain({
|
|
7272
|
-
...opts,
|
|
7273
|
-
domainPrefix: _OpenHiRestApiService.API_DOMAIN_PREFIX
|
|
7274
|
-
});
|
|
7275
|
-
}
|
|
7276
|
-
/**
|
|
7277
|
-
* Returns an IHttpApi by looking up the REST API stack's HTTP API ID from SSM.
|
|
7278
|
-
*/
|
|
7279
|
-
static rootHttpApiFromConstruct(scope) {
|
|
7280
|
-
const httpApiId = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7281
|
-
ssmParamName: RootHttpApi.SSM_PARAM_NAME,
|
|
7282
|
-
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
7283
|
-
});
|
|
7284
|
-
return import_aws_apigatewayv22.HttpApi.fromHttpApiAttributes(scope, "http-api", { httpApiId });
|
|
7285
|
-
}
|
|
7286
|
-
/**
|
|
7287
|
-
* Returns the REST API base URL (e.g. https://api.example.com) by looking it up from SSM.
|
|
7288
|
-
* Use in other stacks for E2E, scripts, or config.
|
|
7289
|
-
*/
|
|
7290
|
-
static restApiBaseUrlFromConstruct(scope) {
|
|
7291
|
-
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7292
|
-
ssmParamName: REST_API_BASE_URL_SSM_NAME,
|
|
7293
|
-
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
7294
|
-
});
|
|
7295
|
-
}
|
|
7296
|
-
/**
|
|
7297
|
-
* Returns the REST API's custom domain name (bare hostname, no scheme — e.g.
|
|
7298
|
-
* `api.example.com`) by looking it up from SSM. Use as the host for a
|
|
7299
|
-
* CloudFront `HttpOrigin` so the website's distribution can proxy `/api/*`
|
|
7300
|
-
* to this stack's API Gateway without per-branch DNS knowledge.
|
|
7301
|
-
*/
|
|
7302
|
-
static restApiDomainNameFromConstruct(scope) {
|
|
7303
|
-
return DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7304
|
-
ssmParamName: REST_API_DOMAIN_NAME_SSM_NAME,
|
|
7305
|
-
serviceType: _OpenHiRestApiService.SERVICE_TYPE
|
|
7306
|
-
});
|
|
7307
|
-
}
|
|
7308
|
-
get serviceType() {
|
|
7309
|
-
return _OpenHiRestApiService.SERVICE_TYPE;
|
|
7310
|
-
}
|
|
7311
|
-
constructor(ohEnv, props = {}) {
|
|
7312
|
-
super(ohEnv, _OpenHiRestApiService.SERVICE_TYPE, props);
|
|
7313
|
-
this.props = props;
|
|
7314
|
-
this.validateConfig(props);
|
|
7315
|
-
const hostedZone = this.createHostedZone();
|
|
7316
|
-
const certificate = this.createCertificate();
|
|
7317
|
-
this.apiDomainName = this.createApiDomainNameString(hostedZone);
|
|
7318
|
-
this.createRestApiBaseUrlParameter(this.apiDomainName);
|
|
7319
|
-
this.createRestApiDomainNameParameter(this.apiDomainName);
|
|
7320
|
-
const domainName = this.createDomainName(hostedZone, certificate);
|
|
7321
|
-
this.rootHttpApi = this.createRootHttpApi(domainName);
|
|
7322
|
-
this.createRestApiLambdaAndRoutes(hostedZone, domainName);
|
|
7323
|
-
}
|
|
7324
|
-
/**
|
|
7325
|
-
* Validates that config required for the REST API stack is present.
|
|
7326
|
-
*/
|
|
7327
|
-
validateConfig(props) {
|
|
7328
|
-
const { config } = props;
|
|
7329
|
-
if (!config) {
|
|
7330
|
-
throw new Error("Config is required");
|
|
7331
|
-
}
|
|
7332
|
-
if (!config.hostedZoneId) {
|
|
7333
|
-
throw new Error("Hosted zone ID is required");
|
|
7334
|
-
}
|
|
7335
|
-
if (!config.zoneName) {
|
|
7336
|
-
throw new Error("Zone name is required");
|
|
7337
|
-
}
|
|
7338
|
-
}
|
|
7339
|
-
/**
|
|
7340
|
-
* Creates the hosted zone reference (imported from config).
|
|
7341
|
-
* Override to customize.
|
|
7342
|
-
*/
|
|
7343
|
-
createHostedZone() {
|
|
7344
|
-
const { config } = this.props;
|
|
7345
|
-
return import_aws_route535.HostedZone.fromHostedZoneAttributes(this, "root-zone", {
|
|
7346
|
-
hostedZoneId: config.hostedZoneId,
|
|
7347
|
-
zoneName: config.zoneName
|
|
7348
|
-
});
|
|
7349
|
-
}
|
|
7350
|
-
/**
|
|
7351
|
-
* Creates the wildcard certificate (imported from Global stack via SSM).
|
|
7352
|
-
* Override to customize.
|
|
7353
|
-
*/
|
|
7354
|
-
createCertificate() {
|
|
7355
|
-
return OpenHiGlobalService.rootWildcardCertificateFromConstruct(this);
|
|
7356
|
-
}
|
|
7357
|
-
/**
|
|
7358
|
-
* Returns the API domain name string (e.g. api.example.com or api-\{prefix\}.example.com).
|
|
7359
|
-
* Delegates to {@link OpenHiRestApiService.composeFullDomain} so the
|
|
7360
|
-
* release-vs-feature composition stays in one place; picks up
|
|
7361
|
-
* `this.defaultReleaseBranch` (not a hard-coded `"main"`).
|
|
7362
|
-
* Override to customize.
|
|
7363
|
-
*/
|
|
7364
|
-
createApiDomainNameString(hostedZone) {
|
|
7365
|
-
return _OpenHiRestApiService.composeFullDomain({
|
|
7366
|
-
branchName: this.branchName,
|
|
7367
|
-
defaultReleaseBranch: this.defaultReleaseBranch,
|
|
7368
|
-
childZonePrefix: this.childZonePrefix,
|
|
7369
|
-
zoneName: hostedZone.zoneName
|
|
7370
|
-
});
|
|
7371
|
-
}
|
|
7372
|
-
/**
|
|
7373
|
-
* Creates the SSM parameter for the REST API base URL.
|
|
7374
|
-
* Look up via {@link OpenHiRestApiService.restApiBaseUrlFromConstruct}.
|
|
7375
|
-
* Override to customize.
|
|
7376
|
-
*/
|
|
7377
|
-
createRestApiBaseUrlParameter(apiDomainName) {
|
|
7378
|
-
const restApiBaseUrl = `https://${apiDomainName}`;
|
|
7379
|
-
new DiscoverableStringParameter(this, "rest-api-base-url-param", {
|
|
7380
|
-
ssmParamName: REST_API_BASE_URL_SSM_NAME,
|
|
7381
|
-
stringValue: restApiBaseUrl,
|
|
7382
|
-
description: "REST API base URL for this deployment (E2E, scripts)"
|
|
7383
|
-
});
|
|
7384
|
-
}
|
|
7385
|
-
/**
|
|
7386
|
-
* Creates the SSM parameter exposing the REST API's custom domain (bare
|
|
7387
|
-
* hostname, no scheme). Consumed by the website service as the CloudFront
|
|
7388
|
-
* `/api/*` origin host.
|
|
7389
|
-
* Look up via {@link OpenHiRestApiService.restApiDomainNameFromConstruct}.
|
|
7390
|
-
* Override to customize.
|
|
7391
|
-
*/
|
|
7392
|
-
createRestApiDomainNameParameter(apiDomainName) {
|
|
7393
|
-
new DiscoverableStringParameter(this, "rest-api-domain-name-param", {
|
|
7394
|
-
ssmParamName: REST_API_DOMAIN_NAME_SSM_NAME,
|
|
7395
|
-
stringValue: apiDomainName,
|
|
7396
|
-
description: "REST API custom domain name (bare hostname) for cross-stack CloudFront origin lookup"
|
|
7397
|
-
});
|
|
7398
|
-
}
|
|
7399
|
-
/**
|
|
7400
|
-
* Creates the API Gateway custom domain name resource.
|
|
7401
|
-
* Override to customize.
|
|
7402
|
-
*/
|
|
7403
|
-
createDomainName(_hostedZone, certificate) {
|
|
7404
|
-
const apiDomainName = this.createApiDomainNameString(_hostedZone);
|
|
7405
|
-
return new import_aws_apigatewayv22.DomainName(this, "domain", {
|
|
7406
|
-
domainName: apiDomainName,
|
|
7407
|
-
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
|
|
7408
7026
|
});
|
|
7409
7027
|
}
|
|
7410
7028
|
/**
|
|
@@ -7429,8 +7047,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7429
7047
|
extraEnvironment
|
|
7430
7048
|
});
|
|
7431
7049
|
lambda.addToRolePolicy(
|
|
7432
|
-
new
|
|
7433
|
-
effect:
|
|
7050
|
+
new import_aws_iam5.PolicyStatement({
|
|
7051
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7434
7052
|
actions: [
|
|
7435
7053
|
"rds-data:ExecuteStatement",
|
|
7436
7054
|
"rds-data:BatchExecuteStatement"
|
|
@@ -7439,8 +7057,8 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7439
7057
|
})
|
|
7440
7058
|
);
|
|
7441
7059
|
lambda.addToRolePolicy(
|
|
7442
|
-
new
|
|
7443
|
-
effect:
|
|
7060
|
+
new import_aws_iam5.PolicyStatement({
|
|
7061
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7444
7062
|
actions: ["secretsmanager:GetSecretValue"],
|
|
7445
7063
|
resources: [postgresSecretArn]
|
|
7446
7064
|
})
|
|
@@ -7458,15 +7076,15 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7458
7076
|
];
|
|
7459
7077
|
dataStoreTable.grant(lambda, ...dynamoActions);
|
|
7460
7078
|
lambda.addToRolePolicy(
|
|
7461
|
-
new
|
|
7462
|
-
effect:
|
|
7079
|
+
new import_aws_iam5.PolicyStatement({
|
|
7080
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7463
7081
|
actions: [...dynamoActions],
|
|
7464
7082
|
resources: [`${dataStoreTable.tableArn}/index/*`]
|
|
7465
7083
|
})
|
|
7466
7084
|
);
|
|
7467
7085
|
lambda.addToRolePolicy(
|
|
7468
|
-
new
|
|
7469
|
-
effect:
|
|
7086
|
+
new import_aws_iam5.PolicyStatement({
|
|
7087
|
+
effect: import_aws_iam5.Effect.ALLOW,
|
|
7470
7088
|
actions: [
|
|
7471
7089
|
"ssm:GetParameter",
|
|
7472
7090
|
"ssm:GetParameters",
|
|
@@ -7547,11 +7165,18 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7547
7165
|
const { corsPreflight: cors, ...restRootHttpApiProps } = this.props.rootHttpApiProps ?? {};
|
|
7548
7166
|
const isNonProd = this.ohEnv.ohStage.stageType !== import_config5.OPEN_HI_STAGE.PROD;
|
|
7549
7167
|
const callerOrigins = cors?.allowOrigins ?? [];
|
|
7550
|
-
const
|
|
7551
|
-
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);
|
|
7552
7177
|
const rootHttpApi = new RootHttpApi(this, {
|
|
7553
7178
|
...restRootHttpApiProps,
|
|
7554
|
-
|
|
7179
|
+
corsPreflight,
|
|
7555
7180
|
defaultDomainMapping: {
|
|
7556
7181
|
domainName,
|
|
7557
7182
|
mappingKey: void 0
|
|
@@ -7565,6 +7190,33 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7565
7190
|
});
|
|
7566
7191
|
return rootHttpApi;
|
|
7567
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
|
+
}
|
|
7568
7220
|
/**
|
|
7569
7221
|
* Builds the full `CorsPreflightOptions` from a merged origins array,
|
|
7570
7222
|
* filling defaults for `allowMethods`/`allowHeaders`/`allowCredentials`/
|
|
@@ -7584,7 +7236,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7584
7236
|
],
|
|
7585
7237
|
allowHeaders: cors?.allowHeaders ?? ["Content-Type", "Authorization"],
|
|
7586
7238
|
allowCredentials: cors?.allowCredentials ?? true,
|
|
7587
|
-
maxAge: cors?.maxAge ??
|
|
7239
|
+
maxAge: cors?.maxAge ?? import_core.Duration.days(1),
|
|
7588
7240
|
...cors?.exposeHeaders !== void 0 && {
|
|
7589
7241
|
exposeHeaders: cors.exposeHeaders
|
|
7590
7242
|
}
|
|
@@ -7602,7 +7254,7 @@ var _OpenHiRestApiService = class _OpenHiRestApiService extends OpenHiService {
|
|
|
7602
7254
|
* client-side from `window.location.origin`.
|
|
7603
7255
|
*/
|
|
7604
7256
|
resolveRuntimeConfigEnvVars() {
|
|
7605
|
-
const cognitoScope = new
|
|
7257
|
+
const cognitoScope = new import_constructs19.Construct(this, "runtime-config");
|
|
7606
7258
|
const userPool = OpenHiAuthService.userPoolFromConstruct(cognitoScope);
|
|
7607
7259
|
const userPoolClient = OpenHiAuthService.userPoolClientFromConstruct(cognitoScope);
|
|
7608
7260
|
const cognitoDomainUrl = OpenHiAuthService.userPoolDomainBaseUrlFromConstruct(cognitoScope);
|
|
@@ -7622,330 +7274,774 @@ _OpenHiRestApiService.SERVICE_TYPE = "rest-api";
|
|
|
7622
7274
|
_OpenHiRestApiService.API_DOMAIN_PREFIX = "api";
|
|
7623
7275
|
var OpenHiRestApiService = _OpenHiRestApiService;
|
|
7624
7276
|
|
|
7625
|
-
// src/services/open-hi-
|
|
7626
|
-
var
|
|
7627
|
-
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 {
|
|
7628
7281
|
/**
|
|
7629
|
-
*
|
|
7630
|
-
*
|
|
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.
|
|
7631
7289
|
*/
|
|
7632
|
-
static
|
|
7633
|
-
return
|
|
7290
|
+
static composeFullDomain(opts) {
|
|
7291
|
+
return OpenHiService.composeServiceDomain({
|
|
7292
|
+
...opts,
|
|
7293
|
+
domainPrefix: opts.domainPrefix ?? _OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX
|
|
7294
|
+
});
|
|
7634
7295
|
}
|
|
7635
|
-
|
|
7636
|
-
|
|
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
|
+
});
|
|
7637
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 {
|
|
7638
7667
|
constructor(ohEnv, props = {}) {
|
|
7639
|
-
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;
|
|
7640
7677
|
this.props = props;
|
|
7641
|
-
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();
|
|
7642
7689
|
}
|
|
7643
|
-
/**
|
|
7644
|
-
|
|
7645
|
-
|
|
7646
|
-
|
|
7647
|
-
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7651
|
-
|
|
7652
|
-
|
|
7653
|
-
|
|
7654
|
-
|
|
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
|
|
7655
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
|
|
7656
7724
|
});
|
|
7725
|
+
return import_aws_cognito4.UserPoolDomain.fromDomainName(scope, "user-pool-domain", domainName);
|
|
7657
7726
|
}
|
|
7658
|
-
};
|
|
7659
|
-
_OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
|
|
7660
|
-
var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
7661
|
-
|
|
7662
|
-
// src/services/open-hi-website-service.ts
|
|
7663
|
-
var import_config6 = __toESM(require_lib());
|
|
7664
|
-
var import_aws_s32 = require("aws-cdk-lib/aws-s3");
|
|
7665
|
-
var SSM_PARAM_NAME_FULL_DOMAIN = "WEBSITE_FULL_DOMAIN";
|
|
7666
|
-
var ADMIN_DOMAIN_PREFIX = "admin";
|
|
7667
|
-
var _OpenHiWebsiteService = class _OpenHiWebsiteService extends OpenHiService {
|
|
7668
7727
|
/**
|
|
7669
|
-
*
|
|
7670
|
-
*
|
|
7671
|
-
*
|
|
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.
|
|
7672
7732
|
*
|
|
7673
|
-
*
|
|
7674
|
-
*
|
|
7675
|
-
*
|
|
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).
|
|
7676
7738
|
*/
|
|
7677
|
-
static
|
|
7678
|
-
|
|
7679
|
-
|
|
7680
|
-
|
|
7739
|
+
static userPoolDomainBaseUrlFromConstruct(scope) {
|
|
7740
|
+
const domainName = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7741
|
+
ssmParamName: CognitoUserPoolDomain.SSM_PARAM_NAME,
|
|
7742
|
+
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
7681
7743
|
});
|
|
7744
|
+
const region = import_core2.Stack.of(scope).region;
|
|
7745
|
+
return `https://${domainName}.auth.${region}.amazoncognito.com`;
|
|
7682
7746
|
}
|
|
7683
7747
|
/**
|
|
7684
|
-
*
|
|
7685
|
-
* deploy of this service.
|
|
7748
|
+
* Returns an IKey (KMS) by looking up the Auth stack's User Pool KMS Key ARN from SSM.
|
|
7686
7749
|
*/
|
|
7687
|
-
static
|
|
7688
|
-
|
|
7689
|
-
ssmParamName:
|
|
7690
|
-
serviceType:
|
|
7750
|
+
static userPoolKmsKeyFromConstruct(scope) {
|
|
7751
|
+
const keyArn = DiscoverableStringParameter.valueForLookupName(scope, {
|
|
7752
|
+
ssmParamName: CognitoUserPoolKmsKey.SSM_PARAM_NAME,
|
|
7753
|
+
serviceType: _OpenHiAuthService.SERVICE_TYPE
|
|
7691
7754
|
});
|
|
7755
|
+
return import_aws_kms2.Key.fromKeyArn(scope, "kms-key", keyArn);
|
|
7756
|
+
}
|
|
7757
|
+
get serviceType() {
|
|
7758
|
+
return _OpenHiAuthService.SERVICE_TYPE;
|
|
7692
7759
|
}
|
|
7693
7760
|
/**
|
|
7694
|
-
*
|
|
7695
|
-
*
|
|
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.
|
|
7696
7764
|
*/
|
|
7697
|
-
|
|
7698
|
-
|
|
7699
|
-
|
|
7700
|
-
|
|
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"
|
|
7701
7771
|
});
|
|
7772
|
+
return key;
|
|
7702
7773
|
}
|
|
7703
7774
|
/**
|
|
7704
|
-
*
|
|
7705
|
-
*
|
|
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).
|
|
7706
7779
|
*/
|
|
7707
|
-
|
|
7708
|
-
|
|
7709
|
-
|
|
7710
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7780
|
+
createPreTokenGenerationLambda() {
|
|
7781
|
+
const construct = new PreTokenGenerationLambda(this, {
|
|
7782
|
+
dynamoTableName: this.dataStoreTable().tableName
|
|
7711
7783
|
});
|
|
7784
|
+
return construct.lambda;
|
|
7712
7785
|
}
|
|
7713
7786
|
/**
|
|
7714
|
-
*
|
|
7715
|
-
*
|
|
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.
|
|
7716
7790
|
*/
|
|
7717
|
-
|
|
7718
|
-
|
|
7719
|
-
|
|
7720
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7721
|
-
});
|
|
7791
|
+
createPostAuthenticationLambda() {
|
|
7792
|
+
const construct = new PostAuthenticationLambda(this);
|
|
7793
|
+
return construct.lambda;
|
|
7722
7794
|
}
|
|
7723
7795
|
/**
|
|
7724
|
-
*
|
|
7725
|
-
*
|
|
7796
|
+
* Creates the Post Confirmation Lambda (Cognito trigger). On sign-up
|
|
7797
|
+
* confirmation, publishes a control-plane workflow event; provisioning lives
|
|
7798
|
+
* behind EventBridge.
|
|
7726
7799
|
*/
|
|
7727
|
-
|
|
7728
|
-
|
|
7729
|
-
|
|
7730
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7800
|
+
createPostConfirmationLambda() {
|
|
7801
|
+
const construct = new PostConfirmationLambda(this, {
|
|
7802
|
+
controlEventBusName: this.controlEventBus().eventBusName
|
|
7731
7803
|
});
|
|
7804
|
+
return construct.lambda;
|
|
7732
7805
|
}
|
|
7733
|
-
|
|
7734
|
-
return
|
|
7806
|
+
createUserOnboardingWorkflow() {
|
|
7807
|
+
return new UserOnboardingWorkflow(this, {
|
|
7808
|
+
controlEventBus: this.controlEventBus(),
|
|
7809
|
+
dataStoreTable: this.dataStoreTable()
|
|
7810
|
+
});
|
|
7735
7811
|
}
|
|
7736
|
-
|
|
7737
|
-
|
|
7738
|
-
|
|
7739
|
-
this.validateConfig(props);
|
|
7740
|
-
const isReleaseBranch = this.branchName === this.defaultReleaseBranch;
|
|
7741
|
-
const hostedZone = this.createHostedZone();
|
|
7742
|
-
this.fullDomain = this.computeFullDomain(hostedZone);
|
|
7743
|
-
const shouldCreateHostingInfra = props.createHostingInfrastructure ?? isReleaseBranch;
|
|
7744
|
-
if (shouldCreateHostingInfra) {
|
|
7745
|
-
const certificate = this.createCertificate();
|
|
7746
|
-
this.staticHosting = this.createStaticHosting({
|
|
7747
|
-
certificate,
|
|
7748
|
-
hostedZone
|
|
7749
|
-
});
|
|
7750
|
-
this.createFullDomainParameter();
|
|
7751
|
-
} else if (!isReleaseBranch) {
|
|
7752
|
-
this.perBranchHostname = this.createPerBranchHostname(hostedZone);
|
|
7753
|
-
}
|
|
7754
|
-
if (props.createStaticContent !== false) {
|
|
7755
|
-
const bucket = this.resolveStaticHostingBucket();
|
|
7756
|
-
this.staticContent = this.createStaticContent(bucket);
|
|
7812
|
+
dataStoreTable() {
|
|
7813
|
+
if (this._dataStoreTable === null) {
|
|
7814
|
+
this._dataStoreTable = OpenHiDataService.dynamoDbDataStoreFromConstruct(this);
|
|
7757
7815
|
}
|
|
7816
|
+
return this._dataStoreTable;
|
|
7758
7817
|
}
|
|
7759
|
-
|
|
7760
|
-
|
|
7761
|
-
|
|
7762
|
-
validateConfig(props) {
|
|
7763
|
-
const { config } = props;
|
|
7764
|
-
if (!config) {
|
|
7765
|
-
throw new Error("Config is required");
|
|
7766
|
-
}
|
|
7767
|
-
if (!config.zoneName) {
|
|
7768
|
-
throw new Error("Zone name is required");
|
|
7769
|
-
}
|
|
7770
|
-
if (!config.hostedZoneId) {
|
|
7771
|
-
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);
|
|
7772
7821
|
}
|
|
7822
|
+
return this._controlEventBus;
|
|
7773
7823
|
}
|
|
7774
7824
|
/**
|
|
7775
|
-
*
|
|
7776
|
-
*
|
|
7777
|
-
* the same zone is imported on feature-branch deploys for any sub-domain
|
|
7778
|
-
* routing.
|
|
7825
|
+
* Creates the Cognito User Pool and exports its ID to SSM.
|
|
7826
|
+
* Look up via {@link OpenHiAuthService.userPoolFromConstruct}.
|
|
7779
7827
|
* Override to customize.
|
|
7780
7828
|
*/
|
|
7781
|
-
|
|
7782
|
-
|
|
7783
|
-
|
|
7784
|
-
|
|
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"
|
|
7785
7851
|
});
|
|
7852
|
+
return userPool;
|
|
7786
7853
|
}
|
|
7787
7854
|
/**
|
|
7788
|
-
*
|
|
7789
|
-
*
|
|
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.
|
|
7790
7862
|
*/
|
|
7791
|
-
|
|
7792
|
-
|
|
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
|
+
);
|
|
7793
7874
|
}
|
|
7794
7875
|
/**
|
|
7795
|
-
*
|
|
7796
|
-
* `
|
|
7797
|
-
* serve at `\<domainPrefix\>.\<zone\>` (e.g. `admin.dev.openhi.org`);
|
|
7798
|
-
* every other deploy serves a per-PR preview at
|
|
7799
|
-
* `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>`
|
|
7800
|
-
* (e.g. `admin-feat-1093-patient-migration.dev.openhi.org`).
|
|
7876
|
+
* Grants the Post Authentication Lambda permission to call
|
|
7877
|
+
* `cognito-idp:AdminUserGlobalSignOut`.
|
|
7801
7878
|
*
|
|
7802
|
-
*
|
|
7803
|
-
*
|
|
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.
|
|
7804
7887
|
*/
|
|
7805
|
-
|
|
7806
|
-
|
|
7807
|
-
|
|
7808
|
-
|
|
7809
|
-
|
|
7810
|
-
|
|
7811
|
-
|
|
7812
|
-
|
|
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
|
+
);
|
|
7813
7901
|
}
|
|
7814
7902
|
/**
|
|
7815
|
-
*
|
|
7816
|
-
*
|
|
7817
|
-
* {@link StaticContent} so the upload prefix always matches the
|
|
7818
|
-
* served hostname.
|
|
7819
|
-
*
|
|
7820
|
-
* Non-release deploys compose the per-PR slug as
|
|
7821
|
-
* `\<domainPrefix\>-\<childZonePrefix\>`, mirroring the REST API's
|
|
7822
|
-
* `api-\<childZonePrefix\>` convention. When `domainPrefix` is `admin`
|
|
7823
|
-
* (the only consumer today), the resulting sub-domain starts with
|
|
7824
|
-
* {@link PER_BRANCH_PREVIEW_PREFIX}, so the per-PR S3 key prefix
|
|
7825
|
-
* 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.
|
|
7826
7905
|
*/
|
|
7827
|
-
|
|
7828
|
-
|
|
7829
|
-
const domainPrefix = this.props.domainPrefix ?? _OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX;
|
|
7830
|
-
if (isReleaseBranch) {
|
|
7831
|
-
return domainPrefix;
|
|
7832
|
-
}
|
|
7833
|
-
return `${domainPrefix}-${this.childZonePrefix}`;
|
|
7906
|
+
grantPostConfirmationPermissions() {
|
|
7907
|
+
this.controlEventBus().grantPutEventsTo(this.postConfirmationLambda);
|
|
7834
7908
|
}
|
|
7835
7909
|
/**
|
|
7836
|
-
* Creates the
|
|
7837
|
-
*
|
|
7838
|
-
*
|
|
7839
|
-
*
|
|
7840
|
-
*
|
|
7841
|
-
* The bucket carries an S3 lifecycle rule that expires per-PR
|
|
7842
|
-
* preview content (keys under {@link PER_BRANCH_PREVIEW_PREFIX})
|
|
7843
|
-
* on non-production stages. PROD never gets the rule — see
|
|
7844
|
-
* `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.
|
|
7845
7915
|
*/
|
|
7846
|
-
|
|
7847
|
-
const
|
|
7848
|
-
const
|
|
7849
|
-
|
|
7850
|
-
|
|
7851
|
-
|
|
7852
|
-
|
|
7853
|
-
|
|
7854
|
-
|
|
7855
|
-
|
|
7856
|
-
|
|
7857
|
-
|
|
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"
|
|
7858
7933
|
});
|
|
7934
|
+
return client;
|
|
7859
7935
|
}
|
|
7860
7936
|
/**
|
|
7861
|
-
*
|
|
7862
|
-
*
|
|
7863
|
-
*
|
|
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.
|
|
7864
7952
|
*/
|
|
7865
|
-
|
|
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 : [];
|
|
7866
7975
|
return {
|
|
7867
|
-
|
|
7976
|
+
callbackUrls: [
|
|
7977
|
+
...deployedOrigins.map((o) => `${o}/oauth/callback`),
|
|
7978
|
+
...localhostCallbacks
|
|
7979
|
+
],
|
|
7980
|
+
logoutUrls: [
|
|
7981
|
+
...deployedOrigins.map((o) => `${o}/oauth/logout`),
|
|
7982
|
+
...localhostLogouts
|
|
7983
|
+
]
|
|
7868
7984
|
};
|
|
7869
7985
|
}
|
|
7870
7986
|
/**
|
|
7871
|
-
* Creates the
|
|
7872
|
-
* 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.
|
|
7873
7990
|
*/
|
|
7874
|
-
|
|
7875
|
-
new
|
|
7876
|
-
|
|
7877
|
-
|
|
7878
|
-
|
|
7879
|
-
|
|
7991
|
+
createUserPoolDomain() {
|
|
7992
|
+
const domain = new CognitoUserPoolDomain(this, {
|
|
7993
|
+
userPool: this.userPool,
|
|
7994
|
+
cognitoDomain: {
|
|
7995
|
+
domainPrefix: `auth-${this.branchHash}`
|
|
7996
|
+
}
|
|
7880
7997
|
});
|
|
7881
|
-
|
|
7882
|
-
|
|
7883
|
-
|
|
7884
|
-
|
|
7885
|
-
* just-created {@link staticHosting} bucket (no SSM round-trip within a
|
|
7886
|
-
* single stack); on every other deploy it is imported from the bucket ARN
|
|
7887
|
-
* the release-branch deploy publishes to SSM, addressed against
|
|
7888
|
-
* {@link OpenHiService.releaseBranchHash}. See
|
|
7889
|
-
* {@link resolveStaticHostingBucket}.
|
|
7890
|
-
*
|
|
7891
|
-
* The S3 key prefix is `\<sub-domain\>.\<zone\>/\<contentDest\>` so the
|
|
7892
|
-
* upload location matches the Host-header-derived folder the Lambda@Edge
|
|
7893
|
-
* viewer-request handler prepends. Passing the zone name (rather than
|
|
7894
|
-
* `this.fullDomain`) for the `fullDomain` prop keeps the prefix flat —
|
|
7895
|
-
* `admin-feat-foo.dev.openhi.org/`, not
|
|
7896
|
-
* `admin-feat-foo.admin.dev.openhi.org/`.
|
|
7897
|
-
*/
|
|
7898
|
-
createStaticContent(bucket) {
|
|
7899
|
-
const { contentSourceDirectory, contentDestinationDirectory } = this.props;
|
|
7900
|
-
return new StaticContent(this, "static-content", {
|
|
7901
|
-
bucket,
|
|
7902
|
-
contentSourceDirectory,
|
|
7903
|
-
contentDestinationDirectory,
|
|
7904
|
-
subDomain: this.computeSubDomain(),
|
|
7905
|
-
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"
|
|
7906
8002
|
});
|
|
8003
|
+
return domain;
|
|
7907
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 {
|
|
7908
8012
|
/**
|
|
7909
|
-
*
|
|
7910
|
-
*
|
|
7911
|
-
* `\<domainPrefix\>-\<childZonePrefix\>.\<zone\>` at the release-branch
|
|
7912
|
-
* CloudFront distribution (resolved from SSM against
|
|
7913
|
-
* {@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.
|
|
7914
8015
|
*/
|
|
7915
|
-
|
|
7916
|
-
return
|
|
7917
|
-
hostname: this.fullDomain,
|
|
7918
|
-
hostedZone,
|
|
7919
|
-
serviceType: _OpenHiWebsiteService.SERVICE_TYPE
|
|
7920
|
-
});
|
|
8016
|
+
static graphqlApiFromConstruct(scope) {
|
|
8017
|
+
return RootGraphqlApi.fromConstruct(scope);
|
|
7921
8018
|
}
|
|
7922
|
-
|
|
7923
|
-
|
|
7924
|
-
|
|
7925
|
-
|
|
7926
|
-
|
|
7927
|
-
|
|
7928
|
-
|
|
7929
|
-
|
|
7930
|
-
|
|
7931
|
-
|
|
7932
|
-
|
|
7933
|
-
|
|
7934
|
-
|
|
7935
|
-
|
|
7936
|
-
|
|
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
|
+
}
|
|
7937
8040
|
});
|
|
7938
|
-
return import_aws_s32.Bucket.fromBucketArn(this, "shared-bucket", bucketArn);
|
|
7939
8041
|
}
|
|
7940
8042
|
};
|
|
7941
|
-
|
|
7942
|
-
|
|
7943
|
-
* Default `domainPrefix` for this service when none is supplied.
|
|
7944
|
-
* Release-branch hostname is `www.<zone>`; per-PR preview hostname is
|
|
7945
|
-
* `www-<childZonePrefix>.<zone>`.
|
|
7946
|
-
*/
|
|
7947
|
-
_OpenHiWebsiteService.DEFAULT_DOMAIN_PREFIX = "www";
|
|
7948
|
-
var OpenHiWebsiteService = _OpenHiWebsiteService;
|
|
8043
|
+
_OpenHiGraphqlService.SERVICE_TYPE = "graphql-api";
|
|
8044
|
+
var OpenHiGraphqlService = _OpenHiGraphqlService;
|
|
7949
8045
|
|
|
7950
8046
|
// src/workflows/control-plane/owning-delete-cascade/events.ts
|
|
7951
8047
|
var import_workflows5 = __toESM(require_lib2());
|
|
@@ -8481,6 +8577,8 @@ var RenameCascadeWorkflow = class extends import_constructs25.Construct {
|
|
|
8481
8577
|
DataStorePostgresReplica,
|
|
8482
8578
|
DiscoverableStringParameter,
|
|
8483
8579
|
DynamoDbDataStore,
|
|
8580
|
+
LOCALHOST_OAUTH_CALLBACK_URLS,
|
|
8581
|
+
LOCALHOST_OAUTH_LOGOUT_URLS,
|
|
8484
8582
|
OPENHI_REPO_TAG_KEY_ENV_VAR,
|
|
8485
8583
|
OPENHI_RESOURCE_URN_SYSTEM,
|
|
8486
8584
|
OPENHI_TAG_KEY_PREFIX_ENV_VAR,
|