dxfl 0.5.2 → 0.5.5
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/CHANGELOG.md +8 -0
- package/README.md +11 -11
- package/deuxfleurs.toml +18 -0
- package/dist/deploy.js +4 -1
- package/dist/deploy_websiteconfig.js +108 -0
- package/dist/errors.js +7 -0
- package/dist/index.js +1 -1
- package/package.json +2 -2
- package/public/TinyCorePure64-15.0.iso +1 -0
- package/public/d/b +2 -0
- package/public/d/c +2 -0
- package/public/d/dd/bla +1 -0
- package/public/d/dd/foo.txt +1 -0
- package/public/def +2 -0
- package/public/error.html +1 -0
- package/public/index.html +1 -0
- package/public/index.html~ +1 -0
- package/public/slt +1 -0
- package/public/test.html +0 -0
- package/public/toto.jpg +0 -0
- package/public/toto.txt +1 -0
- package/public/toto2.txt +1 -0
- package/dist/_empty.js +0 -64
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
# v0.5.4, v0.5.5
|
|
2
|
+
|
|
3
|
+
- Fix bug when deploying compressed files in subdirectories (introduced in 0.5.0)
|
|
4
|
+
|
|
5
|
+
# v0.5.3
|
|
6
|
+
|
|
7
|
+
- Update @aws-sdk/client-s3 to include patch for CVE-2026-26278 (cleaning 0.5.1 and 0.5.2)
|
|
8
|
+
|
|
1
9
|
# v0.5.2
|
|
2
10
|
|
|
3
11
|
- Force fast-xml-parser version on package-lock
|
package/README.md
CHANGED
|
@@ -146,26 +146,26 @@ allowed_origins = "https://www.example.com"
|
|
|
146
146
|
# Required parameter.
|
|
147
147
|
|
|
148
148
|
# HTTP methods allowed in a cross-origin request (in response to a preflight request).
|
|
149
|
-
|
|
149
|
+
allowed_methods = "GET"
|
|
150
150
|
# Possible values:
|
|
151
|
-
# -
|
|
152
|
-
# -
|
|
153
|
-
# -
|
|
151
|
+
# - allowed_methods = "*" : wild card to allow all methods (only GET/HEAD are currently supported by the server)
|
|
152
|
+
# - allowed_methods = "GET" : string of http method ("GET" or "HEAD")
|
|
153
|
+
# - allowed_methods = ["GET", "HEAD"] : array of string http methods
|
|
154
154
|
# Optional parameter. Default value: ["GET", "HEAD"]
|
|
155
155
|
|
|
156
156
|
# Request headers allowed in a preflight request.
|
|
157
|
-
|
|
157
|
+
allowed_headers = "Authorization"
|
|
158
158
|
# Possible values:
|
|
159
|
-
# -
|
|
160
|
-
# -
|
|
161
|
-
# -
|
|
159
|
+
# - allowed_headers = "*" : wildcard to allow all headers
|
|
160
|
+
# - allowed_headers = "Authorization" : string of http header
|
|
161
|
+
# - allowed_headers = ["Authorization", "Age"] : array of string http header
|
|
162
162
|
# Optional parameter. Default value : []
|
|
163
163
|
|
|
164
164
|
# Response headers you want to make available to JavaScript in response to a cross-origin request.
|
|
165
|
-
|
|
165
|
+
expose_headers = "Content-Encoding"
|
|
166
166
|
# Possible values:
|
|
167
|
-
# -
|
|
168
|
-
# -
|
|
167
|
+
# - expose_headers = "Content-Encoding" : string of http header
|
|
168
|
+
# - expose_headers = ["Content-Encoding", "Kuma-Revision"] : array of string http header
|
|
169
169
|
# Optional Parameter. Default value : []
|
|
170
170
|
```
|
|
171
171
|
|
package/deuxfleurs.toml
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
error_page = "error.html"
|
|
2
|
+
|
|
3
|
+
# [[redirects]]
|
|
4
|
+
# from = "TinyCorePure64-15.0.iso"
|
|
5
|
+
# to = "/bbc"
|
|
6
|
+
|
|
7
|
+
[[redirects]]
|
|
8
|
+
from = "/a"
|
|
9
|
+
to = "/def"
|
|
10
|
+
|
|
11
|
+
[[cors]]
|
|
12
|
+
allowed_origins = "foo"
|
|
13
|
+
|
|
14
|
+
[[headers]]
|
|
15
|
+
for = ["**/*.txt"]
|
|
16
|
+
compress = "gzip"
|
|
17
|
+
[headers.values]
|
|
18
|
+
Cache-Control = "hello44"
|
package/dist/deploy.js
CHANGED
|
@@ -83,7 +83,10 @@ export function compressSelectedLocalFiles(localFiles, localCfg) {
|
|
|
83
83
|
if (headers.compress) {
|
|
84
84
|
compressed = true;
|
|
85
85
|
if (headers.compress == "gzip") {
|
|
86
|
-
|
|
86
|
+
// use the original file md5 as filename, this prevents conflicts
|
|
87
|
+
// and is easier than reusing f.s3Path or f.localPath
|
|
88
|
+
// (no need to create subdirectories or handle path delimiters portably)
|
|
89
|
+
const compressedPath = path.join(tmpDir, f.md5);
|
|
87
90
|
yield gzipFile(localPath, compressedPath);
|
|
88
91
|
const [stat, compressedMd5] = yield Promise.all([
|
|
89
92
|
fs.promises.stat(compressedPath),
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { PromisePool } from "@supercharge/promise-pool";
|
|
11
|
+
import { GetBucketWebsiteCommand, HeadObjectCommand, } from "@aws-sdk/client-s3";
|
|
12
|
+
export function getBucketWebsite(client, Bucket) {
|
|
13
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
14
|
+
var _a, _b;
|
|
15
|
+
const cmd = new GetBucketWebsiteCommand({ Bucket });
|
|
16
|
+
const response = yield client.send(cmd);
|
|
17
|
+
if (response.RedirectAllRequestsTo) {
|
|
18
|
+
// NB: garage does not currently support RedirectAllRequestsTo so this should never happen
|
|
19
|
+
throw `remote website configuration: RedirectAllRequestsTo is specified; \
|
|
20
|
+
this is currently unsupported by dxfl`;
|
|
21
|
+
}
|
|
22
|
+
const index_page = (_a = response.ErrorDocument) === null || _a === void 0 ? void 0 : _a.Key;
|
|
23
|
+
const error_page = (_b = response.IndexDocument) === null || _b === void 0 ? void 0 : _b.Suffix;
|
|
24
|
+
let redirects = [];
|
|
25
|
+
if (response.RoutingRules) {
|
|
26
|
+
for (const rule of response.RoutingRules) {
|
|
27
|
+
// If no Condition is specified, then the redirect always applies, which is equivalent
|
|
28
|
+
// to matching on an empty prefix
|
|
29
|
+
let prefix = "";
|
|
30
|
+
let if_error = undefined;
|
|
31
|
+
if (rule.Condition) {
|
|
32
|
+
if (rule.Condition.HttpErrorCodeReturnedEquals) {
|
|
33
|
+
if (rule.Condition.HttpErrorCodeReturnedEquals == "404") {
|
|
34
|
+
if_error = 404;
|
|
35
|
+
}
|
|
36
|
+
else {
|
|
37
|
+
// currently not supported by garage
|
|
38
|
+
throw `remote website configuration: 'if_error' specified with a different \
|
|
39
|
+
code than 404; this is currently unsupported by dxfl`;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
if (rule.Condition.KeyPrefixEquals) {
|
|
43
|
+
prefix = rule.Condition.KeyPrefixEquals;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
const hostname = rule.Redirect.HostName;
|
|
47
|
+
const code = rule.Redirect.HttpRedirectCode
|
|
48
|
+
? parseInt(rule.Redirect.HttpRedirectCode)
|
|
49
|
+
: undefined;
|
|
50
|
+
let protocol = undefined;
|
|
51
|
+
if (rule.Redirect.Protocol) {
|
|
52
|
+
if (rule.Redirect.Protocol == "http" ||
|
|
53
|
+
rule.Redirect.Protocol == "https") {
|
|
54
|
+
protocol = rule.Redirect.Protocol;
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
// currently not supported by garage
|
|
58
|
+
throw `remote website configuration: 'protocol' is neither http or https; \
|
|
59
|
+
this is currently unsupported by dxfl`;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
let to;
|
|
63
|
+
if (rule.Redirect.ReplaceKeyPrefixWith) {
|
|
64
|
+
to = {
|
|
65
|
+
kind: "replace_prefix",
|
|
66
|
+
prefix: rule.Redirect.ReplaceKeyPrefixWith,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
else if (rule.Redirect.ReplaceKeyWith) {
|
|
70
|
+
to = { kind: "replace", target: rule.Redirect.ReplaceKeyWith };
|
|
71
|
+
}
|
|
72
|
+
else {
|
|
73
|
+
// not completely sure whether this is the correct behavior, but it should
|
|
74
|
+
// match garage's implementation
|
|
75
|
+
to = { kind: "replace", target: "" };
|
|
76
|
+
}
|
|
77
|
+
redirects.push({
|
|
78
|
+
kind: "bucket",
|
|
79
|
+
r: { prefix, if_error, hostname, code, protocol, to },
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return { index_page, error_page, redirects };
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// XXX
|
|
87
|
+
const MD5METAFIELD = "dfl-md5sum";
|
|
88
|
+
export function getBucketFilesDetails(client, Bucket, files) {
|
|
89
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
90
|
+
let res = new Map();
|
|
91
|
+
function doFile(file) {
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
var _a;
|
|
94
|
+
const resp = yield client.send(new HeadObjectCommand({ Bucket, Key: file }));
|
|
95
|
+
if (resp.$metadata.httpStatusCode != 200) {
|
|
96
|
+
// TODO: better error handling?
|
|
97
|
+
throw resp;
|
|
98
|
+
}
|
|
99
|
+
res.set(file, {
|
|
100
|
+
md5: (_a = resp.Metadata) === null || _a === void 0 ? void 0 : _a[MD5METAFIELD],
|
|
101
|
+
redirect: resp.WebsiteRedirectLocation,
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
yield PromisePool.for(files).withConcurrency(50).process(doFile);
|
|
106
|
+
return res;
|
|
107
|
+
});
|
|
108
|
+
}
|
package/dist/errors.js
ADDED
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { deploy } from "./deploy.js";
|
|
|
6
6
|
import { empty } from "./empty.js";
|
|
7
7
|
import { vhostsList } from "./vhosts.js";
|
|
8
8
|
import { inspect } from "./inspect.js";
|
|
9
|
-
program.name("dxfl").description("Deuxfleurs CLI tool").version("0.5.
|
|
9
|
+
program.name("dxfl").description("Deuxfleurs CLI tool").version("0.5.5");
|
|
10
10
|
program
|
|
11
11
|
.command("login")
|
|
12
12
|
.description("Link your Deuxfleurs account with this tool.")
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dxfl",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.5",
|
|
4
4
|
"description": "",
|
|
5
5
|
"license": "EUPL-1.2",
|
|
6
6
|
"author": "Deuxfleurs Team <coucou@deuxfleurs.fr>",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"prettier-check": "npx prettier . --check"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@aws-sdk/client-s3": "^3.
|
|
26
|
+
"@aws-sdk/client-s3": "^3.1012.0",
|
|
27
27
|
"@commander-js/extra-typings": "^14.0.0",
|
|
28
28
|
"@supercharge/promise-pool": "^3.2.0",
|
|
29
29
|
"@types/node": "^25.2.1",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
toto
|
package/public/d/b
ADDED
package/public/d/c
ADDED
package/public/d/dd/bla
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
bla
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
test
|
package/public/def
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
oops, 404 error!
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hello there! 花見 toto312
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
hello there! 花見 toto2
|
package/public/slt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
abc
|
package/public/test.html
ADDED
|
File without changes
|
package/public/toto.jpg
ADDED
|
File without changes
|
package/public/toto.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
abc defuieu
|
package/public/toto2.txt
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
abc
|
package/dist/_empty.js
DELETED
|
@@ -1,64 +0,0 @@
|
|
|
1
|
-
// // TODO: Refacto the codebase global before integrate this feature
|
|
2
|
-
export {};
|
|
3
|
-
// export async function empty(
|
|
4
|
-
// vhost: string,
|
|
5
|
-
// options: { dryRun: boolean | undefined },
|
|
6
|
-
// ) {
|
|
7
|
-
// const conf = await openApiConf();
|
|
8
|
-
// // Get website info from guichet (bucket name and keys)
|
|
9
|
-
// const api = new WebsiteApi(conf);
|
|
10
|
-
// let vhostInfo = await api.getWebsite({ vhost }).catch(err => {
|
|
11
|
-
// if (err.response.status == 404) {
|
|
12
|
-
// console.error(`Error: website '${vhost}' does not exist`);
|
|
13
|
-
// } else {
|
|
14
|
-
// console.error(err);
|
|
15
|
-
// }
|
|
16
|
-
// process.exit(1);
|
|
17
|
-
// });
|
|
18
|
-
// // List the files currently stored in the bucket
|
|
19
|
-
// // @FIXME this info could be returned by the guichet API
|
|
20
|
-
// const s3client = new S3Client({
|
|
21
|
-
// endpoint: "https://garage.deuxfleurs.fr",
|
|
22
|
-
// region: "garage",
|
|
23
|
-
// forcePathStyle: true,
|
|
24
|
-
// credentials: {
|
|
25
|
-
// accessKeyId: vhostInfo.accessKeyId!,
|
|
26
|
-
// secretAccessKey: vhostInfo.secretAccessKey!,
|
|
27
|
-
// },
|
|
28
|
-
// });
|
|
29
|
-
// const Bucket = vhostInfo.vhost!.name!;
|
|
30
|
-
// const filesToDelete = [...(await getBucketFiles(s3client, Bucket))].map(
|
|
31
|
-
// ([name, { size }]) => ({
|
|
32
|
-
// name,
|
|
33
|
-
// size,
|
|
34
|
-
// }),
|
|
35
|
-
// );
|
|
36
|
-
// for (const file of filesToDelete) {
|
|
37
|
-
// process.stdout.write(`Deleting ${file.name}\n`);
|
|
38
|
-
// }
|
|
39
|
-
// // If not in dry-run mode, send the delete command
|
|
40
|
-
// if (!options.dryRun) {
|
|
41
|
-
// const resp = await deleteFiles(s3client, Bucket, filesToDelete);
|
|
42
|
-
// if (resp && resp!.$metadata.httpStatusCode != 200) {
|
|
43
|
-
// // TODO: better error handling?
|
|
44
|
-
// console.error(resp);
|
|
45
|
-
// process.exit(1);
|
|
46
|
-
// }
|
|
47
|
-
// }
|
|
48
|
-
// // Display a summary
|
|
49
|
-
// function sum(a: number[]) {
|
|
50
|
-
// return a.reduce((x, y) => x + y, 0);
|
|
51
|
-
// }
|
|
52
|
-
// function formatFiles(n: number) {
|
|
53
|
-
// if (n == 1) {
|
|
54
|
-
// return `${n} file `;
|
|
55
|
-
// } else {
|
|
56
|
-
// return `${n} files`;
|
|
57
|
-
// }
|
|
58
|
-
// }
|
|
59
|
-
// const sizeDeleted = sum(filesToDelete.map(f => f.size ?? 0));
|
|
60
|
-
// process.stdout.write("\nSummary:\n");
|
|
61
|
-
// process.stdout.write(
|
|
62
|
-
// `${formatFiles(filesToDelete.length)} deleted (${formatBytes(sizeDeleted)})\n`,
|
|
63
|
-
// );
|
|
64
|
-
// }
|