u
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import * as db from '../../db/agentprinter.js';
|
||||
import { emit } from '../sse.js';
|
||||
import { enrichAgentPrinter } from '../../services/companyPrinterService.js';
|
||||
|
||||
// #region Helpers
|
||||
|
||||
@@ -91,7 +92,8 @@ export function register(app, basePath = '') {
|
||||
try {
|
||||
const printeragentId = req.query?.printeragentId ? Number(req.query.printeragentId) : undefined;
|
||||
const agentprinters = await db.list(req.user.id, printeragentId);
|
||||
res.status(200).json({ agentprinters });
|
||||
const enriched = agentprinters.map((p) => enrichAgentPrinter(selected.companyId, p));
|
||||
res.status(200).json({ agentprinters: enriched });
|
||||
} catch (error) {
|
||||
const status = agentprinterErrorStatus(error);
|
||||
if (status === 500) console.error('List agentprinters failed:', error);
|
||||
@@ -236,7 +238,7 @@ export function register(app, basePath = '') {
|
||||
if (!agentprinter) {
|
||||
return res.status(404).json({ error: 'Agentprinter not found' });
|
||||
}
|
||||
res.status(200).json({ agentprinter });
|
||||
res.status(200).json({ agentprinter: enrichAgentPrinter(selected.companyId, agentprinter) });
|
||||
} catch (error) {
|
||||
const status = agentprinterErrorStatus(error);
|
||||
if (status === 500) console.error('Get agentprinter failed:', error);
|
||||
@@ -333,7 +335,7 @@ export function register(app, basePath = '') {
|
||||
try {
|
||||
const agentprinter = await db.update(req.user.id, payload);
|
||||
emit('agentprinter', { companyId: selected.companyId });
|
||||
res.status(200).json({ agentprinter });
|
||||
res.status(200).json({ agentprinter: enrichAgentPrinter(selected.companyId, agentprinter) });
|
||||
} catch (error) {
|
||||
const status = agentprinterErrorStatus(error);
|
||||
if (status === 500) console.error('Update agentprinter failed:', error);
|
||||
|
||||
@@ -57,6 +57,7 @@ export function register(app, basePath = '') {
|
||||
* | `printer` | `{}` | `POST /printer`, `PUT /printer/{id}`, `PATCH /printer/{id}`, `DELETE /printer/{id}` |
|
||||
* | `printeragent` | `{}` | `PUT /printeragent`, `PATCH /printeragent/{id}`, `DELETE /printeragent/{id}`, printer agent connect/disconnect/state |
|
||||
* | `agentprinter` | `{ printers }` or `{}` | `PATCH /agentprinter/{id}`, `PUT /agentprinter/order`, `DELETE /agentprinter/{id}`, agent state updates / printer discovery |
|
||||
* | `printerjob` | `{ job }` or `{ jobId, state }` | `POST /zpljob`, `POST /printerjob`, `POST /printerjob/{id}/cancel`, `DELETE /printerjob/{id}`, agent job state updates |
|
||||
* | `warehouse` | `{}` | `POST /warehouse`, `PUT /warehouse/{id}`, `PATCH /warehouse/{id}`, `DELETE /warehouse/{id}` |
|
||||
* | `supplier` | `{}` | `POST /supplier`, `PATCH /supplier/{id}`, `DELETE /supplier/{id}` |
|
||||
* | `vfs` | `{}` | VFS uploads and filesystem mutations such as `POST /vfs/upload`, `POST /vfs/mkdir`, `POST /vfs/rename`, `PUT /vfs/{*path}`, `DELETE /vfs/{*path}` |
|
||||
@@ -99,6 +100,12 @@ export function register(app, basePath = '') {
|
||||
* payload: "{}"
|
||||
* - name: printer
|
||||
* payload: "{}"
|
||||
* - name: printeragent
|
||||
* payload: "{}"
|
||||
* - name: agentprinter
|
||||
* payload: "{ printers } or {}"
|
||||
* - name: printerjob
|
||||
* payload: "{ job } or { jobId, state }"
|
||||
* - name: warehouse
|
||||
* payload: "{}"
|
||||
* - name: supplier
|
||||
|
||||
388
src/api/routes/printerjob.js
Normal file
388
src/api/routes/printerjob.js
Normal file
@@ -0,0 +1,388 @@
|
||||
import * as printerjobDb from '../../db/printerjob.js';
|
||||
import { emit } from '../sse.js';
|
||||
import { notifyAgentJob } from '../agent/index.js';
|
||||
|
||||
// #region Helpers
|
||||
|
||||
function requireSelectedCompany(req) {
|
||||
const companyId = req.user?.selectedCompanyId;
|
||||
if (companyId == null) {
|
||||
return { error: 'No company selected' };
|
||||
}
|
||||
return { companyId: Number(companyId) };
|
||||
}
|
||||
|
||||
function parsePositiveInt(value, field) {
|
||||
const id = Number(value);
|
||||
if (!Number.isInteger(id) || id <= 0) {
|
||||
return { error: `${field} must be a positive integer` };
|
||||
}
|
||||
return { id };
|
||||
}
|
||||
|
||||
function printerjobErrorStatus(error) {
|
||||
if (error.message === 'No company selected') return 400;
|
||||
if (error.message === 'Not authorized') return 403;
|
||||
if (error.message === 'Company not found') return 404;
|
||||
if (error.message === 'Agentprinter not found') return 404;
|
||||
if (error.message === 'Printerjob not found') return 404;
|
||||
if (error.message?.includes('is required')) return 400;
|
||||
if (error.message?.startsWith('Invalid printerjob')) return 400;
|
||||
return 500;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* Registers printerjob routes.
|
||||
* @param {import('express').Application} app
|
||||
* @param {string} [basePath='']
|
||||
*/
|
||||
export function register(app, basePath = '') {
|
||||
/**
|
||||
* @openapi
|
||||
* /printerjob:
|
||||
* get:
|
||||
* tags: [PrinterJob]
|
||||
* summary: List printer jobs
|
||||
* description: Lists printer jobs for the selected company, optionally filtered by agent printer or state.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* parameters:
|
||||
* - in: query
|
||||
* name: agentprinterId
|
||||
* schema:
|
||||
* type: integer
|
||||
* description: Optional agent printer ID filter
|
||||
* - in: query
|
||||
* name: state
|
||||
* schema:
|
||||
* type: string
|
||||
* enum: [pending, processing, completed, cancelled, failed]
|
||||
* description: Optional job state filter
|
||||
* responses:
|
||||
* 200:
|
||||
* description: List of printer jobs
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* printerjobs:
|
||||
* type: array
|
||||
* items:
|
||||
* $ref: '#/components/schemas/Printerjob'
|
||||
* 400:
|
||||
* description: No company selected
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
// #region GET /printerjob
|
||||
|
||||
app.get(`${basePath}/printerjob`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
if (selected.error) {
|
||||
return res.status(400).json({ error: selected.error });
|
||||
}
|
||||
|
||||
try {
|
||||
const agentprinterId = req.query?.agentprinterId ? Number(req.query.agentprinterId) : undefined;
|
||||
const state = req.query?.state ? String(req.query.state).trim().toLowerCase() : undefined;
|
||||
const printerjobs = await printerjobDb.list(req.user.id, { agentprinterId, state });
|
||||
res.status(200).json({ printerjobs });
|
||||
} catch (error) {
|
||||
const status = printerjobErrorStatus(error);
|
||||
if (status === 500) console.error('List printerjobs failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /printerjob/{id}:
|
||||
* get:
|
||||
* tags: [PrinterJob]
|
||||
* summary: Get printer job
|
||||
* description: Retrieves details of a specific printer job by ID.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Printer job details
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* printerjob:
|
||||
* $ref: '#/components/schemas/Printerjob'
|
||||
* 400:
|
||||
* description: Invalid ID or no company selected
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Printer job not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
// #region GET /printerjob/:id
|
||||
|
||||
app.get(`${basePath}/printerjob/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
if (selected.error) {
|
||||
return res.status(400).json({ error: selected.error });
|
||||
}
|
||||
|
||||
const parsed = parsePositiveInt(req.params.id, 'id');
|
||||
if (parsed.error) {
|
||||
return res.status(400).json({ error: parsed.error });
|
||||
}
|
||||
|
||||
try {
|
||||
const printerjob = await printerjobDb.getById(req.user.id, parsed.id);
|
||||
if (!printerjob) {
|
||||
return res.status(404).json({ error: 'Printerjob not found' });
|
||||
}
|
||||
res.status(200).json({ printerjob });
|
||||
} catch (error) {
|
||||
const status = printerjobErrorStatus(error);
|
||||
if (status === 500) console.error('Get printerjob failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /printerjob:
|
||||
* post:
|
||||
* tags: [PrinterJob]
|
||||
* summary: Create print job
|
||||
* description: Creates a new print job for an agent printer. Notifies the agent in real time.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-signals: [printerjob]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [agentprinterId, data]
|
||||
* properties:
|
||||
* agentprinterId:
|
||||
* type: integer
|
||||
* type:
|
||||
* type: string
|
||||
* example: "zpl"
|
||||
* data:
|
||||
* type: string
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Printer job created
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* printerjob:
|
||||
* $ref: '#/components/schemas/Printerjob'
|
||||
*/
|
||||
// #region POST /printerjob
|
||||
|
||||
app.post(`${basePath}/printerjob`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
if (selected.error) {
|
||||
return res.status(400).json({ error: selected.error });
|
||||
}
|
||||
|
||||
const rawApId = req.body?.agentprinterId ?? req.body?.agentprinterid;
|
||||
const parsedApId = parsePositiveInt(rawApId, 'agentprinterId');
|
||||
if (parsedApId.error) {
|
||||
return res.status(400).json({ error: parsedApId.error });
|
||||
}
|
||||
|
||||
const rawData = req.body?.data;
|
||||
if (!rawData || typeof rawData !== 'string' || !rawData.trim()) {
|
||||
return res.status(400).json({ error: 'data is required' });
|
||||
}
|
||||
|
||||
const type = typeof req.body?.type === 'string' && req.body.type.trim()
|
||||
? req.body.type.trim().toLowerCase()
|
||||
: undefined;
|
||||
|
||||
try {
|
||||
const job = await printerjobDb.create(req.user.id, {
|
||||
agentprinterId: parsedApId.id,
|
||||
type,
|
||||
data: rawData.trim(),
|
||||
});
|
||||
|
||||
if (job.printeragentId) {
|
||||
notifyAgentJob(job.printeragentId, 'created', job);
|
||||
}
|
||||
|
||||
emit('printerjob', { companyId: selected.companyId, data: { job } });
|
||||
res.status(201).json({ printerjob: job });
|
||||
} catch (error) {
|
||||
const status = printerjobErrorStatus(error);
|
||||
if (status === 500) console.error('Create printerjob failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /printerjob/{id}/cancel:
|
||||
* post:
|
||||
* tags: [PrinterJob]
|
||||
* summary: Cancel printer job
|
||||
* description: Marks a print job as cancelled and notifies the connected agent in real time.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-signals: [printerjob]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Printer job cancelled
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* printerjob:
|
||||
* $ref: '#/components/schemas/Printerjob'
|
||||
*/
|
||||
// #region POST /printerjob/:id/cancel
|
||||
|
||||
app.post(`${basePath}/printerjob/:id/cancel`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
if (selected.error) {
|
||||
return res.status(400).json({ error: selected.error });
|
||||
}
|
||||
|
||||
const parsed = parsePositiveInt(req.params.id, 'id');
|
||||
if (parsed.error) {
|
||||
return res.status(400).json({ error: parsed.error });
|
||||
}
|
||||
|
||||
try {
|
||||
const job = await printerjobDb.updateState(req.user.id, parsed.id, 'cancelled');
|
||||
|
||||
if (job.printeragentId) {
|
||||
notifyAgentJob(job.printeragentId, 'cancelled', job);
|
||||
}
|
||||
|
||||
emit('printerjob', { companyId: selected.companyId, data: { job } });
|
||||
res.status(200).json({ success: true, printerjob: job });
|
||||
} catch (error) {
|
||||
const status = printerjobErrorStatus(error);
|
||||
if (status === 500) console.error('Cancel printerjob failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* @openapi
|
||||
* /printerjob/{id}:
|
||||
* delete:
|
||||
* tags: [PrinterJob]
|
||||
* summary: Cancel and delete printer job
|
||||
* description: |
|
||||
* Cancels and deletes a print job. If the job was pending or processing, sends a cancellation
|
||||
* event to the connected printer agent before deletion.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-signals: [printerjob]
|
||||
* parameters:
|
||||
* - in: path
|
||||
* name: id
|
||||
* required: true
|
||||
* schema:
|
||||
* type: integer
|
||||
* responses:
|
||||
* 200:
|
||||
* description: Job cancelled and deleted
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* success:
|
||||
* type: boolean
|
||||
* deletedJob:
|
||||
* $ref: '#/components/schemas/Printerjob'
|
||||
*/
|
||||
// #region DELETE /printerjob/:id
|
||||
|
||||
app.delete(`${basePath}/printerjob/:id`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
if (selected.error) {
|
||||
return res.status(400).json({ error: selected.error });
|
||||
}
|
||||
|
||||
const parsed = parsePositiveInt(req.params.id, 'id');
|
||||
if (parsed.error) {
|
||||
return res.status(400).json({ error: parsed.error });
|
||||
}
|
||||
|
||||
try {
|
||||
const job = await printerjobDb.deleteJob(req.user.id, parsed.id);
|
||||
|
||||
if (job.printeragentId) {
|
||||
notifyAgentJob(job.printeragentId, 'cancelled', job);
|
||||
}
|
||||
|
||||
emit('printerjob', {
|
||||
companyId: selected.companyId,
|
||||
data: { jobId: job.id, state: 'cancelled' },
|
||||
});
|
||||
res.status(200).json({ success: true, deletedJob: job });
|
||||
} catch (error) {
|
||||
const status = printerjobErrorStatus(error);
|
||||
if (status === 500) console.error('Delete printerjob failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
}
|
||||
|
||||
176
src/api/routes/zpljob.js
Normal file
176
src/api/routes/zpljob.js
Normal file
@@ -0,0 +1,176 @@
|
||||
import * as printerjobDb from '../../db/printerjob.js';
|
||||
import { emit } from '../sse.js';
|
||||
import { notifyAgentJob } from '../agent/index.js';
|
||||
|
||||
// #region Helpers
|
||||
|
||||
function requireSelectedCompany(req) {
|
||||
const companyId = req.user?.selectedCompanyId;
|
||||
if (companyId == null) {
|
||||
return { error: 'No company selected' };
|
||||
}
|
||||
return { companyId: Number(companyId) };
|
||||
}
|
||||
|
||||
function parsePositiveInt(value, field) {
|
||||
const id = Number(value);
|
||||
if (!Number.isInteger(id) || id <= 0) {
|
||||
return { error: `${field} must be a positive integer` };
|
||||
}
|
||||
return { id };
|
||||
}
|
||||
|
||||
function printerjobErrorStatus(error) {
|
||||
if (error.message === 'No company selected') return 400;
|
||||
if (error.message === 'Not authorized') return 403;
|
||||
if (error.message === 'Company not found') return 404;
|
||||
if (error.message === 'Agentprinter not found') return 404;
|
||||
if (error.message === 'Printerjob not found') return 404;
|
||||
if (error.message?.includes('is required')) return 400;
|
||||
if (error.message?.startsWith('Invalid printerjob')) return 400;
|
||||
return 500;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adjusts or inserts the ZPL print quantity command (^PQ) in the ZPL payload.
|
||||
*
|
||||
* @param {string} data - Raw ZPL string.
|
||||
* @param {number|string} [copiescount=1] - Number of copies.
|
||||
* @returns {string}
|
||||
*/
|
||||
export function applyZplCopies(data, copiescount) {
|
||||
if (!data || typeof data !== 'string') return data;
|
||||
const copies = Number(copiescount);
|
||||
if (!Number.isInteger(copies) || copies <= 1) return data;
|
||||
|
||||
if (/\^PQ\d+/i.test(data)) {
|
||||
return data.replace(/\^PQ\d+/gi, `^PQ${copies}`);
|
||||
}
|
||||
|
||||
if (/\^XZ/i.test(data)) {
|
||||
return data.replace(/\^XZ/gi, `^PQ${copies}\n^XZ`);
|
||||
}
|
||||
|
||||
return `${data}\n^PQ${copies}`;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
/**
|
||||
* Registers zpljob routes.
|
||||
* @param {import('express').Application} app
|
||||
* @param {string} [basePath='']
|
||||
*/
|
||||
export function register(app, basePath = '') {
|
||||
/**
|
||||
* @openapi
|
||||
* /zpljob:
|
||||
* post:
|
||||
* tags: [PrinterJob]
|
||||
* summary: Create ZPL print job
|
||||
* description: |
|
||||
* Issues a new ZPL print job to an agent printer. If copiescount > 1, the ZPL quantity command (^PQ)
|
||||
* is automatically applied. The connected printer agent receives an immediate real-time event.
|
||||
* security:
|
||||
* - cookieAuth: []
|
||||
* x-signals: [printerjob]
|
||||
* requestBody:
|
||||
* required: true
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* required: [agentprinterId, data]
|
||||
* properties:
|
||||
* agentprinterId:
|
||||
* type: integer
|
||||
* description: Target agent printer ID
|
||||
* example: 1
|
||||
* copiescount:
|
||||
* type: integer
|
||||
* description: Optional copy count (defaults to 1)
|
||||
* default: 1
|
||||
* example: 2
|
||||
* data:
|
||||
* type: string
|
||||
* description: Raw ZPL string
|
||||
* example: "^XA^FO50,50^ADN,36,20^FDHello Zebra^FS^XZ"
|
||||
* responses:
|
||||
* 201:
|
||||
* description: Print job created
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* type: object
|
||||
* properties:
|
||||
* printerjob:
|
||||
* $ref: '#/components/schemas/Printerjob'
|
||||
* 400:
|
||||
* description: Invalid input or missing fields
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 401:
|
||||
* description: Unauthorized
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
* 404:
|
||||
* description: Agent printer not found
|
||||
* content:
|
||||
* application/json:
|
||||
* schema:
|
||||
* $ref: '#/components/schemas/Error'
|
||||
*/
|
||||
// #region POST /zpljob
|
||||
|
||||
app.post(`${basePath}/zpljob`, async (req, res) => {
|
||||
const selected = requireSelectedCompany(req);
|
||||
if (selected.error) {
|
||||
return res.status(400).json({ error: selected.error });
|
||||
}
|
||||
|
||||
const rawApId = req.body?.agentprinterId ?? req.body?.agentprinterid;
|
||||
const parsedApId = parsePositiveInt(rawApId, 'agentprinterId');
|
||||
if (parsedApId.error) {
|
||||
return res.status(400).json({ error: parsedApId.error });
|
||||
}
|
||||
|
||||
const rawData = req.body?.data;
|
||||
if (!rawData || typeof rawData !== 'string' || !rawData.trim()) {
|
||||
return res.status(400).json({ error: 'data is required' });
|
||||
}
|
||||
|
||||
const rawCopies = req.body?.copiescount ?? req.body?.copiesCount ?? req.body?.copies;
|
||||
const copies = rawCopies != null ? Number(rawCopies) : 1;
|
||||
if (!Number.isInteger(copies) || copies <= 0) {
|
||||
return res.status(400).json({ error: 'copiescount must be a positive integer' });
|
||||
}
|
||||
|
||||
const finalData = applyZplCopies(rawData.trim(), copies);
|
||||
|
||||
try {
|
||||
const job = await printerjobDb.create(req.user.id, {
|
||||
agentprinterId: parsedApId.id,
|
||||
type: 'zpl',
|
||||
data: finalData,
|
||||
});
|
||||
|
||||
if (job.printeragentId) {
|
||||
notifyAgentJob(job.printeragentId, 'created', job);
|
||||
}
|
||||
|
||||
emit('printerjob', { companyId: selected.companyId, data: { job } });
|
||||
res.status(201).json({ printerjob: job });
|
||||
} catch (error) {
|
||||
const status = printerjobErrorStatus(error);
|
||||
if (status === 500) console.error('Create zpljob failed:', error);
|
||||
res.status(status).json({ error: error.message });
|
||||
}
|
||||
});
|
||||
|
||||
// #endregion
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user