const logger = require('winston'); const { details: { host }, publishing: { disabled, disabledMessage } } = require('@config/siteConfig'); const { sendGATimingEvent } = require('../../../../utils/googleAnalytics.js'); const isApprovedChannel = require('../../../../../utils/isApprovedChannel'); const { publishing: { publishOnlyApproved, approvedChannels } } = require('@config/siteConfig'); const { handleErrorResponse } = require('../../../utils/errorHandlers.js'); const checkClaimAvailability = require('../availability/checkClaimAvailability.js'); const publish = require('./publish.js'); const createPublishParams = require('./createPublishParams.js'); const createThumbnailPublishParams = require('./createThumbnailPublishParams.js'); const parsePublishApiRequestBody = require('./parsePublishApiRequestBody.js'); const parsePublishApiRequestFiles = require('./parsePublishApiRequestFiles.js'); const authenticateUser = require('./authentication.js'); const CLAIM_TAKEN = 'CLAIM_TAKEN'; const UNAPPROVED_CHANNEL = 'UNAPPROVED_CHANNEL'; /* route to publish a claim through the daemon */ const claimPublish = ({ body, files, headers, ip, originalUrl, user, tor }, res) => { // logging logger.info('Publish request:', { ip, headers, body, files, }); // check for disabled publishing if (disabled) { return res.status(503).json({ success: false, message: disabledMessage, }); } // define variables let channelName, channelId, channelPassword, description, fileName, filePath, fileExtension, fileType, gaStartTime, license, name, nsfw, thumbnail, thumbnailFileName, thumbnailFilePath, thumbnailFileType, title; // record the start time of the request gaStartTime = Date.now(); // validate the body and files of the request try { // validateApiPublishRequest(body, files); ({name, nsfw, license, title, description, thumbnail} = parsePublishApiRequestBody(body)); ({fileName, filePath, fileExtension, fileType, thumbnailFileName, thumbnailFilePath, thumbnailFileType} = parsePublishApiRequestFiles(files)); ({channelName, channelId, channelPassword} = body); } catch (error) { return res.status(400).json({success: false, message: error.message}); } // check channel authorization authenticateUser(channelName, channelId, channelPassword, user) .then(({ channelName, channelClaimId }) => { if (publishOnlyApproved && !isApprovedChannel({ longId: channelClaimId }, approvedChannels)) { const error = { name : UNAPPROVED_CHANNEL, message: 'This spee.ch instance only allows publishing to approved channels', }; throw error; } return Promise.all([ checkClaimAvailability(name), createPublishParams(filePath, name, title, description, license, nsfw, thumbnail, channelName, channelClaimId), createThumbnailPublishParams(thumbnailFilePath, name, license, nsfw), ]); }) .then(([ claimAvailable, publishParams, thumbnailPublishParams ]) => { if (!claimAvailable) { const error = { name : CLAIM_TAKEN, message: 'That claim name is already taken', }; throw error; } // publish the thumbnail, if one exists if (thumbnailPublishParams) { publish(thumbnailPublishParams, thumbnailFileName, thumbnailFileType); } // publish the asset return publish(publishParams, fileName, fileType); }) .then(result => { res.status(200).json({ success: true, message: 'publish completed successfully', data : { name, claimId : result.claim_id, url : `${host}/${result.claim_id}/${name}`, // for backwards compatability with app showUrl : `${host}/${result.claim_id}/${name}`, serveUrl: `${host}/${result.claim_id}/${name}${fileExtension}`, lbryTx : result, }, }); // record the publish end time and send to google analytics sendGATimingEvent('end-to-end', 'publish', fileType, gaStartTime, Date.now()); }) .catch(error => { if ([CLAIM_TAKEN, UNAPPROVED_CHANNEL].includes(error.name)) { res.status(400).json({ success: false, message: error.message, }); } handleErrorResponse(originalUrl, ip, error, res); }); }; module.exports = claimPublish;