Skip to content

Commit f985b0e

Browse files
authored
feat: IVS real-time screen share (#29)
* merge: real-time screenshare * update: cdk package-lock, docker version to 1.45.0 * remove: github folder * hotfix: preAuthenticationLambda var name * hotfix: remove readme auto-formatting
1 parent f871a14 commit f985b0e

File tree

388 files changed

+61175
-23332
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

388 files changed

+61175
-23332
lines changed

README.md

Lines changed: 84 additions & 29 deletions
Large diffs are not rendered by default.

cdk/Makefile

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,16 @@ RESET := \033[0m
44

55
.PHONY: help app install bootstrap deploy destroy clean
66

7-
AWS_PROFILE_FLAG = --profile $(AWS_PROFILE)
8-
STAGE ?= dev
9-
PUBLISH ?= false
10-
STACK ?= UGC-$(STAGE)
11-
SCHEDULE ?= rate(48 hours)
12-
CDK_OPTIONS = $(if $(AWS_PROFILE),$(AWS_PROFILE_FLAG)) -c stage=$(STAGE) -c publish=$(PUBLISH) -c stackName=$(STACK) -c scheduleExp="$(strip $(SCHEDULE))"
13-
FE_DEPLOYMENT_STACK = UGC-Frontend-Deployment-$(STAGE)
14-
SEED_COUNT ?= 50
15-
OFFLINE_SESSION_COUNT ?= 1
7+
AWS_PROFILE_FLAG = --profile $(AWS_PROFILE)
8+
STAGE ?= dev
9+
PUBLISH ?= false
10+
STACK ?= UGC-$(STAGE)
11+
COGNITO_CLEANUP_SCHEDULE ?= rate(48 hours)
12+
STAGE_CLEANUP_SCHEDULE ?= rate(24 hours)
13+
CDK_OPTIONS = $(if $(AWS_PROFILE),$(AWS_PROFILE_FLAG)) -c stage=$(STAGE) -c publish=$(PUBLISH) -c stackName=$(STACK) -c cognitoCleanupScheduleExp="$(strip $(COGNITO_CLEANUP_SCHEDULE))" -c stageCleanupScheduleExp="$(strip $(STAGE_CLEANUP_SCHEDULE))"
14+
FE_DEPLOYMENT_STACK = UGC-Frontend-Deployment-$(STAGE)
15+
SEED_COUNT ?= 50
16+
OFFLINE_SESSION_COUNT ?= 1
1617

1718
help: ## Shows this help message
1819
@echo "\n$$(tput bold)Available Rules:$$(tput sgr0)\n"

cdk/README.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ The outcome of the seed command above is 50 new channels and streams, 20 of thes
3434
make seed JSON=./seed.example.json
3535
```
3636

37-
**\*NOTE:** Please refer to the [seed.example.json file](./seed.example.json) so that you may create data that the DynamoDB client can use. Incorrect JSON data will lead to errors when seeding.\*
37+
***NOTE:** Please refer to the [seed.example.json file](./seed.example.json) so that you may create data that the DynamoDB client can use. Incorrect JSON data will lead to errors when seeding.*
3838

3939
All the environment variables can be used together to generate mock data that suites your needs:
4040

@@ -82,3 +82,49 @@ When the postStreamEvents function receives an event with a `session created` ev
8282
When a `session ended` event is received, the corresponding stream record is grabbed and updated by removing the `isOpen` attribute.
8383

8484
You can find the Stream events API code in the `/streamEventsApi` folder.
85+
86+
## Scheduled resource cleanup AWS Lambdas
87+
88+
The scheduled resource cleanup lambdas are created when the stack is deployed. No additional steps are necessary to set up and trigger these functions. The lambda functions exist in the `cdk/lambdas` folder. The schedule can be customized in the `cdk/Makefile`. AWS Lambda supports standard rate and cron expressions for frequencies of up to once per minute. Read more about [schedule expressions using rate or cron](https://docs.aws.amazon.com/lambda/latest/dg/services-cloudwatchevents-expressions.html).
89+
90+
### Amazon IVS idle stages cleanup
91+
92+
The cleanup lambda function follows these steps:
93+
1. It retrieves a list of all stages
94+
2. The `getIdleStageArn` helper function is employed to filter out idle stages that have been in existence for at least 1 hour
95+
3. Subsequently, the filtered idle stages are deleted.
96+
97+
### Amazon Cognito unverified users cleanup
98+
99+
The cleanup lambda function follows these steps:
100+
1. It retrieves a list of users with a `UNCONFIRMED` status
101+
2. Within the unconfirmed users, we filter for users that have existed for at least 24 hours
102+
3. Finally, the filtered unverified and expired users are deleted both from the Cognito user pool and AWS DynamoDB channels table.
103+
104+
105+
## Amazon IVS Real-time host disconnect event handler
106+
107+
Amazon IVS host disconnect event cleanup is designed to delete active stages for which hosts have been disconnected from the session for at least 3 minutes. Upon a host's disconnection from a stage, the responsible endpoint follows these steps:
108+
1. Extracts the host's channel ID from the request body, accommodating both object and JSON string formats
109+
2. It retrieves the corresponding host details, including the stage and session, and formats them into a message body
110+
3. This structured data is sent to an Amazon SQS queue for additional processing. It's important to highlight that the message comes with a 3-minute delay, allowing the host sufficient time to rejoin if they choose to
111+
4. After 3-minute wait time, SQS triggers deleteStage lambda function.
112+
113+
### Amazon IVS Real-time host disconnect event cleanup triggers
114+
115+
This flow is triggered by:
116+
1. Beacon API [Beacon API documention](https://developer.mozilla.org/en-US/docs/Web/API/Beacon_API)
117+
2. Stage participantConnectionChangedEvent event [STAGE_CONNECTION_STATE_CHANGED](https://aws.github.io/amazon-ivs-web-broadcast/docs/sdk-reference/enums/StageEvents#stage_connection_state_changed)
118+
3. EventBridge
119+
4. SQS [Amazon FIFO SQS](#amazon-fifo-sqs)
120+
121+
### Amazon IVS real-time host disconnect event lamnda cleanup
122+
123+
The cleanup lambda function follows these steps:
124+
1. Lambda receives a message from the SQS queue
125+
2. Checks if the host is present in the stage for potential reconnection
126+
3. If the host is not detected in the stage, deletes the stage
127+
4. Updates the channel table to reflect changes such as the removal/nullification of the stageId and stageCreationDate fields.
128+
129+
### Amazon FIFO SQS
130+
A FIFO queue using content body for message deduplication and a 3-minute delayed delivery, allowing the host sufficient time to rejoin if they choose to.

cdk/api/__mocks__/userInfo.json

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
{
2+
"UserItem": {
3+
"trackingId": {
4+
"S": "4wpfqidqx0pv"
5+
},
6+
"avatar": {
7+
"S": "bird2"
8+
},
9+
"streamKeyValue": {
10+
"S": "sk_us-east-1_DbRFyxHFRDYp_Olmd3kZgUt5gOGVriEnjk9jT6Ap9C8"
11+
},
12+
"email": {
13+
"S": "sina@fournine.digital"
14+
},
15+
"ingestEndpoint": {
16+
"S": "bb6c96699fb6.global-contribute.live-video.net"
17+
},
18+
"chatRoomArn": {
19+
"S": "arn:aws:ivschat:us-east-1:045801317995:room/w0QFAKIvsEVe"
20+
},
21+
"channelArn": {
22+
"S": "arn:aws:ivs:us-east-1:045801317995:channel/4WpFQIDqX0pV"
23+
},
24+
"playbackUrl": {
25+
"S": "https://bb6c96699fb6.us-east-1.playback.live-video.net/api/video/v1/us-east-1.045801317995.channel.4WpFQIDqX0pV.m3u8"
26+
},
27+
"username": {
28+
"S": "sina"
29+
},
30+
"channelAssets": {
31+
"M": {}
32+
},
33+
"channelAssetId": {
34+
"S": "7625ec0f-9f2f-54e6-b08a-d6a586b8c4e6"
35+
},
36+
"id": {
37+
"S": "d4c834b8-7021-70bd-467b-2ab09fc164fe"
38+
},
39+
"color": {
40+
"S": "salmon"
41+
},
42+
"streamKeyArn": {
43+
"S": "arn:aws:ivs:us-east-1:045801317995:stream-key/DbRFyxHFRDYp"
44+
}
45+
}
46+
}

cdk/api/buildServer.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import channelRouters from './channel';
55
import channelsRouters from './channels';
66
import metricsRouter from './metrics/';
77
import productsRouters from './product';
8+
import stagesRouter from './stages';
89

910
import configureRoute from './shared/hooks/configureRoute';
1011
import { MAX_SERVER_PARAM_LENGTH } from './shared/constants';
@@ -49,6 +50,8 @@ const buildServer = () => {
4950
// Create /products authenticated resources
5051
server.register(productsRouters, { prefix: 'products' });
5152
}
53+
54+
server.register(stagesRouter, { prefix: 'stages' });
5255
}
5356

5457
if (['all', 'metrics'].includes(serviceName)) {

cdk/api/channel/authRouter/__tests__/banUser.test.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ import {
22
GetItemCommandOutput,
33
UpdateItemCommand
44
} from '@aws-sdk/client-dynamodb';
5-
import {
6-
DisconnectUserCommand,
7-
SendEventCommand
8-
} from '@aws-sdk/client-ivschat';
95
import { mockClient } from 'aws-sdk-client-mock';
106

117
import buildServer from '../../../buildServer';

cdk/api/channel/authRouter/addToFollowingList.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
getChannelId
1515
} from '../../shared/helpers';
1616
import { getUser, getUserByUsername } from '../helpers';
17-
import { UserContext } from '../authorizer';
17+
import { UserContext } from '../../shared/authorizer';
1818

1919
export const getFollowingChannelArn = async ({
2020
reply,

cdk/api/channel/authRouter/banUser.ts

Lines changed: 5 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,15 @@
11
import { FastifyReply, FastifyRequest } from 'fastify';
22
import { convertToAttr, unmarshall } from '@aws-sdk/util-dynamodb';
3-
import {
4-
DisconnectUserCommand,
5-
SendEventCommand
6-
} from '@aws-sdk/client-ivschat';
73
import { UpdateItemCommand } from '@aws-sdk/client-dynamodb';
84

9-
import {
10-
dynamoDbClient,
11-
getUserByChannelArn,
12-
isIvsChatError,
13-
ivsChatClient
14-
} from '../../shared/helpers';
5+
import { dynamoDbClient, getUserByChannelArn } from '../../shared/helpers';
156
import { getUser } from '../helpers';
167
import {
178
UNEXPECTED_EXCEPTION,
189
BAN_USER_EXCEPTION,
1910
USER_NOT_FOUND_EXCEPTION
2011
} from '../../shared/constants';
21-
import { UserContext } from '../authorizer';
12+
import { UserContext } from '../../shared/authorizer';
2213

2314
type BanUserRequestBody = { bannedChannelArn?: string };
2415

@@ -30,7 +21,7 @@ const handler = async (
3021
'user'
3122
) as UserContext;
3223
const { bannedChannelArn } = request.body;
33-
let chatRoomArn, chatRoomOwnerChannelArn, bannedUsername;
24+
let chatRoomOwnerChannelArn;
3425

3526
// Check input
3627
if (!bannedChannelArn) {
@@ -46,8 +37,7 @@ const handler = async (
4637
// Retrieve the chat room data for the requester's channel
4738
try {
4839
const { Item: UserItem = {} } = await getUser(sub);
49-
({ chatRoomArn, channelArn: chatRoomOwnerChannelArn } =
50-
unmarshall(UserItem));
40+
({ channelArn: chatRoomOwnerChannelArn } = unmarshall(UserItem));
5141

5242
// Disallow users from banning themselves from their own channel
5343
if (bannedChannelArn === chatRoomOwnerChannelArn) {
@@ -83,8 +73,7 @@ const handler = async (
8373
}
8474

8575
// Add the bannedUserSub to the bannedUserSubs set in the user table
86-
const { id: bannedUserSub, username } = unmarshall(BannedUserItems[0]);
87-
bannedUsername = username;
76+
const { id: bannedUserSub } = unmarshall(BannedUserItems[0]);
8877

8978
await dynamoDbClient.send(
9079
new UpdateItemCommand({

cdk/api/channel/authRouter/changeUserPreferences.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { FastifyReply, FastifyRequest } from 'fastify';
44
import { CHANGE_USER_PREFERENCES_EXCEPTION } from '../../shared/constants';
55
import { updateDynamoItemAttributes } from '../../shared/helpers';
66
import { processAssetPreference, Preference } from '../helpers';
7-
import { UserContext } from '../authorizer';
7+
import { UserContext } from '../../shared/authorizer';
88

99
interface ChangeUserPreferencesRequestBody {
1010
[key: string]: Preference;

cdk/api/channel/authRouter/changeUsername.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
ResponseBody,
1616
updateDynamoItemAttributes
1717
} from '../../shared/helpers';
18-
import { UserContext } from '../authorizer';
18+
import { UserContext } from '../../shared/authorizer';
1919

2020
type ChangeUsernameRequestBody = { newUsername: string | undefined };
2121

0 commit comments

Comments
 (0)