@datatruck/cli 0.30.0 → 0.31.0
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/Action/BackupAction.d.ts +2 -4
- package/Action/BackupAction.js +2 -10
- package/Action/CopyAction.d.ts +2 -2
- package/Action/CopyAction.js +1 -2
- package/Action/RestoreAction.d.ts +2 -3
- package/Action/RestoreAction.js +1 -2
- package/CHANGELOG.md +16 -0
- package/Command/BackupCommand.d.ts +1 -1
- package/Command/BackupCommand.js +0 -1
- package/Command/CleanCacheCommand.d.ts +1 -1
- package/Command/CommandAbstract.d.ts +4 -3
- package/Command/CommandAbstract.js +3 -1
- package/Command/CopyCommand.d.ts +1 -1
- package/Command/CopyCommand.js +0 -1
- package/Command/RestoreCommand.d.ts +1 -1
- package/Command/RestoreCommand.js +0 -1
- package/Command/StartServerCommand.js +30 -8
- package/Config/Config.d.ts +7 -1
- package/Config/Config.js +79 -26
- package/Config/RepositoryConfig.d.ts +1 -0
- package/Config/RepositoryConfig.js +6 -1
- package/Factory/CommandFactory.d.ts +1 -1
- package/Factory/CommandFactory.js +2 -2
- package/JsonSchema/backup-def.d.ts +30 -0
- package/JsonSchema/backup-def.js +18 -0
- package/JsonSchema/copy-def.d.ts +24 -0
- package/JsonSchema/copy-def.js +15 -0
- package/Repository/DatatruckRepository.js +2 -0
- package/cli.js +5 -7
- package/config.schema.json +189 -39
- package/package.json +2 -1
- package/utils/cli.d.ts +3 -0
- package/utils/cli.js +21 -1
- package/utils/datatruck/client.js +3 -3
- package/utils/datatruck/cron-server.d.ts +23 -0
- package/utils/datatruck/cron-server.js +59 -0
- package/utils/datatruck/{server.d.ts → repository-server.d.ts} +12 -6
- package/utils/datatruck/{server.js → repository-server.js} +45 -28
- package/utils/http.d.ts +8 -2
- package/utils/http.js +16 -15
- package/utils/object.d.ts +1 -1
- package/utils/object.js +6 -6
- package/utils/progress.d.ts +17 -10
- package/utils/progress.js +20 -10
- package/utils/schema.d.ts +34 -0
- package/utils/schema.js +36 -0
- package/utils/string.d.ts +0 -3
- package/utils/string.js +1 -37
- package/utils/ObjectVault.d.ts +0 -13
- package/utils/ObjectVault.js +0 -29
package/cli.js
CHANGED
|
@@ -19,12 +19,11 @@ const fs_2 = require("fs");
|
|
|
19
19
|
const path_1 = require("path");
|
|
20
20
|
function getGlobalOptions() {
|
|
21
21
|
const result = program.opts();
|
|
22
|
+
const parseBool = (v) => v === "true" ? true : v === "false" ? false : v;
|
|
22
23
|
return {
|
|
23
24
|
...result,
|
|
24
|
-
tty: result.tty
|
|
25
|
-
progress: result.progress
|
|
26
|
-
? result.progress
|
|
27
|
-
: result.progress === "true",
|
|
25
|
+
tty: parseBool(result.tty),
|
|
26
|
+
progress: parseBool(result.progress),
|
|
28
27
|
};
|
|
29
28
|
}
|
|
30
29
|
function makeCommand(command) {
|
|
@@ -60,7 +59,7 @@ function makeCommandAction(command) {
|
|
|
60
59
|
exitCode = await (0, CommandFactory_1.CommandFactory)(command, {
|
|
61
60
|
...globalOptions,
|
|
62
61
|
config: config.data,
|
|
63
|
-
}, options).onExec();
|
|
62
|
+
}, options, {}, globalOptions.config).onExec();
|
|
64
63
|
}
|
|
65
64
|
catch (e) {
|
|
66
65
|
const error = e;
|
|
@@ -89,8 +88,7 @@ program.usage("dtt");
|
|
|
89
88
|
program.option("-v,--verbose", "Verbose", (_, previous) => previous + 1, 0);
|
|
90
89
|
program.option("-c,--config <path>", "Config path", process.env["DATATRUCK_CONFIG"] ?? (cwd.endsWith(path_1.sep) ? cwd : `${cwd}${path_1.sep}`));
|
|
91
90
|
program.option("--tty <value>", "TTY mode (auto, true, false)", "auto");
|
|
92
|
-
program.option("--progress <value>", "Progress type (auto, true, false, interval)", "auto");
|
|
93
|
-
program.option("--progress-interval <ms>", "Progress interval", Number, 1000);
|
|
91
|
+
program.option("--progress <value>", "Progress type (auto, true, false, interval, interval:[ms])", "auto");
|
|
94
92
|
program.option("-o,--output-format <format>", "Output format (json, pjson, yaml, table, custom=$, tpl=name)", "table");
|
|
95
93
|
makeCommand(CommandFactory_1.CommandEnum.startServer).alias("start");
|
|
96
94
|
makeCommand(CommandFactory_1.CommandEnum.config).alias("c");
|
package/config.schema.json
CHANGED
|
@@ -1048,67 +1048,217 @@
|
|
|
1048
1048
|
"type": "object",
|
|
1049
1049
|
"additionalProperties": false,
|
|
1050
1050
|
"properties": {
|
|
1051
|
-
"path": {
|
|
1052
|
-
"type": "string"
|
|
1053
|
-
},
|
|
1054
1051
|
"log": {
|
|
1055
1052
|
"type": "boolean"
|
|
1056
1053
|
},
|
|
1057
|
-
"
|
|
1058
|
-
"type": "array",
|
|
1059
|
-
"items": {
|
|
1060
|
-
"type": "object",
|
|
1061
|
-
"additionalProperties": false,
|
|
1062
|
-
"properties": {
|
|
1063
|
-
"name": {
|
|
1064
|
-
"type": "string"
|
|
1065
|
-
},
|
|
1066
|
-
"password": {
|
|
1067
|
-
"type": "string"
|
|
1068
|
-
}
|
|
1069
|
-
}
|
|
1070
|
-
}
|
|
1071
|
-
},
|
|
1072
|
-
"listen": {
|
|
1054
|
+
"repository": {
|
|
1073
1055
|
"type": "object",
|
|
1074
1056
|
"additionalProperties": false,
|
|
1075
1057
|
"properties": {
|
|
1076
|
-
"
|
|
1077
|
-
"type": "integer"
|
|
1078
|
-
},
|
|
1079
|
-
"address": {
|
|
1080
|
-
"type": "string"
|
|
1081
|
-
}
|
|
1082
|
-
}
|
|
1083
|
-
},
|
|
1084
|
-
"trustProxy": {
|
|
1085
|
-
"anyOf": [
|
|
1086
|
-
{
|
|
1058
|
+
"enabled": {
|
|
1087
1059
|
"type": "boolean"
|
|
1088
1060
|
},
|
|
1089
|
-
{
|
|
1061
|
+
"listen": {
|
|
1090
1062
|
"type": "object",
|
|
1091
1063
|
"additionalProperties": false,
|
|
1092
|
-
"required": [
|
|
1093
|
-
"remoteAddressHeader"
|
|
1094
|
-
],
|
|
1095
1064
|
"properties": {
|
|
1096
|
-
"
|
|
1065
|
+
"port": {
|
|
1066
|
+
"type": "integer"
|
|
1067
|
+
},
|
|
1068
|
+
"address": {
|
|
1097
1069
|
"type": "string"
|
|
1098
1070
|
}
|
|
1099
1071
|
}
|
|
1072
|
+
},
|
|
1073
|
+
"trustProxy": {
|
|
1074
|
+
"anyOf": [
|
|
1075
|
+
{
|
|
1076
|
+
"type": "boolean"
|
|
1077
|
+
},
|
|
1078
|
+
{
|
|
1079
|
+
"type": "object",
|
|
1080
|
+
"additionalProperties": false,
|
|
1081
|
+
"required": [
|
|
1082
|
+
"remoteAddressHeader"
|
|
1083
|
+
],
|
|
1084
|
+
"properties": {
|
|
1085
|
+
"remoteAddressHeader": {
|
|
1086
|
+
"type": "string"
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
]
|
|
1091
|
+
},
|
|
1092
|
+
"allowlist": {
|
|
1093
|
+
"type": "object",
|
|
1094
|
+
"additionalProperties": false,
|
|
1095
|
+
"properties": {
|
|
1096
|
+
"enabled": {
|
|
1097
|
+
"type": "boolean"
|
|
1098
|
+
},
|
|
1099
|
+
"remoteAddresses": {
|
|
1100
|
+
"$ref": "#/definitions/stringlist-util"
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
},
|
|
1104
|
+
"backends": {
|
|
1105
|
+
"type": "array",
|
|
1106
|
+
"items": {
|
|
1107
|
+
"type": "object",
|
|
1108
|
+
"additionalProperties": false,
|
|
1109
|
+
"required": [
|
|
1110
|
+
"name",
|
|
1111
|
+
"path"
|
|
1112
|
+
],
|
|
1113
|
+
"properties": {
|
|
1114
|
+
"name": {
|
|
1115
|
+
"type": "string"
|
|
1116
|
+
},
|
|
1117
|
+
"path": {
|
|
1118
|
+
"type": "string"
|
|
1119
|
+
},
|
|
1120
|
+
"users": {
|
|
1121
|
+
"type": "array",
|
|
1122
|
+
"items": {
|
|
1123
|
+
"type": "object",
|
|
1124
|
+
"additionalProperties": false,
|
|
1125
|
+
"required": [
|
|
1126
|
+
"name",
|
|
1127
|
+
"password"
|
|
1128
|
+
],
|
|
1129
|
+
"properties": {
|
|
1130
|
+
"enabled": {
|
|
1131
|
+
"type": "boolean"
|
|
1132
|
+
},
|
|
1133
|
+
"name": {
|
|
1134
|
+
"type": "string"
|
|
1135
|
+
},
|
|
1136
|
+
"password": {
|
|
1137
|
+
"type": "string"
|
|
1138
|
+
}
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1100
1144
|
}
|
|
1101
|
-
|
|
1145
|
+
}
|
|
1102
1146
|
},
|
|
1103
|
-
"
|
|
1147
|
+
"cron": {
|
|
1104
1148
|
"type": "object",
|
|
1105
1149
|
"additionalProperties": false,
|
|
1106
1150
|
"properties": {
|
|
1107
1151
|
"enabled": {
|
|
1108
1152
|
"type": "boolean"
|
|
1109
1153
|
},
|
|
1110
|
-
"
|
|
1111
|
-
"
|
|
1154
|
+
"actions": {
|
|
1155
|
+
"type": "array",
|
|
1156
|
+
"items": {
|
|
1157
|
+
"allOf": [
|
|
1158
|
+
{
|
|
1159
|
+
"type": "object",
|
|
1160
|
+
"required": [
|
|
1161
|
+
"schedule"
|
|
1162
|
+
],
|
|
1163
|
+
"properties": {
|
|
1164
|
+
"schedule": {
|
|
1165
|
+
"type": "string"
|
|
1166
|
+
}
|
|
1167
|
+
}
|
|
1168
|
+
},
|
|
1169
|
+
{
|
|
1170
|
+
"anyOf": [
|
|
1171
|
+
{
|
|
1172
|
+
"if": {
|
|
1173
|
+
"type": "object",
|
|
1174
|
+
"properties": {
|
|
1175
|
+
"type": {
|
|
1176
|
+
"const": "backup"
|
|
1177
|
+
}
|
|
1178
|
+
}
|
|
1179
|
+
},
|
|
1180
|
+
"then": {
|
|
1181
|
+
"type": "object",
|
|
1182
|
+
"properties": {
|
|
1183
|
+
"options": {
|
|
1184
|
+
"type": "object",
|
|
1185
|
+
"additionalProperties": false,
|
|
1186
|
+
"properties": {
|
|
1187
|
+
"package": {
|
|
1188
|
+
"type": "string"
|
|
1189
|
+
},
|
|
1190
|
+
"packageTask": {
|
|
1191
|
+
"type": "string"
|
|
1192
|
+
},
|
|
1193
|
+
"repository": {
|
|
1194
|
+
"type": "string"
|
|
1195
|
+
},
|
|
1196
|
+
"repositoryType": {
|
|
1197
|
+
"enum": [
|
|
1198
|
+
"restic",
|
|
1199
|
+
"datatruck",
|
|
1200
|
+
"git"
|
|
1201
|
+
]
|
|
1202
|
+
},
|
|
1203
|
+
"tag": {
|
|
1204
|
+
"type": "string"
|
|
1205
|
+
},
|
|
1206
|
+
"date": {
|
|
1207
|
+
"type": "string"
|
|
1208
|
+
},
|
|
1209
|
+
"prune": {
|
|
1210
|
+
"type": "boolean"
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
}
|
|
1215
|
+
},
|
|
1216
|
+
"else": false
|
|
1217
|
+
},
|
|
1218
|
+
{
|
|
1219
|
+
"if": {
|
|
1220
|
+
"type": "object",
|
|
1221
|
+
"properties": {
|
|
1222
|
+
"type": {
|
|
1223
|
+
"const": "copy"
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
},
|
|
1227
|
+
"then": {
|
|
1228
|
+
"type": "object",
|
|
1229
|
+
"properties": {
|
|
1230
|
+
"options": {
|
|
1231
|
+
"type": "object",
|
|
1232
|
+
"additionalProperties": false,
|
|
1233
|
+
"properties": {
|
|
1234
|
+
"id": {
|
|
1235
|
+
"type": "string"
|
|
1236
|
+
},
|
|
1237
|
+
"last": {
|
|
1238
|
+
"type": "integer"
|
|
1239
|
+
},
|
|
1240
|
+
"package": {
|
|
1241
|
+
"type": "string"
|
|
1242
|
+
},
|
|
1243
|
+
"packageTask": {
|
|
1244
|
+
"type": "string"
|
|
1245
|
+
},
|
|
1246
|
+
"repository": {
|
|
1247
|
+
"type": "string"
|
|
1248
|
+
},
|
|
1249
|
+
"repository2": {
|
|
1250
|
+
"type": "string"
|
|
1251
|
+
}
|
|
1252
|
+
}
|
|
1253
|
+
}
|
|
1254
|
+
}
|
|
1255
|
+
},
|
|
1256
|
+
"else": false
|
|
1257
|
+
}
|
|
1258
|
+
]
|
|
1259
|
+
}
|
|
1260
|
+
]
|
|
1261
|
+
}
|
|
1112
1262
|
}
|
|
1113
1263
|
}
|
|
1114
1264
|
}
|
package/package.json
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@datatruck/cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.31.0",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@supercharge/promise-pool": "^3.1.0",
|
|
6
6
|
"ajv": "^8.12.0",
|
|
7
7
|
"async": "^3.2.4",
|
|
8
8
|
"chalk": "^4.1.2",
|
|
9
9
|
"commander": "^11.0.0",
|
|
10
|
+
"croner": "^7.0.4",
|
|
10
11
|
"dayjs": "^1.11.10",
|
|
11
12
|
"fast-folder-size": "^2.2.0",
|
|
12
13
|
"fast-glob": "^3.3.1",
|
package/utils/cli.d.ts
CHANGED
|
@@ -19,4 +19,7 @@ export type OptionsType<T1, T2 extends {
|
|
|
19
19
|
export declare function parseOptions<T1, T2 extends {
|
|
20
20
|
[K in keyof T1]: unknown;
|
|
21
21
|
}>(object: T1, options: OptionsType<T1, T2>): T2;
|
|
22
|
+
export declare function stringifyOptions<T1, T2 extends {
|
|
23
|
+
[K in keyof T1]: unknown;
|
|
24
|
+
}>(options: OptionsType<T1, T2>, object: any): string[];
|
|
22
25
|
export declare function confirm(message: string): Promise<unknown>;
|
package/utils/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.confirm = exports.parseOptions = exports.renderObject = exports.renderError = exports.renderResult = exports.logExec = exports.renderProgressBar = exports.showCursorCommand = void 0;
|
|
6
|
+
exports.confirm = exports.stringifyOptions = exports.parseOptions = exports.renderObject = exports.renderError = exports.renderResult = exports.logExec = exports.renderProgressBar = exports.showCursorCommand = void 0;
|
|
7
7
|
const chalk_1 = __importDefault(require("chalk"));
|
|
8
8
|
const chalk_2 = require("chalk");
|
|
9
9
|
const readline_1 = require("readline");
|
|
@@ -96,6 +96,26 @@ function parseOptions(object, options) {
|
|
|
96
96
|
return result;
|
|
97
97
|
}
|
|
98
98
|
exports.parseOptions = parseOptions;
|
|
99
|
+
function stringifyOptions(options, object) {
|
|
100
|
+
const result = [];
|
|
101
|
+
for (const key in options) {
|
|
102
|
+
const fullOpt = options[key].option;
|
|
103
|
+
const [opt] = fullOpt.split(",");
|
|
104
|
+
const isNegative = fullOpt.startsWith("--no");
|
|
105
|
+
const isBool = !fullOpt.includes("<") && !fullOpt.includes("[");
|
|
106
|
+
const defaultsValue = isNegative ? true : options[key].defaults;
|
|
107
|
+
const value = object?.[key] ?? defaultsValue;
|
|
108
|
+
if (isBool) {
|
|
109
|
+
if (object[key])
|
|
110
|
+
result.push(opt);
|
|
111
|
+
}
|
|
112
|
+
else if (value !== undefined) {
|
|
113
|
+
result.push(opt, `${value}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return result;
|
|
117
|
+
}
|
|
118
|
+
exports.stringifyOptions = stringifyOptions;
|
|
99
119
|
function confirm(message) {
|
|
100
120
|
const rl = (0, readline_1.createInterface)({
|
|
101
121
|
input: process.stdin,
|
|
@@ -3,7 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.createFs = exports.isRemoteBackend = exports.RemoteFs = void 0;
|
|
4
4
|
const http_1 = require("../http");
|
|
5
5
|
const virtual_fs_1 = require("../virtual-fs");
|
|
6
|
-
const
|
|
6
|
+
const repository_server_1 = require("./repository-server");
|
|
7
7
|
class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
8
8
|
options;
|
|
9
9
|
url;
|
|
@@ -13,8 +13,8 @@ class RemoteFs extends virtual_fs_1.AbstractFs {
|
|
|
13
13
|
this.options = options;
|
|
14
14
|
const url = new URL(options.backend);
|
|
15
15
|
this.headers = {
|
|
16
|
-
[
|
|
17
|
-
[
|
|
16
|
+
[repository_server_1.headerKey.user]: url.username,
|
|
17
|
+
[repository_server_1.headerKey.password]: url.password,
|
|
18
18
|
};
|
|
19
19
|
url.username = "";
|
|
20
20
|
url.password = "";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { BackupCommandOptions } from "../../Command/BackupCommand";
|
|
2
|
+
import { CopyCommandOptionsType } from "../../Command/CopyCommand";
|
|
3
|
+
export type CronAction = {
|
|
4
|
+
schedule: string;
|
|
5
|
+
name: "backup";
|
|
6
|
+
options: BackupCommandOptions;
|
|
7
|
+
} | {
|
|
8
|
+
schedule: string;
|
|
9
|
+
name: "copy";
|
|
10
|
+
options: CopyCommandOptionsType;
|
|
11
|
+
};
|
|
12
|
+
export type DatatruckCronServerOptions = {
|
|
13
|
+
enabled?: boolean;
|
|
14
|
+
actions?: CronAction[];
|
|
15
|
+
};
|
|
16
|
+
export declare function createCronServer(options: DatatruckCronServerOptions, config: {
|
|
17
|
+
log: boolean;
|
|
18
|
+
verbose: boolean;
|
|
19
|
+
configPath: string;
|
|
20
|
+
}): {
|
|
21
|
+
start: () => void;
|
|
22
|
+
stop: () => void;
|
|
23
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createCronServer = void 0;
|
|
4
|
+
const CommandFactory_1 = require("../../Factory/CommandFactory");
|
|
5
|
+
const cli_1 = require("../cli");
|
|
6
|
+
const process_1 = require("../process");
|
|
7
|
+
const croner_1 = require("croner");
|
|
8
|
+
function createJobs(actions, currentJobs = [], worker) {
|
|
9
|
+
const jobs = [];
|
|
10
|
+
for (const action of actions) {
|
|
11
|
+
const index = actions.indexOf(action);
|
|
12
|
+
const context = JSON.stringify({
|
|
13
|
+
index: actions.indexOf(action),
|
|
14
|
+
data: action,
|
|
15
|
+
});
|
|
16
|
+
const job = currentJobs.at(index);
|
|
17
|
+
if (!job || job.options.context !== context) {
|
|
18
|
+
job?.stop();
|
|
19
|
+
jobs.push((0, croner_1.Cron)(action.schedule, {
|
|
20
|
+
paused: true,
|
|
21
|
+
context: JSON.stringify(action),
|
|
22
|
+
catch: true,
|
|
23
|
+
protect: true,
|
|
24
|
+
}, () => worker(action, index)));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
return jobs;
|
|
28
|
+
}
|
|
29
|
+
function createCronServer(options, config) {
|
|
30
|
+
const worker = async (action, index) => {
|
|
31
|
+
if (config.log)
|
|
32
|
+
console.info(`> [job] ${index} - ${action.name}`);
|
|
33
|
+
try {
|
|
34
|
+
const Command = (0, CommandFactory_1.CommandConstructorFactory)(action.name);
|
|
35
|
+
const command = new Command({ config: { packages: [], repositories: [] } }, {});
|
|
36
|
+
const cliOptions = (0, cli_1.stringifyOptions)(command.onOptions(), action.options);
|
|
37
|
+
const [node, bin] = process.argv;
|
|
38
|
+
await (0, process_1.exec)(node, [bin, "-c", config.configPath, action.name, ...cliOptions], {}, { log: config.verbose });
|
|
39
|
+
if (config.log)
|
|
40
|
+
console.info(`< [job] ${index} - ${action.name}`);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
if (config.log)
|
|
44
|
+
console.error(`< [job] ${index} - ${action.name}`, error);
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
const jobs = createJobs(options.actions || [], [], worker);
|
|
48
|
+
return {
|
|
49
|
+
start: () => {
|
|
50
|
+
for (const job of jobs)
|
|
51
|
+
job.resume();
|
|
52
|
+
},
|
|
53
|
+
stop: () => {
|
|
54
|
+
for (const job of jobs)
|
|
55
|
+
job.stop();
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
exports.createCronServer = createCronServer;
|
|
@@ -1,17 +1,16 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { IncomingMessage } from "http";
|
|
3
3
|
type User = {
|
|
4
|
+
enabled?: boolean;
|
|
4
5
|
name: string;
|
|
5
6
|
password: string;
|
|
6
7
|
};
|
|
7
|
-
export type
|
|
8
|
-
|
|
9
|
-
log?: boolean;
|
|
8
|
+
export type DatatruckRepositoryServerOptions = {
|
|
9
|
+
enabled?: boolean;
|
|
10
10
|
listen?: {
|
|
11
11
|
port?: number;
|
|
12
12
|
address?: string;
|
|
13
13
|
};
|
|
14
|
-
users?: User[];
|
|
15
14
|
trustProxy?: true | {
|
|
16
15
|
remoteAddressHeader: string;
|
|
17
16
|
};
|
|
@@ -20,12 +19,19 @@ export type DatatruckServerOptions = {
|
|
|
20
19
|
* @default true
|
|
21
20
|
*/
|
|
22
21
|
enabled?: boolean;
|
|
23
|
-
|
|
22
|
+
remoteAddresses?: string[];
|
|
24
23
|
};
|
|
24
|
+
backends?: {
|
|
25
|
+
name: string;
|
|
26
|
+
path: string;
|
|
27
|
+
users?: User[];
|
|
28
|
+
}[];
|
|
25
29
|
};
|
|
26
30
|
export declare const headerKey: {
|
|
27
31
|
user: string;
|
|
28
32
|
password: string;
|
|
29
33
|
};
|
|
30
|
-
export declare function
|
|
34
|
+
export declare function createDatatruckRepositoryServer(options: Omit<DatatruckRepositoryServerOptions, "listen">, config?: {
|
|
35
|
+
log?: boolean;
|
|
36
|
+
}): import("node:http").Server<typeof IncomingMessage, typeof import("node:http").ServerResponse>;
|
|
31
37
|
export {};
|
|
@@ -1,41 +1,52 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.createDatatruckRepositoryServer = exports.headerKey = void 0;
|
|
4
4
|
const http_1 = require("../http");
|
|
5
5
|
const virtual_fs_1 = require("../virtual-fs");
|
|
6
6
|
const fs_1 = require("fs");
|
|
7
7
|
const promises_1 = require("fs/promises");
|
|
8
8
|
const http_2 = require("http");
|
|
9
|
-
|
|
9
|
+
exports.headerKey = {
|
|
10
|
+
user: "x-dtt-user",
|
|
11
|
+
password: "x-dtt-password",
|
|
12
|
+
};
|
|
13
|
+
function parseUrl(inUrl, repositoryPrefix = "repo") {
|
|
10
14
|
const url = new URL(`http://127.0.0.1${inUrl}`);
|
|
11
15
|
const inParams = url.searchParams.get("params");
|
|
12
|
-
const action = url.pathname.slice(1);
|
|
13
|
-
if (
|
|
16
|
+
const [prefix, repository, action] = url.pathname.slice(1).split("/");
|
|
17
|
+
if (prefix !== repositoryPrefix) {
|
|
18
|
+
return { repository: undefined, action: undefined, params: [] };
|
|
19
|
+
}
|
|
20
|
+
else if (typeof inParams === "string") {
|
|
14
21
|
const params = JSON.parse(inParams);
|
|
15
22
|
if (!Array.isArray(params))
|
|
16
23
|
throw new Error(`Invalid params`);
|
|
17
|
-
return { action, params };
|
|
24
|
+
return { repository, action, params };
|
|
18
25
|
}
|
|
19
26
|
else {
|
|
20
|
-
return { action, params: [] };
|
|
27
|
+
return { repository, action, params: [] };
|
|
21
28
|
}
|
|
22
29
|
}
|
|
23
|
-
|
|
24
|
-
user: "x-dtt-user",
|
|
25
|
-
password: "x-dtt-password",
|
|
26
|
-
};
|
|
27
|
-
function validateRequest(req, options) {
|
|
30
|
+
function findRepositoryBackend(req, repository, options) {
|
|
28
31
|
const list = options.allowlist;
|
|
29
|
-
if (list && (list.enabled ?? true) && list.
|
|
32
|
+
if (list && (list.enabled ?? true) && list.remoteAddresses) {
|
|
30
33
|
const remoteAddress = getRemoteAddress(req, options);
|
|
31
|
-
if (!remoteAddress || list.
|
|
34
|
+
if (!remoteAddress || !list.remoteAddresses.includes(remoteAddress))
|
|
32
35
|
return false;
|
|
33
36
|
}
|
|
34
37
|
const name = req.headers[exports.headerKey.user]?.toString().trim();
|
|
35
38
|
const password = req.headers[exports.headerKey.password]?.toString().trim();
|
|
36
39
|
if (!name?.length || !password?.length)
|
|
37
40
|
return;
|
|
38
|
-
|
|
41
|
+
const backend = options.backends?.find((e) => e.name === repository);
|
|
42
|
+
if (!backend)
|
|
43
|
+
return;
|
|
44
|
+
const user = backend.users?.find((user) => user.name === name && user.password === password);
|
|
45
|
+
if (!user)
|
|
46
|
+
return;
|
|
47
|
+
if (!(user.enabled ?? true))
|
|
48
|
+
return;
|
|
49
|
+
return backend;
|
|
39
50
|
}
|
|
40
51
|
const getRemoteAddress = (req, options) => {
|
|
41
52
|
return ((options.trustProxy
|
|
@@ -44,24 +55,30 @@ const getRemoteAddress = (req, options) => {
|
|
|
44
55
|
: req.headers[options.trustProxy.remoteAddressHeader]?.toString()
|
|
45
56
|
: undefined) ?? req.socket.remoteAddress);
|
|
46
57
|
};
|
|
47
|
-
function
|
|
48
|
-
const log = options.log ?? true;
|
|
58
|
+
function createDatatruckRepositoryServer(options, config = {}) {
|
|
49
59
|
return (0, http_2.createServer)(async (req, res) => {
|
|
50
60
|
try {
|
|
51
|
-
if (req.url === "/" || req.url === "/favicon.ico")
|
|
61
|
+
if (req.url === "/" || req.url === "/favicon.ico")
|
|
62
|
+
return res.end();
|
|
63
|
+
const { repository, action, params } = parseUrl(req.url);
|
|
64
|
+
if (!repository || !action) {
|
|
65
|
+
res.statusCode = 404;
|
|
52
66
|
return res.end();
|
|
53
67
|
}
|
|
54
|
-
|
|
68
|
+
const backend = findRepositoryBackend(req, repository, options);
|
|
69
|
+
if (!backend) {
|
|
55
70
|
res.statusCode = 401;
|
|
56
71
|
return res.end();
|
|
57
72
|
}
|
|
58
|
-
if (log)
|
|
59
|
-
console.info(`> ${req.url}`);
|
|
73
|
+
if (config.log)
|
|
74
|
+
console.info(`> [repository] ${repository} - ${req.url}`);
|
|
60
75
|
const fs = new virtual_fs_1.LocalFs({
|
|
61
|
-
backend:
|
|
76
|
+
backend: backend.path,
|
|
62
77
|
});
|
|
63
|
-
|
|
64
|
-
|
|
78
|
+
if (action === "comcheck") {
|
|
79
|
+
res.write(JSON.stringify({ success: true }));
|
|
80
|
+
}
|
|
81
|
+
else if (action === "upload") {
|
|
65
82
|
const [target] = params;
|
|
66
83
|
const path = fs.resolvePath(target);
|
|
67
84
|
const file = (0, fs_1.createWriteStream)(path);
|
|
@@ -99,17 +116,17 @@ function createDatatruckServer(options) {
|
|
|
99
116
|
if (json !== undefined)
|
|
100
117
|
res.write(JSON.stringify(json));
|
|
101
118
|
}
|
|
102
|
-
if (log)
|
|
103
|
-
console.info(
|
|
119
|
+
if (config.log)
|
|
120
|
+
console.info(`< [repository] ${repository} - ${action}`);
|
|
104
121
|
res.end();
|
|
105
122
|
}
|
|
106
123
|
catch (error) {
|
|
107
|
-
if (log)
|
|
108
|
-
console.error(
|
|
124
|
+
if (config.log)
|
|
125
|
+
console.error(`< [repository] ${req.url}`, error);
|
|
109
126
|
res.statusCode = 500;
|
|
110
127
|
res.statusMessage = error.message;
|
|
111
128
|
res.end();
|
|
112
129
|
}
|
|
113
130
|
});
|
|
114
131
|
}
|
|
115
|
-
exports.
|
|
132
|
+
exports.createDatatruckRepositoryServer = createDatatruckRepositoryServer;
|
package/utils/http.d.ts
CHANGED
|
@@ -3,10 +3,16 @@ import { BasicProgress } from "./progress";
|
|
|
3
3
|
import { IncomingMessage, Server } from "http";
|
|
4
4
|
export declare function closeServer(server: Server): Promise<void>;
|
|
5
5
|
export declare function readRequestData(req: IncomingMessage): Promise<string | undefined>;
|
|
6
|
-
export
|
|
6
|
+
export type FetchOptions = {
|
|
7
7
|
headers?: Record<string, string>;
|
|
8
8
|
query?: Record<string, string>;
|
|
9
|
-
|
|
9
|
+
statusError?: boolean;
|
|
10
|
+
};
|
|
11
|
+
export declare function fetch(url: string, options?: FetchOptions): Promise<{
|
|
12
|
+
data: string | undefined;
|
|
13
|
+
status: number | undefined;
|
|
14
|
+
}>;
|
|
15
|
+
export declare function fetchJson<T = any>(url: string, options?: FetchOptions): Promise<T | undefined>;
|
|
10
16
|
export declare function post(url: string, data: string, options?: {
|
|
11
17
|
headers?: Record<string, string>;
|
|
12
18
|
query?: Record<string, string>;
|