resource "aws_apigatewayv2_api" "this" { name = local.project protocol_type = "HTTP" description = "Yealink door unlock and lockdown HTTP API" } # Invoke permission is a Lambda resource policy, not AuthorizerCredentialsArn. # The credentials-role path returned 500 without invoking the authorizer # (PLAT-102); resource policy matches the route grants. resource "aws_apigatewayv2_authorizer" "token" { api_id = aws_apigatewayv2_api.this.id name = "${local.project}-token-authorizer" authorizer_type = "REQUEST" authorizer_uri = aws_lambda_function.authorizer.invoke_arn authorizer_payload_format_version = "2.0" authorizer_result_ttl_in_seconds = 300 enable_simple_responses = true identity_sources = ["$request.querystring.token"] } resource "aws_apigatewayv2_integration" "unlock" { api_id = aws_apigatewayv2_api.this.id integration_type = "AWS_PROXY" integration_method = "POST" integration_uri = aws_lambda_function.unlock.invoke_arn payload_format_version = "2.0" timeout_milliseconds = 20000 } resource "aws_apigatewayv2_integration" "lockdown" { api_id = aws_apigatewayv2_api.this.id integration_type = "AWS_PROXY" integration_method = "POST" integration_uri = aws_lambda_function.lockdown.invoke_arn payload_format_version = "2.0" timeout_milliseconds = 15000 } resource "aws_apigatewayv2_route" "unlock" { api_id = aws_apigatewayv2_api.this.id route_key = "GET /unlock" target = "integrations/${aws_apigatewayv2_integration.unlock.id}" authorization_type = "CUSTOM" authorizer_id = aws_apigatewayv2_authorizer.token.id } resource "aws_apigatewayv2_route" "lockdown" { api_id = aws_apigatewayv2_api.this.id route_key = "GET /lockdown" target = "integrations/${aws_apigatewayv2_integration.lockdown.id}" authorization_type = "CUSTOM" authorizer_id = aws_apigatewayv2_authorizer.token.id } resource "aws_apigatewayv2_route" "lockdown_status" { api_id = aws_apigatewayv2_api.this.id route_key = "GET /lockdown/status" target = "integrations/${aws_apigatewayv2_integration.lockdown.id}" authorization_type = "CUSTOM" authorizer_id = aws_apigatewayv2_authorizer.token.id } resource "aws_apigatewayv2_stage" "default" { api_id = aws_apigatewayv2_api.this.id name = "$default" auto_deploy = true access_log_settings { destination_arn = aws_cloudwatch_log_group.api_access.arn format = "{\"requestId\":\"$context.requestId\",\"ip\":\"$context.identity.sourceIp\",\"requestTime\":\"$context.requestTime\",\"method\":\"$context.httpMethod\",\"routeKey\":\"$context.routeKey\",\"status\":\"$context.status\",\"protocol\":\"$context.protocol\",\"responseLength\":\"$context.responseLength\",\"integrationError\":\"$context.integrationErrorMessage\"}" } default_route_settings { throttling_burst_limit = 5 throttling_rate_limit = 2 } depends_on = [ aws_apigatewayv2_route.unlock, aws_apigatewayv2_route.lockdown, aws_apigatewayv2_route.lockdown_status, ] } resource "aws_lambda_permission" "unlock_route" { statement_id = "AllowApiGatewayInvokeUnlock" action = "lambda:InvokeFunction" function_name = aws_lambda_function.unlock.function_name principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/GET/unlock" } resource "aws_lambda_permission" "lockdown_route" { statement_id = "AllowApiGatewayInvokeLockdown" action = "lambda:InvokeFunction" function_name = aws_lambda_function.lockdown.function_name principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/GET/lockdown" } resource "aws_lambda_permission" "lockdown_status_route" { statement_id = "AllowApiGatewayInvokeLockdownStatus" action = "lambda:InvokeFunction" function_name = aws_lambda_function.lockdown.function_name principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.this.execution_arn}/*/GET/lockdown/status" } resource "aws_lambda_permission" "authorizer" { statement_id = "AllowApiGatewayInvokeAuthorizer" action = "lambda:InvokeFunction" function_name = aws_lambda_function.authorizer.function_name principal = "apigateway.amazonaws.com" source_arn = "${aws_apigatewayv2_api.this.execution_arn}/authorizers/${aws_apigatewayv2_authorizer.token.id}" } resource "aws_apigatewayv2_domain_name" "this" { domain_name = var.domain_name domain_name_configuration { certificate_arn = data.aws_acm_certificate.doorunlock.arn endpoint_type = "REGIONAL" security_policy = "TLS_1_2" } } resource "aws_apigatewayv2_api_mapping" "this" { api_id = aws_apigatewayv2_api.this.id domain_name = aws_apigatewayv2_domain_name.this.id stage = aws_apigatewayv2_stage.default.id }