UNPKG

polyv-live-cli

Version:

CLI tool for managing PolyV live streaming services.

690 lines 31.5 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.ChannelHandler = void 0; const base_handler_1 = require("./base.handler"); const channel_service_1 = require("../services/channel.service"); const errors_1 = require("../utils/errors"); class ChannelHandler extends base_handler_1.BaseHandler { constructor(authConfig, serviceConfig) { super(); this.channelService = new channel_service_1.ChannelService(authConfig, serviceConfig); } async create(options) { return this.executeWithErrorHandling(async () => { this.validateCreateOptions(options); const request = this.transformToCreateRequest(options); const channel = await this.channelService.createChannel(request); this.displayChannelCreated(channel, options.output); }, 'channel.create'); } async listChannels(options = {}) { return this.executeWithErrorHandling(async () => { this.validateListOptions(options); const request = this.transformToListRequest(options); const channels = await this.channelService.listChannels(request); this.displayChannelsList(channels, request, options.output); }, 'channel.list'); } async getChannelDetail(options) { return this.executeWithErrorHandling(async () => { this.validateGetOptions(options); const request = this.transformToDetailRequest(options); const channelDetail = await this.channelService.getChannelDetail(request); this.displayChannelDetail(channelDetail, options.output); }, 'channel.get'); } async updateChannel(options) { return this.executeWithErrorHandling(async () => { this.validateUpdateOptions(options); const request = this.transformToUpdateRequest(options); await this.channelService.updateChannel(request); this.displayChannelUpdated(options); }, 'channel.update'); } async deleteChannel(options) { return this.executeWithErrorHandling(async () => { this.validateSingleDeleteOptions(options); if (!options.force) { const { confirmDeletion } = await Promise.resolve().then(() => __importStar(require('../utils/confirmation'))); const confirmed = await confirmDeletion(`Are you sure you want to delete channel '${options.channelId}'? This action cannot be undone.`, 'yes'); if (!confirmed) { this.displayInfo('Channel deletion cancelled by user'); return; } } await this.channelService.deleteChannel(options.channelId); this.displayChannelDeleted(options); }, 'channel.delete'); } async deleteChannels(options) { return this.executeWithErrorHandling(async () => { this.validateDeleteOptions(options); const request = this.transformToDeleteRequest(options); await this.channelService.batchDeleteChannels(request); this.displayChannelsDeleted(options); }, 'channel.delete'); } validateCreateOptions(options) { const errors = []; if (!options.name || typeof options.name !== 'string') { errors.push('Channel name is required and must be a string'); } else if (options.name.trim().length === 0) { errors.push('Channel name cannot be empty'); } else if (options.name.length > 100) { errors.push('Channel name cannot exceed 100 characters'); } if (options.description !== undefined) { if (typeof options.description !== 'string') { errors.push('Description must be a string'); } else if (options.description.length > 500) { errors.push('Description cannot exceed 500 characters'); } } if (options.maxViewers !== undefined) { if (typeof options.maxViewers !== 'number' || options.maxViewers < 0) { errors.push('Max viewers must be a non-negative number'); } else if (options.maxViewers > 100000) { errors.push('Max viewers cannot exceed 100,000'); } } if (options.autoRecord !== undefined && typeof options.autoRecord !== 'boolean') { errors.push('Auto record must be a boolean value'); } if (options.output !== undefined && !['table', 'json'].includes(options.output)) { errors.push('Output format must be either "table" or "json"'); } if (options.scene !== undefined) { const validScenes = ['topclass', 'cloudclass', 'telecast', 'akt']; if (!validScenes.includes(options.scene)) { errors.push(`Scene must be one of: ${validScenes.join(', ')}`); } } if (options.template !== undefined) { const validTemplates = ['ppt', 'video']; if (!validTemplates.includes(options.template)) { errors.push(`Template must be one of: ${validTemplates.join(', ')}`); } } if (options.password !== undefined) { if (typeof options.password !== 'string') { errors.push('Password must be a string'); } else if (options.password.length < 6 || options.password.length > 16) { errors.push('Password must be 6-16 characters long'); } else if (!/^[a-zA-Z0-9]+$/.test(options.password)) { errors.push('Password must contain only alphanumeric characters'); } } if (errors.length > 0) { throw new errors_1.PolyVValidationError(`Channel creation options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed'); } } transformToCreateRequest(options) { const request = { name: options.name.trim(), newScene: options.scene || 'topclass', template: options.template || 'ppt' }; if (options.password) { request.channelPasswd = options.password; } return request; } displayChannelCreated(channel, format = 'table') { const displayData = { channelId: channel.channelId, name: channel.name, userId: channel.userId, password: channel.channelPasswd ? '***' : '-', scene: channel.newScene, template: channel.template, status: channel.status || 'waiting', created: channel.createdAt ? channel.createdAt.toLocaleString() : 'Just now' }; this.displaySuccess('Channel created successfully', displayData, format); if (format === 'table') { this.displayInfo('Channel is ready for streaming configuration'); if (channel.channelPasswd) { this.displayWarning('Channel password has been set - remember to provide it to viewers'); } } } validateListOptions(options) { const errors = []; if (options.page !== undefined) { if (typeof options.page !== 'number' || !Number.isInteger(options.page) || options.page < 1) { errors.push('Page must be a positive integer (minimum 1)'); } } if (options.limit !== undefined) { if (typeof options.limit !== 'number' || !Number.isInteger(options.limit) || options.limit < 1 || options.limit > 100) { errors.push('Limit must be an integer between 1 and 100'); } } if (options.output !== undefined && !['table', 'json'].includes(options.output)) { errors.push('Output format must be either "table" or "json"'); } if (options.categoryId !== undefined && (typeof options.categoryId !== 'string' || options.categoryId.trim().length === 0)) { errors.push('Category ID must be a non-empty string'); } if (options.keyword !== undefined && (typeof options.keyword !== 'string' || options.keyword.trim().length === 0)) { errors.push('Keyword must be a non-empty string'); } if (options.labelId !== undefined && (typeof options.labelId !== 'string' || options.labelId.trim().length === 0)) { errors.push('Label ID must be a non-empty string'); } if (errors.length > 0) { throw new errors_1.PolyVValidationError(`Channel list options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed'); } } validateGetOptions(options) { const errors = []; if (!options.channelId) { errors.push('Channel ID is required'); } else if (typeof options.channelId !== 'string') { errors.push('Channel ID must be a string'); } else if (options.channelId.trim().length === 0) { errors.push('Channel ID cannot be empty'); } if (options.output !== undefined && !['table', 'json'].includes(options.output)) { errors.push('Output format must be either "table" or "json"'); } if (errors.length > 0) { throw new errors_1.PolyVValidationError(`Channel get options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed'); } } validateUpdateOptions(options) { const errors = []; if (!options.channelId) { errors.push('Channel ID is required'); } else if (typeof options.channelId !== 'string') { errors.push('Channel ID must be a string'); } else if (options.channelId.trim().length === 0) { errors.push('Channel ID cannot be empty'); } const updateFields = [ 'name', 'description', 'publisher', 'password', 'maxViewers', 'autoRecord', 'startTime', 'endTime', 'pageView', 'likes', 'coverImg', 'splashImg' ]; const hasUpdateField = updateFields.some(field => options[field] !== undefined); if (!hasUpdateField) { errors.push('At least one update parameter must be provided'); } if (options.name !== undefined) { if (typeof options.name !== 'string') { errors.push('Channel name must be a string'); } else if (options.name.trim().length === 0) { errors.push('Channel name cannot be empty'); } else if (options.name.length > 100) { errors.push('Channel name cannot exceed 100 characters'); } } if (options.description !== undefined) { if (typeof options.description !== 'string') { errors.push('Description must be a string'); } else if (options.description.length > 500) { errors.push('Description cannot exceed 500 characters'); } } if (options.publisher !== undefined) { if (typeof options.publisher !== 'string') { errors.push('Publisher must be a string'); } } if (options.password !== undefined) { if (typeof options.password !== 'string') { errors.push('Password must be a string'); } else if (options.password.length < 6 || options.password.length > 16) { errors.push('Password must be 6-16 characters long'); } else if (!/^[a-zA-Z0-9]+$/.test(options.password)) { errors.push('Password must contain only alphanumeric characters'); } } if (options.maxViewers !== undefined) { if (typeof options.maxViewers !== 'number' || options.maxViewers <= 0) { errors.push('Max viewers must be a positive number'); } } if (options.autoRecord !== undefined && typeof options.autoRecord !== 'boolean') { errors.push('Auto record must be a boolean value'); } if (options.startTime !== undefined) { if (typeof options.startTime !== 'number' || options.startTime < 0) { errors.push('Start time must be a non-negative timestamp'); } } if (options.endTime !== undefined) { if (typeof options.endTime !== 'number' || options.endTime < 0) { errors.push('End time must be a non-negative timestamp'); } if (options.startTime !== undefined && options.endTime <= options.startTime) { errors.push('End time must be greater than start time'); } } if (options.pageView !== undefined) { if (typeof options.pageView !== 'number' || options.pageView < 0) { errors.push('Page view count must be a non-negative number'); } } if (options.likes !== undefined) { if (typeof options.likes !== 'number' || options.likes < 0) { errors.push('Likes count must be a non-negative number'); } } if (options.coverImg !== undefined && typeof options.coverImg !== 'string') { errors.push('Cover image URL must be a string'); } if (options.splashImg !== undefined && typeof options.splashImg !== 'string') { errors.push('Splash image URL must be a string'); } if (options.output !== undefined && !['table', 'json'].includes(options.output)) { errors.push('Output format must be either "table" or "json"'); } if (errors.length > 0) { throw new errors_1.PolyVValidationError(`Channel update options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed'); } } transformToUpdateRequest(options) { const request = { channelId: options.channelId.trim() }; const basicSetting = {}; let hasBasicSetting = false; if (options.name !== undefined) { basicSetting.name = options.name.trim(); hasBasicSetting = true; } if (options.description !== undefined) { basicSetting.desc = options.description; hasBasicSetting = true; } if (options.publisher !== undefined) { basicSetting.publisher = options.publisher; hasBasicSetting = true; } if (options.password !== undefined) { basicSetting.channelPasswd = options.password; hasBasicSetting = true; } if (options.maxViewers !== undefined) { basicSetting.maxViewer = options.maxViewers; hasBasicSetting = true; } if (options.startTime !== undefined) { basicSetting.startTime = options.startTime; hasBasicSetting = true; } if (options.endTime !== undefined) { basicSetting.endTime = options.endTime; hasBasicSetting = true; } if (options.pageView !== undefined) { basicSetting.pageView = options.pageView; hasBasicSetting = true; } if (options.likes !== undefined) { basicSetting.likes = options.likes; hasBasicSetting = true; } if (options.coverImg !== undefined) { basicSetting.coverImg = options.coverImg; hasBasicSetting = true; } if (options.splashImg !== undefined) { basicSetting.splashImg = options.splashImg; hasBasicSetting = true; } if (hasBasicSetting) { request.basicSetting = basicSetting; } return request; } displayChannelUpdated(options) { const updateSummary = []; if (options.name !== undefined) updateSummary.push(`Name: ${options.name}`); if (options.description !== undefined) updateSummary.push(`Description: ${options.description}`); if (options.publisher !== undefined) updateSummary.push(`Publisher: ${options.publisher}`); if (options.password !== undefined) updateSummary.push('Password: ***'); if (options.maxViewers !== undefined) updateSummary.push(`Max Viewers: ${options.maxViewers}`); if (options.startTime !== undefined) updateSummary.push(`Start Time: ${new Date(options.startTime).toLocaleString()}`); if (options.endTime !== undefined) updateSummary.push(`End Time: ${new Date(options.endTime).toLocaleString()}`); if (options.pageView !== undefined) updateSummary.push(`Page Views: ${options.pageView}`); if (options.likes !== undefined) updateSummary.push(`Likes: ${options.likes}`); if (options.coverImg !== undefined) updateSummary.push(`Cover Image: ${options.coverImg}`); if (options.splashImg !== undefined) updateSummary.push(`Splash Image: ${options.splashImg}`); this.displaySuccess(`Channel ${options.channelId} updated successfully`); if (updateSummary.length > 0) { this.displayInfo('Updated fields:'); updateSummary.forEach(summary => this.displayInfo(` • ${summary}`)); } if (options.password) { this.displayWarning('Channel password has been updated - remember to provide it to viewers'); } } validateSingleDeleteOptions(options) { const errors = []; if (!options.channelId) { errors.push('Channel ID is required'); } else if (typeof options.channelId !== 'string') { errors.push('Channel ID must be a string'); } else if (options.channelId.trim().length === 0) { errors.push('Channel ID cannot be empty'); } if (options.force !== undefined && typeof options.force !== 'boolean') { errors.push('Force flag must be a boolean value'); } if (options.output !== undefined && !['table', 'json'].includes(options.output)) { errors.push('Output format must be either "table" or "json"'); } if (errors.length > 0) { throw new errors_1.PolyVValidationError(`Channel delete options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed'); } } validateDeleteOptions(options) { const errors = []; if (!options.channelIds) { errors.push('Channel IDs are required'); } else if (!Array.isArray(options.channelIds)) { errors.push('Channel IDs must be an array'); } else if (options.channelIds.length === 0) { errors.push('At least one channel ID must be provided'); } else if (options.channelIds.length > 100) { errors.push('Cannot delete more than 100 channels at once'); } else { for (let i = 0; i < options.channelIds.length; i++) { const channelId = options.channelIds[i]; if (typeof channelId !== 'string') { errors.push(`Channel ID at position ${i + 1} must be a string`); } else if (channelId.trim().length === 0) { errors.push(`Channel ID at position ${i + 1} cannot be empty`); } } } if (options.output !== undefined && !['table', 'json'].includes(options.output)) { errors.push('Output format must be either "table" or "json"'); } if (errors.length > 0) { throw new errors_1.PolyVValidationError(`Channel delete options validation failed: ${errors.join(', ')}`, 'options', options, 'validation_failed'); } } transformToDeleteRequest(options) { return { channelIds: options.channelIds.map((id) => id.trim()) }; } displayChannelsDeleted(options) { const channelCount = options.channelIds.length; const channelText = channelCount === 1 ? 'channel' : 'channels'; this.displaySuccess(`Successfully deleted ${channelCount} ${channelText}`); if (options.output === 'json') { const jsonData = { deleted: true, channelIds: options.channelIds, count: channelCount }; this.displayData(jsonData, 'json'); } else { this.displayInfo('Deleted channels:'); options.channelIds.forEach((channelId) => { this.displayInfo(` • Channel ${channelId}`); }); } } displayChannelDeleted(options) { this.displaySuccess(`Successfully deleted channel ${options.channelId}`); if (options.output === 'json') { const jsonData = { deleted: true, channelId: options.channelId, timestamp: new Date().toISOString() }; this.displayData(jsonData, 'json'); } else { this.displayInfo(`Channel ${options.channelId} has been permanently deleted`); this.displayWarning('This action cannot be undone'); } } transformToListRequest(options) { const request = {}; if (options.page !== undefined) { request.page = options.page; } if (options.limit !== undefined) { request.limit = options.limit; } if (options.categoryId) { request.categoryId = options.categoryId.trim(); } if (options.keyword) { request.keyword = options.keyword.trim(); } if (options.labelId) { request.labelId = options.labelId.trim(); } return request; } transformToDetailRequest(options) { return { channelId: options.channelId.trim() }; } displayChannelsList(channels, request, format = 'table') { if (channels.length === 0) { this.displayInfo('No channels found'); return; } if (format === 'json') { const jsonData = { channels: channels.map(channel => ({ channelId: channel.channelId, name: channel.name, status: channel.status, scene: channel.scene, template: channel.template, createdAt: channel.createdAt.toISOString(), description: channel.description, maxViewers: channel.maxViewers })), pagination: { page: request.page ?? 1, limit: request.limit ?? 20, total: channels.length } }; this.displayData(jsonData, 'json'); } else { const tableData = channels.map(channel => ({ 'Channel ID': channel.channelId, 'Name': channel.name, 'Status': channel.status, 'Scene': channel.scene, 'Template': channel.template, 'Created': channel.createdAt.toLocaleDateString() })); this.displaySuccess(`Found ${channels.length} channels`, tableData, 'table'); const page = request.page ?? 1; const limit = request.limit ?? 20; this.displayInfo(`Page ${page}, showing up to ${limit} channels per page`); } } displayChannelDetail(channelDetail, format = 'table') { if (format === 'json') { const jsonData = { channelId: channelDetail.channelId, name: channelDetail.name, scene: channelDetail.scene, newScene: channelDetail.newScene, template: channelDetail.template, channelPasswd: channelDetail.channelPasswd ? '***masked***' : null, publisher: channelDetail.publisher, startTime: channelDetail.startTime, endTime: channelDetail.endTime, pureRtcEnabled: channelDetail.pureRtcEnabled, pageView: channelDetail.pageView, likes: channelDetail.likes, coverImg: channelDetail.coverImg, splashImg: channelDetail.splashImg, splashEnabled: channelDetail.splashEnabled, bgImg: channelDetail.bgImg, desc: channelDetail.desc, consultingMenuEnabled: channelDetail.consultingMenuEnabled, maxViewerRestrict: channelDetail.maxViewerRestrict, maxViewer: channelDetail.maxViewer, watchStatus: channelDetail.watchStatus, watchStatusText: channelDetail.watchStatusText, userCategory: channelDetail.userCategory, authSettings: channelDetail.authSettings, linkMicLimit: channelDetail.linkMicLimit, createdAccountId: channelDetail.createdAccountId, createdAccountEmail: channelDetail.createdAccountEmail, createdTime: channelDetail.createdTime, labelData: channelDetail.labelData, clientAloneTemplateBackgroundUrl: channelDetail.clientAloneTemplateBackgroundUrl, liveCdnBackgroundUrl: channelDetail.liveCdnBackgroundUrl, pushUrl: channelDetail.pushUrl ? '***masked***' : null, pushSecret: channelDetail.pushSecret ? '***masked***' : null, streamType: channelDetail.streamType, onlyOutEnabled: channelDetail.onlyOutEnabled }; this.displayData(jsonData, 'json'); } else { this.displaySuccess(`Channel Details: ${channelDetail.name}`, null, 'table'); const basicInfo = { 'Channel ID': channelDetail.channelId.toString(), 'Name': channelDetail.name, 'Publisher': channelDetail.publisher || '-', 'Description': channelDetail.desc || '-', 'Status': `${channelDetail.watchStatus} (${channelDetail.watchStatusText})`, 'Scene': channelDetail.newScene !== 'undefined' ? channelDetail.newScene : channelDetail.scene, 'Template': channelDetail.template !== 'undefined' ? channelDetail.template : '-' }; this.displayInfo('📋 Basic Information'); this.displayData(basicInfo, 'table'); const configInfo = { 'Channel Password': channelDetail.channelPasswd ? 'Set (***masked***)' : 'Not set', 'Pure RTC': channelDetail.pureRtcEnabled === 'Y' ? 'Enabled' : 'Disabled', 'Max Viewer Restrict': channelDetail.maxViewerRestrict === 'Y' ? 'Enabled' : 'Disabled', 'Max Viewers': channelDetail.maxViewer > 0 ? channelDetail.maxViewer.toString() : 'Unlimited', 'Link Mic Limit': channelDetail.linkMicLimit.toString(), 'Consulting Menu': channelDetail.consultingMenuEnabled === 'Y' ? 'Enabled' : 'Disabled', 'Splash Page': channelDetail.splashEnabled === 'Y' ? 'Enabled' : 'Disabled' }; this.displayInfo('⚙️ Configuration'); this.displayData(configInfo, 'table'); const statsInfo = { 'Page Views': channelDetail.pageView.toString(), 'Likes': channelDetail.likes.toString(), 'Start Time': channelDetail.startTime > 0 ? new Date(channelDetail.startTime).toLocaleString() : 'Not set', 'End Time': channelDetail.endTime > 0 ? new Date(channelDetail.endTime).toLocaleString() : 'Not set', 'Created Time': new Date(channelDetail.createdTime).toLocaleString(), 'Created By': channelDetail.createdAccountEmail || channelDetail.createdAccountId }; this.displayInfo('📊 Statistics & Timing'); this.displayData(statsInfo, 'table'); if (channelDetail.userCategory) { const categoryInfo = { 'Category ID': channelDetail.userCategory.categoryId.toString(), 'Category Name': channelDetail.userCategory.categoryName, 'Rank': channelDetail.userCategory.rank.toString() }; this.displayInfo('📁 Category Information'); this.displayData(categoryInfo, 'table'); } if (channelDetail.authSettings && channelDetail.authSettings.length > 0) { const authSummary = channelDetail.authSettings.map((auth) => ({ 'Rank': auth.rank.toString(), 'Type': auth.authType, 'Enabled': auth.enabled, 'Global Setting': auth.globalSettingEnabled })); this.displayInfo('🔐 Authentication Settings'); this.displayData(authSummary, 'table'); } if (channelDetail.labelData && channelDetail.labelData.length > 0) { this.displayInfo(`Labels: ${channelDetail.labelData.join(', ')}`); } const urls = []; if (channelDetail.coverImg) urls.push(`Cover Image: ${channelDetail.coverImg}`); if (channelDetail.splashImg) urls.push(`Splash Image: ${channelDetail.splashImg}`); if (channelDetail.bgImg) urls.push(`Background Image: ${channelDetail.bgImg}`); if (urls.length > 0) { this.displayInfo('Images:\n' + urls.join('\n')); } } } } exports.ChannelHandler = ChannelHandler; //# sourceMappingURL=channel.handler.js.map