mirror of
https://github.com/Sea-Haven-Industries/afterhours-shift-manager.git
synced 2026-09-30 07:53:11 +00:00
Add AccessLogSettings on the implicit HTTP API stage pointing at a new /aws/apigateway/afterhours-shift-manager log group with 90-day retention, plus DefaultRouteSettings throttling (100 rps / 50 burst). Mirrors the M-18 pattern landed on payments-dashboard.
289 lines
9.1 KiB
YAML
289 lines
9.1 KiB
YAML
AWSTemplateFormatVersion: "2010-09-09"
|
|
Transform: AWS::Serverless-2016-10-31
|
|
Description: After-Hours Shift Manager — Slack bot for managing on-call shifts with 3CX integration
|
|
|
|
Parameters:
|
|
Timezone:
|
|
Type: String
|
|
Default: "America/New_York"
|
|
ShiftChannel:
|
|
Type: String
|
|
Description: Slack channel ID for schedule posts and shift notifications
|
|
QueueNumber:
|
|
Type: String
|
|
Default: "801"
|
|
Description: 3CX queue extension number to update
|
|
|
|
Globals:
|
|
Function:
|
|
Runtime: python3.12
|
|
Timeout: 30
|
|
MemorySize: 1024
|
|
Architectures:
|
|
- arm64
|
|
# Access logging + default throttling on the implicit HTTP API (audit M-18).
|
|
HttpApi:
|
|
AccessLogSettings:
|
|
DestinationArn: !GetAtt ApiAccessLogGroup.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"}'
|
|
DefaultRouteSettings:
|
|
ThrottlingBurstLimit: 50
|
|
ThrottlingRateLimit: 100
|
|
|
|
Resources:
|
|
ApiAccessLogGroup:
|
|
Type: AWS::Logs::LogGroup
|
|
Properties:
|
|
LogGroupName: /aws/apigateway/afterhours-shift-manager
|
|
RetentionInDays: 90
|
|
|
|
# --- Shared Lambda Layer ---
|
|
SharedLayer:
|
|
Type: AWS::Serverless::LayerVersion
|
|
Properties:
|
|
LayerName: afterhours-shared
|
|
ContentUri: src/shared/
|
|
CompatibleRuntimes:
|
|
- python3.12
|
|
CompatibleArchitectures:
|
|
- arm64
|
|
Metadata:
|
|
BuildMethod: python3.12
|
|
BuildArchitecture: arm64
|
|
|
|
# --- DynamoDB ---
|
|
ShiftTable:
|
|
Type: AWS::DynamoDB::Table
|
|
Properties:
|
|
TableName: afterhours-shifts
|
|
BillingMode: PAY_PER_REQUEST
|
|
AttributeDefinitions:
|
|
- AttributeName: PK
|
|
AttributeType: S
|
|
- AttributeName: SK
|
|
AttributeType: S
|
|
KeySchema:
|
|
- AttributeName: PK
|
|
KeyType: HASH
|
|
- AttributeName: SK
|
|
KeyType: RANGE
|
|
TimeToLiveSpecification:
|
|
AttributeName: expires_at
|
|
Enabled: true
|
|
|
|
# --- Slack Bot Lambda ---
|
|
SlackBotFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
FunctionName: afterhours-shift-manager
|
|
Handler: handler.handler
|
|
CodeUri: src/slack-bot/
|
|
Layers:
|
|
- !Ref SharedLayer
|
|
Environment:
|
|
Variables:
|
|
SHIFT_TABLE: !Ref ShiftTable
|
|
SLACK_BOT_TOKEN_SECRET: afterhours-shift-manager/slack-bot-token
|
|
SLACK_SIGNING_SECRET: afterhours-shift-manager/slack-signing-secret
|
|
SHIFT_CHANNEL: !Ref ShiftChannel
|
|
TCX_SECRET_PREFIX: afterhours-shift-manager/3cx-
|
|
QUEUE_NUMBER: !Ref QueueNumber
|
|
TZ: !Ref Timezone
|
|
Policies:
|
|
- DynamoDBCrudPolicy:
|
|
TableName: !Ref ShiftTable
|
|
- Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- secretsmanager:GetSecretValue
|
|
Resource:
|
|
- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*"
|
|
Events:
|
|
SlackEvents:
|
|
Type: HttpApi
|
|
Properties:
|
|
Path: /slack/events
|
|
Method: POST
|
|
|
|
# --- Weekly Schedule Post (Monday 7am ET) ---
|
|
WeeklyPostFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
FunctionName: afterhours-weekly-post
|
|
Handler: app.handler
|
|
CodeUri: src/weekly-post/
|
|
Layers:
|
|
- !Ref SharedLayer
|
|
Environment:
|
|
Variables:
|
|
SHIFT_TABLE: !Ref ShiftTable
|
|
SLACK_BOT_TOKEN_SECRET: afterhours-shift-manager/slack-bot-token
|
|
SHIFT_CHANNEL: !Ref ShiftChannel
|
|
SES_SENDER: noreply@seahaven.com
|
|
PAYROLL_RECIPIENTS: payroll@seahaven.com
|
|
PAY_REPORT_USER: U0A3SC48T47
|
|
TZ: !Ref Timezone
|
|
Policies:
|
|
- DynamoDBCrudPolicy:
|
|
TableName: !Ref ShiftTable
|
|
- Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- secretsmanager:GetSecretValue
|
|
Resource:
|
|
- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*"
|
|
- Effect: Allow
|
|
Action:
|
|
- ses:SendEmail
|
|
Resource:
|
|
- !Sub "arn:aws:ses:${AWS::Region}:${AWS::AccountId}:identity/*"
|
|
Events:
|
|
# EST: 7am ET = 12:00 UTC (Nov-Mar)
|
|
WeeklyPostEST:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 12 ? * MON *)
|
|
Description: "Post weekly schedule Monday 7am EST"
|
|
Enabled: true
|
|
# EDT: 7am ET = 11:00 UTC (Mar-Nov)
|
|
WeeklyPostEDT:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 11 ? * MON *)
|
|
Description: "Post weekly schedule Monday 7am EDT"
|
|
Enabled: true
|
|
|
|
# --- Roster Sync Lambda (daily sync from 3CX) ---
|
|
RosterSyncFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
FunctionName: afterhours-roster-sync
|
|
Handler: app.handler
|
|
CodeUri: src/roster-sync/
|
|
Layers:
|
|
- !Ref SharedLayer
|
|
Timeout: 60
|
|
Environment:
|
|
Variables:
|
|
SHIFT_TABLE: !Ref ShiftTable
|
|
TCX_SECRET_PREFIX: afterhours-shift-manager/3cx-
|
|
SYNC_GROUP: DEFAULT
|
|
TZ: !Ref Timezone
|
|
Policies:
|
|
- DynamoDBCrudPolicy:
|
|
TableName: !Ref ShiftTable
|
|
- Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- secretsmanager:GetSecretValue
|
|
Resource:
|
|
- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*"
|
|
Events:
|
|
# Daily at 6am ET (before the 7am schedule post and 8am 3CX scheduler)
|
|
# EST: 6am ET = 11:00 UTC (Nov-Mar)
|
|
RosterSyncEST:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 11 ? * * *)
|
|
Description: "Sync roster from 3CX at 6am EST"
|
|
Enabled: true
|
|
# EDT: 6am ET = 10:00 UTC (Mar-Nov)
|
|
RosterSyncEDT:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 10 ? * * *)
|
|
Description: "Sync roster from 3CX at 6am EDT"
|
|
Enabled: true
|
|
|
|
# --- Ring Scheduler (daily 3CX queue routing updates) ---
|
|
RingSchedulerFunction:
|
|
Type: AWS::Serverless::Function
|
|
Properties:
|
|
FunctionName: afterhours-ring-scheduler
|
|
Handler: app.handler
|
|
CodeUri: src/ring-scheduler/
|
|
Layers:
|
|
- !Ref SharedLayer
|
|
Timeout: 60
|
|
Environment:
|
|
Variables:
|
|
SHIFT_TABLE: !Ref ShiftTable
|
|
TCX_SECRET_PREFIX: afterhours-shift-manager/3cx-
|
|
QUEUE_NUMBER: !Ref QueueNumber
|
|
TZ: !Ref Timezone
|
|
Policies:
|
|
- DynamoDBCrudPolicy:
|
|
TableName: !Ref ShiftTable
|
|
- Statement:
|
|
- Effect: Allow
|
|
Action:
|
|
- secretsmanager:GetSecretValue
|
|
Resource:
|
|
- !Sub "arn:aws:secretsmanager:${AWS::Region}:${AWS::AccountId}:secret:afterhours-shift-manager/*"
|
|
Events:
|
|
# Daily at 8am ET — update after-hours routing
|
|
DailyScheduleEST:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 13 ? * * *)
|
|
Description: "Update 3CX queue at 8am EST"
|
|
Enabled: true
|
|
DailyScheduleEDT:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 12 ? * * *)
|
|
Description: "Update 3CX queue at 8am EDT"
|
|
Enabled: true
|
|
# Weekends at 5pm ET — switch to night shift person
|
|
WeekendEveningEST:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 22 ? * SAT,SUN *)
|
|
Description: "Update 3CX queue at 5pm EST weekends"
|
|
Enabled: true
|
|
WeekendEveningEDT:
|
|
Type: Schedule
|
|
Properties:
|
|
Schedule: cron(0 21 ? * SAT,SUN *)
|
|
Description: "Update 3CX queue at 5pm EDT weekends"
|
|
Enabled: true
|
|
|
|
# --- CloudWatch Log Groups (explicit 60-day retention) ---
|
|
SlackBotLogGroup:
|
|
Type: AWS::Logs::LogGroup
|
|
Properties:
|
|
LogGroupName: !Sub "/aws/lambda/${SlackBotFunction}"
|
|
RetentionInDays: 60
|
|
|
|
WeeklyPostLogGroup:
|
|
Type: AWS::Logs::LogGroup
|
|
Properties:
|
|
LogGroupName: !Sub "/aws/lambda/${WeeklyPostFunction}"
|
|
RetentionInDays: 60
|
|
|
|
RosterSyncLogGroup:
|
|
Type: AWS::Logs::LogGroup
|
|
Properties:
|
|
LogGroupName: !Sub "/aws/lambda/${RosterSyncFunction}"
|
|
RetentionInDays: 60
|
|
|
|
RingSchedulerLogGroup:
|
|
Type: AWS::Logs::LogGroup
|
|
Properties:
|
|
LogGroupName: !Sub "/aws/lambda/${RingSchedulerFunction}"
|
|
RetentionInDays: 60
|
|
|
|
Outputs:
|
|
SlackBotApiUrl:
|
|
Description: URL for Slack app Request URL configuration
|
|
Value: !Sub "https://${ServerlessHttpApi}.execute-api.${AWS::Region}.amazonaws.com/slack/events"
|
|
ShiftTableName:
|
|
Value: !Ref ShiftTable
|
|
SlackBotFunctionArn:
|
|
Value: !GetAtt SlackBotFunction.Arn
|
|
WeeklyPostFunctionArn:
|
|
Value: !GetAtt WeeklyPostFunction.Arn
|
|
RosterSyncFunctionArn:
|
|
Value: !GetAtt RosterSyncFunction.Arn
|
|
RingSchedulerFunctionArn:
|
|
Value: !GetAtt RingSchedulerFunction.Arn
|