const fs = require('fs-extra'); const path = require('path'); const UserError = require('vn-loopback/util/user-error'); const isProduction = require('vn-loopback/server/boot/isProduction'); module.exports = Self => { Self.remoteMethodCtx('upload', { description: 'Upload and attach a access file', accepts: [ { arg: 'appName', type: 'string', required: true, description: 'The app name' }, { arg: 'toVersion', type: 'number', required: true, description: `The new version number` }, { arg: 'branch', type: 'string', required: true, description: `The branch name` }, { arg: 'fromVersion', type: 'string', required: true, description: `The old version number` }, { arg: 'description', type: 'string', required: false, description: `The description of changes` }, { arg: 'unlock', type: 'boolean', required: false, description: `It allows unlock the app` } ], returns: { type: ['object'], root: true }, http: { path: `/upload`, verb: 'POST' } }); Self.upload = async( ctx, appName, toVersion, branch, fromVersion, description, unlock, options ) => { const models = Self.app.models; const myOptions = {}; const $t = ctx.req.__; // $translate const TempContainer = models.TempContainer; const AccessContainer = models.AccessContainer; const fileOptions = {}; let tx; if (typeof options == 'object') Object.assign(myOptions, options); if (!myOptions.transaction) { tx = await Self.beginTransaction({}); myOptions.transaction = tx; } let srcFile; try { const userId = ctx.req.accessToken.userId; const mdbApp = await models.MdbApp.findById(appName, null, myOptions); if (mdbApp && mdbApp.locked && mdbApp.userFk != userId) { throw new UserError($t('App locked', { userId: mdbApp.userFk })); } const existBranch = await models.MdbBranch.findOne({ where: {name: branch} }, myOptions); if (!existBranch) throw new UserError('Not exist this branch'); let lastMethod = await Self.last(ctx, appName, myOptions); lastMethod.version++; if (lastMethod.version != toVersion) throw new UserError('Try again'); const tempContainer = await TempContainer.container('access'); const uploaded = await TempContainer.upload(tempContainer.name, ctx.req, ctx.result, fileOptions); const files = Object.values(uploaded.files).map(file => { return file[0]; }); const uploadedFile = files[0]; const file = await TempContainer.getFile(tempContainer.name, uploadedFile.name); srcFile = path.join(file.client.root, file.container, file.name); const accessContainer = await AccessContainer.container('.archive'); const destinationFile = path.join( accessContainer.client.root, accessContainer.name, appName, `${toVersion}.7z`); if (!isProduction()) await fs.unlink(srcFile); else { await fs.move(srcFile, destinationFile, { overwrite: true }); await fs.chmod(destinationFile, 0o644); const existBranch = await models.MdbBranch.findOne({ where: {name: branch} }, myOptions); if (!existBranch) throw new UserError('Not exist this branch'); const branchPath = path.join(accessContainer.client.root, 'branches', branch); await fs.mkdir(branchPath, {recursive: true}); const destinationBranch = path.join(branchPath, `${appName}.7z`); const destinationRelative = `../../.archive/${appName}/${toVersion}.7z`; try { await fs.unlink(destinationBranch); } catch (e) {} await fs.symlink(destinationRelative, destinationBranch); if (branch == 'master') { const destinationRoot = path.join(accessContainer.client.root, `${appName}.7z`); const rootRelative = `./.archive/${appName}/${toVersion}.7z`; try { await fs.unlink(destinationRoot); } catch (e) {} await fs.symlink(rootRelative, destinationRoot); } } if (description) { let formatDesc; const mainBranches = new Set(['master', 'test', 'dev']); if (mainBranches.has(branch)) formatDesc = `> :branch_${branch}: `; else formatDesc = `> :branch: `; formatDesc += `*${appName.toUpperCase()}* v.${toVersion} `; const oldVersion = await models.MdbVersionTree.findOne({ where: { version: fromVersion, app: appName }, fields: ['branchFk'] }, myOptions); if (!oldVersion || branch == oldVersion.branchFk) formatDesc += `[*${branch}*]: `; else formatDesc += `[*${oldVersion.branchFk}* ยป *${branch}*]: `; const params = await models.MdbConfig.findOne(myOptions); const issueTrackerUrl = params.issueTrackerUrl; const issueNumberRegex = params.issueNumberRegex; const chatDestination = params.chatDestination; const regex = new RegExp(issueNumberRegex, 'g'); formatDesc += description.replace(regex, (match, issueId) => { const newUrl = issueTrackerUrl.replace('{index}', issueId); return `[#${issueId}](${newUrl})`; }); await models.Chat.send(ctx, chatDestination, formatDesc, myOptions); } await models.MdbVersionTree.create({ app: appName, version: toVersion, branchFk: branch, fromVersion, userFk: userId, description, }, myOptions); await models.MdbVersion.upsert({ app: appName, branchFk: branch, version: toVersion }, myOptions); if (unlock) await models.MdbApp.unlock(ctx, appName, myOptions); if (tx) await tx.commit(); } catch (e) { if (tx) await tx.rollback(); if (fs.existsSync(srcFile)) fs.unlink(srcFile); throw e; } }; };