Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: Build and Publish Docker Image
on:
push:
branches:
- main

jobs:
build:
runs-on: ubuntu-latest

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Log in to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_PASSWORD }}

- name: Build and push docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: true
tags: ${{ secrets.DOCKERHUB_USERNAME }}/mitsi-media:latest
79 changes: 79 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
"dotenv": "^17.2.1",
"express": "^5.1.0",
"helmet": "^8.1.0",
"ioredis": "^5.8.2",
"mediasoup": "3.19.4",
"public-ip": "^7.0.1",
"redis": "^5.8.0",
Expand Down
30 changes: 17 additions & 13 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@ import { createServer } from 'https';

import config from './config';
import { Routes } from './routes';
import { redisServer } from './servers/redis-server';
import { grpcServer } from './servers/grpc-server';
import { MediaNodeData } from './types';
import { getRedisKey, registerMediaNode } from './lib/utils';
import { getRedisKey, handleHeartBeat, registerMediaNode } from './lib/utils';
import { mediaSoupServer } from './servers/mediasoup-server';
import { Actions } from './types/actions';
import { ioRedisServer } from './servers/ioredis-server';

const app = express();
app.use(cors(config.cors));
Expand All @@ -20,19 +19,23 @@ app.use('/', Routes);

const httpsServer = createServer(config.httpsServerOptions, app);

let medianodeData: MediaNodeData;
// let medianodeData: MediaNodeData;
let heartBeatInterval: NodeJS.Timeout;

(async (): Promise<void> => {
try {
await redisServer.connect();
await ioRedisServer.connect();
httpsServer.listen(config.port, () => {
console.log(`Server running on port ${config.port}`);
});
await grpcServer.start();

await mediaSoupServer.start();

medianodeData = await registerMediaNode();
await registerMediaNode();

heartBeatInterval = setInterval(handleHeartBeat, 120000);

console.log('Register medianode');
} catch (error) {
console.error('Initialization error:', error);
Expand All @@ -42,19 +45,20 @@ let medianodeData: MediaNodeData;

const shutdown = async (): Promise<void> => {
try {
await redisServer.sRem(
getRedisKey['medianodes'](),
JSON.stringify(medianodeData)
);
await redisServer.publish({
await ioRedisServer.publish({
channel: Actions.Message,
action: Actions.MediaNodeRemoved,
args: { id: medianodeData.id },
args: { id: config.nodeId },
});

await ioRedisServer.del(getRedisKey['medianode'](config.nodeId));

console.log('Delete medianode');
httpsServer.close();
mediaSoupServer.shutdown();
await redisServer.disconnect();
await ioRedisServer.disconnect();

clearInterval(heartBeatInterval);
console.log('Application shut down gracefully');
process.exit(0);
} catch (err) {
Expand Down
33 changes: 26 additions & 7 deletions src/lib/utils.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import config from '../config';
import { redisServer } from '../servers/redis-server';
import { ioRedisServer } from '../servers/ioredis-server';
import { MediaNodeData } from '../types';
import { Actions } from '../types/actions';

export const HEARTBEAT_TIMEOUT = 60000; // 90 seconds / 1.30mins
export const MEDIANODE_TTL = 300000; // 180seconds 3mins

export const getRedisKey = {
room: (roomId: string): string => `room:${roomId}`,
Expand All @@ -13,7 +14,8 @@ export const getRedisKey = {
roomActiveSpeakerPeerId: (roomId: string): string =>
`room:${roomId}:activespeakerpeerid`,
rooms: (): string => `rooms`,
medianodes: (): string => `medianodes`,
medianodes: (): string => `medianodes`, // todo delete
medianode: (nodeId: string): string => `medianodes:${nodeId}`,
signalnodes: (): string => `signalnodes`,
roomMedianodes: (roomId: string): string => `room:${roomId}:medianodes`,
roomSignalnodes: (roomId: string): string => `room:${roomId}:signalnodes`,
Expand All @@ -34,13 +36,23 @@ export const registerMediaNode = async (): Promise<MediaNodeData> => {
};
// todo remove data from redis if it matches this node id

await redisServer.sAdd(
getRedisKey['medianodes'](),
JSON.stringify(medianodeData)
);
// await redisServer.sAdd(
// {etRedisKey['medianodes'](),
// JSON.stringify(medianodeData)
// );

// register medianode in redis hash for easy lookup
await ioRedisServer.hSet(getRedisKey['medianode'](medianodeData.id), {
...medianodeData,
});

// set ttl for medianode
await ioRedisServer.expire(
getRedisKey['medianode'](medianodeData.id),
MEDIANODE_TTL / 1000
);
// publish update to notify other services
await redisServer.publish({
await ioRedisServer.publish({
channel: Actions.Message,
action: Actions.MediaNodeAdded,
args: { ...medianodeData },
Expand All @@ -51,6 +63,13 @@ export const registerMediaNode = async (): Promise<MediaNodeData> => {
}
};

export const handleHeartBeat = async (): Promise<void> => {
ioRedisServer
.expire(getRedisKey['medianode'](config.nodeId), MEDIANODE_TTL / 1000)
.catch(error => console.log(error));
console.log('handleHeartBeat');
};

export const parseArguments = (args?: string): { [key: string]: unknown } => {
let parsedArgs: { [key: string]: unknown } = {};
if (args) {
Expand Down
18 changes: 0 additions & 18 deletions src/servers/grpc-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,30 +26,12 @@ class GrpcServer extends EventEmitter {

this.server = new grpc.Server();

// this.server = new grpc.Server({
// 'grpc.keepalive_time_ms': 10000,
// 'grpc.keepalive_timeout_ms': 5000,
// 'grpc.keepalive_permit_without_calls': 1,
// 'grpc.http2.max_pings_without_data': 0,
// 'grpc.http2.min_time_between_pings_ms': 10000,
// 'grpc.http2.min_ping_interval_without_data_ms': 300000,
// 'grpc.max_connection_idle_ms': 300000,
// 'grpc.max_connection_age_ms': 600000,
// 'grpc.max_connection_age_grace_ms': 30000,
// 'grpc.max_receive_message_length': 4 * 1024 * 1024, // 4MB
// 'grpc.max_send_message_length': 4 * 1024 * 1024, // 4MB
// });

this.startTime = new Date();
this.cleanupInterval = null;
this.healthCheckInterval = null;
this.metricsInterval = null;

this.setup();

// Graceful shutdown handling
// process.on('SIGINT', () => this.gracefulShutdown('SIGINT'));
// process.on('SIGTERM', () => this.gracefulShutdown('SIGTERM'));
}

static getInstance(): GrpcServer {
Expand Down
Loading