Skip to content

Commit b8005c4

Browse files
committed
feat(chaincast) - changed the complete chaincast setup to livekit video conference
1 parent 8f4d2b2 commit b8005c4

32 files changed

Lines changed: 722 additions & 441 deletions

File tree

backend/src/core/interfaces/services/chainCast/IChainCast.service.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export interface IChainCastService {
3131

3232
// User ChainCast participation
3333
getCommunityChainCasts(communityId: string, userId: string, query: GetChainCastsQueryDto): Promise<ChainCastsListResponseDto>;
34-
joinChainCast(userId: string, data: JoinChainCastDto): Promise<{ success: boolean; message: string; streamUrl?: string }>;
34+
joinChainCast(userId: string, data: JoinChainCastDto): Promise<{ success: boolean; message: string; streamUrl?: string; livekitToken?: string; serverUrl?: string }>;
3535
leaveChainCast(userId: string, chainCastId: string): Promise<{ success: boolean; message: string }>;
3636

3737
// Participant management
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { VideoGrant } from "livekit-server-sdk";
2+
13
export interface ILiveKitService {
2-
generateToken(roomName: string, identity: string, name?: string): Promise<string>;
4+
generateToken(roomName: string, identity: string, name?: string, grants?: VideoGrant): Promise<string>;
35
}

backend/src/dtos/chainCast/ChainCast.dto.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ export class ChainCastResponseDto {
232232
isParticipant!: boolean;
233233
userRole?: 'viewer' | 'moderator' | 'admin';
234234
streamUrl?: string;
235+
livekitToken?: string;
236+
serverUrl?: string;
235237
createdAt!: Date;
236238
updatedAt!: Date;
237239

backend/src/models/community.model.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ const CommunitySchema: Schema<ICommunity> = new Schema({
4747
isVerified: { type: Boolean, default: false },
4848
communityAdmins: [{ type: Schema.Types.ObjectId, ref: 'CommunityAdmin' }],
4949
settings: {
50-
allowChainCast: { type: Boolean, default: false }, // Changed to false by default
50+
allowChainCast: { type: Boolean, default: true }, // Changed to true by default to allow ChainCast for all admins
5151
allowGroupChat: { type: Boolean, default: true },
5252
allowPosts: { type: Boolean, default: true },
53-
allowQuests: { type: Boolean, default: false },
53+
allowQuests: { type: Boolean, default: true }, // Changed to true by default to allow Quests for all admins
5454
},
5555
}, {
5656
timestamps: true

backend/src/routes/communityAdmin.routes.ts

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -687,18 +687,14 @@ router.get(
687687
"/subscription/chaincast-access",
688688
authMiddleware,
689689
roleMiddleware(["communityAdmin"]),
690-
async (req, res) => {
691-
try {
692-
const subscription = await communityAdminSubscriptionController.getSubscription(req, res);
693-
// This will be handled by the existing getSubscription method
694-
// The frontend will check the subscription status to determine access
695-
} catch (error) {
696-
res.status(500).json({
697-
success: false,
698-
data: { hasAccess: false },
699-
error: "Failed to check ChainCast access"
700-
});
701-
}
690+
(req, res) => {
691+
res.status(200).json({
692+
success: true,
693+
data: {
694+
hasAccess: true,
695+
message: "ChainCast access is now available to all community admins"
696+
}
697+
});
702698
}
703699
);
704700

backend/src/services/chainCast/ChainCast.service.ts

Lines changed: 101 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { IChainCastRepository } from "../../core/interfaces/repositories/chainCa
99
import { ICommunityAdminRepository } from "../../core/interfaces/repositories/ICommunityAdminRepository";
1010
import { IUserRepository } from "../../core/interfaces/repositories/IUser.repository";
1111
import CommunityMemberModel from "../../models/communityMember.model";
12+
import { ILiveKitService } from "../../core/interfaces/services/livekit/ILiveKit.service";
1213
import { ErrorMessages, SuccessMessages, LoggerMessages } from "../../enums/messages.enum";
1314
import {
1415
CreateChainCastDto,
@@ -44,7 +45,8 @@ export class ChainCastService implements IChainCastService {
4445
private _chainCastRepository: IChainCastRepository,
4546
@inject(TYPES.ICommunityAdminRepository)
4647
private _adminRepository: ICommunityAdminRepository,
47-
@inject(TYPES.IUserRepository) private _userRepository: IUserRepository
48+
@inject(TYPES.IUserRepository) private _userRepository: IUserRepository,
49+
@inject(TYPES.ILiveKitService) private _liveKitService: ILiveKitService
4850
) { }
4951

5052
// Admin ChainCast management
@@ -248,13 +250,41 @@ export class ChainCastService implements IChainCastService {
248250
}
249251

250252
const populatedAdmin = chainCast.adminId as unknown as PopulatedAdmin;
251-
return new ChainCastResponseDto(
253+
const response = new ChainCastResponseDto(
252254
chainCast,
253255
populatedAdmin,
254256
userRole,
255257
canJoin,
256258
canModerate
257259
);
260+
261+
// If live and user is authenticated, provide a token
262+
if (chainCast.status === "live" && userId) {
263+
const roomName = `chaincast_${chainCastId}`;
264+
const isHost = userRole === "admin";
265+
266+
let identityName = "Guest";
267+
if (isHost) {
268+
identityName = populatedAdmin.name || "Host";
269+
} else {
270+
try {
271+
const user = await this._userRepository.findById(userId);
272+
identityName = user?.name || user?.username || `Guest_${userId.substring(0, 4)}`;
273+
} catch (e) {
274+
identityName = `Guest_${userId.substring(0, 4)}`;
275+
}
276+
}
277+
278+
response.livekitToken = await this._generateLiveKitToken(
279+
roomName,
280+
userId,
281+
identityName,
282+
isHost
283+
);
284+
response.serverUrl = process.env.LIVE_KIT_WEBSOCKET_URL || process.env.LIVE_KIT_URL || process.env.LIVEKIT_URL;
285+
}
286+
287+
return response;
258288
} catch (error) {
259289
logger.error(LoggerMessages.GET_CHAINCAST_BY_ID_ERROR, error);
260290
if (error instanceof CustomError) {
@@ -440,6 +470,29 @@ export class ChainCastService implements IChainCastService {
440470
);
441471
}
442472

473+
if (chainCast.status === "live") {
474+
// Already live, just return with a fresh token
475+
const admin = await this._adminRepository.findById(adminId);
476+
const roomName = `chaincast_${chainCastId}`;
477+
const token = await this._generateLiveKitToken(
478+
roomName,
479+
adminId,
480+
admin?.name || 'Host',
481+
true
482+
);
483+
484+
const response = new ChainCastResponseDto(
485+
chainCast,
486+
admin!,
487+
"admin",
488+
true,
489+
true
490+
);
491+
response.livekitToken = token;
492+
response.serverUrl = process.env.LIVE_KIT_WEBSOCKET_URL || process.env.LIVE_KIT_URL || process.env.LIVEKIT_URL;
493+
return response;
494+
}
495+
443496
if (chainCast.status !== "scheduled") {
444497
throw new CustomError(
445498
ErrorMessages.CHAINCAST_NOT_SCHEDULED,
@@ -472,13 +525,27 @@ export class ChainCastService implements IChainCastService {
472525
await this._createAdminParticipant(chainCastId, adminId);
473526

474527
const admin = await this._adminRepository.findById(adminId);
475-
return new ChainCastResponseDto(
528+
529+
// Generate LiveKit token for host
530+
const roomName = `chaincast_${chainCastId}`;
531+
const token = await this._generateLiveKitToken(
532+
roomName,
533+
adminId,
534+
admin?.name || 'Host',
535+
true
536+
);
537+
538+
const response = new ChainCastResponseDto(
476539
updatedChainCast,
477540
admin,
478541
"admin",
479542
true,
480543
true
481544
);
545+
response.livekitToken = token;
546+
response.serverUrl = process.env.LIVE_KIT_WEBSOCKET_URL || process.env.LIVE_KIT_URL || process.env.LIVEKIT_URL;
547+
548+
return response;
482549
} catch (error) {
483550
logger.error(LoggerMessages.START_CHAINCAST_ERROR, error);
484551
if (error instanceof CustomError) {
@@ -645,12 +712,12 @@ export class ChainCastService implements IChainCastService {
645712
* Joins a ChainCast.
646713
* @param {string} userId - User ID.
647714
* @param {JoinChainCastDto} data - Join data.
648-
* @returns {Promise<{ success: boolean; message: string; streamUrl?: string }>} Join result.
715+
* @returns {Promise<{ success: boolean; message: string; streamUrl?: string; livekitToken?: string; serverUrl?: string }>} Join result.
649716
*/
650717
async joinChainCast(
651718
userId: string,
652719
data: JoinChainCastDto
653-
): Promise<{ success: boolean; message: string; streamUrl?: string }> {
720+
): Promise<{ success: boolean; message: string; streamUrl?: string; livekitToken?: string; serverUrl?: string }> {
654721
try {
655722
const chainCast = await this._chainCastRepository.findChainCastById(
656723
data.chainCastId
@@ -742,10 +809,22 @@ export class ChainCastService implements IChainCastService {
742809
};
743810
await this._chainCastRepository.updateChainCast(data.chainCastId, updateDataForStats as Partial<IChainCast>);
744811

812+
// Generate LiveKit token for user
813+
const user = await this._userRepository.findById(userId);
814+
const roomName = `chaincast_${data.chainCastId}`;
815+
const token = await this._generateLiveKitToken(
816+
roomName,
817+
userId,
818+
user?.name || user?.username || "Participant",
819+
false
820+
);
821+
745822
return {
746823
success: true,
747824
message: SuccessMessages.JOIN_CHAINCAST_SUCCESS,
748-
streamUrl: chainCast.streamData.streamUrl,
825+
streamUrl: chainCast.streamData.streamUrl, // legacy
826+
livekitToken: token,
827+
serverUrl: process.env.LIVE_KIT_WEBSOCKET_URL || process.env.LIVE_KIT_URL || process.env.LIVEKIT_URL,
749828
};
750829
} catch (error) {
751830
logger.error(LoggerMessages.JOIN_CHAINCAST_ERROR, error);
@@ -1544,4 +1623,20 @@ export class ChainCastService implements IChainCastService {
15441623
// This would be done with a bulk update in MongoDB
15451624
// For now, we'll leave this as a placeholder
15461625
}
1626+
private async _generateLiveKitToken(
1627+
roomName: string,
1628+
identity: string,
1629+
username: string,
1630+
isHost: boolean = false
1631+
): Promise<string> {
1632+
const grants = {
1633+
roomJoin: true,
1634+
canPublish: isHost,
1635+
canSubscribe: true,
1636+
canPublishData: true,
1637+
roomAdmin: isHost,
1638+
};
1639+
1640+
return this._liveKitService.generateToken(roomName, identity, username, grants);
1641+
}
15471642
}

backend/src/services/community/Community.service.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ export class CommunityService implements ICommunityService {
6565
rules: community.rules || [],
6666
socialLinks: (community.socialLinks as unknown) as Record<string, unknown>[] || [],
6767
settings: {
68-
allowChainCast: community.settings?.allowChainCast || false,
69-
allowGroupChat: community.settings?.allowGroupChat || true,
70-
allowPosts: community.settings?.allowPosts || true,
71-
allowQuests: community.settings?.allowQuests || false
68+
allowChainCast: community.settings?.allowChainCast !== false,
69+
allowGroupChat: community.settings?.allowGroupChat !== false,
70+
allowPosts: community.settings?.allowPosts !== false,
71+
allowQuests: community.settings?.allowQuests !== false
7272
},
7373
createdAt: community.createdAt,
7474
isMember,

backend/src/services/community/UserMyCommunities.service.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ICommunityRepository } from "../../core/interfaces/repositories/ICommun
44
import { TYPES } from "../../core/types/types";
55
import { CustomError } from "../../utils/customError";
66
import { StatusCode } from "../../enums/statusCode.enum";
7-
import {
7+
import {
88
MyCommunitiesListResponseDto,
99
MyCommunitiesStatsDto,
1010
MyCommunitiesActivityResponseDto,
@@ -17,7 +17,7 @@ import { Types } from "mongoose";
1717
export class UserMyCommunitiesService implements IUserMyCommunitiesService {
1818
constructor(
1919
@inject(TYPES.ICommunityRepository) private _communityRepository: ICommunityRepository
20-
) {}
20+
) { }
2121

2222
async getMyCommunities(
2323
userId: string,
@@ -60,10 +60,10 @@ export class UserMyCommunitiesService implements IUserMyCommunitiesService {
6060
totalPosts: membership.totalPosts || 0,
6161
isActive: membership.isActive,
6262
settings: {
63-
allowChainCast: community.settings?.allowChainCast || false,
64-
allowGroupChat: community.settings?.allowGroupChat || true,
65-
allowPosts: community.settings?.allowPosts || true,
66-
allowQuests: community.settings?.allowQuests || false
63+
allowChainCast: community.settings?.allowChainCast !== false,
64+
allowGroupChat: community.settings?.allowGroupChat !== false,
65+
allowPosts: community.settings?.allowPosts !== false,
66+
allowQuests: community.settings?.allowQuests !== false
6767
},
6868
notifications: membership.notifications || false,
6969
createdAt: community.createdAt

backend/src/services/communityAdmin/CommunityAdminAuth.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,10 +1052,10 @@ export class CommunityAdminAuthService implements ICommunityAdminAuthService {
10521052
members: [],
10531053
communityAdmins: [],
10541054
settings: {
1055-
allowChainCast: false,
1055+
allowChainCast: true, // Enabled by default for all new community admins
10561056
allowGroupChat: true,
10571057
allowPosts: true,
1058-
allowQuests: false,
1058+
allowQuests: true, // Enabled by default for all new community admins
10591059
},
10601060
});
10611061

backend/src/services/communityAdmin/CommunityAdminDashbaord.service.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,10 @@ export class CommunityAdminDashboardService implements ICommunityAdminDashboardS
103103
activeMembers,
104104
isVerified: community.isVerified,
105105
settings: {
106-
allowChainCast: community.settings?.allowChainCast || false,
106+
allowChainCast: community.settings?.allowChainCast !== false, // Default to true now
107107
allowGroupChat: community.settings?.allowGroupChat !== false,
108108
allowPosts: community.settings?.allowPosts !== false,
109-
allowQuests: community.settings?.allowQuests || false
109+
allowQuests: community.settings?.allowQuests !== false // Default to true now
110110
},
111111
socialLinks
112112
};

0 commit comments

Comments
 (0)