decap-server 3.10.0 → 3.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"middlewares.js","mappings":"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","sources":["webpack://decap-server/../decap-cms-lib-util/src/APIUtils.ts","webpack://decap-server/./src/logger.ts","webpack://decap-server/./src/middlewares/common/index.ts","webpack://decap-server/./src/middlewares/joi/customValidators.ts","webpack://decap-server/./src/middlewares/joi/index.ts","webpack://decap-server/./src/middlewares/localFs/index.ts","webpack://decap-server/./src/middlewares/localGit/index.ts","webpack://decap-server/./src/middlewares/utils/entries.ts","webpack://decap-server/./src/middlewares/utils/fs.ts","webpack://decap-server/external commonjs \"@hapi/joi\"","webpack://decap-server/external commonjs \"async-mutex\"","webpack://decap-server/external commonjs \"cors\"","webpack://decap-server/external commonjs \"express\"","webpack://decap-server/external commonjs \"morgan\"","webpack://decap-server/external commonjs \"simple-git\"","webpack://decap-server/external commonjs \"what-the-diff\"","webpack://decap-server/external commonjs \"winston\"","webpack://decap-server/external node-commonjs \"crypto\"","webpack://decap-server/external node-commonjs \"fs\"","webpack://decap-server/external node-commonjs \"path\"","webpack://decap-server/webpack/bootstrap","webpack://decap-server/./src/middlewares.ts"],"sourcesContent":["export const CMS_BRANCH_PREFIX = 'cms';\nexport const DEFAULT_PR_BODY = 'Automatically generated by Decap CMS';\nexport const MERGE_COMMIT_MESSAGE = 'Automatically generated. Merged on Decap CMS.';\n\nconst DEFAULT_DECAP_CMS_LABEL_PREFIX = 'decap-cms/';\n\nfunction getLabelPrefix(labelPrefix: string) {\n return labelPrefix || DEFAULT_DECAP_CMS_LABEL_PREFIX;\n}\n\nexport function isCMSLabel(label: string, labelPrefix: string) {\n return label.startsWith(getLabelPrefix(labelPrefix));\n}\n\nexport function labelToStatus(label: string, labelPrefix: string) {\n return label.slice(getLabelPrefix(labelPrefix).length);\n}\n\nexport function statusToLabel(status: string, labelPrefix: string) {\n return `${getLabelPrefix(labelPrefix)}${status}`;\n}\n\nexport function generateContentKey(collectionName: string, slug: string) {\n return `${collectionName}/${slug}`;\n}\n\nexport function parseContentKey(contentKey: string) {\n const index = contentKey.indexOf('/');\n return { collection: contentKey.slice(0, index), slug: contentKey.slice(index + 1) };\n}\n\nexport function contentKeyFromBranch(branch: string) {\n return branch.slice(`${CMS_BRANCH_PREFIX}/`.length);\n}\n\nexport function branchFromContentKey(contentKey: string) {\n return `${CMS_BRANCH_PREFIX}/${contentKey}`;\n}\n","import winston from 'winston';\n\nconst { combine, colorize, simple } = winston.format;\n\ntype LogOptions = {\n level: string;\n};\n\nexport function createLogger({ level }: LogOptions) {\n return winston.createLogger({\n level,\n format: combine(colorize(), simple()),\n transports: [new winston.transports.Console()],\n });\n}\n","import express from 'express';\nimport morgan from 'morgan';\nimport cors from 'cors';\n\nimport type winston from 'winston';\n\nexport type Options = {\n logger: winston.Logger;\n};\n\nexport function registerCommonMiddlewares(app: express.Express, options: Options) {\n const { logger } = options;\n const stream = {\n write: (message: string) => {\n logger.debug(String(message).trim());\n },\n };\n app.use(morgan('combined', { stream }));\n app.use(\n cors({\n origin: process.env.ORIGIN || /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/,\n }),\n );\n app.use(express.json({ limit: '50mb' }));\n}\n","import Joi from '@hapi/joi';\nimport path from 'path';\n\nexport function pathTraversal(repoPath: string) {\n return Joi.extend({\n type: 'path',\n base: Joi.string().required(),\n messages: {\n 'path.invalid': '{{#label}} must resolve to a path under the configured repository',\n },\n validate(value, helpers) {\n const resolvedPath = path.join(repoPath, value);\n if (!resolvedPath.startsWith(repoPath)) {\n return { value, errors: helpers.error('path.invalid') };\n }\n },\n }).path();\n}\n","import Joi from '@hapi/joi';\n\nimport type express from 'express';\n\nconst allowedActions = [\n 'info',\n 'entriesByFolder',\n 'entriesByFiles',\n 'getEntry',\n 'unpublishedEntries',\n 'unpublishedEntry',\n 'unpublishedEntryDataFile',\n 'unpublishedEntryMediaFile',\n 'deleteUnpublishedEntry',\n 'persistEntry',\n 'updateUnpublishedEntryStatus',\n 'publishUnpublishedEntry',\n 'getMedia',\n 'getMediaFile',\n 'persistMedia',\n 'deleteFile',\n 'deleteFiles',\n 'getDeployPreview',\n];\n\nconst requiredString = Joi.string().required();\nconst requiredNumber = Joi.number().required();\nconst requiredBool = Joi.bool().required();\n\nconst collection = requiredString;\nconst slug = requiredString;\n\nexport function defaultSchema({ path = requiredString } = {}) {\n const defaultParams = Joi.object({\n branch: requiredString,\n });\n\n const asset = Joi.object({\n path,\n content: requiredString,\n encoding: requiredString.valid('base64'),\n });\n\n const dataFile = Joi.object({\n slug: requiredString,\n path,\n raw: requiredString,\n newPath: path.optional(),\n });\n\n const params = Joi.when('action', {\n switch: [\n {\n is: 'info',\n then: Joi.allow(),\n },\n {\n is: 'entriesByFolder',\n then: defaultParams\n .keys({\n folder: path,\n extension: requiredString,\n depth: requiredNumber,\n })\n .required(),\n },\n {\n is: 'entriesByFiles',\n then: defaultParams.keys({\n files: Joi.array()\n .items(Joi.object({ path, label: Joi.string() }))\n .required(),\n }),\n },\n {\n is: 'getEntry',\n then: defaultParams\n .keys({\n path,\n })\n .required(),\n },\n {\n is: 'unpublishedEntries',\n then: defaultParams.keys({ branch: requiredString }).required(),\n },\n {\n is: 'unpublishedEntry',\n then: defaultParams\n .keys({\n id: Joi.string().optional(),\n collection: Joi.string().optional(),\n slug: Joi.string().optional(),\n cmsLabelPrefix: Joi.string().optional(),\n })\n .required(),\n },\n {\n is: 'unpublishedEntryDataFile',\n then: defaultParams\n .keys({\n collection,\n slug,\n id: requiredString,\n path,\n })\n .required(),\n },\n {\n is: 'unpublishedEntryMediaFile',\n then: defaultParams\n .keys({\n collection,\n slug,\n id: requiredString,\n path,\n })\n .required(),\n },\n {\n is: 'deleteUnpublishedEntry',\n then: defaultParams\n .keys({\n collection,\n slug,\n })\n .required(),\n },\n {\n is: 'persistEntry',\n then: defaultParams\n .keys({\n cmsLabelPrefix: Joi.string().optional(),\n entry: dataFile, // entry is kept for backwards compatibility\n dataFiles: Joi.array().items(dataFile),\n assets: Joi.array().items(asset).required(),\n options: Joi.object({\n collectionName: Joi.string(),\n commitMessage: requiredString,\n useWorkflow: requiredBool,\n status: requiredString,\n }).required(),\n })\n .xor('entry', 'dataFiles')\n .required(),\n },\n {\n is: 'updateUnpublishedEntryStatus',\n then: defaultParams\n .keys({\n collection,\n slug,\n newStatus: requiredString,\n cmsLabelPrefix: Joi.string().optional(),\n })\n .required(),\n },\n {\n is: 'publishUnpublishedEntry',\n then: defaultParams\n .keys({\n collection,\n slug,\n })\n .required(),\n },\n {\n is: 'getMedia',\n then: defaultParams\n .keys({\n mediaFolder: path,\n })\n .required(),\n },\n {\n is: 'getMediaFile',\n then: defaultParams\n .keys({\n path,\n })\n .required(),\n },\n {\n is: 'persistMedia',\n then: defaultParams\n .keys({\n asset: asset.required(),\n options: Joi.object({\n commitMessage: requiredString,\n }).required(),\n })\n .required(),\n },\n {\n is: 'deleteFile',\n then: defaultParams\n .keys({\n path,\n options: Joi.object({\n commitMessage: requiredString,\n }).required(),\n })\n .required(),\n },\n {\n is: 'deleteFiles',\n then: defaultParams\n .keys({\n paths: Joi.array().items(path).min(1).required(),\n options: Joi.object({\n commitMessage: requiredString,\n }).required(),\n })\n .required(),\n },\n {\n is: 'getDeployPreview',\n then: defaultParams\n .keys({\n collection,\n slug,\n })\n .required(),\n },\n ],\n otherwise: Joi.forbidden(),\n });\n\n return Joi.object({\n action: Joi.valid(...allowedActions).required(),\n params,\n });\n}\n\nexport function joi(schema: Joi.Schema) {\n return (req: express.Request, res: express.Response, next: express.NextFunction) => {\n const { error } = schema.validate(req.body, { allowUnknown: true });\n if (error) {\n const { details } = error;\n const message = details.map(i => i.message).join(',');\n res.status(422).json({ error: message });\n } else {\n next();\n }\n };\n}\n","import path from 'path';\n\nimport { defaultSchema, joi } from '../joi';\nimport { pathTraversal } from '../joi/customValidators';\nimport { listRepoFiles, deleteFile, writeFile, move } from '../utils/fs';\nimport { entriesFromFiles, readMediaFile } from '../utils/entries';\n\nimport type {\n EntriesByFolderParams,\n EntriesByFilesParams,\n GetEntryParams,\n PersistEntryParams,\n GetMediaParams,\n GetMediaFileParams,\n PersistMediaParams,\n DeleteFileParams,\n DeleteFilesParams,\n DataFile,\n} from '../types';\nimport type express from 'express';\nimport type winston from 'winston';\n\ntype FsOptions = {\n repoPath: string;\n logger: winston.Logger;\n};\n\nexport function localFsMiddleware({ repoPath, logger }: FsOptions) {\n return async function (req: express.Request, res: express.Response) {\n try {\n const { body } = req;\n\n switch (body.action) {\n case 'info': {\n res.json({\n repo: path.basename(repoPath),\n publish_modes: ['simple'],\n type: 'local_fs',\n });\n break;\n }\n case 'entriesByFolder': {\n const payload = body.params as EntriesByFolderParams;\n const { folder, extension, depth } = payload;\n const entries = await listRepoFiles(repoPath, folder, extension, depth).then(files =>\n entriesFromFiles(\n repoPath,\n files.map(file => ({ path: file })),\n ),\n );\n res.json(entries);\n break;\n }\n case 'entriesByFiles': {\n const payload = body.params as EntriesByFilesParams;\n const entries = await entriesFromFiles(repoPath, payload.files);\n res.json(entries);\n break;\n }\n case 'getEntry': {\n const payload = body.params as GetEntryParams;\n const [entry] = await entriesFromFiles(repoPath, [{ path: payload.path }]);\n res.json(entry);\n break;\n }\n case 'persistEntry': {\n const {\n entry,\n dataFiles = [entry as DataFile],\n assets,\n options,\n } = body.params as PersistEntryParams;\n const hasSubfolders = options?.hasSubfolders !== false;\n await Promise.all(\n dataFiles.map(dataFile => writeFile(path.join(repoPath, dataFile.path), dataFile.raw)),\n );\n // save assets\n await Promise.all(\n assets.map(a =>\n writeFile(path.join(repoPath, a.path), Buffer.from(a.content, a.encoding)),\n ),\n );\n if (dataFiles.every(dataFile => dataFile.newPath)) {\n dataFiles.forEach(async dataFile => {\n await move(\n path.join(repoPath, dataFile.path),\n path.join(repoPath, dataFile.newPath!),\n hasSubfolders,\n );\n });\n }\n res.json({ message: 'entry persisted' });\n break;\n }\n case 'getMedia': {\n const { mediaFolder } = body.params as GetMediaParams;\n const files = await listRepoFiles(repoPath, mediaFolder, '', 1);\n const mediaFiles = await Promise.all(files.map(file => readMediaFile(repoPath, file)));\n res.json(mediaFiles);\n break;\n }\n case 'getMediaFile': {\n const { path } = body.params as GetMediaFileParams;\n const mediaFile = await readMediaFile(repoPath, path);\n res.json(mediaFile);\n break;\n }\n case 'persistMedia': {\n const { asset } = body.params as PersistMediaParams;\n await writeFile(\n path.join(repoPath, asset.path),\n Buffer.from(asset.content, asset.encoding),\n );\n const file = await readMediaFile(repoPath, asset.path);\n res.json(file);\n break;\n }\n case 'deleteFile': {\n const { path: filePath } = body.params as DeleteFileParams;\n await deleteFile(repoPath, filePath);\n res.json({ message: `deleted file ${filePath}` });\n break;\n }\n case 'deleteFiles': {\n const { paths } = body.params as DeleteFilesParams;\n await Promise.all(paths.map(filePath => deleteFile(repoPath, filePath)));\n res.json({ message: `deleted files ${paths.join(', ')}` });\n break;\n }\n case 'getDeployPreview': {\n res.json(null);\n break;\n }\n default: {\n const message = `Unknown action ${body.action}`;\n res.status(422).json({ error: message });\n break;\n }\n }\n } catch (e) {\n logger.error(\n `Error handling ${JSON.stringify(req.body)}: ${\n e instanceof Error ? e.message : 'Unknown error'\n }`,\n );\n res.status(500).json({ error: 'Unknown error' });\n }\n };\n}\n\nexport function getSchema({ repoPath }: { repoPath: string }) {\n const schema = defaultSchema({ path: pathTraversal(repoPath) });\n return schema;\n}\n\ntype Options = {\n logger: winston.Logger;\n};\n\nexport async function registerMiddleware(app: express.Express, options: Options) {\n const { logger } = options;\n const repoPath = path.resolve(process.env.GIT_REPO_DIRECTORY || process.cwd());\n app.post('/api/v1', joi(getSchema({ repoPath })));\n app.post('/api/v1', localFsMiddleware({ repoPath, logger }));\n logger.info(`Decap CMS File System Proxy Server configured with ${repoPath}`);\n}\n","import path from 'path';\nimport { promises as fs } from 'fs';\nimport {\n branchFromContentKey,\n generateContentKey,\n contentKeyFromBranch,\n CMS_BRANCH_PREFIX,\n statusToLabel,\n labelToStatus,\n parseContentKey,\n} from 'decap-cms-lib-util/src/APIUtils';\nimport { parse } from 'what-the-diff';\n// eslint-disable-next-line import/no-named-as-default\nimport simpleGit from 'simple-git';\nimport { Mutex, withTimeout } from 'async-mutex';\n\nimport { defaultSchema, joi } from '../joi';\nimport { pathTraversal } from '../joi/customValidators';\nimport { listRepoFiles, writeFile, move, deleteFile, getUpdateDate } from '../utils/fs';\nimport { entriesFromFiles, readMediaFile } from '../utils/entries';\n\nimport type {\n EntriesByFolderParams,\n EntriesByFilesParams,\n GetEntryParams,\n DefaultParams,\n UnpublishedEntryParams,\n PersistEntryParams,\n GetMediaParams,\n Asset,\n PublishUnpublishedEntryParams,\n PersistMediaParams,\n DeleteFileParams,\n UpdateUnpublishedEntryStatusParams,\n DataFile,\n GetMediaFileParams,\n DeleteEntryParams,\n DeleteFilesParams,\n UnpublishedEntryDataFileParams,\n UnpublishedEntryMediaFileParams,\n} from '../types';\nimport type express from 'express';\nimport type winston from 'winston';\nimport type { SimpleGit } from 'simple-git';\n\nasync function commit(git: SimpleGit, commitMessage: string) {\n await git.add('.');\n await git.commit(commitMessage, undefined, {\n // setting the value to a string passes name=value\n // any other value passes just the key\n '--no-verify': null,\n '--no-gpg-sign': null,\n });\n}\n\nasync function getCurrentBranch(git: SimpleGit) {\n const currentBranch = await git.branchLocal().then(summary => summary.current);\n return currentBranch;\n}\n\nasync function runOnBranch<T>(git: SimpleGit, branch: string, func: () => Promise<T>) {\n const currentBranch = await getCurrentBranch(git);\n try {\n if (currentBranch !== branch) {\n await git.checkout(branch);\n }\n const result = await func();\n return result;\n } finally {\n await git.checkout(currentBranch);\n }\n}\n\nfunction branchDescription(branch: string) {\n return `branch.${branch}.description`;\n}\n\ntype GitOptions = {\n repoPath: string;\n logger: winston.Logger;\n};\n\nasync function commitEntry(\n git: SimpleGit,\n repoPath: string,\n dataFiles: DataFile[],\n assets: Asset[],\n commitMessage: string,\n hasSubfolders = true,\n) {\n // save entry content\n await Promise.all(\n dataFiles.map(dataFile => writeFile(path.join(repoPath, dataFile.path), dataFile.raw)),\n );\n // save assets\n await Promise.all(\n assets.map(a => writeFile(path.join(repoPath, a.path), Buffer.from(a.content, a.encoding))),\n );\n if (dataFiles.every(dataFile => dataFile.newPath)) {\n dataFiles.forEach(async dataFile => {\n await move(\n path.join(repoPath, dataFile.path),\n path.join(repoPath, dataFile.newPath!),\n hasSubfolders,\n );\n });\n }\n\n // commits files\n await commit(git, commitMessage);\n}\n\nasync function rebase(git: SimpleGit, branch: string) {\n const gpgSign = await git.raw(['config', 'commit.gpgsign']);\n try {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', 'false');\n }\n await git.rebase([branch, '--no-verify']);\n } finally {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', gpgSign);\n }\n }\n}\n\nasync function merge(git: SimpleGit, from: string, to: string) {\n const gpgSign = await git.raw(['config', 'commit.gpgsign']);\n try {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', 'false');\n }\n await git.mergeFromTo(from, to);\n } finally {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', gpgSign);\n }\n }\n}\n\nasync function isBranchExists(git: SimpleGit, branch: string) {\n const branchExists = await git.branchLocal().then(({ all }) => all.includes(branch));\n return branchExists;\n}\n\nasync function getDiffs(git: SimpleGit, source: string, dest: string) {\n const rawDiff = await git.diff([source, dest]);\n const diffs = parse(rawDiff).map(d => {\n const oldPath = d.oldPath?.replace(/b\\//, '') || '';\n const newPath = d.newPath?.replace(/b\\//, '') || '';\n const path = newPath || (oldPath as string);\n return {\n oldPath,\n newPath,\n status: d.status,\n newFile: d.status === 'added',\n path,\n id: path,\n binary: d.binary || /.svg$/.test(path),\n };\n });\n return diffs;\n}\n\nexport async function validateRepo({ repoPath }: { repoPath: string }) {\n const git = simpleGit(repoPath);\n const isRepo = await git.checkIsRepo();\n if (!isRepo) {\n throw Error(`${repoPath} is not a valid git repository`);\n }\n}\n\nexport function getSchema({ repoPath }: { repoPath: string }) {\n const schema = defaultSchema({ path: pathTraversal(repoPath) });\n return schema;\n}\n\nexport function localGitMiddleware({ repoPath, logger }: GitOptions) {\n const git = simpleGit(repoPath);\n\n // we can only perform a single git operation at any given time\n const mutex = withTimeout(new Mutex(), 3000, new Error('Request timed out'));\n\n return async function (req: express.Request, res: express.Response) {\n let release;\n try {\n release = await mutex.acquire();\n const { body } = req;\n if (body.action === 'info') {\n res.json({\n repo: path.basename(repoPath),\n publish_modes: ['simple', 'editorial_workflow'],\n type: 'local_git',\n });\n return;\n }\n const { branch } = body.params as DefaultParams;\n\n const branchExists = await isBranchExists(git, branch);\n if (!branchExists) {\n const message = `Default branch '${branch}' doesn't exist`;\n res.status(422).json({ error: message });\n return;\n }\n\n switch (body.action) {\n case 'entriesByFolder': {\n const payload = body.params as EntriesByFolderParams;\n const { folder, extension, depth } = payload;\n const entries = await runOnBranch(git, branch, () =>\n listRepoFiles(repoPath, folder, extension, depth).then(files =>\n entriesFromFiles(\n repoPath,\n files.map(file => ({ path: file })),\n ),\n ),\n );\n res.json(entries);\n break;\n }\n case 'entriesByFiles': {\n const payload = body.params as EntriesByFilesParams;\n const entries = await runOnBranch(git, branch, () =>\n entriesFromFiles(repoPath, payload.files),\n );\n res.json(entries);\n break;\n }\n case 'getEntry': {\n const payload = body.params as GetEntryParams;\n const [entry] = await runOnBranch(git, branch, () =>\n entriesFromFiles(repoPath, [{ path: payload.path }]),\n );\n res.json(entry);\n break;\n }\n case 'unpublishedEntries': {\n const cmsBranches = await git\n .branchLocal()\n .then(result => result.all.filter(b => b.startsWith(`${CMS_BRANCH_PREFIX}/`)));\n res.json(cmsBranches.map(contentKeyFromBranch));\n break;\n }\n case 'unpublishedEntry': {\n let { id, collection, slug, cmsLabelPrefix } = body.params as UnpublishedEntryParams;\n if (id) {\n ({ collection, slug } = parseContentKey(id));\n }\n const contentKey = generateContentKey(collection as string, slug as string);\n const cmsBranch = branchFromContentKey(contentKey);\n const branchExists = await isBranchExists(git, cmsBranch);\n if (branchExists) {\n const diffs = await getDiffs(git, branch, cmsBranch);\n const label = await git.raw(['config', branchDescription(cmsBranch)]);\n const status = label && labelToStatus(label.trim(), cmsLabelPrefix || '');\n const updatedAt =\n diffs.length >= 0\n ? await runOnBranch(git, cmsBranch, async () => {\n const dates = await Promise.all(\n diffs.map(({ newPath }) => getUpdateDate(repoPath, newPath)),\n );\n return dates.reduce((a, b) => {\n return a > b ? a : b;\n });\n })\n : new Date();\n const unpublishedEntry = {\n collection,\n slug,\n status,\n diffs,\n updatedAt,\n };\n res.json(unpublishedEntry);\n } else {\n return res.status(404).json({ message: 'Not Found' });\n }\n break;\n }\n case 'unpublishedEntryDataFile': {\n const { path, collection, slug } = body.params as UnpublishedEntryDataFileParams;\n const contentKey = generateContentKey(collection as string, slug as string);\n const cmsBranch = branchFromContentKey(contentKey);\n const [entry] = await runOnBranch(git, cmsBranch, () =>\n entriesFromFiles(repoPath, [{ path }]),\n );\n res.json({ data: entry.data });\n break;\n }\n case 'unpublishedEntryMediaFile': {\n const { path, collection, slug } = body.params as UnpublishedEntryMediaFileParams;\n const contentKey = generateContentKey(collection as string, slug as string);\n const cmsBranch = branchFromContentKey(contentKey);\n const file = await runOnBranch(git, cmsBranch, () => readMediaFile(repoPath, path));\n res.json(file);\n break;\n }\n case 'deleteUnpublishedEntry': {\n const { collection, slug } = body.params as DeleteEntryParams;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n const currentBranch = await getCurrentBranch(git);\n if (currentBranch === cmsBranch) {\n await git.checkoutLocalBranch(branch);\n }\n await git.branch(['-D', cmsBranch]);\n res.json({ message: `deleted branch: ${cmsBranch}` });\n break;\n }\n case 'persistEntry': {\n const {\n cmsLabelPrefix,\n entry,\n dataFiles = [entry as DataFile],\n assets,\n options,\n } = body.params as PersistEntryParams;\n const hasSubfolders = options?.hasSubfolders !== false;\n\n if (!options.useWorkflow) {\n await runOnBranch(git, branch, async () => {\n await commitEntry(\n git,\n repoPath,\n dataFiles,\n assets,\n options.commitMessage,\n hasSubfolders,\n );\n });\n } else {\n const slug = dataFiles[0].slug;\n const collection = options.collectionName as string;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n await runOnBranch(git, branch, async () => {\n const branchExists = await isBranchExists(git, cmsBranch);\n if (branchExists) {\n await git.checkout(cmsBranch);\n } else {\n await git.checkoutLocalBranch(cmsBranch);\n }\n await rebase(git, branch);\n const diffs = await getDiffs(git, branch, cmsBranch);\n // delete media files that have been removed from the entry\n const toDelete = diffs.filter(\n d => d.binary && !assets.map(a => a.path).includes(d.path),\n );\n await Promise.all(toDelete.map(f => fs.unlink(path.join(repoPath, f.path))));\n await commitEntry(\n git,\n repoPath,\n dataFiles,\n assets,\n options.commitMessage,\n hasSubfolders,\n );\n\n // add status for new entries\n if (!branchExists) {\n const description = statusToLabel(options.status, cmsLabelPrefix || '');\n await git.addConfig(branchDescription(cmsBranch), description);\n }\n });\n }\n res.json({ message: 'entry persisted' });\n break;\n }\n case 'updateUnpublishedEntryStatus': {\n const { collection, slug, newStatus, cmsLabelPrefix } =\n body.params as UpdateUnpublishedEntryStatusParams;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n const description = statusToLabel(newStatus, cmsLabelPrefix || '');\n await git.addConfig(branchDescription(cmsBranch), description);\n res.json({ message: `${branch} description was updated to ${description}` });\n break;\n }\n case 'publishUnpublishedEntry': {\n const { collection, slug } = body.params as PublishUnpublishedEntryParams;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n await merge(git, cmsBranch, branch);\n await git.deleteLocalBranch(cmsBranch);\n res.json({ message: `branch ${cmsBranch} merged to ${branch}` });\n break;\n }\n case 'getMedia': {\n const { mediaFolder } = body.params as GetMediaParams;\n const mediaFiles = await runOnBranch(git, branch, async () => {\n const files = await listRepoFiles(repoPath, mediaFolder, '', 1);\n const serializedFiles = await Promise.all(\n files.map(file => readMediaFile(repoPath, file)),\n );\n return serializedFiles;\n });\n res.json(mediaFiles);\n break;\n }\n case 'getMediaFile': {\n const { path } = body.params as GetMediaFileParams;\n const mediaFile = await runOnBranch(git, branch, () => {\n return readMediaFile(repoPath, path);\n });\n res.json(mediaFile);\n break;\n }\n case 'persistMedia': {\n const {\n asset,\n options: { commitMessage },\n } = body.params as PersistMediaParams;\n\n const file = await runOnBranch(git, branch, async () => {\n await writeFile(\n path.join(repoPath, asset.path),\n Buffer.from(asset.content, asset.encoding),\n );\n await commit(git, commitMessage);\n return readMediaFile(repoPath, asset.path);\n });\n res.json(file);\n break;\n }\n case 'deleteFile': {\n const {\n path: filePath,\n options: { commitMessage },\n } = body.params as DeleteFileParams;\n await runOnBranch(git, branch, async () => {\n await deleteFile(repoPath, filePath);\n await commit(git, commitMessage);\n });\n res.json({ message: `deleted file ${filePath}` });\n break;\n }\n case 'deleteFiles': {\n const {\n paths,\n options: { commitMessage },\n } = body.params as DeleteFilesParams;\n await runOnBranch(git, branch, async () => {\n await Promise.all(paths.map(filePath => deleteFile(repoPath, filePath)));\n await commit(git, commitMessage);\n });\n res.json({ message: `deleted files ${paths.join(', ')}` });\n break;\n }\n case 'getDeployPreview': {\n res.json(null);\n break;\n }\n default: {\n const message = `Unknown action ${body.action}`;\n res.status(422).json({ error: message });\n break;\n }\n }\n } catch (e) {\n logger.error(\n `Error handling ${JSON.stringify(req.body)}: ${\n e instanceof Error ? e.message : 'Unknown error'\n }`,\n );\n res.status(500).json({ error: 'Unknown error' });\n } finally {\n release && release();\n }\n };\n}\n\ntype Options = {\n logger: winston.Logger;\n};\n\nexport async function registerMiddleware(app: express.Express, options: Options) {\n const { logger } = options;\n const repoPath = path.resolve(process.env.GIT_REPO_DIRECTORY || process.cwd());\n await validateRepo({ repoPath });\n app.post('/api/v1', joi(getSchema({ repoPath })));\n app.post('/api/v1', localGitMiddleware({ repoPath, logger }));\n logger.info(`Decap CMS Git Proxy Server configured with ${repoPath}`);\n}\n","import crypto from 'crypto';\nimport path from 'path';\nimport { promises as fs } from 'fs';\n\nfunction sha256(buffer: Buffer) {\n return crypto.createHash('sha256').update(buffer).digest('hex');\n}\n\n// normalize windows os path format\nfunction normalizePath(path: string) {\n return path.replace(/\\\\/g, '/');\n}\n\nexport async function entriesFromFiles(\n repoPath: string,\n files: { path: string; label?: string }[],\n) {\n return Promise.all(\n files.map(async file => {\n try {\n const content = await fs.readFile(path.join(repoPath, file.path));\n return {\n data: content.toString(),\n file: { path: normalizePath(file.path), label: file.label, id: sha256(content) },\n };\n } catch (e) {\n return {\n data: null,\n file: { path: normalizePath(file.path), label: file.label, id: null },\n };\n }\n }),\n );\n}\n\nexport async function readMediaFile(repoPath: string, file: string) {\n const encoding = 'base64';\n const buffer = await fs.readFile(path.join(repoPath, file));\n const id = sha256(buffer);\n\n return {\n id,\n content: buffer.toString(encoding),\n encoding,\n path: normalizePath(file),\n name: path.basename(file),\n };\n}\n","import path from 'path';\nimport { promises as fs } from 'fs';\n\nasync function listFiles(dir: string, extension: string, depth: number): Promise<string[]> {\n if (depth <= 0) {\n return [];\n }\n\n try {\n const dirents = await fs.readdir(dir, { withFileTypes: true });\n const files = await Promise.all(\n dirents.map(dirent => {\n const res = path.join(dir, dirent.name);\n return dirent.isDirectory()\n ? listFiles(res, extension, depth - 1)\n : [res].filter(f => f.endsWith(extension));\n }),\n );\n return ([] as string[]).concat(...files);\n } catch (e) {\n return [];\n }\n}\n\nexport async function listRepoFiles(\n repoPath: string,\n folder: string,\n extension: string,\n depth: number,\n) {\n const files = await listFiles(path.join(repoPath, folder), extension, depth);\n return files.map(f => f.slice(repoPath.length + 1));\n}\n\nexport async function writeFile(filePath: string, content: Buffer | string) {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n await fs.writeFile(filePath, content);\n}\n\nexport async function deleteFile(repoPath: string, filePath: string) {\n await fs.unlink(path.join(repoPath, filePath)).catch(() => undefined);\n}\n\nasync function moveFile(from: string, to: string) {\n await fs.mkdir(path.dirname(to), { recursive: true });\n await fs.rename(from, to);\n}\n\nexport async function move(from: string, to: string, hasSubfolders = true) {\n // move file\n await moveFile(from, to);\n\n if (hasSubfolders) {\n // Legacy behavior (subfolders: true, default): move all files in the directory\n // This is for collections where all files in a folder represent a single entry\n const sourceDir = path.dirname(from);\n const destDir = path.dirname(to);\n const allFiles = await listFiles(sourceDir, '', 100);\n await Promise.all(allFiles.map(file => moveFile(file, file.replace(sourceDir, destDir))));\n }\n}\n\nexport async function getUpdateDate(repoPath: string, filePath: string) {\n return fs\n .stat(path.join(repoPath, filePath))\n .then(stat => stat.mtime)\n .catch(() => new Date());\n}\n","module.exports = require(\"@hapi/joi\");","module.exports = require(\"async-mutex\");","module.exports = require(\"cors\");","module.exports = require(\"express\");","module.exports = require(\"morgan\");","module.exports = require(\"simple-git\");","module.exports = require(\"what-the-diff\");","module.exports = require(\"winston\");","module.exports = require(\"crypto\");","module.exports = require(\"fs\");","module.exports = require(\"path\");","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","import { registerCommonMiddlewares } from './middlewares/common';\nimport { registerMiddleware as localGit } from './middlewares/localGit';\nimport { registerMiddleware as localFs } from './middlewares/localFs';\nimport { createLogger } from './logger';\n\nimport type express from 'express';\n\ntype Options = {\n logLevel?: string;\n};\n\nfunction createOptions(options: Options) {\n return {\n logger: createLogger({ level: options.logLevel || 'info' }),\n };\n}\n\nexport async function registerLocalGit(app: express.Express, options: Options = {}) {\n const opts = createOptions(options);\n registerCommonMiddlewares(app, opts);\n await localGit(app, opts);\n}\n\nexport async function registerLocalFs(app: express.Express, options: Options = {}) {\n const opts = createOptions(options);\n registerCommonMiddlewares(app, opts);\n await localFs(app, opts);\n}\n"],"names":["getLabelPrefix","labelPrefix","exports","CMS_BRANCH_PREFIX","labelToStatus","label","slice","length","statusToLabel","status","generateContentKey","collectionName","slug","parseContentKey","contentKey","index","indexOf","collection","contentKeyFromBranch","branch","branchFromContentKey","winston_1","__importDefault","__webpack_require__","combine","colorize","simple","default","format","createLogger","level","transports","Console","express_1","morgan_1","cors_1","registerCommonMiddlewares","app","options","logger","stream","write","message","debug","String","trim","use","origin","process","env","ORIGIN","json","limit","joi_1","path_1","pathTraversal","repoPath","extend","type","base","string","required","messages","validate","value","helpers","join","startsWith","errors","error","path","allowedActions","requiredString","requiredNumber","number","requiredBool","bool","defaultSchema","defaultParams","object","asset","content","encoding","valid","dataFile","raw","newPath","optional","params","when","switch","is","then","allow","keys","folder","extension","depth","files","array","items","id","cmsLabelPrefix","entry","dataFiles","assets","commitMessage","useWorkflow","xor","newStatus","mediaFolder","paths","min","otherwise","forbidden","action","joi","schema","req","res","next","body","allowUnknown","details","map","i","customValidators_1","fs_1","entries_1","localFsMiddleware","async","repo","basename","publish_modes","payload","entries","listRepoFiles","entriesFromFiles","file","hasSubfolders","Promise","all","writeFile","a","Buffer","from","every","forEach","move","mediaFiles","readMediaFile","mediaFile","filePath","deleteFile","e","JSON","stringify","Error","getSchema","registerMiddleware","resolve","GIT_REPO_DIRECTORY","cwd","post","info","APIUtils_1","what_the_diff_1","simple_git_1","async_mutex_1","fs_2","commit","git","add","undefined","getCurrentBranch","branchLocal","summary","current","runOnBranch","func","currentBranch","checkout","branchDescription","commitEntry","isBranchExists","includes","getDiffs","source","dest","rawDiff","diff","parse","d","oldPath","_a","replace","_b","newFile","binary","test","validateRepo","checkIsRepo","localGitMiddleware","mutex","withTimeout","Mutex","release","acquire","cmsBranches","result","filter","b","cmsBranch","diffs","updatedAt","getUpdateDate","reduce","Date","unpublishedEntry","data","checkoutLocalBranch","branchExists","gpgSign","addConfig","rebase","toDelete","f","promises","unlink","description","to","mergeFromTo","merge","deleteLocalBranch","crypto_1","sha256","buffer","createHash","update","digest","normalizePath","readFile","toString","name","listFiles","dir","dirents","readdir","withFileTypes","dirent","isDirectory","endsWith","concat","moveFile","mkdir","dirname","recursive","rename","catch","sourceDir","destDir","allFiles","stat","mtime","module","require","__webpack_module_cache__","moduleId","cachedModule","__webpack_modules__","call","common_1","localGit_1","localFs_1","logger_1","createOptions","logLevel","registerLocalGit","opts","registerLocalFs"],"sourceRoot":""}
1
+ {"version":3,"file":"middlewares.js","mappings":";mCAMA,SAASA,EAAeC,GACtB,OAAOA,GAH8B,YAIvC,iJARaC,EAAAC,kBAAoB,MAcjCD,EAAAE,cAAA,SAA8BC,EAAeJ,GAC3C,OAAOI,EAAMC,MAAMN,EAAeC,GAAaM,OACjD,EAEAL,EAAAM,cAAA,SAA8BC,EAAgBR,GAC5C,MAAO,GAAGD,EAAeC,KAAeQ,GAC1C,EAEAP,EAAAQ,mBAAA,SAAmCC,EAAwBC,GACzD,MAAO,GAAGD,KAAkBC,GAC9B,EAEAV,EAAAW,gBAAA,SAAgCC,GAC9B,MAAMC,EAAQD,EAAWE,QAAQ,KACjC,MAAO,CAAEC,WAAYH,EAAWR,MAAM,EAAGS,GAAQH,KAAME,EAAWR,MAAMS,EAAQ,GAClF,EAEAb,EAAAgB,qBAAA,SAAqCC,GACnC,OAAOA,EAAOb,MAAM,GAAGJ,EAAAC,qBAAqBI,OAC9C,EAEAL,EAAAkB,qBAAA,SAAqCN,GACnC,MAAO,GAAGZ,EAAAC,qBAAqBW,GACjC,yKCrCA,MAAAO,EAAAC,EAAAC,EAAA,OAEMC,QAAEA,EAAOC,SAAEA,EAAQC,OAAEA,GAAWL,EAAAM,QAAQC,OAM9C1B,EAAA2B,aAAA,UAA6BC,MAAEA,IAC7B,OAAOT,EAAAM,QAAQE,aAAa,CAC1BC,QACAF,OAAQJ,EAAQC,IAAYC,KAC5BK,WAAY,CAAC,IAAIV,EAAAM,QAAQI,WAAWC,UAExC,sLCdA,MAAAC,EAAAX,EAAAC,EAAA,MACAW,EAAAZ,EAAAC,EAAA,KACAY,EAAAb,EAAAC,EAAA,MAQArB,EAAAkC,0BAAA,SAA0CC,EAAsBC,GAC9D,MAAMC,OAAEA,GAAWD,EACbE,EAAS,CACbC,MAAQC,IACNH,EAAOI,MAAMC,OAAOF,GAASG,UAGjCR,EAAIS,KAAI,EAAAZ,EAAAP,SAAO,WAAY,CAAEa,YAC7BH,EAAIS,KACF,EAAAX,EAAAR,SAAK,CACHoB,OAAQC,QAAQC,IAAIC,QAAU,kDAGlCb,EAAIS,IAAIb,EAAAN,QAAQwB,KAAK,CAAEC,MAAO,SAChC,0KCxBA,MAAAC,EAAA/B,EAAAC,EAAA,MAEA+B,EAAA/B,EAAA,KAEArB,EAAAqD,cAAA,SAA8BC,GAC5B,OAAOH,EAAA1B,QAAI8B,OAAO,CAChBC,KAAM,OACNC,KAAMN,EAAA1B,QAAIiC,SAASC,WACnBC,SAAU,CACR,eAAgB,qEAElB,QAAAC,CAASC,EAAOC,GACd,KACE,EAAAX,EAAAY,iBAAgBV,EAAUQ,GAC1B,MAAOG,GACP,MAAO,CAAEH,QAAOI,OAAQH,EAAQI,MAAM,iBAE1C,IACCC,MACL,gLCnBA,MAAAjB,EAAA/B,EAAAC,EAAA,MAIMgD,EAAiB,CACrB,OACA,kBACA,iBACA,WACA,qBACA,mBACA,2BACA,4BACA,yBACA,eACA,+BACA,0BACA,WACA,eACA,eACA,aACA,cACA,oBAGIC,EAAiBnB,EAAA1B,QAAIiC,SAASC,WAC9BY,EAAiBpB,EAAA1B,QAAI+C,SAASb,WAC9Bc,EAAetB,EAAA1B,QAAIiD,OAAOf,WAE1B5C,EAAauD,EACb5D,EAAO4D,EAEbtE,EAAA2E,cAAA,UAA8BP,KAAEA,EAAOE,GAAmB,IACxD,MAAMM,EAAgBzB,EAAA1B,QAAIoD,OAAO,CAC/B5D,OAAQqD,IAGJQ,EAAQ3B,EAAA1B,QAAIoD,OAAO,CACvBT,OACAW,QAAST,EACTU,SAAUV,EAAeW,MAAM,YAG3BC,EAAW/B,EAAA1B,QAAIoD,OAAO,CAC1BnE,KAAM4D,EACNF,OACAe,IAAKb,EACLc,QAAShB,EAAKiB,aAGVC,EAASnC,EAAA1B,QAAI8D,KAAK,SAAU,CAChCC,OAAQ,CACN,CACEC,GAAI,OACJC,KAAMvC,EAAA1B,QAAIkE,SAEZ,CACEF,GAAI,kBACJC,KAAMd,EACHgB,KAAK,CACJC,OAAQzB,EACR0B,UAAWxB,EACXyB,MAAOxB,IAERZ,YAEL,CACE8B,GAAI,iBACJC,KAAMd,EAAcgB,KAAK,CACvBI,MAAO7C,EAAA1B,QAAIwE,QACRC,MAAM/C,EAAA1B,QAAIoD,OAAO,CAAET,OAAMjE,MAAOgD,EAAA1B,QAAIiC,YACpCC,cAGP,CACE8B,GAAI,WACJC,KAAMd,EACHgB,KAAK,CACJxB,SAEDT,YAEL,CACE8B,GAAI,qBACJC,KAAMd,EAAcgB,KAAK,CAAE3E,OAAQqD,IAAkBX,YAEvD,CACE8B,GAAI,mBACJC,KAAMd,EACHgB,KAAK,CACJO,GAAIhD,EAAA1B,QAAIiC,SAAS2B,WACjBtE,WAAYoC,EAAA1B,QAAIiC,SAAS2B,WACzB3E,KAAMyC,EAAA1B,QAAIiC,SAAS2B,WACnBe,eAAgBjD,EAAA1B,QAAIiC,SAAS2B,aAE9B1B,YAEL,CACE8B,GAAI,2BACJC,KAAMd,EACHgB,KAAK,CACJ7E,aACAL,OACAyF,GAAI7B,EACJF,SAEDT,YAEL,CACE8B,GAAI,4BACJC,KAAMd,EACHgB,KAAK,CACJ7E,aACAL,OACAyF,GAAI7B,EACJF,SAEDT,YAEL,CACE8B,GAAI,yBACJC,KAAMd,EACHgB,KAAK,CACJ7E,aACAL,SAEDiD,YAEL,CACE8B,GAAI,eACJC,KAAMd,EACHgB,KAAK,CACJQ,eAAgBjD,EAAA1B,QAAIiC,SAAS2B,WAC7BgB,MAAOnB,EACPoB,UAAWnD,EAAA1B,QAAIwE,QAAQC,MAAMhB,GAC7BqB,OAAQpD,EAAA1B,QAAIwE,QAAQC,MAAMpB,GAAOnB,WACjCvB,QAASe,EAAA1B,QAAIoD,OAAO,CAClBpE,eAAgB0C,EAAA1B,QAAIiC,SACpB8C,cAAelC,EACfmC,YAAahC,EACblE,OAAQ+D,IACPX,aAEJ+C,IAAI,QAAS,aACb/C,YAEL,CACE8B,GAAI,+BACJC,KAAMd,EACHgB,KAAK,CACJ7E,aACAL,OACAiG,UAAWrC,EACX8B,eAAgBjD,EAAA1B,QAAIiC,SAAS2B,aAE9B1B,YAEL,CACE8B,GAAI,0BACJC,KAAMd,EACHgB,KAAK,CACJ7E,aACAL,SAEDiD,YAEL,CACE8B,GAAI,WACJC,KAAMd,EACHgB,KAAK,CACJgB,YAAaxC,IAEdT,YAEL,CACE8B,GAAI,eACJC,KAAMd,EACHgB,KAAK,CACJxB,SAEDT,YAEL,CACE8B,GAAI,eACJC,KAAMd,EACHgB,KAAK,CACJd,MAAOA,EAAMnB,WACbvB,QAASe,EAAA1B,QAAIoD,OAAO,CAClB2B,cAAelC,IACdX,aAEJA,YAEL,CACE8B,GAAI,aACJC,KAAMd,EACHgB,KAAK,CACJxB,OACAhC,QAASe,EAAA1B,QAAIoD,OAAO,CAClB2B,cAAelC,IACdX,aAEJA,YAEL,CACE8B,GAAI,cACJC,KAAMd,EACHgB,KAAK,CACJiB,MAAO1D,EAAA1B,QAAIwE,QAAQC,MAAM9B,GAAM0C,IAAI,GAAGnD,WACtCvB,QAASe,EAAA1B,QAAIoD,OAAO,CAClB2B,cAAelC,IACdX,aAEJA,YAEL,CACE8B,GAAI,mBACJC,KAAMd,EACHgB,KAAK,CACJ7E,aACAL,SAEDiD,aAGPoD,UAAW5D,EAAA1B,QAAIuF,cAGjB,OAAO7D,EAAA1B,QAAIoD,OAAO,CAChBoC,OAAQ9D,EAAA1B,QAAIwD,SAASZ,GAAgBV,WACrC2B,UAEJ,EAEAtF,EAAAkH,IAAA,SAAoBC,GAClB,MAAO,CAACC,EAAsBC,EAAuBC,KACnD,MAAMnD,MAAEA,GAAUgD,EAAOtD,SAASuD,EAAIG,KAAM,CAAEC,cAAc,IAC5D,GAAIrD,EAAO,CACT,MAAMsD,QAAEA,GAAYtD,EACd3B,EAAUiF,EAAQC,IAAIC,GAAKA,EAAEnF,SAASoF,KAAK,KACjDP,EAAI9G,OAAO,KAAK0C,KAAK,CAAEkB,MAAO3B,SAE9B8E,IAGN,+MCrPA,MAAAlE,EAAAhC,EAAAC,EAAA,MAEA8B,EAAA9B,EAAA,KACAwG,EAAAxG,EAAA,KACAyG,EAAAzG,EAAA,KACA0G,EAAA1G,EAAA,KAsBA,SAAgB2G,GAAkB1E,SAAEA,EAAQjB,OAAEA,IAC5C,OAAO4F,eAAgBb,EAAsBC,GAC3C,IACE,MAAME,KAAEA,GAASH,EAEjB,OAAQG,EAAKN,QACX,IAAK,OACHI,EAAIpE,KAAK,CACPiF,KAAM9E,EAAA3B,QAAK0G,SAAS7E,GACpB8E,cAAe,CAAC,UAChB5E,KAAM,aAER,MAEF,IAAK,kBAAmB,CACtB,MAAM6E,EAAUd,EAAKjC,QACfO,OAAEA,EAAMC,UAAEA,EAASC,MAAEA,GAAUsC,EAC/BC,QAAgB,EAAAR,EAAAS,eAAcjF,EAAUuC,EAAQC,EAAWC,GAAOL,KAAKM,IAC3E,EAAA+B,EAAAS,kBACElF,EACA0C,EAAM0B,IAAIe,IAAQ,CAAGrE,KAAMqE,OAG/BpB,EAAIpE,KAAKqF,GACT,MAEF,IAAK,iBAAkB,CACrB,MAAMD,EAAUd,EAAKjC,OACfgD,QAAgB,EAAAP,EAAAS,kBAAiBlF,EAAU+E,EAAQrC,OACzDqB,EAAIpE,KAAKqF,GACT,MAEF,IAAK,WAAY,CACf,MAAMD,EAAUd,EAAKjC,QACde,SAAe,EAAA0B,EAAAS,kBAAiBlF,EAAU,CAAC,CAAEc,KAAMiE,EAAQjE,QAClEiD,EAAIpE,KAAKoD,GACT,MAEF,IAAK,eAAgB,CACnB,MAAMA,MACJA,EAAKC,UACLA,EAAY,CAACD,GAAkBE,OAC/BA,EAAMnE,QACNA,GACEmF,EAAKjC,OACHoD,GAA2C,KAA3BtG,aAAO,EAAPA,EAASsG,qBACzBC,QAAQC,IACZtC,EAAUoB,IAAIxC,IAAY,EAAA4C,EAAAe,WAAUvF,EAAU4B,EAASd,KAAMc,EAASC,aAGlEwD,QAAQC,IACZrC,EAAOmB,IAAIoB,IAAK,EAAAhB,EAAAe,WAAUvF,EAAUwF,EAAE1E,KAAM2E,OAAOC,KAAKF,EAAE/D,QAAS+D,EAAE9D,aAEnEsB,EAAU2C,MAAM/D,GAAYA,EAASE,UACvCkB,EAAU4C,QAAQjB,MAAM/C,UAChB,EAAA4C,EAAAqB,MAAK7F,EAAU4B,EAASd,KAAMc,EAASE,QAAUsD,KAG3DrB,EAAIpE,KAAK,CAAET,QAAS,oBACpB,MAEF,IAAK,WAAY,CACf,MAAMoE,YAAEA,GAAgBW,EAAKjC,OACvBU,QAAc,EAAA8B,EAAAS,eAAcjF,EAAUsD,EAAa,GAAI,GACvDwC,QAAmBT,QAAQC,IAAI5C,EAAM0B,IAAIe,IAAQ,EAAAV,EAAAsB,eAAc/F,EAAUmF,KAC/EpB,EAAIpE,KAAKmG,GACT,MAEF,IAAK,eAAgB,CACnB,MAAMhF,KAAEA,GAASmD,EAAKjC,OAChBgE,QAAkB,EAAAvB,EAAAsB,eAAc/F,EAAUc,GAChDiD,EAAIpE,KAAKqG,GACT,MAEF,IAAK,eAAgB,CACnB,MAAMxE,MAAEA,GAAUyC,EAAKjC,aACjB,EAAAwC,EAAAe,WAAUvF,EAAUwB,EAAMV,KAAM2E,OAAOC,KAAKlE,EAAMC,QAASD,EAAME,WACvE,MAAMyD,QAAa,EAAAV,EAAAsB,eAAc/F,EAAUwB,EAAMV,MACjDiD,EAAIpE,KAAKwF,GACT,MAEF,IAAK,aAAc,CACjB,MAAQrE,KAAMmF,GAAahC,EAAKjC,aAC1B,EAAAwC,EAAA0B,YAAWlG,EAAUiG,GAC3BlC,EAAIpE,KAAK,CAAET,QAAS,gBAAgB+G,MACpC,MAEF,IAAK,cAAe,CAClB,MAAM1C,MAAEA,GAAUU,EAAKjC,aACjBqD,QAAQC,IAAI/B,EAAMa,IAAI6B,IAAY,EAAAzB,EAAA0B,YAAWlG,EAAUiG,KAC7DlC,EAAIpE,KAAK,CAAET,QAAS,iBAAiBqE,EAAMe,KAAK,UAChD,MAEF,IAAK,mBACHP,EAAIpE,KAAK,MACT,MAEF,QAAS,CACP,MAAMT,EAAU,kBAAkB+E,EAAKN,SACvCI,EAAI9G,OAAO,KAAK0C,KAAK,CAAEkB,MAAO3B,IAC9B,QAGJ,MAAOyB,GACP5B,EAAO8B,MACL,kBAAkBsF,KAAKC,UAAUtC,EAAIG,UACnCtD,aAAa0F,MAAQ1F,EAAEzB,QAAU,mBAGrC6E,EAAI9G,OAAO,KAAK0C,KAAK,CAAEkB,MAAO,kBAElC,CACF,CAEA,SAAgByF,GAAUtG,SAAEA,IAE1B,OADe,EAAAH,EAAAwB,eAAc,CAAEP,MAAM,EAAAyD,EAAAxE,eAAcC,IAErD,CArHAtD,EAAAgI,kBAAAA,EAkHAhI,EAAA4J,UAAAA,EASA5J,EAAA6J,mBAAO5B,eAAkC9F,EAAsBC,GAC7D,MAAMC,OAAEA,GAAWD,EACbkB,EAAWF,EAAA3B,QAAKqI,QAAQhH,QAAQC,IAAIgH,oBAAsBjH,QAAQkH,OACxE7H,EAAI8H,KAAK,WAAW,EAAA9G,EAAA+D,KAAI0C,EAAU,CAAEtG,eACpCnB,EAAI8H,KAAK,UAAWjC,EAAkB,CAAE1E,WAAUjB,YAClDA,EAAO6H,KAAK,sDAAsD5G,IACpE,+NC5JA,MAAAF,EAAAhC,EAAAC,EAAA,MACAyG,EAAAzG,EAAA,KACA8I,EAAA9I,EAAA,KASA+I,EAAA/I,EAAA,KAEAgJ,EAAAjJ,EAAAC,EAAA,MACAiJ,EAAAjJ,EAAA,KAEA8B,EAAA9B,EAAA,KACAwG,EAAAxG,EAAA,KACAkJ,EAAAlJ,EAAA,KACA0G,EAAA1G,EAAA,KACAmJ,EAAAnJ,EAAA,KA0BA4G,eAAewC,EAAOC,EAAgBlE,SAC9BkE,EAAIC,IAAI,WACRD,EAAID,OAAOjE,OAAeoE,EAAW,CAGzC,cAAe,KACf,gBAAiB,MAErB,CAEA3C,eAAe4C,EAAiBH,GAE9B,aAD4BA,EAAII,cAAcpF,KAAKqF,GAAWA,EAAQC,QAExE,CAEA/C,eAAegD,EAAeP,EAAgBzJ,EAAgBiK,GAC5D,MAAMC,QAAsBN,EAAiBH,GAC7C,IAKE,OAJIS,IAAkBlK,SACdyJ,EAAIU,SAASnK,SAEAiK,kBAGfR,EAAIU,SAASD,GAEvB,CAEA,SAASE,EAAkBpK,GACzB,MAAO,UAAUA,eACnB,CAOAgH,eAAeqD,EACbZ,EACApH,EACAgD,EACAC,EACAC,EACAkC,GAAgB,SAGVC,QAAQC,IAAItC,EAAUoB,IAAIxC,IAAY,EAAAqF,EAAA1B,WAAUvF,EAAU4B,EAASd,KAAMc,EAASC,aAElFwD,QAAQC,IACZrC,EAAOmB,IAAIoB,IAAK,EAAAyB,EAAA1B,WAAUvF,EAAUwF,EAAE1E,KAAM2E,OAAOC,KAAKF,EAAE/D,QAAS+D,EAAE9D,aAEnEsB,EAAU2C,MAAM/D,GAAYA,EAASE,UACvCkB,EAAU4C,QAAQjB,MAAM/C,UAChB,EAAAqF,EAAApB,MAAK7F,EAAU4B,EAASd,KAAMc,EAASE,QAAUsD,WAKrD+B,EAAOC,EAAKlE,EACpB,CA8BAyB,eAAesD,EAAeb,EAAgBzJ,GAE5C,aAD2ByJ,EAAII,cAAcpF,KAAK,EAAGkD,SAAUA,EAAI4C,SAASvK,GAE9E,CAEAgH,eAAewD,EAASf,EAAgBgB,EAAgBC,GACtD,MAAMC,QAAgBlB,EAAImB,KAAK,CAACH,EAAQC,IAexC,OAdc,EAAAvB,EAAA0B,OAAMF,GAASlE,IAAIqE,YAC/B,MAAMC,GAAmB,QAATC,EAAAF,EAAEC,eAAO,IAAAC,OAAA,EAAAA,EAAEC,QAAQ,MAAO,MAAO,GAC3C9G,GAAmB,QAAT+G,EAAAJ,EAAE3G,eAAO,IAAA+G,OAAA,EAAAA,EAAED,QAAQ,MAAO,MAAO,GAC3C9H,EAAOgB,GAAY4G,EACzB,MAAO,CACLA,UACA5G,UACA7E,OAAQwL,EAAExL,OACV6L,QAAsB,UAAbL,EAAExL,OACX6D,OACA+B,GAAI/B,EACJiI,OAAQN,EAAEM,QAAU,QAAQC,KAAKlI,KAIvC,CAEO6D,eAAesE,GAAajJ,SAAEA,IACnC,MAAMoH,GAAM,EAAAL,EAAA5I,SAAU6B,GAEtB,UADqBoH,EAAI8B,cAEvB,MAAM7C,MAAM,GAAGrG,kCAEnB,CAEA,SAAgBsG,GAAUtG,SAAEA,IAE1B,OADe,EAAAH,EAAAwB,eAAc,CAAEP,MAAM,EAAAyD,EAAAxE,eAAcC,IAErD,CAEA,SAAgBmJ,GAAmBnJ,SAAEA,EAAQjB,OAAEA,IAC7C,MAAMqI,GAAM,EAAAL,EAAA5I,SAAU6B,GAGhBoJ,GAAQ,EAAApC,EAAAqC,aAAY,IAAIrC,EAAAsC,MAAS,IAAM,IAAIjD,MAAM,sBAEvD,OAAO1B,eAAgBb,EAAsBC,GAC3C,IAAIwF,EACJ,IACEA,QAAgBH,EAAMI,UACtB,MAAMvF,KAAEA,GAASH,EACjB,GAAoB,SAAhBG,EAAKN,OAMP,YALAI,EAAIpE,KAAK,CACPiF,KAAM9E,EAAA3B,QAAK0G,SAAS7E,GACpB8E,cAAe,CAAC,SAAU,sBAC1B5E,KAAM,cAIV,MAAMvC,OAAEA,GAAWsG,EAAKjC,OAGxB,UAD2BiG,EAAeb,EAAKzJ,GAC5B,CACjB,MAAMuB,EAAU,mBAAmBvB,mBAEnC,YADAoG,EAAI9G,OAAO,KAAK0C,KAAK,CAAEkB,MAAO3B,IAIhC,OAAQ+E,EAAKN,QACX,IAAK,kBAAmB,CACtB,MAAMoB,EAAUd,EAAKjC,QACfO,OAAEA,EAAMC,UAAEA,EAASC,MAAEA,GAAUsC,EAC/BC,QAAgB2C,EAAYP,EAAKzJ,EAAQ,KAC7C,EAAAsJ,EAAAhC,eAAcjF,EAAUuC,EAAQC,EAAWC,GAAOL,KAAKM,IACrD,EAAA+B,EAAAS,kBACElF,EACA0C,EAAM0B,IAAIe,IAAQ,CAAGrE,KAAMqE,QAIjCpB,EAAIpE,KAAKqF,GACT,MAEF,IAAK,iBAAkB,CACrB,MAAMD,EAAUd,EAAKjC,OACfgD,QAAgB2C,EAAYP,EAAKzJ,EAAQ,KAC7C,EAAA8G,EAAAS,kBAAiBlF,EAAU+E,EAAQrC,QAErCqB,EAAIpE,KAAKqF,GACT,MAEF,IAAK,WAAY,CACf,MAAMD,EAAUd,EAAKjC,QACde,SAAe4E,EAAYP,EAAKzJ,EAAQ,KAC7C,EAAA8G,EAAAS,kBAAiBlF,EAAU,CAAC,CAAEc,KAAMiE,EAAQjE,SAE9CiD,EAAIpE,KAAKoD,GACT,MAEF,IAAK,qBAAsB,CACzB,MAAM0G,QAAoBrC,EACvBI,cACApF,KAAKsH,GAAUA,EAAOpE,IAAIqE,OAAOC,GAAKA,EAAEC,WAAW,GAAGhD,EAAAlK,wBACzDoH,EAAIpE,KAAK8J,EAAYrF,IAAIyC,EAAAnJ,uBACzB,MAEF,IAAK,mBAAoB,CACvB,IAAImF,GAAEA,EAAEpF,WAAEA,EAAUL,KAAEA,EAAI0F,eAAEA,GAAmBmB,EAAKjC,OAChDa,KACCpF,aAAYL,SAAS,EAAAyJ,EAAAxJ,iBAAgBwF,IAE1C,MAAMvF,GAAa,EAAAuJ,EAAA3J,oBAAmBO,EAAsBL,GACtD0M,GAAY,EAAAjD,EAAAjJ,sBAAqBN,GAEvC,UAD2B2K,EAAeb,EAAK0C,GAyB7C,OAAO/F,EAAI9G,OAAO,KAAK0C,KAAK,CAAET,QAAS,cAxBvB,CAChB,MAAM6K,QAAc5B,EAASf,EAAKzJ,EAAQmM,GACpCjN,QAAcuK,EAAIvF,IAAI,CAAC,SAAUkG,EAAkB+B,KACnD7M,EAASJ,IAAS,EAAAgK,EAAAjK,eAAcC,EAAMwC,OAAQyD,GAAkB,IAChEkH,EACJD,EAAMhN,QAAU,QACN4K,EAAYP,EAAK0C,EAAWnF,gBACZU,QAAQC,IAC1ByE,EAAM3F,IAAI,EAAGtC,cAAc,EAAAmF,EAAAgD,eAAcjK,EAAU8B,MAExCoI,OAAO,CAAC1E,EAAGoE,IACfpE,EAAIoE,EAAIpE,EAAIoE,IAGvB,IAAIO,KACJC,EAAmB,CACvB3M,aACAL,OACAH,SACA8M,QACAC,aAEFjG,EAAIpE,KAAKyK,GAIX,MAEF,IAAK,2BAA4B,CAC/B,MAAMtJ,KAAEA,EAAIrD,WAAEA,EAAUL,KAAEA,GAAS6G,EAAKjC,OAClC1E,GAAa,EAAAuJ,EAAA3J,oBAAmBO,EAAsBL,GACtD0M,GAAY,EAAAjD,EAAAjJ,sBAAqBN,IAChCyF,SAAe4E,EAAYP,EAAK0C,EAAW,KAChD,EAAArF,EAAAS,kBAAiBlF,EAAU,CAAC,CAAEc,WAEhCiD,EAAIpE,KAAK,CAAE0K,KAAMtH,EAAMsH,OACvB,MAEF,IAAK,4BAA6B,CAChC,MAAMvJ,KAAEA,EAAIrD,WAAEA,EAAUL,KAAEA,GAAS6G,EAAKjC,OAClC1E,GAAa,EAAAuJ,EAAA3J,oBAAmBO,EAAsBL,GACtD0M,GAAY,EAAAjD,EAAAjJ,sBAAqBN,GACjC6H,QAAawC,EAAYP,EAAK0C,EAAW,KAAM,EAAArF,EAAAsB,eAAc/F,EAAUc,IAC7EiD,EAAIpE,KAAKwF,GACT,MAEF,IAAK,yBAA0B,CAC7B,MAAM1H,WAAEA,EAAUL,KAAEA,GAAS6G,EAAKjC,OAC5B1E,GAAa,EAAAuJ,EAAA3J,oBAAmBO,EAAYL,GAC5C0M,GAAY,EAAAjD,EAAAjJ,sBAAqBN,SACXiK,EAAiBH,KACvB0C,SACd1C,EAAIkD,oBAAoB3M,SAE1ByJ,EAAIzJ,OAAO,CAAC,KAAMmM,IACxB/F,EAAIpE,KAAK,CAAET,QAAS,mBAAmB4K,MACvC,MAEF,IAAK,eAAgB,CACnB,MAAMhH,eACJA,EAAcC,MACdA,EAAKC,UACLA,EAAY,CAACD,GAAkBE,OAC/BA,EAAMnE,QACNA,GACEmF,EAAKjC,OACHoD,GAA2C,KAA3BtG,aAAO,EAAPA,EAASsG,eAE/B,GAAKtG,EAAQqE,YAWN,CACL,MAAM/F,EAAO4F,EAAU,GAAG5F,KACpBK,EAAaqB,EAAQ3B,eACrBG,GAAa,EAAAuJ,EAAA3J,oBAAmBO,EAAYL,GAC5C0M,GAAY,EAAAjD,EAAAjJ,sBAAqBN,SACjCqK,EAAYP,EAAKzJ,EAAQgH,UAC7B,MAAM4F,QAAqBtC,EAAeb,EAAK0C,GAC3CS,QACInD,EAAIU,SAASgC,SAEb1C,EAAIkD,oBAAoBR,SApO9CnF,eAAsByC,EAAgBzJ,GACpC,MAAM6M,QAAgBpD,EAAIvF,IAAI,CAAC,SAAU,mBACzC,IACkB,SAAZ2I,SACIpD,EAAIqD,UAAU,iBAAkB,eAElCrD,EAAIsD,OAAO,CAAC/M,EAAQ,wBAEV,SAAZ6M,SACIpD,EAAIqD,UAAU,iBAAkBD,GAG5C,CA0NoBE,CAAOtD,EAAKzJ,GAClB,MAEMgN,SAFcxC,EAASf,EAAKzJ,EAAQmM,IAEnBH,OACrBlB,GAAKA,EAAEM,SAAW9F,EAAOmB,IAAIoB,GAAKA,EAAE1E,MAAMoH,SAASO,EAAE3H,OAevD,SAbMuE,QAAQC,IACZqF,EAASvG,IAAIO,MAAMiG,GAAKpG,EAAAqG,SAAGC,aAAa,EAAA5D,EAAA6D,yBAAwB/K,EAAU4K,EAAE9J,eAExEkH,EACJZ,EACApH,EACAgD,EACAC,EACAnE,EAAQoE,cACRkC,IAIGmF,EAAc,CACjB,MAAMS,GAAc,EAAAnE,EAAA7J,eAAc8B,EAAQ7B,OAAQ6F,GAAkB,UAC9DsE,EAAIqD,UAAU1C,EAAkB+B,GAAYkB,iBA3ChDrD,EAAYP,EAAKzJ,EAAQgH,gBACvBqD,EACJZ,EACApH,EACAgD,EACAC,EACAnE,EAAQoE,cACRkC,KAwCNrB,EAAIpE,KAAK,CAAET,QAAS,oBACpB,MAEF,IAAK,+BAAgC,CACnC,MAAMzB,WAAEA,EAAUL,KAAEA,EAAIiG,UAAEA,EAASP,eAAEA,GACnCmB,EAAKjC,OACD1E,GAAa,EAAAuJ,EAAA3J,oBAAmBO,EAAYL,GAC5C0M,GAAY,EAAAjD,EAAAjJ,sBAAqBN,GACjC0N,GAAc,EAAAnE,EAAA7J,eAAcqG,EAAWP,GAAkB,UACzDsE,EAAIqD,UAAU1C,EAAkB+B,GAAYkB,GAClDjH,EAAIpE,KAAK,CAAET,QAAS,GAAGvB,gCAAqCqN,MAC5D,MAEF,IAAK,0BAA2B,CAC9B,MAAMvN,WAAEA,EAAUL,KAAEA,GAAS6G,EAAKjC,OAC5B1E,GAAa,EAAAuJ,EAAA3J,oBAAmBO,EAAYL,GAC5C0M,GAAY,EAAAjD,EAAAjJ,sBAAqBN,SAjQjDqH,eAAqByC,EAAgB1B,EAAcuF,GACjD,MAAMT,QAAgBpD,EAAIvF,IAAI,CAAC,SAAU,mBACzC,IACkB,SAAZ2I,SACIpD,EAAIqD,UAAU,iBAAkB,eAElCrD,EAAI8D,YAAYxF,EAAMuF,WAEZ,SAAZT,SACIpD,EAAIqD,UAAU,iBAAkBD,GAG5C,CAsPgBW,CAAM/D,EAAK0C,EAAWnM,SACtByJ,EAAIgE,kBAAkBtB,GAC5B/F,EAAIpE,KAAK,CAAET,QAAS,UAAU4K,eAAuBnM,MACrD,MAEF,IAAK,WAAY,CACf,MAAM2F,YAAEA,GAAgBW,EAAKjC,OACvB8D,QAAmB6B,EAAYP,EAAKzJ,EAAQgH,UAChD,MAAMjC,QAAc,EAAAuE,EAAAhC,eAAcjF,EAAUsD,EAAa,GAAI,GAI7D,aAH8B+B,QAAQC,IACpC5C,EAAM0B,IAAIe,IAAQ,EAAAV,EAAAsB,eAAc/F,EAAUmF,OAI9CpB,EAAIpE,KAAKmG,GACT,MAEF,IAAK,eAAgB,CACnB,MAAMhF,KAAEA,GAASmD,EAAKjC,OAChBgE,QAAkB2B,EAAYP,EAAKzJ,EAAQ,KACxC,EAAA8G,EAAAsB,eAAc/F,EAAUc,IAEjCiD,EAAIpE,KAAKqG,GACT,MAEF,IAAK,eAAgB,CACnB,MAAMxE,MACJA,EACA1C,SAASoE,cAAEA,IACTe,EAAKjC,OAEHmD,QAAawC,EAAYP,EAAKzJ,EAAQgH,gBACpC,EAAAsC,EAAA1B,WAAUvF,EAAUwB,EAAMV,KAAM2E,OAAOC,KAAKlE,EAAMC,QAASD,EAAME,iBACjEyF,EAAOC,EAAKlE,IACX,EAAAuB,EAAAsB,eAAc/F,EAAUwB,EAAMV,QAEvCiD,EAAIpE,KAAKwF,GACT,MAEF,IAAK,aAAc,CACjB,MACErE,KAAMmF,EACNnH,SAASoE,cAAEA,IACTe,EAAKjC,aACH2F,EAAYP,EAAKzJ,EAAQgH,gBACvB,EAAAsC,EAAAf,YAAWlG,EAAUiG,SACrBkB,EAAOC,EAAKlE,KAEpBa,EAAIpE,KAAK,CAAET,QAAS,gBAAgB+G,MACpC,MAEF,IAAK,cAAe,CAClB,MAAM1C,MACJA,EACAzE,SAASoE,cAAEA,IACTe,EAAKjC,aACH2F,EAAYP,EAAKzJ,EAAQgH,gBACvBU,QAAQC,IAAI/B,EAAMa,IAAI6B,IAAY,EAAAgB,EAAAf,YAAWlG,EAAUiG,WACvDkB,EAAOC,EAAKlE,KAEpBa,EAAIpE,KAAK,CAAET,QAAS,iBAAiBqE,EAAMe,KAAK,UAChD,MAEF,IAAK,mBACHP,EAAIpE,KAAK,MACT,MAEF,QAAS,CACP,MAAMT,EAAU,kBAAkB+E,EAAKN,SACvCI,EAAI9G,OAAO,KAAK0C,KAAK,CAAEkB,MAAO3B,IAC9B,QAGJ,MAAOyB,GACP5B,EAAO8B,MACL,kBAAkBsF,KAAKC,UAAUtC,EAAIG,UACnCtD,aAAa0F,MAAQ1F,EAAEzB,QAAU,mBAGrC6E,EAAI9G,OAAO,KAAK0C,KAAK,CAAEkB,MAAO,0BAE9B0I,GAAWA,IAEf,CACF,CAhTA7M,EAAAuM,aAAAA,EAQAvM,EAAA4J,UAAAA,EAKA5J,EAAAyM,mBAAAA,EAySAzM,EAAA6J,mBAAO5B,eAAkC9F,EAAsBC,GAC7D,MAAMC,OAAEA,GAAWD,EACbkB,EAAWF,EAAA3B,QAAKqI,QAAQhH,QAAQC,IAAIgH,oBAAsBjH,QAAQkH,aAClEuC,EAAa,CAAEjJ,aACrBnB,EAAI8H,KAAK,WAAW,EAAA9G,EAAA+D,KAAI0C,EAAU,CAAEtG,eACpCnB,EAAI8H,KAAK,UAAWwC,EAAmB,CAAEnJ,WAAUjB,YACnDA,EAAO6H,KAAK,8CAA8C5G,IAC5D,6LC5dA,MAAAqL,EAAAvN,EAAAC,EAAA,MACA+B,EAAAhC,EAAAC,EAAA,MACAyG,EAAAzG,EAAA,KAEAmJ,EAAAnJ,EAAA,KAEA,SAASuN,EAAOC,GACd,OAAOF,EAAAlN,QAAOqN,WAAW,UAAUC,OAAOF,GAAQG,OAAO,MAC3D,CAGA,SAASC,EAAc7K,GACrB,OAAOA,EAAK8H,QAAQ,MAAO,IAC7B,CAEAlM,EAAAwI,iBAAOP,eACL3E,EACA0C,GAEA,OAAO2C,QAAQC,IACb5C,EAAM0B,IAAIO,MAAMQ,IACd,IACE,MAAM1D,QAAgB+C,EAAAqG,SAAGe,eAAe,EAAA1E,EAAA6D,yBAAwB/K,EAAUmF,EAAKrE,OAC/E,MAAO,CACLuJ,KAAM5I,EAAQoK,WACd1G,KAAM,CAAErE,KAAM6K,EAAcxG,EAAKrE,MAAOjE,MAAOsI,EAAKtI,MAAOgG,GAAIyI,EAAO7J,KAExE,MAAOd,GACP,MAAO,CACL0J,KAAM,KACNlF,KAAM,CAAErE,KAAM6K,EAAcxG,EAAKrE,MAAOjE,MAAOsI,EAAKtI,MAAOgG,GAAI,UAKzE,EAEAnG,EAAAqJ,cAAOpB,eAA6B3E,EAAkBmF,GACpD,MAAMzD,EAAW,SACX6J,QAAe/G,EAAAqG,SAAGe,eAAe,EAAA1E,EAAA6D,yBAAwB/K,EAAUmF,IAGzE,MAAO,CACLtC,GAHSyI,EAAOC,GAIhB9J,QAAS8J,EAAOM,SAASnK,GACzBA,WACAZ,KAAM6K,EAAcxG,GACpB2G,KAAMhM,EAAA3B,QAAK0G,SAASM,GAExB,0NCjDA,MAAArF,EAAAhC,EAAAC,EAAA,MACAyG,EAAAzG,EAAA,KAEAmJ,EAAAnJ,EAAA,KAEA4G,eAAeoH,EAAUC,EAAaxJ,EAAmBC,GACvD,GAAIA,GAAS,EACX,MAAO,GAGT,IACE,MAAMwJ,QAAgBzH,EAAAqG,SAAGqB,QAAQF,EAAK,CAAEG,eAAe,IACjDzJ,QAAc2C,QAAQC,IAC1B2G,EAAQ7H,IAAIgI,IACV,MAAMrI,EAAMjE,EAAA3B,QAAKmG,KAAK0H,EAAKI,EAAON,MAClC,OAAOM,EAAOC,cACVN,EAAUhI,EAAKvB,EAAWC,EAAQ,GAClC,CAACsB,GAAK4F,OAAOiB,GAAKA,EAAE0B,SAAS9J,OAGrC,MAAQ,GAAgB+J,UAAU7J,GAClC,MAAO/B,GACP,MAAO,GAEX,CAwBAgE,eAAe6H,EAAS9G,EAAcuF,SAC9BzG,EAAAqG,SAAG4B,MAAM3M,EAAA3B,QAAKuO,QAAQzB,GAAK,CAAE0B,WAAW,UACxCnI,EAAAqG,SAAG+B,OAAOlH,EAAMuF,EACxB,CAzBAvO,EAAAuI,cAAON,eACL3E,EACAuC,EACAC,EACAC,GAEA,MAAMoK,QAAiBrI,EAAAqG,SAAGiC,SAAShN,EAAA3B,QAAKqI,QAAQxG,IAEhD,aADoB+L,QAAgB,EAAA7E,EAAA6D,yBAAwB8B,EAAUtK,GAASC,EAAWC,IAC7E2B,IAAIwG,GAAK9K,EAAA3B,QAAK4O,SAASF,EAAUjC,GAChD,EAEAlO,EAAA6I,UAAOZ,eAAyB3E,EAAkBiG,EAAkBxE,GAClE,MAAMuL,QAAqB,EAAA9F,EAAA+F,oBAAmBjN,EAAUiG,SAClDzB,EAAAqG,SAAG4B,MAAM3M,EAAA3B,QAAKuO,QAAQM,GAAe,CAAEL,WAAW,UAClDnI,EAAAqG,SAAGtF,UAAUyH,EAAcvL,EACnC,EAEA/E,EAAAwJ,WAAOvB,eAA0B3E,EAAkBiG,GACjD,MAAM+G,QAAqB,EAAA9F,EAAA+F,oBAAmBjN,EAAUiG,SAClDzB,EAAAqG,SAAGC,OAAOkC,GAAcE,MAAM,OACtC,EAOAxQ,EAAAmJ,KAAOlB,eAAoB3E,EAAkB0F,EAAcuF,EAAY7F,GAAgB,GACrF,MAAM+H,QAAqB,EAAAjG,EAAA6D,yBAAwB/K,EAAU0F,GACvD0H,QAAmB,EAAAlG,EAAA+F,oBAAmBjN,EAAUiL,GAKtD,SAFMuB,EAASW,EAAcC,GAEzBhI,EAAe,CAGjB,MAAMiI,EAAYvN,EAAA3B,QAAKuO,QAAQS,GACzBG,EAAUxN,EAAA3B,QAAKuO,QAAQU,GACvBG,QAAiBxB,EAAUsB,EAAW,GAAI,WAC1ChI,QAAQC,IAAIiI,EAASnJ,IAAIe,GAAQqH,EAASrH,EAAMA,EAAKyD,QAAQyE,EAAWC,MAElF,EAEA5Q,EAAAuN,cAAOtF,eAA6B3E,EAAkBiG,GACpD,IACE,aAAazB,EAAAqG,SACV2C,WAAW,EAAAtG,EAAA6D,yBAAwB/K,EAAUiG,IAC7C7D,KAAKoL,GAAQA,EAAKC,OACrB,MAAO9M,GACP,OAAO,IAAIwJ,KAEf,2NC9EA,MAAArK,EAAAhC,EAAAC,EAAA,MACAyG,EAAAzG,EAAA,KAIA,SAAS2P,EAAoBb,EAAkBG,GAC7C,MAAMW,EAAe7N,EAAA3B,QAAK4O,SAASF,EAAUG,GAE7C,GACmB,OAAjBW,GACAA,EAAa9D,WAAW,KAAK/J,EAAA3B,QAAKyP,QAClC9N,EAAA3B,QAAK0P,WAAWF,GAEhB,MAAM,IAAItH,MAVa,oDAY3B,CAEA,SAAgB3F,EAAgBV,EAAkBiG,GAChD,MAAM4G,EAAW/M,EAAA3B,QAAKqI,QAAQxG,GACxBgN,EAAelN,EAAA3B,QAAKqI,QAAQqG,EAAU5G,GAG5C,OAFAyH,EAAoBb,EAAUG,GAEvBA,CACT,CANAtQ,EAAAgE,gBAAAA,EAQAhE,EAAAqO,wBAAOpG,eAAuC3E,EAAkBiG,GAC9D,MAAM4G,QAAiBrI,EAAAqG,SAAGiC,SAAShN,EAAA3B,QAAKqI,QAAQxG,IAC1CgN,QAAqBxI,EAAAqG,SAAGiC,SAASpM,EAAgBV,EAAUiG,IAGjE,OAFAyH,EAAoBb,EAAUG,GAEvBA,CACT,EAEAtQ,EAAAuQ,mBAAOtI,eAAkC3E,EAAkBiG,GACzD,MAAM4G,QAAiBrI,EAAAqG,SAAGiC,SAAShN,EAAA3B,QAAKqI,QAAQxG,IAE1C8N,EAA4B,GAClC,IACIC,EADAC,EAFiBtN,EAAgBV,EAAUiG,GAK/C,MAAQ8H,GAAkB,CACxB,UACQvJ,EAAAqG,SAAGoD,MAAMD,GACf,MAAOrN,GACP,GAA0C,WAArCA,EAA4BuN,KAC/B,MAAMvN,EAERmN,EAAgBK,QAAQrO,EAAA3B,QAAK0G,SAASmJ,IACtCA,EAAelO,EAAA3B,QAAKuO,QAAQsB,GAC5B,SAGFD,QAAyBvJ,EAAAqG,SAAGiC,SAASkB,GAKvC,OAFAN,EAAoBb,EAAUkB,GAEvBjO,EAAA3B,QAAKmG,KAAKyJ,KAAqBD,EACxC,UC1DAM,EAAA1R,QAAA2R,QAAA,qBCAAD,EAAA1R,QAAA2R,QAAA,uBCAAD,EAAA1R,QAAA2R,QAAA,gBCAAD,EAAA1R,QAAA2R,QAAA,kBCAAD,EAAA1R,QAAA2R,QAAA,kBCAAD,EAAA1R,QAAA2R,QAAA,sBCAAD,EAAA1R,QAAA2R,QAAA,yBCAAD,EAAA1R,QAAA2R,QAAA,mBCAAD,EAAA1R,QAAA2R,QAAA,kBCAAD,EAAA1R,QAAA2R,QAAA,cCAAD,EAAA1R,QAAA2R,QAAA,UCCA,MAAAC,EAAA,GAGA,SAAAvQ,EAAAwQ,GAEA,MAAAC,EAAAF,EAAAC,GACA,QAAAjH,IAAAkH,EACA,OAAAA,EAAA9R,QAGA,MAAA0R,EAAAE,EAAAC,GAAA,CAGA7R,QAAA,IAOA,OAHA+R,EAAAF,GAAAG,KAAAN,EAAA1R,QAAA0R,EAAAA,EAAA1R,QAAAqB,GAGAqQ,EAAA1R,OACA,qHCtBA,MAAAiS,EAAA5Q,EAAA,KACA6Q,EAAA7Q,EAAA,KACA8Q,EAAA9Q,EAAA,KACA+Q,EAAA/Q,EAAA,KAQA,SAASgR,EAAcjQ,GACrB,MAAO,CACLC,QAAQ,EAAA+P,EAAAzQ,cAAa,CAAEC,MAAOQ,EAAQkQ,UAAY,SAEtD,CAEAtS,EAAAuS,iBAAOtK,eAAgC9F,EAAsBC,EAAmB,IAC9E,MAAMoQ,EAAOH,EAAcjQ,IAC3B,EAAA6P,EAAA/P,2BAA0BC,EAAKqQ,SACzB,EAAAN,EAAArI,oBAAS1H,EAAKqQ,EACtB,EAEAxS,EAAAyS,gBAAOxK,eAA+B9F,EAAsBC,EAAmB,IAC7E,MAAMoQ,EAAOH,EAAcjQ,IAC3B,EAAA6P,EAAA/P,2BAA0BC,EAAKqQ,SACzB,EAAAL,EAAAtI,oBAAQ1H,EAAKqQ,EACrB","sources":["webpack://decap-server/../decap-cms-lib-util/src/APIUtils.ts","webpack://decap-server/./src/logger.ts","webpack://decap-server/./src/middlewares/common/index.ts","webpack://decap-server/./src/middlewares/joi/customValidators.ts","webpack://decap-server/./src/middlewares/joi/index.ts","webpack://decap-server/./src/middlewares/localFs/index.ts","webpack://decap-server/./src/middlewares/localGit/index.ts","webpack://decap-server/./src/middlewares/utils/entries.ts","webpack://decap-server/./src/middlewares/utils/fs.ts","webpack://decap-server/./src/middlewares/utils/path.ts","webpack://decap-server/external commonjs \"@hapi/joi\"","webpack://decap-server/external commonjs \"async-mutex\"","webpack://decap-server/external commonjs \"cors\"","webpack://decap-server/external commonjs \"express\"","webpack://decap-server/external commonjs \"morgan\"","webpack://decap-server/external commonjs \"simple-git\"","webpack://decap-server/external commonjs \"what-the-diff\"","webpack://decap-server/external commonjs \"winston\"","webpack://decap-server/external node-commonjs \"crypto\"","webpack://decap-server/external node-commonjs \"fs\"","webpack://decap-server/external node-commonjs \"path\"","webpack://decap-server/webpack/bootstrap","webpack://decap-server/./src/middlewares.ts"],"sourcesContent":["export const CMS_BRANCH_PREFIX = 'cms';\nexport const DEFAULT_PR_BODY = 'Automatically generated by Decap CMS';\nexport const MERGE_COMMIT_MESSAGE = 'Automatically generated. Merged on Decap CMS.';\n\nconst DEFAULT_DECAP_CMS_LABEL_PREFIX = 'decap-cms/';\n\nfunction getLabelPrefix(labelPrefix: string) {\n return labelPrefix || DEFAULT_DECAP_CMS_LABEL_PREFIX;\n}\n\nexport function isCMSLabel(label: string, labelPrefix: string) {\n return label.startsWith(getLabelPrefix(labelPrefix));\n}\n\nexport function labelToStatus(label: string, labelPrefix: string) {\n return label.slice(getLabelPrefix(labelPrefix).length);\n}\n\nexport function statusToLabel(status: string, labelPrefix: string) {\n return `${getLabelPrefix(labelPrefix)}${status}`;\n}\n\nexport function generateContentKey(collectionName: string, slug: string) {\n return `${collectionName}/${slug}`;\n}\n\nexport function parseContentKey(contentKey: string) {\n const index = contentKey.indexOf('/');\n return { collection: contentKey.slice(0, index), slug: contentKey.slice(index + 1) };\n}\n\nexport function contentKeyFromBranch(branch: string) {\n return branch.slice(`${CMS_BRANCH_PREFIX}/`.length);\n}\n\nexport function branchFromContentKey(contentKey: string) {\n return `${CMS_BRANCH_PREFIX}/${contentKey}`;\n}\n","import winston from 'winston';\n\nconst { combine, colorize, simple } = winston.format;\n\ntype LogOptions = {\n level: string;\n};\n\nexport function createLogger({ level }: LogOptions) {\n return winston.createLogger({\n level,\n format: combine(colorize(), simple()),\n transports: [new winston.transports.Console()],\n });\n}\n","import express from 'express';\nimport morgan from 'morgan';\nimport cors from 'cors';\n\nimport type winston from 'winston';\n\nexport type Options = {\n logger: winston.Logger;\n};\n\nexport function registerCommonMiddlewares(app: express.Express, options: Options) {\n const { logger } = options;\n const stream = {\n write: (message: string) => {\n logger.debug(String(message).trim());\n },\n };\n app.use(morgan('combined', { stream }));\n app.use(\n cors({\n origin: process.env.ORIGIN || /^https?:\\/\\/(localhost|127\\.0\\.0\\.1)(:\\d+)?$/,\n }),\n );\n app.use(express.json({ limit: '50mb' }));\n}\n","import Joi from '@hapi/joi';\n\nimport { resolveRepoPath } from '../utils/path';\n\nexport function pathTraversal(repoPath: string) {\n return Joi.extend({\n type: 'path',\n base: Joi.string().required(),\n messages: {\n 'path.invalid': '{{#label}} must resolve to a path under the configured repository',\n },\n validate(value, helpers) {\n try {\n resolveRepoPath(repoPath, value);\n } catch (e) {\n return { value, errors: helpers.error('path.invalid') };\n }\n },\n }).path();\n}\n","import Joi from '@hapi/joi';\n\nimport type express from 'express';\n\nconst allowedActions = [\n 'info',\n 'entriesByFolder',\n 'entriesByFiles',\n 'getEntry',\n 'unpublishedEntries',\n 'unpublishedEntry',\n 'unpublishedEntryDataFile',\n 'unpublishedEntryMediaFile',\n 'deleteUnpublishedEntry',\n 'persistEntry',\n 'updateUnpublishedEntryStatus',\n 'publishUnpublishedEntry',\n 'getMedia',\n 'getMediaFile',\n 'persistMedia',\n 'deleteFile',\n 'deleteFiles',\n 'getDeployPreview',\n];\n\nconst requiredString = Joi.string().required();\nconst requiredNumber = Joi.number().required();\nconst requiredBool = Joi.bool().required();\n\nconst collection = requiredString;\nconst slug = requiredString;\n\nexport function defaultSchema({ path = requiredString } = {}) {\n const defaultParams = Joi.object({\n branch: requiredString,\n });\n\n const asset = Joi.object({\n path,\n content: requiredString,\n encoding: requiredString.valid('base64'),\n });\n\n const dataFile = Joi.object({\n slug: requiredString,\n path,\n raw: requiredString,\n newPath: path.optional(),\n });\n\n const params = Joi.when('action', {\n switch: [\n {\n is: 'info',\n then: Joi.allow(),\n },\n {\n is: 'entriesByFolder',\n then: defaultParams\n .keys({\n folder: path,\n extension: requiredString,\n depth: requiredNumber,\n })\n .required(),\n },\n {\n is: 'entriesByFiles',\n then: defaultParams.keys({\n files: Joi.array()\n .items(Joi.object({ path, label: Joi.string() }))\n .required(),\n }),\n },\n {\n is: 'getEntry',\n then: defaultParams\n .keys({\n path,\n })\n .required(),\n },\n {\n is: 'unpublishedEntries',\n then: defaultParams.keys({ branch: requiredString }).required(),\n },\n {\n is: 'unpublishedEntry',\n then: defaultParams\n .keys({\n id: Joi.string().optional(),\n collection: Joi.string().optional(),\n slug: Joi.string().optional(),\n cmsLabelPrefix: Joi.string().optional(),\n })\n .required(),\n },\n {\n is: 'unpublishedEntryDataFile',\n then: defaultParams\n .keys({\n collection,\n slug,\n id: requiredString,\n path,\n })\n .required(),\n },\n {\n is: 'unpublishedEntryMediaFile',\n then: defaultParams\n .keys({\n collection,\n slug,\n id: requiredString,\n path,\n })\n .required(),\n },\n {\n is: 'deleteUnpublishedEntry',\n then: defaultParams\n .keys({\n collection,\n slug,\n })\n .required(),\n },\n {\n is: 'persistEntry',\n then: defaultParams\n .keys({\n cmsLabelPrefix: Joi.string().optional(),\n entry: dataFile, // entry is kept for backwards compatibility\n dataFiles: Joi.array().items(dataFile),\n assets: Joi.array().items(asset).required(),\n options: Joi.object({\n collectionName: Joi.string(),\n commitMessage: requiredString,\n useWorkflow: requiredBool,\n status: requiredString,\n }).required(),\n })\n .xor('entry', 'dataFiles')\n .required(),\n },\n {\n is: 'updateUnpublishedEntryStatus',\n then: defaultParams\n .keys({\n collection,\n slug,\n newStatus: requiredString,\n cmsLabelPrefix: Joi.string().optional(),\n })\n .required(),\n },\n {\n is: 'publishUnpublishedEntry',\n then: defaultParams\n .keys({\n collection,\n slug,\n })\n .required(),\n },\n {\n is: 'getMedia',\n then: defaultParams\n .keys({\n mediaFolder: path,\n })\n .required(),\n },\n {\n is: 'getMediaFile',\n then: defaultParams\n .keys({\n path,\n })\n .required(),\n },\n {\n is: 'persistMedia',\n then: defaultParams\n .keys({\n asset: asset.required(),\n options: Joi.object({\n commitMessage: requiredString,\n }).required(),\n })\n .required(),\n },\n {\n is: 'deleteFile',\n then: defaultParams\n .keys({\n path,\n options: Joi.object({\n commitMessage: requiredString,\n }).required(),\n })\n .required(),\n },\n {\n is: 'deleteFiles',\n then: defaultParams\n .keys({\n paths: Joi.array().items(path).min(1).required(),\n options: Joi.object({\n commitMessage: requiredString,\n }).required(),\n })\n .required(),\n },\n {\n is: 'getDeployPreview',\n then: defaultParams\n .keys({\n collection,\n slug,\n })\n .required(),\n },\n ],\n otherwise: Joi.forbidden(),\n });\n\n return Joi.object({\n action: Joi.valid(...allowedActions).required(),\n params,\n });\n}\n\nexport function joi(schema: Joi.Schema) {\n return (req: express.Request, res: express.Response, next: express.NextFunction) => {\n const { error } = schema.validate(req.body, { allowUnknown: true });\n if (error) {\n const { details } = error;\n const message = details.map(i => i.message).join(',');\n res.status(422).json({ error: message });\n } else {\n next();\n }\n };\n}\n","import path from 'path';\n\nimport { defaultSchema, joi } from '../joi';\nimport { pathTraversal } from '../joi/customValidators';\nimport { listRepoFiles, deleteFile, writeFile, move } from '../utils/fs';\nimport { entriesFromFiles, readMediaFile } from '../utils/entries';\n\nimport type {\n EntriesByFolderParams,\n EntriesByFilesParams,\n GetEntryParams,\n PersistEntryParams,\n GetMediaParams,\n GetMediaFileParams,\n PersistMediaParams,\n DeleteFileParams,\n DeleteFilesParams,\n DataFile,\n} from '../types';\nimport type express from 'express';\nimport type winston from 'winston';\n\ntype FsOptions = {\n repoPath: string;\n logger: winston.Logger;\n};\n\nexport function localFsMiddleware({ repoPath, logger }: FsOptions) {\n return async function (req: express.Request, res: express.Response) {\n try {\n const { body } = req;\n\n switch (body.action) {\n case 'info': {\n res.json({\n repo: path.basename(repoPath),\n publish_modes: ['simple'],\n type: 'local_fs',\n });\n break;\n }\n case 'entriesByFolder': {\n const payload = body.params as EntriesByFolderParams;\n const { folder, extension, depth } = payload;\n const entries = await listRepoFiles(repoPath, folder, extension, depth).then(files =>\n entriesFromFiles(\n repoPath,\n files.map(file => ({ path: file })),\n ),\n );\n res.json(entries);\n break;\n }\n case 'entriesByFiles': {\n const payload = body.params as EntriesByFilesParams;\n const entries = await entriesFromFiles(repoPath, payload.files);\n res.json(entries);\n break;\n }\n case 'getEntry': {\n const payload = body.params as GetEntryParams;\n const [entry] = await entriesFromFiles(repoPath, [{ path: payload.path }]);\n res.json(entry);\n break;\n }\n case 'persistEntry': {\n const {\n entry,\n dataFiles = [entry as DataFile],\n assets,\n options,\n } = body.params as PersistEntryParams;\n const hasSubfolders = options?.hasSubfolders !== false;\n await Promise.all(\n dataFiles.map(dataFile => writeFile(repoPath, dataFile.path, dataFile.raw)),\n );\n // save assets\n await Promise.all(\n assets.map(a => writeFile(repoPath, a.path, Buffer.from(a.content, a.encoding))),\n );\n if (dataFiles.every(dataFile => dataFile.newPath)) {\n dataFiles.forEach(async dataFile => {\n await move(repoPath, dataFile.path, dataFile.newPath!, hasSubfolders);\n });\n }\n res.json({ message: 'entry persisted' });\n break;\n }\n case 'getMedia': {\n const { mediaFolder } = body.params as GetMediaParams;\n const files = await listRepoFiles(repoPath, mediaFolder, '', 1);\n const mediaFiles = await Promise.all(files.map(file => readMediaFile(repoPath, file)));\n res.json(mediaFiles);\n break;\n }\n case 'getMediaFile': {\n const { path } = body.params as GetMediaFileParams;\n const mediaFile = await readMediaFile(repoPath, path);\n res.json(mediaFile);\n break;\n }\n case 'persistMedia': {\n const { asset } = body.params as PersistMediaParams;\n await writeFile(repoPath, asset.path, Buffer.from(asset.content, asset.encoding));\n const file = await readMediaFile(repoPath, asset.path);\n res.json(file);\n break;\n }\n case 'deleteFile': {\n const { path: filePath } = body.params as DeleteFileParams;\n await deleteFile(repoPath, filePath);\n res.json({ message: `deleted file ${filePath}` });\n break;\n }\n case 'deleteFiles': {\n const { paths } = body.params as DeleteFilesParams;\n await Promise.all(paths.map(filePath => deleteFile(repoPath, filePath)));\n res.json({ message: `deleted files ${paths.join(', ')}` });\n break;\n }\n case 'getDeployPreview': {\n res.json(null);\n break;\n }\n default: {\n const message = `Unknown action ${body.action}`;\n res.status(422).json({ error: message });\n break;\n }\n }\n } catch (e) {\n logger.error(\n `Error handling ${JSON.stringify(req.body)}: ${\n e instanceof Error ? e.message : 'Unknown error'\n }`,\n );\n res.status(500).json({ error: 'Unknown error' });\n }\n };\n}\n\nexport function getSchema({ repoPath }: { repoPath: string }) {\n const schema = defaultSchema({ path: pathTraversal(repoPath) });\n return schema;\n}\n\ntype Options = {\n logger: winston.Logger;\n};\n\nexport async function registerMiddleware(app: express.Express, options: Options) {\n const { logger } = options;\n const repoPath = path.resolve(process.env.GIT_REPO_DIRECTORY || process.cwd());\n app.post('/api/v1', joi(getSchema({ repoPath })));\n app.post('/api/v1', localFsMiddleware({ repoPath, logger }));\n logger.info(`Decap CMS File System Proxy Server configured with ${repoPath}`);\n}\n","import path from 'path';\nimport { promises as fs } from 'fs';\nimport {\n branchFromContentKey,\n generateContentKey,\n contentKeyFromBranch,\n CMS_BRANCH_PREFIX,\n statusToLabel,\n labelToStatus,\n parseContentKey,\n} from 'decap-cms-lib-util/src/APIUtils';\nimport { parse } from 'what-the-diff';\n// eslint-disable-next-line import/no-named-as-default\nimport simpleGit from 'simple-git';\nimport { Mutex, withTimeout } from 'async-mutex';\n\nimport { defaultSchema, joi } from '../joi';\nimport { pathTraversal } from '../joi/customValidators';\nimport { listRepoFiles, writeFile, move, deleteFile, getUpdateDate } from '../utils/fs';\nimport { entriesFromFiles, readMediaFile } from '../utils/entries';\nimport { resolveExistingRepoPath } from '../utils/path';\n\nimport type {\n EntriesByFolderParams,\n EntriesByFilesParams,\n GetEntryParams,\n DefaultParams,\n UnpublishedEntryParams,\n PersistEntryParams,\n GetMediaParams,\n Asset,\n PublishUnpublishedEntryParams,\n PersistMediaParams,\n DeleteFileParams,\n UpdateUnpublishedEntryStatusParams,\n DataFile,\n GetMediaFileParams,\n DeleteEntryParams,\n DeleteFilesParams,\n UnpublishedEntryDataFileParams,\n UnpublishedEntryMediaFileParams,\n} from '../types';\nimport type express from 'express';\nimport type winston from 'winston';\nimport type { SimpleGit } from 'simple-git';\n\nasync function commit(git: SimpleGit, commitMessage: string) {\n await git.add('.');\n await git.commit(commitMessage, undefined, {\n // setting the value to a string passes name=value\n // any other value passes just the key\n '--no-verify': null,\n '--no-gpg-sign': null,\n });\n}\n\nasync function getCurrentBranch(git: SimpleGit) {\n const currentBranch = await git.branchLocal().then(summary => summary.current);\n return currentBranch;\n}\n\nasync function runOnBranch<T>(git: SimpleGit, branch: string, func: () => Promise<T>) {\n const currentBranch = await getCurrentBranch(git);\n try {\n if (currentBranch !== branch) {\n await git.checkout(branch);\n }\n const result = await func();\n return result;\n } finally {\n await git.checkout(currentBranch);\n }\n}\n\nfunction branchDescription(branch: string) {\n return `branch.${branch}.description`;\n}\n\ntype GitOptions = {\n repoPath: string;\n logger: winston.Logger;\n};\n\nasync function commitEntry(\n git: SimpleGit,\n repoPath: string,\n dataFiles: DataFile[],\n assets: Asset[],\n commitMessage: string,\n hasSubfolders = true,\n) {\n // save entry content\n await Promise.all(dataFiles.map(dataFile => writeFile(repoPath, dataFile.path, dataFile.raw)));\n // save assets\n await Promise.all(\n assets.map(a => writeFile(repoPath, a.path, Buffer.from(a.content, a.encoding))),\n );\n if (dataFiles.every(dataFile => dataFile.newPath)) {\n dataFiles.forEach(async dataFile => {\n await move(repoPath, dataFile.path, dataFile.newPath!, hasSubfolders);\n });\n }\n\n // commits files\n await commit(git, commitMessage);\n}\n\nasync function rebase(git: SimpleGit, branch: string) {\n const gpgSign = await git.raw(['config', 'commit.gpgsign']);\n try {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', 'false');\n }\n await git.rebase([branch, '--no-verify']);\n } finally {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', gpgSign);\n }\n }\n}\n\nasync function merge(git: SimpleGit, from: string, to: string) {\n const gpgSign = await git.raw(['config', 'commit.gpgsign']);\n try {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', 'false');\n }\n await git.mergeFromTo(from, to);\n } finally {\n if (gpgSign === 'true') {\n await git.addConfig('commit.gpgsign', gpgSign);\n }\n }\n}\n\nasync function isBranchExists(git: SimpleGit, branch: string) {\n const branchExists = await git.branchLocal().then(({ all }) => all.includes(branch));\n return branchExists;\n}\n\nasync function getDiffs(git: SimpleGit, source: string, dest: string) {\n const rawDiff = await git.diff([source, dest]);\n const diffs = parse(rawDiff).map(d => {\n const oldPath = d.oldPath?.replace(/b\\//, '') || '';\n const newPath = d.newPath?.replace(/b\\//, '') || '';\n const path = newPath || (oldPath as string);\n return {\n oldPath,\n newPath,\n status: d.status,\n newFile: d.status === 'added',\n path,\n id: path,\n binary: d.binary || /.svg$/.test(path),\n };\n });\n return diffs;\n}\n\nexport async function validateRepo({ repoPath }: { repoPath: string }) {\n const git = simpleGit(repoPath);\n const isRepo = await git.checkIsRepo();\n if (!isRepo) {\n throw Error(`${repoPath} is not a valid git repository`);\n }\n}\n\nexport function getSchema({ repoPath }: { repoPath: string }) {\n const schema = defaultSchema({ path: pathTraversal(repoPath) });\n return schema;\n}\n\nexport function localGitMiddleware({ repoPath, logger }: GitOptions) {\n const git = simpleGit(repoPath);\n\n // we can only perform a single git operation at any given time\n const mutex = withTimeout(new Mutex(), 3000, new Error('Request timed out'));\n\n return async function (req: express.Request, res: express.Response) {\n let release;\n try {\n release = await mutex.acquire();\n const { body } = req;\n if (body.action === 'info') {\n res.json({\n repo: path.basename(repoPath),\n publish_modes: ['simple', 'editorial_workflow'],\n type: 'local_git',\n });\n return;\n }\n const { branch } = body.params as DefaultParams;\n\n const branchExists = await isBranchExists(git, branch);\n if (!branchExists) {\n const message = `Default branch '${branch}' doesn't exist`;\n res.status(422).json({ error: message });\n return;\n }\n\n switch (body.action) {\n case 'entriesByFolder': {\n const payload = body.params as EntriesByFolderParams;\n const { folder, extension, depth } = payload;\n const entries = await runOnBranch(git, branch, () =>\n listRepoFiles(repoPath, folder, extension, depth).then(files =>\n entriesFromFiles(\n repoPath,\n files.map(file => ({ path: file })),\n ),\n ),\n );\n res.json(entries);\n break;\n }\n case 'entriesByFiles': {\n const payload = body.params as EntriesByFilesParams;\n const entries = await runOnBranch(git, branch, () =>\n entriesFromFiles(repoPath, payload.files),\n );\n res.json(entries);\n break;\n }\n case 'getEntry': {\n const payload = body.params as GetEntryParams;\n const [entry] = await runOnBranch(git, branch, () =>\n entriesFromFiles(repoPath, [{ path: payload.path }]),\n );\n res.json(entry);\n break;\n }\n case 'unpublishedEntries': {\n const cmsBranches = await git\n .branchLocal()\n .then(result => result.all.filter(b => b.startsWith(`${CMS_BRANCH_PREFIX}/`)));\n res.json(cmsBranches.map(contentKeyFromBranch));\n break;\n }\n case 'unpublishedEntry': {\n let { id, collection, slug, cmsLabelPrefix } = body.params as UnpublishedEntryParams;\n if (id) {\n ({ collection, slug } = parseContentKey(id));\n }\n const contentKey = generateContentKey(collection as string, slug as string);\n const cmsBranch = branchFromContentKey(contentKey);\n const branchExists = await isBranchExists(git, cmsBranch);\n if (branchExists) {\n const diffs = await getDiffs(git, branch, cmsBranch);\n const label = await git.raw(['config', branchDescription(cmsBranch)]);\n const status = label && labelToStatus(label.trim(), cmsLabelPrefix || '');\n const updatedAt =\n diffs.length >= 0\n ? await runOnBranch(git, cmsBranch, async () => {\n const dates = await Promise.all(\n diffs.map(({ newPath }) => getUpdateDate(repoPath, newPath)),\n );\n return dates.reduce((a, b) => {\n return a > b ? a : b;\n });\n })\n : new Date();\n const unpublishedEntry = {\n collection,\n slug,\n status,\n diffs,\n updatedAt,\n };\n res.json(unpublishedEntry);\n } else {\n return res.status(404).json({ message: 'Not Found' });\n }\n break;\n }\n case 'unpublishedEntryDataFile': {\n const { path, collection, slug } = body.params as UnpublishedEntryDataFileParams;\n const contentKey = generateContentKey(collection as string, slug as string);\n const cmsBranch = branchFromContentKey(contentKey);\n const [entry] = await runOnBranch(git, cmsBranch, () =>\n entriesFromFiles(repoPath, [{ path }]),\n );\n res.json({ data: entry.data });\n break;\n }\n case 'unpublishedEntryMediaFile': {\n const { path, collection, slug } = body.params as UnpublishedEntryMediaFileParams;\n const contentKey = generateContentKey(collection as string, slug as string);\n const cmsBranch = branchFromContentKey(contentKey);\n const file = await runOnBranch(git, cmsBranch, () => readMediaFile(repoPath, path));\n res.json(file);\n break;\n }\n case 'deleteUnpublishedEntry': {\n const { collection, slug } = body.params as DeleteEntryParams;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n const currentBranch = await getCurrentBranch(git);\n if (currentBranch === cmsBranch) {\n await git.checkoutLocalBranch(branch);\n }\n await git.branch(['-D', cmsBranch]);\n res.json({ message: `deleted branch: ${cmsBranch}` });\n break;\n }\n case 'persistEntry': {\n const {\n cmsLabelPrefix,\n entry,\n dataFiles = [entry as DataFile],\n assets,\n options,\n } = body.params as PersistEntryParams;\n const hasSubfolders = options?.hasSubfolders !== false;\n\n if (!options.useWorkflow) {\n await runOnBranch(git, branch, async () => {\n await commitEntry(\n git,\n repoPath,\n dataFiles,\n assets,\n options.commitMessage,\n hasSubfolders,\n );\n });\n } else {\n const slug = dataFiles[0].slug;\n const collection = options.collectionName as string;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n await runOnBranch(git, branch, async () => {\n const branchExists = await isBranchExists(git, cmsBranch);\n if (branchExists) {\n await git.checkout(cmsBranch);\n } else {\n await git.checkoutLocalBranch(cmsBranch);\n }\n await rebase(git, branch);\n const diffs = await getDiffs(git, branch, cmsBranch);\n // delete media files that have been removed from the entry\n const toDelete = diffs.filter(\n d => d.binary && !assets.map(a => a.path).includes(d.path),\n );\n await Promise.all(\n toDelete.map(async f => fs.unlink(await resolveExistingRepoPath(repoPath, f.path))),\n );\n await commitEntry(\n git,\n repoPath,\n dataFiles,\n assets,\n options.commitMessage,\n hasSubfolders,\n );\n\n // add status for new entries\n if (!branchExists) {\n const description = statusToLabel(options.status, cmsLabelPrefix || '');\n await git.addConfig(branchDescription(cmsBranch), description);\n }\n });\n }\n res.json({ message: 'entry persisted' });\n break;\n }\n case 'updateUnpublishedEntryStatus': {\n const { collection, slug, newStatus, cmsLabelPrefix } =\n body.params as UpdateUnpublishedEntryStatusParams;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n const description = statusToLabel(newStatus, cmsLabelPrefix || '');\n await git.addConfig(branchDescription(cmsBranch), description);\n res.json({ message: `${branch} description was updated to ${description}` });\n break;\n }\n case 'publishUnpublishedEntry': {\n const { collection, slug } = body.params as PublishUnpublishedEntryParams;\n const contentKey = generateContentKey(collection, slug);\n const cmsBranch = branchFromContentKey(contentKey);\n await merge(git, cmsBranch, branch);\n await git.deleteLocalBranch(cmsBranch);\n res.json({ message: `branch ${cmsBranch} merged to ${branch}` });\n break;\n }\n case 'getMedia': {\n const { mediaFolder } = body.params as GetMediaParams;\n const mediaFiles = await runOnBranch(git, branch, async () => {\n const files = await listRepoFiles(repoPath, mediaFolder, '', 1);\n const serializedFiles = await Promise.all(\n files.map(file => readMediaFile(repoPath, file)),\n );\n return serializedFiles;\n });\n res.json(mediaFiles);\n break;\n }\n case 'getMediaFile': {\n const { path } = body.params as GetMediaFileParams;\n const mediaFile = await runOnBranch(git, branch, () => {\n return readMediaFile(repoPath, path);\n });\n res.json(mediaFile);\n break;\n }\n case 'persistMedia': {\n const {\n asset,\n options: { commitMessage },\n } = body.params as PersistMediaParams;\n\n const file = await runOnBranch(git, branch, async () => {\n await writeFile(repoPath, asset.path, Buffer.from(asset.content, asset.encoding));\n await commit(git, commitMessage);\n return readMediaFile(repoPath, asset.path);\n });\n res.json(file);\n break;\n }\n case 'deleteFile': {\n const {\n path: filePath,\n options: { commitMessage },\n } = body.params as DeleteFileParams;\n await runOnBranch(git, branch, async () => {\n await deleteFile(repoPath, filePath);\n await commit(git, commitMessage);\n });\n res.json({ message: `deleted file ${filePath}` });\n break;\n }\n case 'deleteFiles': {\n const {\n paths,\n options: { commitMessage },\n } = body.params as DeleteFilesParams;\n await runOnBranch(git, branch, async () => {\n await Promise.all(paths.map(filePath => deleteFile(repoPath, filePath)));\n await commit(git, commitMessage);\n });\n res.json({ message: `deleted files ${paths.join(', ')}` });\n break;\n }\n case 'getDeployPreview': {\n res.json(null);\n break;\n }\n default: {\n const message = `Unknown action ${body.action}`;\n res.status(422).json({ error: message });\n break;\n }\n }\n } catch (e) {\n logger.error(\n `Error handling ${JSON.stringify(req.body)}: ${\n e instanceof Error ? e.message : 'Unknown error'\n }`,\n );\n res.status(500).json({ error: 'Unknown error' });\n } finally {\n release && release();\n }\n };\n}\n\ntype Options = {\n logger: winston.Logger;\n};\n\nexport async function registerMiddleware(app: express.Express, options: Options) {\n const { logger } = options;\n const repoPath = path.resolve(process.env.GIT_REPO_DIRECTORY || process.cwd());\n await validateRepo({ repoPath });\n app.post('/api/v1', joi(getSchema({ repoPath })));\n app.post('/api/v1', localGitMiddleware({ repoPath, logger }));\n logger.info(`Decap CMS Git Proxy Server configured with ${repoPath}`);\n}\n","import crypto from 'crypto';\nimport path from 'path';\nimport { promises as fs } from 'fs';\n\nimport { resolveExistingRepoPath } from './path';\n\nfunction sha256(buffer: Buffer) {\n return crypto.createHash('sha256').update(buffer).digest('hex');\n}\n\n// normalize windows os path format\nfunction normalizePath(path: string) {\n return path.replace(/\\\\/g, '/');\n}\n\nexport async function entriesFromFiles(\n repoPath: string,\n files: { path: string; label?: string }[],\n) {\n return Promise.all(\n files.map(async file => {\n try {\n const content = await fs.readFile(await resolveExistingRepoPath(repoPath, file.path));\n return {\n data: content.toString(),\n file: { path: normalizePath(file.path), label: file.label, id: sha256(content) },\n };\n } catch (e) {\n return {\n data: null,\n file: { path: normalizePath(file.path), label: file.label, id: null },\n };\n }\n }),\n );\n}\n\nexport async function readMediaFile(repoPath: string, file: string) {\n const encoding = 'base64';\n const buffer = await fs.readFile(await resolveExistingRepoPath(repoPath, file));\n const id = sha256(buffer);\n\n return {\n id,\n content: buffer.toString(encoding),\n encoding,\n path: normalizePath(file),\n name: path.basename(file),\n };\n}\n","import path from 'path';\nimport { promises as fs } from 'fs';\n\nimport { resolveExistingRepoPath, resolveNewRepoPath } from './path';\n\nasync function listFiles(dir: string, extension: string, depth: number): Promise<string[]> {\n if (depth <= 0) {\n return [];\n }\n\n try {\n const dirents = await fs.readdir(dir, { withFileTypes: true });\n const files = await Promise.all(\n dirents.map(dirent => {\n const res = path.join(dir, dirent.name);\n return dirent.isDirectory()\n ? listFiles(res, extension, depth - 1)\n : [res].filter(f => f.endsWith(extension));\n }),\n );\n return ([] as string[]).concat(...files);\n } catch (e) {\n return [];\n }\n}\n\nexport async function listRepoFiles(\n repoPath: string,\n folder: string,\n extension: string,\n depth: number,\n) {\n const repoRoot = await fs.realpath(path.resolve(repoPath));\n const files = await listFiles(await resolveExistingRepoPath(repoRoot, folder), extension, depth);\n return files.map(f => path.relative(repoRoot, f));\n}\n\nexport async function writeFile(repoPath: string, filePath: string, content: Buffer | string) {\n const resolvedPath = await resolveNewRepoPath(repoPath, filePath);\n await fs.mkdir(path.dirname(resolvedPath), { recursive: true });\n await fs.writeFile(resolvedPath, content);\n}\n\nexport async function deleteFile(repoPath: string, filePath: string) {\n const resolvedPath = await resolveNewRepoPath(repoPath, filePath);\n await fs.unlink(resolvedPath).catch(() => undefined);\n}\n\nasync function moveFile(from: string, to: string) {\n await fs.mkdir(path.dirname(to), { recursive: true });\n await fs.rename(from, to);\n}\n\nexport async function move(repoPath: string, from: string, to: string, hasSubfolders = true) {\n const resolvedFrom = await resolveExistingRepoPath(repoPath, from);\n const resolvedTo = await resolveNewRepoPath(repoPath, to);\n\n // move file\n await moveFile(resolvedFrom, resolvedTo);\n\n if (hasSubfolders) {\n // Legacy behavior (subfolders: true, default): move all files in the directory\n // This is for collections where all files in a folder represent a single entry\n const sourceDir = path.dirname(resolvedFrom);\n const destDir = path.dirname(resolvedTo);\n const allFiles = await listFiles(sourceDir, '', 100);\n await Promise.all(allFiles.map(file => moveFile(file, file.replace(sourceDir, destDir))));\n }\n}\n\nexport async function getUpdateDate(repoPath: string, filePath: string) {\n try {\n return await fs\n .stat(await resolveExistingRepoPath(repoPath, filePath))\n .then(stat => stat.mtime);\n } catch (e) {\n return new Date();\n }\n}\n","import path from 'path';\nimport { promises as fs } from 'fs';\n\nconst invalidPathMessage = 'Path must resolve under the configured repository';\n\nfunction assertPathUnderRoot(repoRoot: string, resolvedPath: string) {\n const relativePath = path.relative(repoRoot, resolvedPath);\n\n if (\n relativePath === '..' ||\n relativePath.startsWith(`..${path.sep}`) ||\n path.isAbsolute(relativePath)\n ) {\n throw new Error(invalidPathMessage);\n }\n}\n\nexport function resolveRepoPath(repoPath: string, filePath: string) {\n const repoRoot = path.resolve(repoPath);\n const resolvedPath = path.resolve(repoRoot, filePath);\n assertPathUnderRoot(repoRoot, resolvedPath);\n\n return resolvedPath;\n}\n\nexport async function resolveExistingRepoPath(repoPath: string, filePath: string) {\n const repoRoot = await fs.realpath(path.resolve(repoPath));\n const resolvedPath = await fs.realpath(resolveRepoPath(repoPath, filePath));\n assertPathUnderRoot(repoRoot, resolvedPath);\n\n return resolvedPath;\n}\n\nexport async function resolveNewRepoPath(repoPath: string, filePath: string) {\n const repoRoot = await fs.realpath(path.resolve(repoPath));\n const resolvedPath = resolveRepoPath(repoPath, filePath);\n const missingSegments: string[] = [];\n let existingPath = resolvedPath;\n let realExistingPath: string | undefined;\n\n while (!realExistingPath) {\n try {\n await fs.lstat(existingPath);\n } catch (e) {\n if ((e as NodeJS.ErrnoException).code !== 'ENOENT') {\n throw e;\n }\n missingSegments.unshift(path.basename(existingPath));\n existingPath = path.dirname(existingPath);\n continue;\n }\n\n realExistingPath = await fs.realpath(existingPath);\n }\n\n assertPathUnderRoot(repoRoot, realExistingPath);\n\n return path.join(realExistingPath, ...missingSegments);\n}\n","module.exports = require(\"@hapi/joi\");","module.exports = require(\"async-mutex\");","module.exports = require(\"cors\");","module.exports = require(\"express\");","module.exports = require(\"morgan\");","module.exports = require(\"simple-git\");","module.exports = require(\"what-the-diff\");","module.exports = require(\"winston\");","module.exports = require(\"crypto\");","module.exports = require(\"fs\");","module.exports = require(\"path\");","// The module cache\nconst __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tconst cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Create a new module (and put it into the cache)\n\tconst module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId].call(module.exports, module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","import { registerCommonMiddlewares } from './middlewares/common';\nimport { registerMiddleware as localGit } from './middlewares/localGit';\nimport { registerMiddleware as localFs } from './middlewares/localFs';\nimport { createLogger } from './logger';\n\nimport type express from 'express';\n\ntype Options = {\n logLevel?: string;\n};\n\nfunction createOptions(options: Options) {\n return {\n logger: createLogger({ level: options.logLevel || 'info' }),\n };\n}\n\nexport async function registerLocalGit(app: express.Express, options: Options = {}) {\n const opts = createOptions(options);\n registerCommonMiddlewares(app, opts);\n await localGit(app, opts);\n}\n\nexport async function registerLocalFs(app: express.Express, options: Options = {}) {\n const opts = createOptions(options);\n registerCommonMiddlewares(app, opts);\n await localFs(app, opts);\n}\n"],"names":["getLabelPrefix","labelPrefix","exports","CMS_BRANCH_PREFIX","labelToStatus","label","slice","length","statusToLabel","status","generateContentKey","collectionName","slug","parseContentKey","contentKey","index","indexOf","collection","contentKeyFromBranch","branch","branchFromContentKey","winston_1","__importDefault","__webpack_require__","combine","colorize","simple","default","format","createLogger","level","transports","Console","express_1","morgan_1","cors_1","registerCommonMiddlewares","app","options","logger","stream","write","message","debug","String","trim","use","origin","process","env","ORIGIN","json","limit","joi_1","path_1","pathTraversal","repoPath","extend","type","base","string","required","messages","validate","value","helpers","resolveRepoPath","e","errors","error","path","allowedActions","requiredString","requiredNumber","number","requiredBool","bool","defaultSchema","defaultParams","object","asset","content","encoding","valid","dataFile","raw","newPath","optional","params","when","switch","is","then","allow","keys","folder","extension","depth","files","array","items","id","cmsLabelPrefix","entry","dataFiles","assets","commitMessage","useWorkflow","xor","newStatus","mediaFolder","paths","min","otherwise","forbidden","action","joi","schema","req","res","next","body","allowUnknown","details","map","i","join","customValidators_1","fs_1","entries_1","localFsMiddleware","async","repo","basename","publish_modes","payload","entries","listRepoFiles","entriesFromFiles","file","hasSubfolders","Promise","all","writeFile","a","Buffer","from","every","forEach","move","mediaFiles","readMediaFile","mediaFile","filePath","deleteFile","JSON","stringify","Error","getSchema","registerMiddleware","resolve","GIT_REPO_DIRECTORY","cwd","post","info","APIUtils_1","what_the_diff_1","simple_git_1","async_mutex_1","fs_2","path_2","commit","git","add","undefined","getCurrentBranch","branchLocal","summary","current","runOnBranch","func","currentBranch","checkout","branchDescription","commitEntry","isBranchExists","includes","getDiffs","source","dest","rawDiff","diff","parse","d","oldPath","_a","replace","_b","newFile","binary","test","validateRepo","checkIsRepo","localGitMiddleware","mutex","withTimeout","Mutex","release","acquire","cmsBranches","result","filter","b","startsWith","cmsBranch","diffs","updatedAt","getUpdateDate","reduce","Date","unpublishedEntry","data","checkoutLocalBranch","branchExists","gpgSign","addConfig","rebase","toDelete","f","promises","unlink","resolveExistingRepoPath","description","to","mergeFromTo","merge","deleteLocalBranch","crypto_1","sha256","buffer","createHash","update","digest","normalizePath","readFile","toString","name","listFiles","dir","dirents","readdir","withFileTypes","dirent","isDirectory","endsWith","concat","moveFile","mkdir","dirname","recursive","rename","repoRoot","realpath","relative","resolvedPath","resolveNewRepoPath","catch","resolvedFrom","resolvedTo","sourceDir","destDir","allFiles","stat","mtime","assertPathUnderRoot","relativePath","sep","isAbsolute","missingSegments","realExistingPath","existingPath","lstat","code","unshift","module","require","__webpack_module_cache__","moduleId","cachedModule","__webpack_modules__","call","common_1","localGit_1","localFs_1","logger_1","createOptions","logLevel","registerLocalGit","opts","registerLocalFs"],"sourceRoot":""}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "decap-server",
3
3
  "description": "Proxy server to be used with Decap CMS proxy backend",
4
- "version": "3.10.0",
4
+ "version": "3.11.0",
5
5
  "repository": "https://github.com/decaporg/decap-cms/tree/main/packages/decap-server",
6
6
  "bugs": "https://github.com/decaporg/decap-cms/issues",
7
7
  "license": "MIT",
@@ -54,5 +54,5 @@
54
54
  "bin": {
55
55
  "decap-server": "./dist/index.js"
56
56
  },
57
- "gitHead": "ffe5ab7e61f4e7cb374a413bf9fe55f088d20ba2"
57
+ "gitHead": "63c8abdbd1f5c530d6f3d78dccca3379d0370c3e"
58
58
  }
@@ -1,5 +1,6 @@
1
1
  import Joi from '@hapi/joi';
2
- import path from 'path';
2
+
3
+ import { resolveRepoPath } from '../utils/path';
3
4
 
4
5
  export function pathTraversal(repoPath: string) {
5
6
  return Joi.extend({
@@ -9,8 +10,9 @@ export function pathTraversal(repoPath: string) {
9
10
  'path.invalid': '{{#label}} must resolve to a path under the configured repository',
10
11
  },
11
12
  validate(value, helpers) {
12
- const resolvedPath = path.join(repoPath, value);
13
- if (!resolvedPath.startsWith(repoPath)) {
13
+ try {
14
+ resolveRepoPath(repoPath, value);
15
+ } catch (e) {
14
16
  return { value, errors: helpers.error('path.invalid') };
15
17
  }
16
18
  },
@@ -72,21 +72,15 @@ export function localFsMiddleware({ repoPath, logger }: FsOptions) {
72
72
  } = body.params as PersistEntryParams;
73
73
  const hasSubfolders = options?.hasSubfolders !== false;
74
74
  await Promise.all(
75
- dataFiles.map(dataFile => writeFile(path.join(repoPath, dataFile.path), dataFile.raw)),
75
+ dataFiles.map(dataFile => writeFile(repoPath, dataFile.path, dataFile.raw)),
76
76
  );
77
77
  // save assets
78
78
  await Promise.all(
79
- assets.map(a =>
80
- writeFile(path.join(repoPath, a.path), Buffer.from(a.content, a.encoding)),
81
- ),
79
+ assets.map(a => writeFile(repoPath, a.path, Buffer.from(a.content, a.encoding))),
82
80
  );
83
81
  if (dataFiles.every(dataFile => dataFile.newPath)) {
84
82
  dataFiles.forEach(async dataFile => {
85
- await move(
86
- path.join(repoPath, dataFile.path),
87
- path.join(repoPath, dataFile.newPath!),
88
- hasSubfolders,
89
- );
83
+ await move(repoPath, dataFile.path, dataFile.newPath!, hasSubfolders);
90
84
  });
91
85
  }
92
86
  res.json({ message: 'entry persisted' });
@@ -107,10 +101,7 @@ export function localFsMiddleware({ repoPath, logger }: FsOptions) {
107
101
  }
108
102
  case 'persistMedia': {
109
103
  const { asset } = body.params as PersistMediaParams;
110
- await writeFile(
111
- path.join(repoPath, asset.path),
112
- Buffer.from(asset.content, asset.encoding),
113
- );
104
+ await writeFile(repoPath, asset.path, Buffer.from(asset.content, asset.encoding));
114
105
  const file = await readMediaFile(repoPath, asset.path);
115
106
  res.json(file);
116
107
  break;
@@ -53,6 +53,40 @@ describe('localGitMiddleware', () => {
53
53
  });
54
54
 
55
55
  describe('getSchema', () => {
56
+ it.each([
57
+ [
58
+ 'getMediaFile',
59
+ { ...defaultParams, path: '../repo-owned/secret.txt' },
60
+ '"params.path" must resolve to a path under the configured repository',
61
+ ],
62
+ [
63
+ 'persistMedia',
64
+ {
65
+ ...defaultParams,
66
+ asset: {
67
+ path: '../repo-owned/write.txt',
68
+ content: 'b3V0c2lkZS13cml0ZS1wcm9vZgo=',
69
+ encoding: 'base64',
70
+ },
71
+ options: { commitMessage: 'proof-write' },
72
+ },
73
+ '"params.asset.path" must resolve to a path under the configured repository',
74
+ ],
75
+ [
76
+ 'deleteFile',
77
+ {
78
+ ...defaultParams,
79
+ path: '../repo-owned/delete-me.txt',
80
+ options: { commitMessage: 'proof-delete' },
81
+ },
82
+ '"params.path" must resolve to a path under the configured repository',
83
+ ],
84
+ ])('should reject sibling-prefix traversal for %s', (action, params, expectedMessage) => {
85
+ const schema = getSchema({ repoPath: '/Users/user/documents/code/repo' });
86
+
87
+ assetFailure(schema.validate({ action, params }), expectedMessage);
88
+ });
89
+
56
90
  it('should throw on path traversal', () => {
57
91
  const schema = getSchema({ repoPath: '/Users/user/documents/code/repo' });
58
92
 
@@ -18,6 +18,7 @@ import { defaultSchema, joi } from '../joi';
18
18
  import { pathTraversal } from '../joi/customValidators';
19
19
  import { listRepoFiles, writeFile, move, deleteFile, getUpdateDate } from '../utils/fs';
20
20
  import { entriesFromFiles, readMediaFile } from '../utils/entries';
21
+ import { resolveExistingRepoPath } from '../utils/path';
21
22
 
22
23
  import type {
23
24
  EntriesByFolderParams,
@@ -89,20 +90,14 @@ async function commitEntry(
89
90
  hasSubfolders = true,
90
91
  ) {
91
92
  // save entry content
92
- await Promise.all(
93
- dataFiles.map(dataFile => writeFile(path.join(repoPath, dataFile.path), dataFile.raw)),
94
- );
93
+ await Promise.all(dataFiles.map(dataFile => writeFile(repoPath, dataFile.path, dataFile.raw)));
95
94
  // save assets
96
95
  await Promise.all(
97
- assets.map(a => writeFile(path.join(repoPath, a.path), Buffer.from(a.content, a.encoding))),
96
+ assets.map(a => writeFile(repoPath, a.path, Buffer.from(a.content, a.encoding))),
98
97
  );
99
98
  if (dataFiles.every(dataFile => dataFile.newPath)) {
100
99
  dataFiles.forEach(async dataFile => {
101
- await move(
102
- path.join(repoPath, dataFile.path),
103
- path.join(repoPath, dataFile.newPath!),
104
- hasSubfolders,
105
- );
100
+ await move(repoPath, dataFile.path, dataFile.newPath!, hasSubfolders);
106
101
  });
107
102
  }
108
103
 
@@ -346,7 +341,9 @@ export function localGitMiddleware({ repoPath, logger }: GitOptions) {
346
341
  const toDelete = diffs.filter(
347
342
  d => d.binary && !assets.map(a => a.path).includes(d.path),
348
343
  );
349
- await Promise.all(toDelete.map(f => fs.unlink(path.join(repoPath, f.path))));
344
+ await Promise.all(
345
+ toDelete.map(async f => fs.unlink(await resolveExistingRepoPath(repoPath, f.path))),
346
+ );
350
347
  await commitEntry(
351
348
  git,
352
349
  repoPath,
@@ -412,10 +409,7 @@ export function localGitMiddleware({ repoPath, logger }: GitOptions) {
412
409
  } = body.params as PersistMediaParams;
413
410
 
414
411
  const file = await runOnBranch(git, branch, async () => {
415
- await writeFile(
416
- path.join(repoPath, asset.path),
417
- Buffer.from(asset.content, asset.encoding),
418
- );
412
+ await writeFile(repoPath, asset.path, Buffer.from(asset.content, asset.encoding));
419
413
  await commit(git, commitMessage);
420
414
  return readMediaFile(repoPath, asset.path);
421
415
  });
@@ -2,6 +2,8 @@ import crypto from 'crypto';
2
2
  import path from 'path';
3
3
  import { promises as fs } from 'fs';
4
4
 
5
+ import { resolveExistingRepoPath } from './path';
6
+
5
7
  function sha256(buffer: Buffer) {
6
8
  return crypto.createHash('sha256').update(buffer).digest('hex');
7
9
  }
@@ -18,7 +20,7 @@ export async function entriesFromFiles(
18
20
  return Promise.all(
19
21
  files.map(async file => {
20
22
  try {
21
- const content = await fs.readFile(path.join(repoPath, file.path));
23
+ const content = await fs.readFile(await resolveExistingRepoPath(repoPath, file.path));
22
24
  return {
23
25
  data: content.toString(),
24
26
  file: { path: normalizePath(file.path), label: file.label, id: sha256(content) },
@@ -35,7 +37,7 @@ export async function entriesFromFiles(
35
37
 
36
38
  export async function readMediaFile(repoPath: string, file: string) {
37
39
  const encoding = 'base64';
38
- const buffer = await fs.readFile(path.join(repoPath, file));
40
+ const buffer = await fs.readFile(await resolveExistingRepoPath(repoPath, file));
39
41
  const id = sha256(buffer);
40
42
 
41
43
  return {
@@ -0,0 +1,66 @@
1
+ import { promises as fs } from 'fs';
2
+ import os from 'os';
3
+ import path from 'path';
4
+
5
+ import { readMediaFile } from './entries';
6
+ import { deleteFile, writeFile } from './fs';
7
+
8
+ describe('repository filesystem boundary', () => {
9
+ let temporaryPath: string;
10
+ let repoPath: string;
11
+ let outsidePath: string;
12
+
13
+ beforeEach(async () => {
14
+ temporaryPath = await fs.mkdtemp(path.join(os.tmpdir(), 'decap-server-'));
15
+ repoPath = path.join(temporaryPath, 'repo');
16
+ outsidePath = path.join(temporaryPath, 'repo-owned');
17
+ await Promise.all([fs.mkdir(repoPath), fs.mkdir(outsidePath)]);
18
+ });
19
+
20
+ afterEach(async () => {
21
+ await fs.rmdir(temporaryPath, { recursive: true });
22
+ });
23
+
24
+ it('blocks reads, writes, and deletes through a sibling-prefix traversal', async () => {
25
+ const traversalPath = path.join('..', 'repo-owned', 'secret.txt');
26
+ const outsideFile = path.join(outsidePath, 'secret.txt');
27
+ await fs.writeFile(outsideFile, 'outside-proof');
28
+
29
+ await expect(readMediaFile(repoPath, traversalPath)).rejects.toThrow(
30
+ 'Path must resolve under the configured repository',
31
+ );
32
+ await expect(writeFile(repoPath, traversalPath, 'changed')).rejects.toThrow(
33
+ 'Path must resolve under the configured repository',
34
+ );
35
+ await expect(deleteFile(repoPath, traversalPath)).rejects.toThrow(
36
+ 'Path must resolve under the configured repository',
37
+ );
38
+ await expect(fs.readFile(outsideFile, 'utf8')).resolves.toBe('outside-proof');
39
+ });
40
+
41
+ it('blocks reads, writes, and deletes through a repository symlink', async () => {
42
+ const linkPath = path.join(repoPath, 'linked');
43
+ await fs.symlink(outsidePath, linkPath, process.platform === 'win32' ? 'junction' : 'dir');
44
+ await fs.writeFile(path.join(outsidePath, 'secret.txt'), 'outside-proof');
45
+ await fs.writeFile(path.join(outsidePath, 'delete-me.txt'), 'keep-me');
46
+
47
+ await expect(readMediaFile(repoPath, 'linked/secret.txt')).rejects.toThrow(
48
+ 'Path must resolve under the configured repository',
49
+ );
50
+ await expect(writeFile(repoPath, 'linked/write.txt', 'changed')).rejects.toThrow(
51
+ 'Path must resolve under the configured repository',
52
+ );
53
+ await expect(deleteFile(repoPath, 'linked/delete-me.txt')).rejects.toThrow(
54
+ 'Path must resolve under the configured repository',
55
+ );
56
+ await expect(fs.readFile(path.join(outsidePath, 'secret.txt'), 'utf8')).resolves.toBe(
57
+ 'outside-proof',
58
+ );
59
+ await expect(fs.readFile(path.join(outsidePath, 'delete-me.txt'), 'utf8')).resolves.toBe(
60
+ 'keep-me',
61
+ );
62
+ await expect(fs.stat(path.join(outsidePath, 'write.txt'))).rejects.toMatchObject({
63
+ code: 'ENOENT',
64
+ });
65
+ });
66
+ });