mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-17 13:20:31 +00:00
feat: Media attachment system
This commit is contained in:
@@ -22,4 +22,11 @@ module.exports = {
|
||||
superUser: 'root',
|
||||
superPassword: '123123123',
|
||||
},
|
||||
mail: {
|
||||
host: 'smtp.mailtrap.io',
|
||||
port: 587,
|
||||
secure: false,
|
||||
username: '842f331d3dc005',
|
||||
password: '172f97b34f1a17',
|
||||
}
|
||||
};
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
|
||||
exports.up = function(knex) {
|
||||
return knex.schema.createTable('media', (table) => {
|
||||
table.increments();
|
||||
table.string('attachment_file');
|
||||
table.timestamps();
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function(knex) {
|
||||
return knex.schema.dropTableIfExists('media');
|
||||
};
|
||||
@@ -147,11 +147,11 @@ export default {
|
||||
const hashedPassword = await hashPassword(form.password);
|
||||
const userInsert = {
|
||||
...pick(form, ['first_name', 'last_name', 'email', 'phone_number']),
|
||||
password: hashedPassword,
|
||||
active: true,
|
||||
};
|
||||
const registeredUser = await SystemUser.query().insert({
|
||||
...userInsert,
|
||||
password: hashedPassword,
|
||||
tenant_id: tenantOrganization.id,
|
||||
});
|
||||
await dbManager.createDb(`bigcapital_tenant_${organizationId}`);
|
||||
|
||||
146
server/src/http/controllers/Media.js
Normal file
146
server/src/http/controllers/Media.js
Normal file
@@ -0,0 +1,146 @@
|
||||
|
||||
import express from 'express';
|
||||
import { check, param, query, validationResult } from 'express-validator';
|
||||
import fs from 'fs';
|
||||
import asyncMiddleware from '@/http/middleware/asyncMiddleware';
|
||||
import TenancyMiddleware from '@/http/middleware/TenancyMiddleware';
|
||||
import jwtAuth from '@/http/middleware/jwtAuth';
|
||||
import Logger from '@/services/Logger';
|
||||
|
||||
const fsPromises = fs.promises;
|
||||
|
||||
export default {
|
||||
/**
|
||||
* Router constructor.
|
||||
*/
|
||||
router() {
|
||||
const router = express.Router();
|
||||
|
||||
router.use(jwtAuth);
|
||||
router.use(TenancyMiddleware);
|
||||
|
||||
router.post('/upload',
|
||||
this.upload.validation,
|
||||
asyncMiddleware(this.upload.handler));
|
||||
|
||||
router.delete('/delete/:id',
|
||||
this.delete.validation,
|
||||
asyncMiddleware(this.delete.handler));
|
||||
|
||||
router.get('/',
|
||||
this.get.validation,
|
||||
asyncMiddleware(this.get.handler));
|
||||
|
||||
return router;
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve all or the given attachment ids.
|
||||
*/
|
||||
get: {
|
||||
validation: [
|
||||
query('ids'),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
const { Media } = req.models;
|
||||
const media = await Media.query().onBuild((builder) => {
|
||||
|
||||
if (req.query.ids) {
|
||||
const ids = Array.isArray(req.query.ids) ? req.query.ids : [req.query.ids];
|
||||
builder.whereIn('id', ids);
|
||||
}
|
||||
});
|
||||
|
||||
return res.status(200).send({ media });
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Uploads the given attachment file.
|
||||
*/
|
||||
upload: {
|
||||
validation: [
|
||||
// check('attachment').exists(),
|
||||
],
|
||||
async handler(req, res) {
|
||||
if (!req.files.attachment) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'ATTACHMENT.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
const publicPath = 'storage/app/public/';
|
||||
const attachmentsMimes = ['image/png', 'image/jpeg'];
|
||||
const { attachment } = req.files;
|
||||
const { Media } = req.models;
|
||||
|
||||
const errorReasons = [];
|
||||
|
||||
// Validate the attachment.
|
||||
if (attachment && attachmentsMimes.indexOf(attachment.mimetype) === -1) {
|
||||
errorReasons.push({ type: 'ATTACHMENT.MINETYPE.NOT.SUPPORTED', code: 160 });
|
||||
}
|
||||
// Catch all error reasons to response 400.
|
||||
if (errorReasons.length > 0) {
|
||||
return res.status(400).send({ errors: errorReasons });
|
||||
}
|
||||
|
||||
try {
|
||||
await attachment.mv(`${publicPath}${req.organizationId}/${attachment.md5}.png`);
|
||||
Logger.log('info', 'Attachment uploaded successfully');
|
||||
} catch (error) {
|
||||
Logger.log('info', 'Attachment uploading failed.', { error });
|
||||
}
|
||||
|
||||
const media = await Media.query().insert({
|
||||
attachment_file: `${attachment.md5}.png`,
|
||||
});
|
||||
return res.status(200).send({ media });
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Deletes the given attachment ids from file system and database.
|
||||
*/
|
||||
delete: {
|
||||
validation: [
|
||||
param('id').exists().isNumeric().toInt(),
|
||||
],
|
||||
async handler(req, res) {
|
||||
const validationErrors = validationResult(req);
|
||||
|
||||
if (!validationErrors.isEmpty()) {
|
||||
return res.boom.badData(null, {
|
||||
code: 'validation_error', ...validationErrors,
|
||||
});
|
||||
}
|
||||
const { Media } = req.models;
|
||||
const { id } = req.params;
|
||||
const media = await Media.query().where('id', id).first();
|
||||
|
||||
if (!media) {
|
||||
return res.status(400).send({
|
||||
errors: [{ type: 'MEDIA.ID.NOT.FOUND', code: 200 }],
|
||||
});
|
||||
}
|
||||
const publicPath = 'storage/app/public/';
|
||||
const tenantPath = `${publicPath}${req.organizationId}`;
|
||||
|
||||
try {
|
||||
await fsPromises.unlink(`${tenantPath}/${media.attachmentFile}`);
|
||||
Logger.log('error', 'Attachment file has been deleted.');
|
||||
} catch (error) {
|
||||
Logger.log('error', 'Delete item attachment file delete failed.', { error });
|
||||
}
|
||||
await Media.query().where('id', media.id).delete();
|
||||
|
||||
return res.status(200).send();
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -25,6 +25,7 @@ import Resources from './controllers/Resources';
|
||||
import ExchangeRates from '@/http/controllers/ExchangeRates';
|
||||
// import SalesReports from '@/http/controllers/SalesReports';
|
||||
// import PurchasesReports from '@/http/controllers/PurchasesReports';
|
||||
import Media from '@/http/controllers/Media';
|
||||
|
||||
export default (app) => {
|
||||
// app.use('/api/oauth2', OAuth2.router());
|
||||
@@ -51,6 +52,7 @@ export default (app) => {
|
||||
// app.use('/api/budget', Budget.router());
|
||||
app.use('/api/resources', Resources.router());
|
||||
app.use('/api/exchange_rates', ExchangeRates.router());
|
||||
app.use('/api/media', Media.router());
|
||||
// app.use('/api/currency_adjustment', CurrencyAdjustment.router());
|
||||
// app.use('/api/reports/sales', SalesReports.router());
|
||||
// app.use('/api/reports/purchases', PurchasesReports.router());
|
||||
|
||||
10
server/src/models/Media.js
Normal file
10
server/src/models/Media.js
Normal file
@@ -0,0 +1,10 @@
|
||||
import TenantModel from '@/models/TenantModel';
|
||||
|
||||
export default class Media extends TenantModel {
|
||||
/**
|
||||
* Table name
|
||||
*/
|
||||
static get tableName() {
|
||||
return 'media';
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user