mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-06 22:40:14 -05:00
Implemented '/media/upload' API endpoint
refs https://linear.app/tryghost/issue/CORE-121/create-a-video-storage-adapter - This is an experimental implementation of video file upload support - Also the output serializer skipped use of url utils in favor of inline implementatoin - this should almost certainly be it's own package
This commit is contained in:
parent
4907b7bf1e
commit
4a551661d9
7 changed files with 129 additions and 0 deletions
|
@ -105,6 +105,10 @@ module.exports = {
|
|||
return shared.pipeline(require('./images'), localUtils);
|
||||
},
|
||||
|
||||
get media() {
|
||||
return shared.pipeline(require('./media'), localUtils);
|
||||
},
|
||||
|
||||
get tags() {
|
||||
return shared.pipeline(require('./tags'), localUtils);
|
||||
},
|
||||
|
|
12
core/server/api/canary/media.js
Normal file
12
core/server/api/canary/media.js
Normal file
|
@ -0,0 +1,12 @@
|
|||
const storage = require('../../adapters/storage');
|
||||
|
||||
module.exports = {
|
||||
docName: 'media',
|
||||
upload: {
|
||||
statusCode: 201,
|
||||
permissions: false,
|
||||
query(frame) {
|
||||
return storage.getStorage('media').save(frame.file);
|
||||
}
|
||||
}
|
||||
};
|
|
@ -85,6 +85,10 @@ module.exports = {
|
|||
return require('./images');
|
||||
},
|
||||
|
||||
get media() {
|
||||
return require('./media');
|
||||
},
|
||||
|
||||
get tags() {
|
||||
return require('./tags');
|
||||
},
|
||||
|
|
27
core/server/api/canary/utils/serializers/output/media.js
Normal file
27
core/server/api/canary/utils/serializers/output/media.js
Normal file
|
@ -0,0 +1,27 @@
|
|||
const config = require('../../../../../../shared/config');
|
||||
|
||||
function getURL(urlPath) {
|
||||
const STATIC_VIDEO_URL_PREFIX = 'content/media';
|
||||
const imagePathRe = new RegExp('^' + config.getSubdir() + '/' + STATIC_VIDEO_URL_PREFIX);
|
||||
const absolute = imagePathRe.test(urlPath) ? true : false;
|
||||
|
||||
if (absolute) {
|
||||
// Remove the sub-directory from the URL because ghostConfig will add it back.
|
||||
urlPath = urlPath.replace(new RegExp('^' + config.getSubdir()), '');
|
||||
const baseUrl = config.getSiteUrl().replace(/\/$/, '');
|
||||
urlPath = baseUrl + urlPath;
|
||||
}
|
||||
|
||||
return urlPath;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
upload(path, apiConfig, frame) {
|
||||
return frame.response = {
|
||||
media: [{
|
||||
url: getURL(path),
|
||||
ref: frame.data.ref || null
|
||||
}]
|
||||
};
|
||||
}
|
||||
};
|
|
@ -236,6 +236,14 @@ module.exports = function apiRoutes() {
|
|||
http(api.images.upload)
|
||||
);
|
||||
|
||||
// ## media
|
||||
router.post('/media/upload',
|
||||
mw.authAdminApi,
|
||||
apiMw.upload.single('file'),
|
||||
apiMw.upload.validation({type: 'media'}),
|
||||
http(api.media.upload)
|
||||
);
|
||||
|
||||
// ## Invites
|
||||
router.get('/invites', mw.authAdminApi, http(api.invites.browse));
|
||||
router.get('/invites/:id', mw.authAdminApi, http(api.invites.read));
|
||||
|
|
|
@ -30,6 +30,10 @@
|
|||
"extensions": [".jpg", ".jpeg", ".gif", ".png", ".svg", ".svgz", ".ico", ".webp"],
|
||||
"contentTypes": ["image/jpeg", "image/png", "image/gif", "image/svg+xml", "image/x-icon", "image/vnd.microsoft.icon", "image/webp"]
|
||||
},
|
||||
"media": {
|
||||
"extensions": [".mp4",".webm", ".ogv"],
|
||||
"contentTypes": ["video/mp4", "video/webm", "video/ogg"]
|
||||
},
|
||||
"icons": {
|
||||
"extensions": [".png", ".ico"],
|
||||
"contentTypes": ["image/png", "image/x-icon", "image/vnd.microsoft.icon"]
|
||||
|
|
70
test/e2e-api/admin/media.test.js
Normal file
70
test/e2e-api/admin/media.test.js
Normal file
|
@ -0,0 +1,70 @@
|
|||
const path = require('path');
|
||||
const fs = require('fs-extra');
|
||||
const should = require('should');
|
||||
const supertest = require('supertest');
|
||||
const localUtils = require('./utils');
|
||||
const testUtils = require('../../utils');
|
||||
const config = require('../../../core/shared/config');
|
||||
|
||||
describe('Media API', function () {
|
||||
// NOTE: holds paths to media that need to be cleaned up after the tests are run
|
||||
const media = [];
|
||||
let request;
|
||||
|
||||
before(async function () {
|
||||
await testUtils.startGhost();
|
||||
request = supertest.agent(config.get('url'));
|
||||
await localUtils.doAuth(request);
|
||||
});
|
||||
|
||||
after(function () {
|
||||
media.forEach(function (image) {
|
||||
fs.removeSync(config.get('paths').appRoot + image);
|
||||
});
|
||||
});
|
||||
|
||||
it('Can upload a MP4', async function () {
|
||||
const res = await request.post(localUtils.API.getApiQuery('media/upload'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.field('purpose', 'video')
|
||||
.field('ref', 'https://ghost.org/sample_640x360.mp4')
|
||||
.attach('file', path.join(__dirname, '/../../utils/fixtures/media/sample_640x360.mp4'))
|
||||
.expect(201);
|
||||
|
||||
res.body.media[0].url.should.match(new RegExp(`${config.get('url')}/content/media/\\d+/\\d+/sample_640x360.mp4`));
|
||||
res.body.media[0].ref.should.equal('https://ghost.org/sample_640x360.mp4');
|
||||
|
||||
media.push(res.body.media[0].url.replace(config.get('url'), ''));
|
||||
});
|
||||
|
||||
it('Can upload a WebM', async function () {
|
||||
const res = await request.post(localUtils.API.getApiQuery('media/upload'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.field('purpose', 'video')
|
||||
.field('ref', 'https://ghost.org/sample_640x360.webm')
|
||||
.attach('file', path.join(__dirname, '/../../utils/fixtures/media/sample_640x360.webm'))
|
||||
.expect(201);
|
||||
|
||||
res.body.media[0].url.should.match(new RegExp(`${config.get('url')}/content/media/\\d+/\\d+/sample_640x360.webm`));
|
||||
res.body.media[0].ref.should.equal('https://ghost.org/sample_640x360.webm');
|
||||
|
||||
media.push(res.body.media[0].url.replace(config.get('url'), ''));
|
||||
});
|
||||
|
||||
it('Can upload an Ogg', async function () {
|
||||
const res = await request.post(localUtils.API.getApiQuery('media/upload'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.field('purpose', 'video')
|
||||
.field('ref', 'https://ghost.org/sample_640x360.ogv')
|
||||
.attach('file', path.join(__dirname, '/../../utils/fixtures/media/sample_640x360.ogv'))
|
||||
.expect(201);
|
||||
|
||||
res.body.media[0].url.should.match(new RegExp(`${config.get('url')}/content/media/\\d+/\\d+/sample_640x360.ogv`));
|
||||
res.body.media[0].ref.should.equal('https://ghost.org/sample_640x360.ogv');
|
||||
|
||||
media.push(res.body.media[0].url.replace(config.get('url'), ''));
|
||||
});
|
||||
});
|
Loading…
Reference in a new issue