multipart
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
# Server
|
# Server
|
||||||
PORT=4208
|
PORT=4208
|
||||||
|
BODY_LIMIT=100mb
|
||||||
|
|
||||||
# CORS — comma-separated allowed origins
|
# CORS — comma-separated allowed origins
|
||||||
CORS_ORIGINS=http://localhost:5173
|
CORS_ORIGINS=http://localhost:5173
|
||||||
|
|||||||
@@ -4480,7 +4480,7 @@
|
|||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "Attached documents (pdf or txt) stored with the delivery\nand returned in `files` on every delivery response.\n",
|
"description": "Attached documents (pdf or txt) stored with the delivery\nand returned in `files` on every delivery response.\nThumbnails (AVIF) are generated server-side.\n",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/DeliveryFileInput"
|
"$ref": "#/components/schemas/DeliveryFileInput"
|
||||||
}
|
}
|
||||||
@@ -4509,6 +4509,28 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"data"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "JSON string containing manifest header and line items ({ externalNumber, date, supplierId, lines, filesMeta? }).\n"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "binary"
|
||||||
|
},
|
||||||
|
"description": "Raw binary PDF or text files attached natively via standard HTTP multipart upload."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -4702,7 +4724,7 @@
|
|||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"type": "array",
|
"type": "array",
|
||||||
"description": "Appends attachments (pdf or txt) to the delivery.\nOmit to leave existing files unchanged.\n",
|
"description": "Appends attachments (pdf or txt) to the delivery.\nOmit to leave existing files unchanged.\nThumbnails (AVIF) are generated server-side.\n",
|
||||||
"items": {
|
"items": {
|
||||||
"$ref": "#/components/schemas/DeliveryFileInput"
|
"$ref": "#/components/schemas/DeliveryFileInput"
|
||||||
}
|
}
|
||||||
@@ -4713,6 +4735,25 @@
|
|||||||
"lines": []
|
"lines": []
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"data": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "JSON string containing patch fields ({ externalNumber, date, supplierId, lines, filesMeta? }).\n"
|
||||||
|
},
|
||||||
|
"files": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string",
|
||||||
|
"format": "binary"
|
||||||
|
},
|
||||||
|
"description": "Raw binary PDF or text files to append to the delivery."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -10047,7 +10088,7 @@
|
|||||||
},
|
},
|
||||||
"DeliveryFile": {
|
"DeliveryFile": {
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"description": "Attachment metadata (binary content is served via the attachment content endpoints)",
|
"description": "Attachment metadata (binary content is served via the attachment content endpoints). Thumbnails (AVIF) are generated server-side.",
|
||||||
"required": [
|
"required": [
|
||||||
"id",
|
"id",
|
||||||
"manifestId",
|
"manifestId",
|
||||||
@@ -10097,7 +10138,8 @@
|
|||||||
"thumbavif": {
|
"thumbavif": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"format": "byte",
|
"format": "byte",
|
||||||
"nullable": true
|
"nullable": true,
|
||||||
|
"description": "Base64-encoded AVIF thumbnail generated server-side (null for text files)"
|
||||||
},
|
},
|
||||||
"thumbx": {
|
"thumbx": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
@@ -10124,7 +10166,7 @@
|
|||||||
"name",
|
"name",
|
||||||
"content"
|
"content"
|
||||||
],
|
],
|
||||||
"description": "Attachment input. Only application/pdf and text/plain are accepted.",
|
"description": "Attachment input. Only application/pdf and text/plain are accepted. Thumbnails are generated server-side and must not be supplied by the client.",
|
||||||
"properties": {
|
"properties": {
|
||||||
"mimetype": {
|
"mimetype": {
|
||||||
"type": "string",
|
"type": "string",
|
||||||
|
|||||||
@@ -38,6 +38,14 @@ The manifest `number` is allocated automatically via
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Body (`multipart/form-data`)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `data` | yes | string | JSON string containing manifest header and line items ({ externalNumber, date, supplierId, lines, filesMeta? }).
|
||||||
|
|
|
||||||
|
| `files` | no | file | Raw binary PDF or text files attached natively via standard HTTP multipart upload. |
|
||||||
|
|
||||||
## Response `201`
|
## Response `201`
|
||||||
|
|
||||||
Created delivery
|
Created delivery
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ replaces all lines; if omitted, leaves lines unchanged.
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Body (`multipart/form-data`)
|
||||||
|
|
||||||
|
| Field | Required | Type | Description |
|
||||||
|
|-------|----------|------|-------------|
|
||||||
|
| `data` | no | string | JSON string containing patch fields ({ externalNumber, date, supplierId, lines, filesMeta? }).
|
||||||
|
|
|
||||||
|
| `files` | no | file | Raw binary PDF or text files to append to the delivery. |
|
||||||
|
|
||||||
## Response `200`
|
## Response `200`
|
||||||
|
|
||||||
Updated delivery
|
Updated delivery
|
||||||
|
|||||||
@@ -153,7 +153,6 @@ function renderRequestBody(requestBody) {
|
|||||||
lines.push('');
|
lines.push('');
|
||||||
lines.push(formatSchema(jsonSchema, spec.components));
|
lines.push(formatSchema(jsonSchema, spec.components));
|
||||||
lines.push('');
|
lines.push('');
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// multipart/form-data — render fields as a table when properties are present
|
// multipart/form-data — render fields as a table when properties are present
|
||||||
@@ -166,29 +165,30 @@ function renderRequestBody(requestBody) {
|
|||||||
lines.push('| Field | Required | Type | Description |');
|
lines.push('| Field | Required | Type | Description |');
|
||||||
lines.push('|-------|----------|------|-------------|');
|
lines.push('|-------|----------|------|-------------|');
|
||||||
for (const [name, prop] of Object.entries(schema.properties)) {
|
for (const [name, prop] of Object.entries(schema.properties)) {
|
||||||
const type = prop.format === 'binary' ? 'file' : (prop.type || '');
|
const type = prop.format === 'binary' || prop.items?.format === 'binary' ? 'file' : (prop.type || '');
|
||||||
lines.push(`| \`${name}\` | ${required.has(name) ? 'yes' : 'no'} | ${type} | ${prop.description || ''} |`);
|
lines.push(`| \`${name}\` | ${required.has(name) ? 'yes' : 'no'} | ${type} | ${prop.description || ''} |`);
|
||||||
}
|
}
|
||||||
lines.push('');
|
lines.push('');
|
||||||
return lines;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lines.push('## Body');
|
if (!jsonSchema && !multipart?.schema?.properties) {
|
||||||
lines.push('');
|
lines.push('## Body');
|
||||||
for (const type of types) {
|
|
||||||
const schema = requestBody.content[type]?.schema;
|
|
||||||
if (schema?.format === 'binary') {
|
|
||||||
lines.push(`Raw binary (\`${type}\`).`);
|
|
||||||
} else {
|
|
||||||
lines.push(`\`${type}\``);
|
|
||||||
if (schema) {
|
|
||||||
lines.push('');
|
|
||||||
lines.push(formatSchema(schema, spec.components));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
lines.push('');
|
lines.push('');
|
||||||
|
for (const type of types) {
|
||||||
|
const schema = requestBody.content[type]?.schema;
|
||||||
|
if (schema?.format === 'binary') {
|
||||||
|
lines.push(`Raw binary (\`${type}\`).`);
|
||||||
|
} else {
|
||||||
|
lines.push(`\`${type}\``);
|
||||||
|
if (schema) {
|
||||||
|
lines.push('');
|
||||||
|
lines.push(formatSchema(schema, spec.components));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lines.push('');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return lines;
|
return lines.length ? lines : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function docsFileFor(apiPath, method) {
|
function docsFileFor(apiPath, method) {
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ async function createApi() {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
app.use(corsMiddleware);
|
app.use(corsMiddleware);
|
||||||
app.use(express.json());
|
app.use(express.json({ limit: env.bodyLimit }));
|
||||||
app.use(cookieParser());
|
app.use(cookieParser());
|
||||||
app.use(loggerMiddleware);
|
app.use(loggerMiddleware);
|
||||||
app.use(authMiddleware);
|
app.use(authMiddleware);
|
||||||
@@ -79,6 +79,16 @@ async function createApi() {
|
|||||||
|
|
||||||
await loadRoutes(app, '/api');
|
await loadRoutes(app, '/api');
|
||||||
|
|
||||||
|
app.use((err, _req, res, next) => {
|
||||||
|
if (err.type === 'entity.too.large') {
|
||||||
|
return res.status(413).json({ error: 'Payload too large' });
|
||||||
|
}
|
||||||
|
if (err instanceof SyntaxError && err.status === 400 && 'body' in err) {
|
||||||
|
return res.status(400).json({ error: 'Invalid JSON body' });
|
||||||
|
}
|
||||||
|
next(err);
|
||||||
|
});
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -435,7 +435,7 @@
|
|||||||
* $ref: '#/components/schemas/DeliveryFile'
|
* $ref: '#/components/schemas/DeliveryFile'
|
||||||
* DeliveryFile:
|
* DeliveryFile:
|
||||||
* type: object
|
* type: object
|
||||||
* description: Attachment metadata (binary content is served via the attachment content endpoints)
|
* description: Attachment metadata (binary content is served via the attachment content endpoints). Thumbnails (AVIF) are generated server-side.
|
||||||
* required: [id, manifestId, fileId, sort, hash, mimetype, name, size]
|
* required: [id, manifestId, fileId, sort, hash, mimetype, name, size]
|
||||||
* properties:
|
* properties:
|
||||||
* id:
|
* id:
|
||||||
@@ -467,6 +467,7 @@
|
|||||||
* type: string
|
* type: string
|
||||||
* format: byte
|
* format: byte
|
||||||
* nullable: true
|
* nullable: true
|
||||||
|
* description: Base64-encoded AVIF thumbnail generated server-side (null for text files)
|
||||||
* thumbx:
|
* thumbx:
|
||||||
* type: integer
|
* type: integer
|
||||||
* nullable: true
|
* nullable: true
|
||||||
@@ -482,7 +483,7 @@
|
|||||||
* DeliveryFileInput:
|
* DeliveryFileInput:
|
||||||
* type: object
|
* type: object
|
||||||
* required: [mimetype, name, content]
|
* required: [mimetype, name, content]
|
||||||
* description: Attachment input. Only application/pdf and text/plain are accepted.
|
* description: Attachment input. Only application/pdf and text/plain are accepted. Thumbnails are generated server-side and must not be supplied by the client.
|
||||||
* properties:
|
* properties:
|
||||||
* mimetype:
|
* mimetype:
|
||||||
* type: string
|
* type: string
|
||||||
|
|||||||
@@ -1,5 +1,78 @@
|
|||||||
|
import multer from 'multer';
|
||||||
import * as db from '../../db/delivery.js';
|
import * as db from '../../db/delivery.js';
|
||||||
|
|
||||||
|
// Shared memory storage for delivery attachments (max 50 MB)
|
||||||
|
const multerUpload = multer({
|
||||||
|
storage: multer.memoryStorage(),
|
||||||
|
limits: { fileSize: 50 * 1024 * 1024 },
|
||||||
|
fileFilter: (_req, file, cb) => {
|
||||||
|
const name = file.originalname?.toLowerCase() || '';
|
||||||
|
const isPdf = file.mimetype === 'application/pdf' || name.endsWith('.pdf');
|
||||||
|
const isText = file.mimetype.startsWith('text/') || /\.(txt|text|md|csv)$/.test(name);
|
||||||
|
if (isPdf || isText) {
|
||||||
|
cb(null, true);
|
||||||
|
} else {
|
||||||
|
cb(new Error('Only PDF or text files are accepted as attachments'));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}).array('files');
|
||||||
|
|
||||||
|
function uploadDeliveryFiles(req, res, next) {
|
||||||
|
multerUpload(req, res, (err) => {
|
||||||
|
if (err) {
|
||||||
|
if (err.code === 'LIMIT_FILE_SIZE') {
|
||||||
|
return res.status(413).json({ error: 'File too large (max 50 MB)' });
|
||||||
|
}
|
||||||
|
return res.status(400).json({ error: err.message });
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to normalize payload from either multipart/form-data or application/json
|
||||||
|
*/
|
||||||
|
function parseDeliveryPayload(req) {
|
||||||
|
let payload = req.body ?? {};
|
||||||
|
// If sent as multipart/form-data with a JSON `data` field:
|
||||||
|
if (typeof payload.data === 'string') {
|
||||||
|
try {
|
||||||
|
payload = JSON.parse(payload.data);
|
||||||
|
} catch {
|
||||||
|
throw new Error('Invalid JSON in "data" field');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof payload !== 'object' || payload === null) {
|
||||||
|
throw new Error('Invalid delivery');
|
||||||
|
}
|
||||||
|
|
||||||
|
const { externalNumber, date, supplierId, lines } = payload;
|
||||||
|
let files = payload.files;
|
||||||
|
// If binary files were uploaded via multer:
|
||||||
|
if (Array.isArray(req.files) && req.files.length > 0) {
|
||||||
|
const filesMeta = Array.isArray(payload.filesMeta)
|
||||||
|
? payload.filesMeta
|
||||||
|
: (Array.isArray(payload.files) ? payload.files : []);
|
||||||
|
const metaByName = new Map(
|
||||||
|
filesMeta.filter((m) => m?.name).map((m) => [m.name, m])
|
||||||
|
);
|
||||||
|
files = req.files.map((f, idx) => {
|
||||||
|
const metaObj = metaByName.get(f.originalname) || filesMeta[idx] || {};
|
||||||
|
const name = f.originalname || '';
|
||||||
|
const isPdf = f.mimetype === 'application/pdf' || name.toLowerCase().endsWith('.pdf');
|
||||||
|
const mimetype = isPdf ? 'application/pdf' : 'text/plain';
|
||||||
|
return {
|
||||||
|
mimetype,
|
||||||
|
name: f.originalname,
|
||||||
|
content: f.buffer, // Raw Buffer directly accepted by libdashstore/attachments.js
|
||||||
|
size: f.size,
|
||||||
|
meta: metaObj.meta ?? null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return { payload, externalNumber, date, supplierId, lines, files };
|
||||||
|
}
|
||||||
|
|
||||||
function requireSelectedCompany(req) {
|
function requireSelectedCompany(req) {
|
||||||
const companyId = req.user.selectedCompanyId;
|
const companyId = req.user.selectedCompanyId;
|
||||||
if (companyId == null) {
|
if (companyId == null) {
|
||||||
@@ -17,6 +90,9 @@ function deliveryErrorStatus(error) {
|
|||||||
if (error.message === 'Article not found') return 404;
|
if (error.message === 'Article not found') return 404;
|
||||||
if (error.message === 'Invalid delivery') return 400;
|
if (error.message === 'Invalid delivery') return 400;
|
||||||
if (error.message === 'Invalid lines') return 400;
|
if (error.message === 'Invalid lines') return 400;
|
||||||
|
if (error.message === 'Invalid file') return 400;
|
||||||
|
if (error.message === 'Invalid JSON in "data" field') return 400;
|
||||||
|
if (error.message === 'Only PDF or text files are accepted as attachments') return 400;
|
||||||
return 500;
|
return 500;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,6 +188,7 @@ export function register(app, basePath = '') {
|
|||||||
* description: |
|
* description: |
|
||||||
* Attached documents (pdf or txt) stored with the delivery
|
* Attached documents (pdf or txt) stored with the delivery
|
||||||
* and returned in `files` on every delivery response.
|
* and returned in `files` on every delivery response.
|
||||||
|
* Thumbnails (AVIF) are generated server-side.
|
||||||
* items:
|
* items:
|
||||||
* $ref: '#/components/schemas/DeliveryFileInput'
|
* $ref: '#/components/schemas/DeliveryFileInput'
|
||||||
* example:
|
* example:
|
||||||
@@ -129,6 +206,21 @@ export function register(app, basePath = '') {
|
|||||||
* - mimetype: application/pdf
|
* - mimetype: application/pdf
|
||||||
* name: delivery-note.pdf
|
* name: delivery-note.pdf
|
||||||
* content: '<base64 encoded content>'
|
* content: '<base64 encoded content>'
|
||||||
|
* multipart/form-data:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* required: [data]
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: string
|
||||||
|
* description: |
|
||||||
|
* JSON string containing manifest header and line items ({ externalNumber, date, supplierId, lines, filesMeta? }).
|
||||||
|
* files:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* type: string
|
||||||
|
* format: binary
|
||||||
|
* description: Raw binary PDF or text files attached natively via standard HTTP multipart upload.
|
||||||
* responses:
|
* responses:
|
||||||
* 201:
|
* 201:
|
||||||
* description: Created delivery
|
* description: Created delivery
|
||||||
@@ -180,15 +272,14 @@ export function register(app, basePath = '') {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.post(`${basePath}/delivery`, async (req, res) => {
|
app.post(`${basePath}/delivery`, uploadDeliveryFiles, async (req, res) => {
|
||||||
const selected = requireSelectedCompany(req);
|
const selected = requireSelectedCompany(req);
|
||||||
if (selected.error) {
|
if (selected.error) {
|
||||||
return res.status(400).json({ error: selected.error });
|
return res.status(400).json({ error: selected.error });
|
||||||
}
|
}
|
||||||
|
|
||||||
const { externalNumber, date, supplierId, lines, files } = req.body ?? {};
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const { externalNumber, date, supplierId, lines, files } = parseDeliveryPayload(req);
|
||||||
const delivery = await db.create(req.user.id, {
|
const delivery = await db.create(req.user.id, {
|
||||||
externalNumber,
|
externalNumber,
|
||||||
date,
|
date,
|
||||||
@@ -297,11 +388,26 @@ export function register(app, basePath = '') {
|
|||||||
* description: |
|
* description: |
|
||||||
* Appends attachments (pdf or txt) to the delivery.
|
* Appends attachments (pdf or txt) to the delivery.
|
||||||
* Omit to leave existing files unchanged.
|
* Omit to leave existing files unchanged.
|
||||||
|
* Thumbnails (AVIF) are generated server-side.
|
||||||
* items:
|
* items:
|
||||||
* $ref: '#/components/schemas/DeliveryFileInput'
|
* $ref: '#/components/schemas/DeliveryFileInput'
|
||||||
* example:
|
* example:
|
||||||
* externalNumber: DHL-124
|
* externalNumber: DHL-124
|
||||||
* lines: []
|
* lines: []
|
||||||
|
* multipart/form-data:
|
||||||
|
* schema:
|
||||||
|
* type: object
|
||||||
|
* properties:
|
||||||
|
* data:
|
||||||
|
* type: string
|
||||||
|
* description: |
|
||||||
|
* JSON string containing patch fields ({ externalNumber, date, supplierId, lines, filesMeta? }).
|
||||||
|
* files:
|
||||||
|
* type: array
|
||||||
|
* items:
|
||||||
|
* type: string
|
||||||
|
* format: binary
|
||||||
|
* description: Raw binary PDF or text files to append to the delivery.
|
||||||
* responses:
|
* responses:
|
||||||
* 200:
|
* 200:
|
||||||
* description: Updated delivery
|
* description: Updated delivery
|
||||||
@@ -399,7 +505,7 @@ export function register(app, basePath = '') {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.patch(`${basePath}/delivery/:id`, async (req, res) => {
|
app.patch(`${basePath}/delivery/:id`, uploadDeliveryFiles, async (req, res) => {
|
||||||
const selected = requireSelectedCompany(req);
|
const selected = requireSelectedCompany(req);
|
||||||
if (selected.error) {
|
if (selected.error) {
|
||||||
return res.status(400).json({ error: selected.error });
|
return res.status(400).json({ error: selected.error });
|
||||||
@@ -410,25 +516,25 @@ export function register(app, basePath = '') {
|
|||||||
return res.status(400).json({ error: 'id is required' });
|
return res.status(400).json({ error: 'id is required' });
|
||||||
}
|
}
|
||||||
|
|
||||||
const body = req.body ?? {};
|
|
||||||
const patch = {};
|
|
||||||
if (Object.prototype.hasOwnProperty.call(body, 'externalNumber')) {
|
|
||||||
patch.externalNumber = body.externalNumber;
|
|
||||||
}
|
|
||||||
if (Object.prototype.hasOwnProperty.call(body, 'date')) {
|
|
||||||
patch.date = body.date;
|
|
||||||
}
|
|
||||||
if (Object.prototype.hasOwnProperty.call(body, 'supplierId')) {
|
|
||||||
patch.supplierId = body.supplierId;
|
|
||||||
}
|
|
||||||
if (Object.prototype.hasOwnProperty.call(body, 'lines')) {
|
|
||||||
patch.lines = body.lines;
|
|
||||||
}
|
|
||||||
if (Object.prototype.hasOwnProperty.call(body, 'files')) {
|
|
||||||
patch.files = body.files;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const parsed = parseDeliveryPayload(req);
|
||||||
|
const patch = {};
|
||||||
|
if (Object.prototype.hasOwnProperty.call(parsed.payload, 'externalNumber')) {
|
||||||
|
patch.externalNumber = parsed.externalNumber;
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(parsed.payload, 'date')) {
|
||||||
|
patch.date = parsed.date;
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(parsed.payload, 'supplierId')) {
|
||||||
|
patch.supplierId = parsed.supplierId;
|
||||||
|
}
|
||||||
|
if (Object.prototype.hasOwnProperty.call(parsed.payload, 'lines')) {
|
||||||
|
patch.lines = parsed.lines;
|
||||||
|
}
|
||||||
|
if (parsed.files !== undefined) {
|
||||||
|
patch.files = parsed.files;
|
||||||
|
}
|
||||||
|
|
||||||
const delivery = await db.update(req.user.id, id, patch);
|
const delivery = await db.update(req.user.id, id, patch);
|
||||||
res.status(200).json({ delivery });
|
res.status(200).json({ delivery });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ dotenv.config({ path: resolve(__dirname, '../../.env'), quiet: true });
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
port: Number(process.env.PORT) || 4208,
|
port: Number(process.env.PORT) || 4208,
|
||||||
|
bodyLimit: process.env.BODY_LIMIT || '100mb',
|
||||||
corsOrigins: (process.env.CORS_ORIGINS || 'http://localhost:5173')
|
corsOrigins: (process.env.CORS_ORIGINS || 'http://localhost:5173')
|
||||||
.split(',')
|
.split(',')
|
||||||
.map((s) => s.trim())
|
.map((s) => s.trim())
|
||||||
|
|||||||
Reference in New Issue
Block a user