mirror of
https://github.com/plankanban/planka.git
synced 2025-07-18 12:49:43 +02:00
parent
7ef55ec578
commit
7d138b858d
10 changed files with 156 additions and 19 deletions
|
@ -57,7 +57,7 @@ COPY --from=client-builder /app/build/index.html views
|
||||||
|
|
||||||
VOLUME /app/public/user-avatars
|
VOLUME /app/public/user-avatars
|
||||||
VOLUME /app/public/project-background-images
|
VOLUME /app/public/project-background-images
|
||||||
VOLUME /app/public/attachments
|
VOLUME /app/private/attachments
|
||||||
|
|
||||||
EXPOSE 1337
|
EXPOSE 1337
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ services:
|
||||||
volumes:
|
volumes:
|
||||||
- user-avatars:/app/public/user-avatars
|
- user-avatars:/app/public/user-avatars
|
||||||
- project-background-images:/app/public/project-background-images
|
- project-background-images:/app/public/project-background-images
|
||||||
- attachments:/app/public/attachments
|
- attachments:/app/private/attachments
|
||||||
ports:
|
ports:
|
||||||
- 3000:1337
|
- 3000:1337
|
||||||
environment:
|
environment:
|
||||||
|
|
8
server/.gitignore
vendored
8
server/.gitignore
vendored
|
@ -135,9 +135,11 @@ public/user-avatars/*
|
||||||
!public/project-background-images
|
!public/project-background-images
|
||||||
public/project-background-images/*
|
public/project-background-images/*
|
||||||
!public/project-background-images/.gitkeep
|
!public/project-background-images/.gitkeep
|
||||||
!public/attachments
|
|
||||||
public/attachments/*
|
private/*
|
||||||
!public/attachments/.gitkeep
|
!private/attachments
|
||||||
|
private/attachments/*
|
||||||
|
!private/attachments/.gitkeep
|
||||||
|
|
||||||
views/*
|
views/*
|
||||||
!views/.gitkeep
|
!views/.gitkeep
|
||||||
|
|
67
server/api/controllers/attachments/download-thumbnail.js
Normal file
67
server/api/controllers/attachments/download-thumbnail.js
Normal file
|
@ -0,0 +1,67 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const Errors = {
|
||||||
|
ATTACHMENT_NOT_FOUND: {
|
||||||
|
attachmentNotFound: 'Attachment not found',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
inputs: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
regex: /^[0-9]+$/,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
filename: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
exits: {
|
||||||
|
attachmentNotFound: {
|
||||||
|
responseType: 'notFound',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
async fn(inputs, exits) {
|
||||||
|
const { currentUser } = this.req;
|
||||||
|
|
||||||
|
const { attachment, card, project } = await sails.helpers.attachments
|
||||||
|
.getProjectPath(inputs.id)
|
||||||
|
.intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND);
|
||||||
|
|
||||||
|
const isBoardMember = await sails.helpers.users.isBoardMember(currentUser.id, card.boardId);
|
||||||
|
|
||||||
|
if (!isBoardMember) {
|
||||||
|
const isProjectManager = await sails.helpers.users.isProjectManager(
|
||||||
|
currentUser.id,
|
||||||
|
project.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isProjectManager) {
|
||||||
|
throw Errors.ATTACHMENT_NOT_FOUND; // Forbidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attachment.isImage) {
|
||||||
|
throw Errors.ATTACHMENT_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(
|
||||||
|
sails.config.custom.attachmentsPath,
|
||||||
|
attachment.dirname,
|
||||||
|
'thumbnails',
|
||||||
|
inputs.filename,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
throw Errors.ATTACHMENT_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.res.setHeader('Content-Disposition', `inline; ${inputs.filename}`);
|
||||||
|
return exits.success(fs.createReadStream(filePath));
|
||||||
|
},
|
||||||
|
};
|
69
server/api/controllers/attachments/download.js
Normal file
69
server/api/controllers/attachments/download.js
Normal file
|
@ -0,0 +1,69 @@
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const Errors = {
|
||||||
|
ATTACHMENT_NOT_FOUND: {
|
||||||
|
attachmentNotFound: 'Attachment not found',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
inputs: {
|
||||||
|
id: {
|
||||||
|
type: 'string',
|
||||||
|
regex: /^[0-9]+$/,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
filename: {
|
||||||
|
type: 'string',
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
exits: {
|
||||||
|
attachmentNotFound: {
|
||||||
|
responseType: 'notFound',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
async fn(inputs, exits) {
|
||||||
|
const { currentUser } = this.req;
|
||||||
|
|
||||||
|
const { attachment, card, project } = await sails.helpers.attachments
|
||||||
|
.getProjectPath(inputs.id)
|
||||||
|
.intercept('pathNotFound', () => Errors.ATTACHMENT_NOT_FOUND);
|
||||||
|
|
||||||
|
const isBoardMember = await sails.helpers.users.isBoardMember(currentUser.id, card.boardId);
|
||||||
|
|
||||||
|
if (!isBoardMember) {
|
||||||
|
const isProjectManager = await sails.helpers.users.isProjectManager(
|
||||||
|
currentUser.id,
|
||||||
|
project.id,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!isProjectManager) {
|
||||||
|
throw Errors.ATTACHMENT_NOT_FOUND; // Forbidden
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const filePath = path.join(
|
||||||
|
sails.config.custom.attachmentsPath,
|
||||||
|
attachment.dirname,
|
||||||
|
attachment.filename,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (!fs.existsSync(filePath)) {
|
||||||
|
throw Errors.ATTACHMENT_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
|
let contentDisposition;
|
||||||
|
if (attachment.isImage || path.extname(attachment.filename) === '.pdf') {
|
||||||
|
contentDisposition = 'inline';
|
||||||
|
} else {
|
||||||
|
contentDisposition = `attachment; ${inputs.filename}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.res.setHeader('Content-Disposition', contentDisposition);
|
||||||
|
return exits.success(fs.createReadStream(filePath));
|
||||||
|
},
|
||||||
|
};
|
|
@ -52,9 +52,9 @@ module.exports = {
|
||||||
customToJSON() {
|
customToJSON() {
|
||||||
return {
|
return {
|
||||||
..._.omit(this, ['dirname', 'filename', 'isImage']),
|
..._.omit(this, ['dirname', 'filename', 'isImage']),
|
||||||
url: `${sails.config.custom.attachmentsUrl}/${this.dirname}/${this.filename}`,
|
url: `${sails.config.custom.attachmentsUrl}/${this.id}/download/${this.filename}`,
|
||||||
coverUrl: this.isImage
|
coverUrl: this.isImage
|
||||||
? `${sails.config.custom.attachmentsUrl}/${this.dirname}/thumbnails/cover-256.jpg`
|
? `${sails.config.custom.attachmentsUrl}/${this.id}/download/thumbnails/cover-256.jpg`
|
||||||
: null,
|
: null,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
|
@ -26,6 +26,6 @@ module.exports.custom = {
|
||||||
projectBackgroundImagesPath: path.join(sails.config.paths.public, 'project-background-images'),
|
projectBackgroundImagesPath: path.join(sails.config.paths.public, 'project-background-images'),
|
||||||
projectBackgroundImagesUrl: `${process.env.BASE_URL}/project-background-images`,
|
projectBackgroundImagesUrl: `${process.env.BASE_URL}/project-background-images`,
|
||||||
|
|
||||||
attachmentsPath: path.join(sails.config.paths.public, 'attachments'),
|
attachmentsPath: path.join(sails.config.appPath, 'private', 'attachments'),
|
||||||
attachmentsUrl: `${process.env.BASE_URL}/attachments`,
|
attachmentsUrl: `${process.env.BASE_URL}/attachments`,
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,21 +18,10 @@ module.exports.policies = {
|
||||||
|
|
||||||
'*': 'is-authenticated',
|
'*': 'is-authenticated',
|
||||||
|
|
||||||
// 'users/index': ['is-authenticated', 'is-admin'],
|
|
||||||
'users/create': ['is-authenticated', 'is-admin'],
|
'users/create': ['is-authenticated', 'is-admin'],
|
||||||
'users/delete': ['is-authenticated', 'is-admin'],
|
'users/delete': ['is-authenticated', 'is-admin'],
|
||||||
|
|
||||||
'projects/create': ['is-authenticated', 'is-admin'],
|
'projects/create': ['is-authenticated', 'is-admin'],
|
||||||
// 'projects/update': ['is-authenticated', 'is-admin'],
|
|
||||||
// 'projects/update-background-image': ['is-authenticated', 'is-admin'],
|
|
||||||
// 'projects/delete': ['is-authenticated', 'is-admin'],
|
|
||||||
|
|
||||||
// 'project-memberships/create': ['is-authenticated', 'is-admin'],
|
|
||||||
// 'project-memberships/delete': ['is-authenticated', 'is-admin'],
|
|
||||||
|
|
||||||
// 'boards/create': ['is-authenticated', 'is-admin'],
|
|
||||||
// 'boards/update': ['is-authenticated', 'is-admin'],
|
|
||||||
// 'boards/delete': ['is-authenticated', 'is-admin'],
|
|
||||||
|
|
||||||
'access-tokens/create': true,
|
'access-tokens/create': true,
|
||||||
};
|
};
|
||||||
|
|
|
@ -75,6 +75,16 @@ module.exports.routes = {
|
||||||
'GET /api/notifications/:id': 'notifications/show',
|
'GET /api/notifications/:id': 'notifications/show',
|
||||||
'PATCH /api/notifications/:ids': 'notifications/update',
|
'PATCH /api/notifications/:ids': 'notifications/update',
|
||||||
|
|
||||||
|
'GET /attachments/:id/download/:filename': {
|
||||||
|
action: 'attachments/download',
|
||||||
|
skipAssets: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
'GET /attachments/:id/download/thumbnails/:filename': {
|
||||||
|
action: 'attachments/download-thumbnail',
|
||||||
|
skipAssets: false,
|
||||||
|
},
|
||||||
|
|
||||||
'GET /*': {
|
'GET /*': {
|
||||||
view: 'index',
|
view: 'index',
|
||||||
skipAssets: true,
|
skipAssets: true,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue