@ruiapp/rapid-core 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/http/formDataParser.d.ts +22 -0
- package/dist/core/providers/runtimeProvider.d.ts +4 -0
- package/dist/core/routeContext.d.ts +0 -1
- package/dist/index.js +320 -124
- package/dist/plugins/fileManager/httpHandlers/downloadDocument.d.ts +4 -0
- package/dist/plugins/fileManager/httpHandlers/downloadFile.d.ts +4 -0
- package/dist/plugins/fileManager/httpHandlers/uploadFile.d.ts +4 -0
- package/dist/plugins/fileManager/mod.d.ts +15 -0
- package/dist/utilities/fsUtility.d.ts +6 -0
- package/package.json +3 -2
- package/src/core/http/formDataParser.ts +101 -0
- package/src/core/pluginManager.ts +2 -0
- package/src/core/providers/runtimeProvider.ts +5 -0
- package/src/core/request.ts +2 -1
- package/src/core/routeContext.ts +0 -1
- package/src/core/routesBuilder.ts +2 -14
- package/src/plugins/fileManager/httpHandlers/downloadDocument.ts +40 -0
- package/src/plugins/fileManager/httpHandlers/downloadFile.ts +32 -0
- package/src/plugins/fileManager/httpHandlers/uploadFile.ts +37 -0
- package/src/plugins/fileManager/mod.ts +48 -0
- package/src/utilities/fsUtility.ts +62 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export type BodyData = Record<string, string | File | (string | File)[]>;
|
|
2
|
+
export type ParseBodyOptions = {
|
|
3
|
+
/**
|
|
4
|
+
* Parse all fields with multiple values should be parsed as an array.
|
|
5
|
+
* @default false
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* const data = new FormData()
|
|
9
|
+
* data.append('file', 'aaa')
|
|
10
|
+
* data.append('file', 'bbb')
|
|
11
|
+
* data.append('message', 'hello')
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* If `all` is `false`:
|
|
15
|
+
* parseBody should return `{ file: 'bbb', message: 'hello' }`
|
|
16
|
+
*
|
|
17
|
+
* If `all` is `true`:
|
|
18
|
+
* parseBody should return `{ file: ['aaa', 'bbb'], message: 'hello' }`
|
|
19
|
+
*/
|
|
20
|
+
all?: boolean;
|
|
21
|
+
};
|
|
22
|
+
export declare const parseFormDataBody: <T extends BodyData = BodyData>(request: Request, options?: ParseBodyOptions) => Promise<T>;
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
4
|
|
|
5
5
|
var _ = require('lodash');
|
|
6
|
+
var path = require('path');
|
|
7
|
+
var fs = require('fs');
|
|
8
|
+
var uuid = require('uuid');
|
|
6
9
|
var Router = require('koa-tree-router');
|
|
7
10
|
var qs = require('qs');
|
|
8
11
|
var jsonwebtoken = require('jsonwebtoken');
|
|
@@ -30,6 +33,8 @@ function _interopNamespace(e) {
|
|
|
30
33
|
}
|
|
31
34
|
|
|
32
35
|
var ___namespace = /*#__PURE__*/_interopNamespace(_);
|
|
36
|
+
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
37
|
+
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
33
38
|
var Router__default = /*#__PURE__*/_interopDefaultLegacy(Router);
|
|
34
39
|
var qs__default = /*#__PURE__*/_interopDefaultLegacy(qs);
|
|
35
40
|
var crypto__default = /*#__PURE__*/_interopDefaultLegacy(crypto);
|
|
@@ -1034,20 +1039,20 @@ async function updateEntityById(server, dataAccessor, options) {
|
|
|
1034
1039
|
return updatedEntity;
|
|
1035
1040
|
}
|
|
1036
1041
|
|
|
1037
|
-
const code$
|
|
1038
|
-
async function handler$
|
|
1042
|
+
const code$p = "listMetaModels";
|
|
1043
|
+
async function handler$j(plugin, ctx, options) {
|
|
1039
1044
|
const { applicationConfig } = ctx;
|
|
1040
1045
|
ctx.output = { list: applicationConfig.models };
|
|
1041
1046
|
}
|
|
1042
1047
|
|
|
1043
1048
|
var listMetaModels = /*#__PURE__*/Object.freeze({
|
|
1044
1049
|
__proto__: null,
|
|
1045
|
-
code: code$
|
|
1046
|
-
handler: handler$
|
|
1050
|
+
code: code$p,
|
|
1051
|
+
handler: handler$j
|
|
1047
1052
|
});
|
|
1048
1053
|
|
|
1049
|
-
const code$
|
|
1050
|
-
async function handler$
|
|
1054
|
+
const code$o = "getMetaModelDetail";
|
|
1055
|
+
async function handler$i(plugin, ctx, options) {
|
|
1051
1056
|
const { server, input } = ctx;
|
|
1052
1057
|
const model = server.getModel(input);
|
|
1053
1058
|
ctx.output = model;
|
|
@@ -1055,33 +1060,33 @@ async function handler$f(plugin, ctx, options) {
|
|
|
1055
1060
|
|
|
1056
1061
|
var getMetaModelDetail = /*#__PURE__*/Object.freeze({
|
|
1057
1062
|
__proto__: null,
|
|
1058
|
-
code: code$
|
|
1059
|
-
handler: handler$
|
|
1063
|
+
code: code$o,
|
|
1064
|
+
handler: handler$i
|
|
1060
1065
|
});
|
|
1061
1066
|
|
|
1062
1067
|
/**
|
|
1063
1068
|
* Meta manager plugin
|
|
1064
1069
|
*/
|
|
1065
|
-
const code$
|
|
1066
|
-
const description$
|
|
1067
|
-
const extendingAbilities$
|
|
1068
|
-
const configurableTargets$
|
|
1069
|
-
const configurations$
|
|
1070
|
-
let _plugin$
|
|
1071
|
-
async function initPlugin$
|
|
1072
|
-
_plugin$
|
|
1073
|
-
}
|
|
1074
|
-
async function registerHttpHandlers$
|
|
1075
|
-
server.registerHttpHandler(_plugin$
|
|
1076
|
-
server.registerHttpHandler(_plugin$
|
|
1077
|
-
}
|
|
1078
|
-
async function registerEventHandlers$
|
|
1070
|
+
const code$n = "metaManager";
|
|
1071
|
+
const description$5 = "metaManager";
|
|
1072
|
+
const extendingAbilities$5 = [];
|
|
1073
|
+
const configurableTargets$5 = [];
|
|
1074
|
+
const configurations$5 = [];
|
|
1075
|
+
let _plugin$5;
|
|
1076
|
+
async function initPlugin$5(plugin, server) {
|
|
1077
|
+
_plugin$5 = plugin;
|
|
1078
|
+
}
|
|
1079
|
+
async function registerHttpHandlers$5(server) {
|
|
1080
|
+
server.registerHttpHandler(_plugin$5, listMetaModels);
|
|
1081
|
+
server.registerHttpHandler(_plugin$5, getMetaModelDetail);
|
|
1082
|
+
}
|
|
1083
|
+
async function registerEventHandlers$4(server) {
|
|
1079
1084
|
server.registerEventHandler("entity.create", handleEntityCreateEvent.bind(null, server));
|
|
1080
1085
|
server.registerEventHandler("entity.update", handleEntityUpdateEvent.bind(null, server));
|
|
1081
1086
|
server.registerEventHandler("entity.delete", handleEntityDeleteEvent.bind(null, server));
|
|
1082
1087
|
}
|
|
1083
1088
|
async function handleEntityCreateEvent(server, sender, payload) {
|
|
1084
|
-
if (sender === _plugin$
|
|
1089
|
+
if (sender === _plugin$5) {
|
|
1085
1090
|
return;
|
|
1086
1091
|
}
|
|
1087
1092
|
if (payload.namespace === "meta" && payload.modelSingularCode === "model") {
|
|
@@ -1089,7 +1094,7 @@ async function handleEntityCreateEvent(server, sender, payload) {
|
|
|
1089
1094
|
}
|
|
1090
1095
|
}
|
|
1091
1096
|
async function handleEntityUpdateEvent(server, sender, payload) {
|
|
1092
|
-
if (sender === _plugin$
|
|
1097
|
+
if (sender === _plugin$5) {
|
|
1093
1098
|
return;
|
|
1094
1099
|
}
|
|
1095
1100
|
if (payload.namespace === "meta" && payload.modelSingularCode === "model") {
|
|
@@ -1097,7 +1102,7 @@ async function handleEntityUpdateEvent(server, sender, payload) {
|
|
|
1097
1102
|
}
|
|
1098
1103
|
}
|
|
1099
1104
|
async function handleEntityDeleteEvent(server, sender, payload) {
|
|
1100
|
-
if (sender === _plugin$
|
|
1105
|
+
if (sender === _plugin$5) {
|
|
1101
1106
|
return;
|
|
1102
1107
|
}
|
|
1103
1108
|
if (payload.namespace !== "meta") {
|
|
@@ -1130,7 +1135,7 @@ async function handleEntityDeleteEvent(server, sender, payload) {
|
|
|
1130
1135
|
}
|
|
1131
1136
|
}
|
|
1132
1137
|
}
|
|
1133
|
-
async function configureModels$
|
|
1138
|
+
async function configureModels$4(server, applicationConfig) {
|
|
1134
1139
|
try {
|
|
1135
1140
|
const models = await listCollections(server, applicationConfig);
|
|
1136
1141
|
server.appendApplicationConfig({ models });
|
|
@@ -1149,7 +1154,7 @@ function listCollections(server, applicationConfig) {
|
|
|
1149
1154
|
properties: model.properties.map((item) => item.code),
|
|
1150
1155
|
});
|
|
1151
1156
|
}
|
|
1152
|
-
async function onApplicationLoaded$
|
|
1157
|
+
async function onApplicationLoaded$6(server, applicationConfig) {
|
|
1153
1158
|
console.log("metaManager.onApplicationLoaded");
|
|
1154
1159
|
await syncDatabaseSchema(server, applicationConfig);
|
|
1155
1160
|
}
|
|
@@ -1341,16 +1346,16 @@ const pgPropertyTypeColumnMap = {
|
|
|
1341
1346
|
|
|
1342
1347
|
var metaManager = /*#__PURE__*/Object.freeze({
|
|
1343
1348
|
__proto__: null,
|
|
1344
|
-
code: code$
|
|
1345
|
-
description: description$
|
|
1346
|
-
extendingAbilities: extendingAbilities$
|
|
1347
|
-
configurableTargets: configurableTargets$
|
|
1348
|
-
configurations: configurations$
|
|
1349
|
-
initPlugin: initPlugin$
|
|
1350
|
-
registerHttpHandlers: registerHttpHandlers$
|
|
1351
|
-
registerEventHandlers: registerEventHandlers$
|
|
1352
|
-
configureModels: configureModels$
|
|
1353
|
-
onApplicationLoaded: onApplicationLoaded$
|
|
1349
|
+
code: code$n,
|
|
1350
|
+
description: description$5,
|
|
1351
|
+
extendingAbilities: extendingAbilities$5,
|
|
1352
|
+
configurableTargets: configurableTargets$5,
|
|
1353
|
+
configurations: configurations$5,
|
|
1354
|
+
initPlugin: initPlugin$5,
|
|
1355
|
+
registerHttpHandlers: registerHttpHandlers$5,
|
|
1356
|
+
registerEventHandlers: registerEventHandlers$4,
|
|
1357
|
+
configureModels: configureModels$4,
|
|
1358
|
+
onApplicationLoaded: onApplicationLoaded$6
|
|
1354
1359
|
});
|
|
1355
1360
|
|
|
1356
1361
|
function mergeInput(defaultInput, input, fixedInput) {
|
|
@@ -1418,9 +1423,9 @@ function transformFilterWithSubFilters(filter) {
|
|
|
1418
1423
|
return filter;
|
|
1419
1424
|
}
|
|
1420
1425
|
|
|
1421
|
-
const code$
|
|
1422
|
-
async function handler$
|
|
1423
|
-
await runCollectionEntityHttpHandler(ctx, options, code$
|
|
1426
|
+
const code$m = "findCollectionEntities";
|
|
1427
|
+
async function handler$h(plugin, ctx, options) {
|
|
1428
|
+
await runCollectionEntityHttpHandler(ctx, options, code$m, async (dataAccessor, input) => {
|
|
1424
1429
|
input.filters = removeFiltersWithNullValue(input.filters);
|
|
1425
1430
|
const entities = await findEntities(ctx.server, dataAccessor, input);
|
|
1426
1431
|
const result = { list: entities };
|
|
@@ -1435,13 +1440,13 @@ async function handler$e(plugin, ctx, options) {
|
|
|
1435
1440
|
|
|
1436
1441
|
var findCollectionEntitiesHttpHandler = /*#__PURE__*/Object.freeze({
|
|
1437
1442
|
__proto__: null,
|
|
1438
|
-
code: code$
|
|
1439
|
-
handler: handler$
|
|
1443
|
+
code: code$m,
|
|
1444
|
+
handler: handler$h
|
|
1440
1445
|
});
|
|
1441
1446
|
|
|
1442
|
-
const code$
|
|
1443
|
-
async function handler$
|
|
1444
|
-
console.debug(`Running ${code$
|
|
1447
|
+
const code$l = "findCollectionEntityById";
|
|
1448
|
+
async function handler$g(plugin, ctx, options) {
|
|
1449
|
+
console.debug(`Running ${code$l} handler...`);
|
|
1445
1450
|
const { server, input } = ctx;
|
|
1446
1451
|
const { id } = input;
|
|
1447
1452
|
const dataAccessor = server.getDataAccessor(options);
|
|
@@ -1462,13 +1467,13 @@ async function handler$d(plugin, ctx, options) {
|
|
|
1462
1467
|
|
|
1463
1468
|
var findCollectionEntityById = /*#__PURE__*/Object.freeze({
|
|
1464
1469
|
__proto__: null,
|
|
1465
|
-
code: code$
|
|
1466
|
-
handler: handler$
|
|
1470
|
+
code: code$l,
|
|
1471
|
+
handler: handler$g
|
|
1467
1472
|
});
|
|
1468
1473
|
|
|
1469
|
-
const code$
|
|
1470
|
-
async function handler$
|
|
1471
|
-
await runCollectionEntityHttpHandler(ctx, options, code$
|
|
1474
|
+
const code$k = "countCollectionEntities";
|
|
1475
|
+
async function handler$f(plugin, ctx, options) {
|
|
1476
|
+
await runCollectionEntityHttpHandler(ctx, options, code$k, (dataAccessor, input) => {
|
|
1472
1477
|
input.filters = removeFiltersWithNullValue(input.filters);
|
|
1473
1478
|
return dataAccessor.count(input);
|
|
1474
1479
|
});
|
|
@@ -1476,15 +1481,15 @@ async function handler$c(plugin, ctx, options) {
|
|
|
1476
1481
|
|
|
1477
1482
|
var countCollectionEntities = /*#__PURE__*/Object.freeze({
|
|
1478
1483
|
__proto__: null,
|
|
1479
|
-
code: code$
|
|
1480
|
-
handler: handler$
|
|
1484
|
+
code: code$k,
|
|
1485
|
+
handler: handler$f
|
|
1481
1486
|
});
|
|
1482
1487
|
|
|
1483
|
-
const code$
|
|
1484
|
-
async function handler$
|
|
1488
|
+
const code$j = "createCollectionEntity";
|
|
1489
|
+
async function handler$e(plugin, ctx, options) {
|
|
1485
1490
|
const { server, input } = ctx;
|
|
1486
1491
|
const { defaultInput, fixedInput } = options;
|
|
1487
|
-
console.debug(`Running ${code$
|
|
1492
|
+
console.debug(`Running ${code$j} handler...`);
|
|
1488
1493
|
console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
|
|
1489
1494
|
const mergedInput = mergeInput(defaultInput, input, fixedInput);
|
|
1490
1495
|
console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
|
|
@@ -1507,15 +1512,15 @@ async function handler$b(plugin, ctx, options) {
|
|
|
1507
1512
|
|
|
1508
1513
|
var createCollectionEntity = /*#__PURE__*/Object.freeze({
|
|
1509
1514
|
__proto__: null,
|
|
1510
|
-
code: code$
|
|
1511
|
-
handler: handler$
|
|
1515
|
+
code: code$j,
|
|
1516
|
+
handler: handler$e
|
|
1512
1517
|
});
|
|
1513
1518
|
|
|
1514
|
-
const code$
|
|
1515
|
-
async function handler$
|
|
1519
|
+
const code$i = "createCollectionEntitiesBatch";
|
|
1520
|
+
async function handler$d(plugin, ctx, options) {
|
|
1516
1521
|
const { server, input } = ctx;
|
|
1517
1522
|
const { defaultInput, fixedInput } = options;
|
|
1518
|
-
console.debug(`Running ${code$
|
|
1523
|
+
console.debug(`Running ${code$i} handler...`);
|
|
1519
1524
|
const { entities } = input;
|
|
1520
1525
|
if (!_.isArray(entities)) {
|
|
1521
1526
|
throw new Error("input.entities should be an array.");
|
|
@@ -1546,8 +1551,8 @@ async function handler$a(plugin, ctx, options) {
|
|
|
1546
1551
|
|
|
1547
1552
|
var createCollectionEntitiesBatch = /*#__PURE__*/Object.freeze({
|
|
1548
1553
|
__proto__: null,
|
|
1549
|
-
code: code$
|
|
1550
|
-
handler: handler$
|
|
1554
|
+
code: code$i,
|
|
1555
|
+
handler: handler$d
|
|
1551
1556
|
});
|
|
1552
1557
|
|
|
1553
1558
|
function getEntityPartChanges(before, after) {
|
|
@@ -1571,11 +1576,11 @@ function getEntityPartChanges(before, after) {
|
|
|
1571
1576
|
return changed;
|
|
1572
1577
|
}
|
|
1573
1578
|
|
|
1574
|
-
const code$
|
|
1575
|
-
async function handler$
|
|
1579
|
+
const code$h = "updateCollectionEntityById";
|
|
1580
|
+
async function handler$c(plugin, ctx, options) {
|
|
1576
1581
|
const { server, input } = ctx;
|
|
1577
1582
|
const { defaultInput, fixedInput } = options;
|
|
1578
|
-
console.debug(`Running ${code$
|
|
1583
|
+
console.debug(`Running ${code$h} handler...`);
|
|
1579
1584
|
console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
|
|
1580
1585
|
const mergedInput = mergeInput(defaultInput, input, fixedInput);
|
|
1581
1586
|
console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
|
|
@@ -1605,13 +1610,13 @@ async function handler$9(plugin, ctx, options) {
|
|
|
1605
1610
|
|
|
1606
1611
|
var updateCollectionEntityById = /*#__PURE__*/Object.freeze({
|
|
1607
1612
|
__proto__: null,
|
|
1608
|
-
code: code$
|
|
1609
|
-
handler: handler$
|
|
1613
|
+
code: code$h,
|
|
1614
|
+
handler: handler$c
|
|
1610
1615
|
});
|
|
1611
1616
|
|
|
1612
|
-
const code$
|
|
1613
|
-
async function handler$
|
|
1614
|
-
console.debug(`Running ${code$
|
|
1617
|
+
const code$g = "deleteCollectionEntityById";
|
|
1618
|
+
async function handler$b(plugin, ctx, options) {
|
|
1619
|
+
console.debug(`Running ${code$g} handler...`);
|
|
1615
1620
|
const { server, input } = ctx;
|
|
1616
1621
|
const dataAccessor = server.getDataAccessor(options);
|
|
1617
1622
|
const id = input.id;
|
|
@@ -1632,16 +1637,16 @@ async function handler$8(plugin, ctx, options) {
|
|
|
1632
1637
|
|
|
1633
1638
|
var deleteCollectionEntityById = /*#__PURE__*/Object.freeze({
|
|
1634
1639
|
__proto__: null,
|
|
1635
|
-
code: code$
|
|
1636
|
-
handler: handler$
|
|
1640
|
+
code: code$g,
|
|
1641
|
+
handler: handler$b
|
|
1637
1642
|
});
|
|
1638
1643
|
|
|
1639
|
-
const code$
|
|
1640
|
-
async function handler$
|
|
1644
|
+
const code$f = "addEntityRelations";
|
|
1645
|
+
async function handler$a(plugin, ctx, options) {
|
|
1641
1646
|
const { server, input } = ctx;
|
|
1642
1647
|
const { queryBuilder } = server;
|
|
1643
1648
|
const { defaultInput, fixedInput } = options;
|
|
1644
|
-
console.debug(`Running ${code$
|
|
1649
|
+
console.debug(`Running ${code$f} handler...`);
|
|
1645
1650
|
console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
|
|
1646
1651
|
const mergedInput = mergeInput(defaultInput, input, fixedInput);
|
|
1647
1652
|
console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
|
|
@@ -1684,16 +1689,16 @@ async function handler$7(plugin, ctx, options) {
|
|
|
1684
1689
|
|
|
1685
1690
|
var addEntityRelations = /*#__PURE__*/Object.freeze({
|
|
1686
1691
|
__proto__: null,
|
|
1687
|
-
code: code$
|
|
1688
|
-
handler: handler$
|
|
1692
|
+
code: code$f,
|
|
1693
|
+
handler: handler$a
|
|
1689
1694
|
});
|
|
1690
1695
|
|
|
1691
|
-
const code$
|
|
1692
|
-
async function handler$
|
|
1696
|
+
const code$e = "removeEntityRelations";
|
|
1697
|
+
async function handler$9(plugin, ctx, options) {
|
|
1693
1698
|
const { server, input } = ctx;
|
|
1694
1699
|
const { queryBuilder } = server;
|
|
1695
1700
|
const { defaultInput, fixedInput } = options;
|
|
1696
|
-
console.debug(`Running ${code$
|
|
1701
|
+
console.debug(`Running ${code$e} handler...`);
|
|
1697
1702
|
console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
|
|
1698
1703
|
const mergedInput = mergeInput(defaultInput, input, fixedInput);
|
|
1699
1704
|
console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
|
|
@@ -1733,15 +1738,15 @@ async function handler$6(plugin, ctx, options) {
|
|
|
1733
1738
|
|
|
1734
1739
|
var removeEntityRelations = /*#__PURE__*/Object.freeze({
|
|
1735
1740
|
__proto__: null,
|
|
1736
|
-
code: code$
|
|
1737
|
-
handler: handler$
|
|
1741
|
+
code: code$e,
|
|
1742
|
+
handler: handler$9
|
|
1738
1743
|
});
|
|
1739
1744
|
|
|
1740
|
-
const code$
|
|
1741
|
-
async function handler$
|
|
1745
|
+
const code$d = "queryDatabase";
|
|
1746
|
+
async function handler$8(plugin, ctx, options) {
|
|
1742
1747
|
const { server, input } = ctx;
|
|
1743
1748
|
const { sql, querySingle, defaultInput, fixedInput } = options;
|
|
1744
|
-
console.debug(`Running ${code$
|
|
1749
|
+
console.debug(`Running ${code$d} handler...`);
|
|
1745
1750
|
console.debug(`defaultInput: ${JSON.stringify(defaultInput)}`);
|
|
1746
1751
|
const mergedInput = mergeInput(defaultInput, input, fixedInput);
|
|
1747
1752
|
console.debug(`fixedInput: ${JSON.stringify(fixedInput)}`);
|
|
@@ -1757,8 +1762,8 @@ async function handler$5(plugin, ctx, options) {
|
|
|
1757
1762
|
|
|
1758
1763
|
var queryDatabase = /*#__PURE__*/Object.freeze({
|
|
1759
1764
|
__proto__: null,
|
|
1760
|
-
code: code$
|
|
1761
|
-
handler: handler$
|
|
1765
|
+
code: code$d,
|
|
1766
|
+
handler: handler$8
|
|
1762
1767
|
});
|
|
1763
1768
|
|
|
1764
1769
|
/**
|
|
@@ -1766,11 +1771,11 @@ var queryDatabase = /*#__PURE__*/Object.freeze({
|
|
|
1766
1771
|
* This plugin provide:
|
|
1767
1772
|
* - routes for manage data in database.
|
|
1768
1773
|
*/
|
|
1769
|
-
const code$
|
|
1770
|
-
const description$
|
|
1771
|
-
const extendingAbilities$
|
|
1772
|
-
const configurableTargets$
|
|
1773
|
-
const configurations$
|
|
1774
|
+
const code$c = "dataManager";
|
|
1775
|
+
const description$4 = "对数据进行管理,提供增删改查等接口。";
|
|
1776
|
+
const extendingAbilities$4 = [];
|
|
1777
|
+
const configurableTargets$4 = [];
|
|
1778
|
+
const configurations$4 = [];
|
|
1774
1779
|
const routeConfigs = [
|
|
1775
1780
|
{
|
|
1776
1781
|
code: "createBatch",
|
|
@@ -1827,21 +1832,21 @@ const routeConfigs = [
|
|
|
1827
1832
|
handlerCode: "deleteCollectionEntityById",
|
|
1828
1833
|
},
|
|
1829
1834
|
];
|
|
1830
|
-
let _plugin$
|
|
1831
|
-
async function initPlugin$
|
|
1832
|
-
_plugin$
|
|
1835
|
+
let _plugin$4;
|
|
1836
|
+
async function initPlugin$4(plugin, server) {
|
|
1837
|
+
_plugin$4 = plugin;
|
|
1833
1838
|
}
|
|
1834
|
-
async function registerHttpHandlers$
|
|
1835
|
-
server.registerHttpHandler(_plugin$
|
|
1836
|
-
server.registerHttpHandler(_plugin$
|
|
1837
|
-
server.registerHttpHandler(_plugin$
|
|
1838
|
-
server.registerHttpHandler(_plugin$
|
|
1839
|
-
server.registerHttpHandler(_plugin$
|
|
1840
|
-
server.registerHttpHandler(_plugin$
|
|
1841
|
-
server.registerHttpHandler(_plugin$
|
|
1842
|
-
server.registerHttpHandler(_plugin$
|
|
1843
|
-
server.registerHttpHandler(_plugin$
|
|
1844
|
-
server.registerHttpHandler(_plugin$
|
|
1839
|
+
async function registerHttpHandlers$4(server) {
|
|
1840
|
+
server.registerHttpHandler(_plugin$4, findCollectionEntitiesHttpHandler);
|
|
1841
|
+
server.registerHttpHandler(_plugin$4, findCollectionEntityById);
|
|
1842
|
+
server.registerHttpHandler(_plugin$4, countCollectionEntities);
|
|
1843
|
+
server.registerHttpHandler(_plugin$4, createCollectionEntity);
|
|
1844
|
+
server.registerHttpHandler(_plugin$4, createCollectionEntitiesBatch);
|
|
1845
|
+
server.registerHttpHandler(_plugin$4, updateCollectionEntityById);
|
|
1846
|
+
server.registerHttpHandler(_plugin$4, addEntityRelations);
|
|
1847
|
+
server.registerHttpHandler(_plugin$4, removeEntityRelations);
|
|
1848
|
+
server.registerHttpHandler(_plugin$4, deleteCollectionEntityById);
|
|
1849
|
+
server.registerHttpHandler(_plugin$4, queryDatabase);
|
|
1845
1850
|
}
|
|
1846
1851
|
async function configureRoutes$3(server, applicationConfig) {
|
|
1847
1852
|
const { models } = applicationConfig;
|
|
@@ -1870,11 +1875,158 @@ async function configureRoutes$3(server, applicationConfig) {
|
|
|
1870
1875
|
});
|
|
1871
1876
|
server.appendApplicationConfig({ routes });
|
|
1872
1877
|
}
|
|
1873
|
-
async function onApplicationLoaded$
|
|
1878
|
+
async function onApplicationLoaded$5(server, application) {
|
|
1874
1879
|
console.log("[dataManager.onApplicationLoaded]");
|
|
1875
1880
|
}
|
|
1876
1881
|
|
|
1877
1882
|
var dataManager = /*#__PURE__*/Object.freeze({
|
|
1883
|
+
__proto__: null,
|
|
1884
|
+
code: code$c,
|
|
1885
|
+
description: description$4,
|
|
1886
|
+
extendingAbilities: extendingAbilities$4,
|
|
1887
|
+
configurableTargets: configurableTargets$4,
|
|
1888
|
+
configurations: configurations$4,
|
|
1889
|
+
initPlugin: initPlugin$4,
|
|
1890
|
+
registerHttpHandlers: registerHttpHandlers$4,
|
|
1891
|
+
configureRoutes: configureRoutes$3,
|
|
1892
|
+
onApplicationLoaded: onApplicationLoaded$5
|
|
1893
|
+
});
|
|
1894
|
+
|
|
1895
|
+
async function readFile(path) {
|
|
1896
|
+
return new Promise((resolve, reject) => {
|
|
1897
|
+
fs__default["default"].readFile(path, null, (err, data) => {
|
|
1898
|
+
if (err) {
|
|
1899
|
+
reject(err);
|
|
1900
|
+
}
|
|
1901
|
+
else {
|
|
1902
|
+
resolve(data);
|
|
1903
|
+
}
|
|
1904
|
+
});
|
|
1905
|
+
});
|
|
1906
|
+
}
|
|
1907
|
+
async function appendFile(path, data) {
|
|
1908
|
+
return new Promise((resolve, reject) => {
|
|
1909
|
+
fs__default["default"].appendFile(path, Buffer.from(data), (err) => {
|
|
1910
|
+
if (err) {
|
|
1911
|
+
reject(err);
|
|
1912
|
+
}
|
|
1913
|
+
else {
|
|
1914
|
+
resolve();
|
|
1915
|
+
}
|
|
1916
|
+
});
|
|
1917
|
+
});
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
const code$b = "downloadDocument";
|
|
1921
|
+
async function handler$7(plugin, ctx, options) {
|
|
1922
|
+
const { server, applicationConfig, routerContext, input } = ctx;
|
|
1923
|
+
const { request, response } = routerContext;
|
|
1924
|
+
const documentDataAccessor = ctx.server.getDataAccessor({
|
|
1925
|
+
singularCode: "ecm_document",
|
|
1926
|
+
});
|
|
1927
|
+
const storageDataAccessor = ctx.server.getDataAccessor({
|
|
1928
|
+
singularCode: "ecm_storage_object",
|
|
1929
|
+
});
|
|
1930
|
+
const document = await documentDataAccessor.findById(input.documentId);
|
|
1931
|
+
if (!document) {
|
|
1932
|
+
ctx.output = { error: new Error("Document not found.") };
|
|
1933
|
+
return;
|
|
1934
|
+
}
|
|
1935
|
+
const storageObject = await storageDataAccessor.findById(document.storage_object_id);
|
|
1936
|
+
if (!storageObject) {
|
|
1937
|
+
ctx.output = { error: new Error("Storage object not found.") };
|
|
1938
|
+
return;
|
|
1939
|
+
}
|
|
1940
|
+
const fileKey = storageObject.key;
|
|
1941
|
+
const filePathName = path__default["default"].join(server.config.localFileStoragePath, fileKey);
|
|
1942
|
+
const attachmentFileName = document.name;
|
|
1943
|
+
response.body = await readFile(filePathName);
|
|
1944
|
+
response.headers.set("Content-Disposition", `attachment; filename="${encodeURIComponent(attachmentFileName)}"`);
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1947
|
+
var downloadDocumentHttpHandler = /*#__PURE__*/Object.freeze({
|
|
1948
|
+
__proto__: null,
|
|
1949
|
+
code: code$b,
|
|
1950
|
+
handler: handler$7
|
|
1951
|
+
});
|
|
1952
|
+
|
|
1953
|
+
const code$a = "downloadFile";
|
|
1954
|
+
async function handler$6(plugin, ctx, options) {
|
|
1955
|
+
const { server, applicationConfig, routerContext, input } = ctx;
|
|
1956
|
+
const { request, response } = routerContext;
|
|
1957
|
+
const dataAccessor = ctx.server.getDataAccessor({
|
|
1958
|
+
singularCode: "ecm_storage_object",
|
|
1959
|
+
});
|
|
1960
|
+
const storageObject = await dataAccessor.findById(input.fileId);
|
|
1961
|
+
if (!storageObject) {
|
|
1962
|
+
ctx.output = { error: new Error("Storage object not found.") };
|
|
1963
|
+
return;
|
|
1964
|
+
}
|
|
1965
|
+
const fileKey = storageObject.key;
|
|
1966
|
+
const filePathName = path__default["default"].join(server.config.localFileStoragePath, fileKey);
|
|
1967
|
+
const attachmentFileName = input.fileName || path__default["default"].basename(fileKey);
|
|
1968
|
+
response.body = await readFile(filePathName);
|
|
1969
|
+
response.headers.set("Content-Disposition", `attachment; filename="${encodeURIComponent(attachmentFileName)}"`);
|
|
1970
|
+
}
|
|
1971
|
+
|
|
1972
|
+
var downloadFileHttpHandler = /*#__PURE__*/Object.freeze({
|
|
1973
|
+
__proto__: null,
|
|
1974
|
+
code: code$a,
|
|
1975
|
+
handler: handler$6
|
|
1976
|
+
});
|
|
1977
|
+
|
|
1978
|
+
const code$9 = "uploadFile";
|
|
1979
|
+
async function handler$5(plugin, ctx, options) {
|
|
1980
|
+
const { server, applicationConfig, routerContext, input } = ctx;
|
|
1981
|
+
let file = input.files;
|
|
1982
|
+
if (_.isArray(file)) {
|
|
1983
|
+
file = file[0];
|
|
1984
|
+
}
|
|
1985
|
+
if (!file) {
|
|
1986
|
+
ctx.status = 400;
|
|
1987
|
+
ctx.output = { error: "File not found in request body." };
|
|
1988
|
+
return;
|
|
1989
|
+
}
|
|
1990
|
+
const extName = path__default["default"].extname(file.name);
|
|
1991
|
+
const fileKey = `${uuid.v1()}${extName}`;
|
|
1992
|
+
const filePathName = path__default["default"].join(server.config.localFileStoragePath, fileKey);
|
|
1993
|
+
const fileBuffer = await file.arrayBuffer();
|
|
1994
|
+
await appendFile(filePathName, fileBuffer);
|
|
1995
|
+
ctx.output = { ok: true, fileKey };
|
|
1996
|
+
}
|
|
1997
|
+
|
|
1998
|
+
var uploadFileHttpHandler = /*#__PURE__*/Object.freeze({
|
|
1999
|
+
__proto__: null,
|
|
2000
|
+
code: code$9,
|
|
2001
|
+
handler: handler$5
|
|
2002
|
+
});
|
|
2003
|
+
|
|
2004
|
+
/**
|
|
2005
|
+
* File manager plugin
|
|
2006
|
+
*/
|
|
2007
|
+
const code$8 = "fileManager";
|
|
2008
|
+
const description$3 = "fileManager";
|
|
2009
|
+
const extendingAbilities$3 = [];
|
|
2010
|
+
const configurableTargets$3 = [];
|
|
2011
|
+
const configurations$3 = [];
|
|
2012
|
+
let _plugin$3;
|
|
2013
|
+
async function initPlugin$3(plugin, server) {
|
|
2014
|
+
_plugin$3 = plugin;
|
|
2015
|
+
}
|
|
2016
|
+
async function registerHttpHandlers$3(server) {
|
|
2017
|
+
server.registerHttpHandler(_plugin$3, downloadDocumentHttpHandler);
|
|
2018
|
+
server.registerHttpHandler(_plugin$3, downloadFileHttpHandler);
|
|
2019
|
+
server.registerHttpHandler(_plugin$3, uploadFileHttpHandler);
|
|
2020
|
+
}
|
|
2021
|
+
async function registerEventHandlers$3(server) {
|
|
2022
|
+
}
|
|
2023
|
+
async function configureModels$3(server, applicationConfig) {
|
|
2024
|
+
}
|
|
2025
|
+
async function onApplicationLoaded$4(server, applicationConfig) {
|
|
2026
|
+
console.log("fileManager.onApplicationLoaded");
|
|
2027
|
+
}
|
|
2028
|
+
|
|
2029
|
+
var fileManager = /*#__PURE__*/Object.freeze({
|
|
1878
2030
|
__proto__: null,
|
|
1879
2031
|
code: code$8,
|
|
1880
2032
|
description: description$3,
|
|
@@ -1883,7 +2035,8 @@ var dataManager = /*#__PURE__*/Object.freeze({
|
|
|
1883
2035
|
configurations: configurations$3,
|
|
1884
2036
|
initPlugin: initPlugin$3,
|
|
1885
2037
|
registerHttpHandlers: registerHttpHandlers$3,
|
|
1886
|
-
|
|
2038
|
+
registerEventHandlers: registerEventHandlers$3,
|
|
2039
|
+
configureModels: configureModels$3,
|
|
1887
2040
|
onApplicationLoaded: onApplicationLoaded$4
|
|
1888
2041
|
});
|
|
1889
2042
|
|
|
@@ -1990,16 +2143,7 @@ async function buildRoutes(server, applicationConfig) {
|
|
|
1990
2143
|
requestMethod === "PATCH")) {
|
|
1991
2144
|
const body = request.body;
|
|
1992
2145
|
if (body) {
|
|
1993
|
-
|
|
1994
|
-
const formDataReader = body.value;
|
|
1995
|
-
const formDataBody = await formDataReader.read({ maxFileSize: 1073741824 /* 1GB */ });
|
|
1996
|
-
Object.assign(input, {
|
|
1997
|
-
formData: formDataBody
|
|
1998
|
-
});
|
|
1999
|
-
}
|
|
2000
|
-
else {
|
|
2001
|
-
Object.assign(input, body.value);
|
|
2002
|
-
}
|
|
2146
|
+
Object.assign(input, body.value);
|
|
2003
2147
|
}
|
|
2004
2148
|
}
|
|
2005
2149
|
// Normalize input value
|
|
@@ -2022,11 +2166,8 @@ async function buildRoutes(server, applicationConfig) {
|
|
|
2022
2166
|
await result;
|
|
2023
2167
|
}
|
|
2024
2168
|
}
|
|
2025
|
-
if (handlerContext.status) {
|
|
2026
|
-
routerContext.status = handlerContext.status;
|
|
2027
|
-
}
|
|
2028
2169
|
if (!isNullOrUndefined(handlerContext.output)) {
|
|
2029
|
-
routerContext.json(handlerContext.output);
|
|
2170
|
+
routerContext.json(handlerContext.output, handlerContext.status);
|
|
2030
2171
|
}
|
|
2031
2172
|
});
|
|
2032
2173
|
});
|
|
@@ -2887,6 +3028,7 @@ const plugins = [];
|
|
|
2887
3028
|
async function loadPlugins() {
|
|
2888
3029
|
plugins.push(metaManager);
|
|
2889
3030
|
plugins.push(dataManager);
|
|
3031
|
+
plugins.push(fileManager);
|
|
2890
3032
|
plugins.push(routeManager);
|
|
2891
3033
|
plugins.push(webhooks$1);
|
|
2892
3034
|
plugins.push(authManager);
|
|
@@ -3064,7 +3206,6 @@ class RouteContext {
|
|
|
3064
3206
|
state;
|
|
3065
3207
|
method;
|
|
3066
3208
|
path;
|
|
3067
|
-
status;
|
|
3068
3209
|
params;
|
|
3069
3210
|
constructor(request) {
|
|
3070
3211
|
this.request = request;
|
|
@@ -3086,6 +3227,61 @@ class RouteContext {
|
|
|
3086
3227
|
}
|
|
3087
3228
|
}
|
|
3088
3229
|
|
|
3230
|
+
const parseFormDataBody = async (request, options = { all: false }) => {
|
|
3231
|
+
const contentType = request.headers.get('Content-Type');
|
|
3232
|
+
if (isFormDataContent(contentType)) {
|
|
3233
|
+
return parseFormData(request, options);
|
|
3234
|
+
}
|
|
3235
|
+
return {};
|
|
3236
|
+
};
|
|
3237
|
+
function isFormDataContent(contentType) {
|
|
3238
|
+
if (contentType === null) {
|
|
3239
|
+
return false;
|
|
3240
|
+
}
|
|
3241
|
+
return (contentType.startsWith('multipart/form-data') ||
|
|
3242
|
+
contentType.startsWith('application/x-www-form-urlencoded'));
|
|
3243
|
+
}
|
|
3244
|
+
async function parseFormData(request, options) {
|
|
3245
|
+
const formData = await request.formData();
|
|
3246
|
+
if (formData) {
|
|
3247
|
+
return convertFormDataToBodyData(formData, options);
|
|
3248
|
+
}
|
|
3249
|
+
return {};
|
|
3250
|
+
}
|
|
3251
|
+
function convertFormDataToBodyData(formData, options) {
|
|
3252
|
+
const form = {};
|
|
3253
|
+
formData.forEach((value, key) => {
|
|
3254
|
+
const shouldParseAllValues = options.all || key.endsWith('[]');
|
|
3255
|
+
if (!shouldParseAllValues) {
|
|
3256
|
+
form[key] = value;
|
|
3257
|
+
}
|
|
3258
|
+
else {
|
|
3259
|
+
handleParsingAllValues(form, key, value);
|
|
3260
|
+
}
|
|
3261
|
+
});
|
|
3262
|
+
return form;
|
|
3263
|
+
}
|
|
3264
|
+
const handleParsingAllValues = (form, key, value) => {
|
|
3265
|
+
if (form[key] && isArrayField(form[key])) {
|
|
3266
|
+
appendToExistingArray(form[key], value);
|
|
3267
|
+
}
|
|
3268
|
+
else if (form[key]) {
|
|
3269
|
+
convertToNewArray(form, key, value);
|
|
3270
|
+
}
|
|
3271
|
+
else {
|
|
3272
|
+
form[key] = value;
|
|
3273
|
+
}
|
|
3274
|
+
};
|
|
3275
|
+
function isArrayField(field) {
|
|
3276
|
+
return Array.isArray(field);
|
|
3277
|
+
}
|
|
3278
|
+
const appendToExistingArray = (arr, value) => {
|
|
3279
|
+
arr.push(value);
|
|
3280
|
+
};
|
|
3281
|
+
const convertToNewArray = (form, key, value) => {
|
|
3282
|
+
form[key] = [form[key], value];
|
|
3283
|
+
};
|
|
3284
|
+
|
|
3089
3285
|
const GlobalRequest = global.Request;
|
|
3090
3286
|
class RapidRequest {
|
|
3091
3287
|
#raw;
|
|
@@ -3125,7 +3321,7 @@ class RapidRequest {
|
|
|
3125
3321
|
else if (contentType.startsWith("multipart/form-data")) {
|
|
3126
3322
|
this.#body = {
|
|
3127
3323
|
type: "form-data",
|
|
3128
|
-
value: await req
|
|
3324
|
+
value: await parseFormDataBody(req),
|
|
3129
3325
|
};
|
|
3130
3326
|
}
|
|
3131
3327
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File manager plugin
|
|
3
|
+
*/
|
|
4
|
+
import { IPluginInstance, RpdApplicationConfig } from "~/types";
|
|
5
|
+
import { IRpdServer, RpdConfigurationItemOptions, RpdServerPluginConfigurableTargetOptions, RpdServerPluginExtendingAbilities } from "~/core/server";
|
|
6
|
+
export declare const code = "fileManager";
|
|
7
|
+
export declare const description = "fileManager";
|
|
8
|
+
export declare const extendingAbilities: RpdServerPluginExtendingAbilities[];
|
|
9
|
+
export declare const configurableTargets: RpdServerPluginConfigurableTargetOptions[];
|
|
10
|
+
export declare const configurations: RpdConfigurationItemOptions[];
|
|
11
|
+
export declare function initPlugin(plugin: IPluginInstance, server: IRpdServer): Promise<void>;
|
|
12
|
+
export declare function registerHttpHandlers(server: IRpdServer): Promise<void>;
|
|
13
|
+
export declare function registerEventHandlers(server: IRpdServer): Promise<void>;
|
|
14
|
+
export declare function configureModels(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<void>;
|
|
15
|
+
export declare function onApplicationLoaded(server: IRpdServer, applicationConfig: RpdApplicationConfig): Promise<void>;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
export declare function readFile(path: string): Promise<Buffer>;
|
|
3
|
+
export declare function copyFile(fromPath: string, toPath: string): Promise<void>;
|
|
4
|
+
export declare function removeFile(path: string): Promise<void>;
|
|
5
|
+
export declare function moveFile(fromPath: string, toPath: string): Promise<void>;
|
|
6
|
+
export declare function appendFile(path: string, data: ArrayBuffer): Promise<void>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ruiapp/rapid-core",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.4",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"keywords": [],
|
|
@@ -20,7 +20,8 @@
|
|
|
20
20
|
"jsonwebtoken": "^9.0.2",
|
|
21
21
|
"koa-tree-router": "^0.12.1",
|
|
22
22
|
"lodash": "^4.17.21",
|
|
23
|
-
"qs": "^6.11.0"
|
|
23
|
+
"qs": "^6.11.0",
|
|
24
|
+
"uuid": "^9.0.1"
|
|
24
25
|
},
|
|
25
26
|
"scripts": {
|
|
26
27
|
"build": "rimraf dist && rollup --config",
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { RapidRequest } from '../request'
|
|
2
|
+
|
|
3
|
+
export type BodyData = Record<string, string | File | (string | File)[]>
|
|
4
|
+
export type ParseBodyOptions = {
|
|
5
|
+
/**
|
|
6
|
+
* Parse all fields with multiple values should be parsed as an array.
|
|
7
|
+
* @default false
|
|
8
|
+
* @example
|
|
9
|
+
* ```ts
|
|
10
|
+
* const data = new FormData()
|
|
11
|
+
* data.append('file', 'aaa')
|
|
12
|
+
* data.append('file', 'bbb')
|
|
13
|
+
* data.append('message', 'hello')
|
|
14
|
+
* ```
|
|
15
|
+
*
|
|
16
|
+
* If `all` is `false`:
|
|
17
|
+
* parseBody should return `{ file: 'bbb', message: 'hello' }`
|
|
18
|
+
*
|
|
19
|
+
* If `all` is `true`:
|
|
20
|
+
* parseBody should return `{ file: ['aaa', 'bbb'], message: 'hello' }`
|
|
21
|
+
*/
|
|
22
|
+
all?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export const parseFormDataBody = async <T extends BodyData = BodyData>(
|
|
26
|
+
request: Request,
|
|
27
|
+
options: ParseBodyOptions = { all: false }
|
|
28
|
+
): Promise<T> => {
|
|
29
|
+
const contentType = request.headers.get('Content-Type')
|
|
30
|
+
|
|
31
|
+
if (isFormDataContent(contentType)) {
|
|
32
|
+
return parseFormData<T>(request, options)
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return {} as T
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function isFormDataContent(contentType: string | null): boolean {
|
|
39
|
+
if (contentType === null) {
|
|
40
|
+
return false
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return (
|
|
44
|
+
contentType.startsWith('multipart/form-data') ||
|
|
45
|
+
contentType.startsWith('application/x-www-form-urlencoded')
|
|
46
|
+
)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
async function parseFormData<T extends BodyData = BodyData>(
|
|
50
|
+
request: Request,
|
|
51
|
+
options: ParseBodyOptions
|
|
52
|
+
): Promise<T> {
|
|
53
|
+
const formData = await (request as Request).formData()
|
|
54
|
+
|
|
55
|
+
if (formData) {
|
|
56
|
+
return convertFormDataToBodyData<T>(formData, options)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return {} as T
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function convertFormDataToBodyData<T extends BodyData = BodyData>(
|
|
63
|
+
formData: FormData,
|
|
64
|
+
options: ParseBodyOptions
|
|
65
|
+
): T {
|
|
66
|
+
const form: BodyData = {}
|
|
67
|
+
|
|
68
|
+
formData.forEach((value, key) => {
|
|
69
|
+
const shouldParseAllValues = options.all || key.endsWith('[]')
|
|
70
|
+
|
|
71
|
+
if (!shouldParseAllValues) {
|
|
72
|
+
form[key] = value
|
|
73
|
+
} else {
|
|
74
|
+
handleParsingAllValues(form, key, value)
|
|
75
|
+
}
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
return form as T
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const handleParsingAllValues = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
|
82
|
+
if (form[key] && isArrayField(form[key])) {
|
|
83
|
+
appendToExistingArray(form[key] as (string | File)[], value)
|
|
84
|
+
} else if (form[key]) {
|
|
85
|
+
convertToNewArray(form, key, value)
|
|
86
|
+
} else {
|
|
87
|
+
form[key] = value
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function isArrayField(field: unknown): field is (string | File)[] {
|
|
92
|
+
return Array.isArray(field)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const appendToExistingArray = (arr: (string | File)[], value: FormDataEntryValue): void => {
|
|
96
|
+
arr.push(value)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const convertToNewArray = (form: BodyData, key: string, value: FormDataEntryValue): void => {
|
|
100
|
+
form[key] = [form[key] as string | File, value]
|
|
101
|
+
}
|
|
@@ -2,6 +2,7 @@ import { RpdApplicationConfig } from "~/types";
|
|
|
2
2
|
import Plugin from "./plugin";
|
|
3
3
|
import * as metaManager from "~/plugins/metaManager/mod";
|
|
4
4
|
import * as dataManager from "~/plugins/dataManager/mod";
|
|
5
|
+
import * as fileManager from "~/plugins/fileManager/mod";
|
|
5
6
|
import * as routeManager from "~/plugins/routeManager/mod";
|
|
6
7
|
import * as webhooks from "~/plugins/webhooks/mod";
|
|
7
8
|
import * as authManager from "~/plugins/authManager/mod";
|
|
@@ -12,6 +13,7 @@ const plugins: IRpdServerPlugin[] = [];
|
|
|
12
13
|
export async function loadPlugins() {
|
|
13
14
|
plugins.push(metaManager);
|
|
14
15
|
plugins.push(dataManager);
|
|
16
|
+
plugins.push(fileManager);
|
|
15
17
|
plugins.push(routeManager);
|
|
16
18
|
plugins.push(webhooks);
|
|
17
19
|
plugins.push(authManager);
|
package/src/core/request.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import qs from "qs";
|
|
2
|
+
import { parseFormDataBody } from "./http/formDataParser";
|
|
2
3
|
|
|
3
4
|
export const GlobalRequest = global.Request;
|
|
4
5
|
|
|
@@ -46,7 +47,7 @@ export class RapidRequest {
|
|
|
46
47
|
} else if (contentType.startsWith("multipart/form-data")) {
|
|
47
48
|
this.#body = {
|
|
48
49
|
type: "form-data",
|
|
49
|
-
value: await req
|
|
50
|
+
value: await parseFormDataBody(req),
|
|
50
51
|
}
|
|
51
52
|
}
|
|
52
53
|
} else {
|
package/src/core/routeContext.ts
CHANGED
|
@@ -47,15 +47,7 @@ export async function buildRoutes(
|
|
|
47
47
|
) {
|
|
48
48
|
const body = request.body;
|
|
49
49
|
if (body) {
|
|
50
|
-
|
|
51
|
-
const formDataReader = body.value;
|
|
52
|
-
const formDataBody = await formDataReader.read({ maxFileSize: 1073741824 /* 1GB */});
|
|
53
|
-
Object.assign(input, {
|
|
54
|
-
formData: formDataBody
|
|
55
|
-
});
|
|
56
|
-
} else {
|
|
57
|
-
Object.assign(input, body.value);
|
|
58
|
-
}
|
|
50
|
+
Object.assign(input, body.value);
|
|
59
51
|
}
|
|
60
52
|
}
|
|
61
53
|
|
|
@@ -84,12 +76,8 @@ export async function buildRoutes(
|
|
|
84
76
|
}
|
|
85
77
|
}
|
|
86
78
|
|
|
87
|
-
if (handlerContext.status) {
|
|
88
|
-
routerContext.status = handlerContext.status;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
79
|
if (!isNullOrUndefined(handlerContext.output)) {
|
|
92
|
-
routerContext.json(handlerContext.output);
|
|
80
|
+
routerContext.json(handlerContext.output, handlerContext.status);
|
|
93
81
|
}
|
|
94
82
|
},
|
|
95
83
|
);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { IPluginInstance } from "~/types";
|
|
3
|
+
import { HttpHandlerContext } from "~/core/httpHandler";
|
|
4
|
+
import { readFile } from "~/utilities/fsUtility";
|
|
5
|
+
|
|
6
|
+
export const code = "downloadDocument";
|
|
7
|
+
|
|
8
|
+
export async function handler(
|
|
9
|
+
plugin: IPluginInstance,
|
|
10
|
+
ctx: HttpHandlerContext,
|
|
11
|
+
options: any,
|
|
12
|
+
) {
|
|
13
|
+
const { server, applicationConfig, routerContext, input } = ctx;
|
|
14
|
+
const { request, response } = routerContext;
|
|
15
|
+
|
|
16
|
+
const documentDataAccessor = ctx.server.getDataAccessor({
|
|
17
|
+
singularCode: "ecm_document",
|
|
18
|
+
});
|
|
19
|
+
const storageDataAccessor = ctx.server.getDataAccessor({
|
|
20
|
+
singularCode: "ecm_storage_object",
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const document = await documentDataAccessor.findById(input.documentId);
|
|
24
|
+
if (!document) {
|
|
25
|
+
ctx.output = { error: new Error("Document not found.") };
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const storageObject = await storageDataAccessor.findById(document.storage_object_id);
|
|
29
|
+
if (!storageObject) {
|
|
30
|
+
ctx.output = { error: new Error("Storage object not found.") };
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const fileKey = storageObject.key;
|
|
35
|
+
const filePathName = path.join(server.config.localFileStoragePath, fileKey);
|
|
36
|
+
const attachmentFileName = document.name;
|
|
37
|
+
|
|
38
|
+
response.body = await readFile(filePathName);
|
|
39
|
+
response.headers.set("Content-Disposition", `attachment; filename="${encodeURIComponent(attachmentFileName)}"`)
|
|
40
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import path from "path";
|
|
2
|
+
import { IPluginInstance } from "~/types";
|
|
3
|
+
import { readFile } from "~/utilities/fsUtility";
|
|
4
|
+
import { HttpHandlerContext } from "~/core/httpHandler";
|
|
5
|
+
|
|
6
|
+
export const code = "downloadFile";
|
|
7
|
+
|
|
8
|
+
export async function handler(
|
|
9
|
+
plugin: IPluginInstance,
|
|
10
|
+
ctx: HttpHandlerContext,
|
|
11
|
+
options: any,
|
|
12
|
+
) {
|
|
13
|
+
const { server, applicationConfig, routerContext, input } = ctx;
|
|
14
|
+
const { request, response } = routerContext;
|
|
15
|
+
|
|
16
|
+
const dataAccessor = ctx.server.getDataAccessor({
|
|
17
|
+
singularCode: "ecm_storage_object",
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const storageObject = await dataAccessor.findById(input.fileId);
|
|
21
|
+
if (!storageObject) {
|
|
22
|
+
ctx.output = { error: new Error("Storage object not found.") };
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const fileKey = storageObject.key;
|
|
27
|
+
const filePathName = path.join(server.config.localFileStoragePath, fileKey);
|
|
28
|
+
const attachmentFileName = input.fileName || path.basename(fileKey);
|
|
29
|
+
|
|
30
|
+
response.body = await readFile(filePathName);
|
|
31
|
+
response.headers.set("Content-Disposition", `attachment; filename="${encodeURIComponent(attachmentFileName)}"`)
|
|
32
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { v1 as uuidv1 } from "uuid";
|
|
2
|
+
import { IPluginInstance } from "~/types";
|
|
3
|
+
import { appendFile } from "~/utilities/fsUtility";
|
|
4
|
+
import { HttpHandlerContext } from "~/core/httpHandler";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import { isArray } from "lodash";
|
|
7
|
+
|
|
8
|
+
export const code = "uploadFile";
|
|
9
|
+
|
|
10
|
+
export async function handler(
|
|
11
|
+
plugin: IPluginInstance,
|
|
12
|
+
ctx: HttpHandlerContext,
|
|
13
|
+
options: any,
|
|
14
|
+
) {
|
|
15
|
+
const { server, applicationConfig, routerContext, input } = ctx;
|
|
16
|
+
const { request, response } = routerContext;
|
|
17
|
+
|
|
18
|
+
let file: File | File[] | null = input.files;
|
|
19
|
+
if (isArray(file)) {
|
|
20
|
+
file = file[0];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!file) {
|
|
24
|
+
ctx.status = 400;
|
|
25
|
+
ctx.output = { error: "File not found in request body."};
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const extName = path.extname(file.name);
|
|
30
|
+
const fileKey = `${uuidv1()}${extName}`
|
|
31
|
+
const filePathName = path.join(server.config.localFileStoragePath, fileKey);
|
|
32
|
+
|
|
33
|
+
const fileBuffer = await file.arrayBuffer();
|
|
34
|
+
await appendFile(filePathName, fileBuffer);
|
|
35
|
+
|
|
36
|
+
ctx.output = { ok: true, fileKey };
|
|
37
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File manager plugin
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import * as _ from "lodash";
|
|
6
|
+
import {
|
|
7
|
+
IPluginInstance,
|
|
8
|
+
RpdApplicationConfig,
|
|
9
|
+
} from "~/types";
|
|
10
|
+
|
|
11
|
+
import * as downloadDocumentHttpHandler from "./httpHandlers/downloadDocument";
|
|
12
|
+
import * as downloadFileHttpHandler from "./httpHandlers/downloadFile";
|
|
13
|
+
import * as uploadFileHttpHandler from "./httpHandlers/uploadFile";
|
|
14
|
+
import { IRpdServer, RpdConfigurationItemOptions, RpdServerPluginConfigurableTargetOptions, RpdServerPluginExtendingAbilities } from "~/core/server";
|
|
15
|
+
|
|
16
|
+
export const code = "fileManager";
|
|
17
|
+
export const description = "fileManager";
|
|
18
|
+
export const extendingAbilities: RpdServerPluginExtendingAbilities[] = [];
|
|
19
|
+
export const configurableTargets: RpdServerPluginConfigurableTargetOptions[] = [];
|
|
20
|
+
export const configurations: RpdConfigurationItemOptions[] = [];
|
|
21
|
+
|
|
22
|
+
let _plugin: IPluginInstance;
|
|
23
|
+
|
|
24
|
+
export async function initPlugin(plugin: IPluginInstance, server: IRpdServer) {
|
|
25
|
+
_plugin = plugin;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export async function registerHttpHandlers(server: IRpdServer) {
|
|
29
|
+
server.registerHttpHandler(_plugin, downloadDocumentHttpHandler);
|
|
30
|
+
server.registerHttpHandler(_plugin, downloadFileHttpHandler);
|
|
31
|
+
server.registerHttpHandler(_plugin, uploadFileHttpHandler);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export async function registerEventHandlers(server: IRpdServer) {
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export async function configureModels(
|
|
38
|
+
server: IRpdServer,
|
|
39
|
+
applicationConfig: RpdApplicationConfig,
|
|
40
|
+
) {
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export async function onApplicationLoaded(
|
|
44
|
+
server: IRpdServer,
|
|
45
|
+
applicationConfig: RpdApplicationConfig,
|
|
46
|
+
) {
|
|
47
|
+
console.log("fileManager.onApplicationLoaded");
|
|
48
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
|
|
3
|
+
export async function readFile(path: string): Promise<Buffer> {
|
|
4
|
+
return new Promise((resolve, reject) => {
|
|
5
|
+
fs.readFile(path, null, (err, data) => {
|
|
6
|
+
if (err) {
|
|
7
|
+
reject(err);
|
|
8
|
+
} else {
|
|
9
|
+
resolve(data);
|
|
10
|
+
}
|
|
11
|
+
})
|
|
12
|
+
})
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export async function copyFile(fromPath: string, toPath: string): Promise<void> {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
fs.copyFile(fromPath, toPath, (err) => {
|
|
18
|
+
if (err) {
|
|
19
|
+
reject(err);
|
|
20
|
+
} else {
|
|
21
|
+
resolve();
|
|
22
|
+
}
|
|
23
|
+
})
|
|
24
|
+
})
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export async function removeFile(path: string): Promise<void> {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
fs.rm(path, (err) => {
|
|
30
|
+
if (err) {
|
|
31
|
+
reject(err);
|
|
32
|
+
} else {
|
|
33
|
+
resolve();
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
})
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export async function moveFile(fromPath: string, toPath: string): Promise<void> {
|
|
40
|
+
return new Promise((resolve, reject) => {
|
|
41
|
+
fs.rename(fromPath, toPath, (err) => {
|
|
42
|
+
if (err) {
|
|
43
|
+
reject(err);
|
|
44
|
+
} else {
|
|
45
|
+
resolve();
|
|
46
|
+
}
|
|
47
|
+
})
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function appendFile(path: string, data: ArrayBuffer): Promise<void> {
|
|
52
|
+
return new Promise((resolve, reject) => {
|
|
53
|
+
fs.appendFile(path, Buffer.from(data), (err) => {
|
|
54
|
+
if (err) {
|
|
55
|
+
reject(err);
|
|
56
|
+
} else {
|
|
57
|
+
resolve();
|
|
58
|
+
}
|
|
59
|
+
})
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
}
|