cnpmcore 4.25.0 → 4.25.1
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.
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { type FetchResult } from './AbstractBinary.ts';
|
|
2
2
|
import { BucketBinary } from './BucketBinary.ts';
|
|
3
|
+
/**
|
|
4
|
+
* NW.js binary adapter.
|
|
5
|
+
*
|
|
6
|
+
* Root directory: scraped from https://dl.nwjs.io/ (HTML index)
|
|
7
|
+
* Sub directories: listed via Cloudflare R2 S3-compatible API (ListObjectsV2)
|
|
8
|
+
*
|
|
9
|
+
* The R2 bucket credentials are publicly available in the nwjs frontend page.
|
|
10
|
+
* @see https://github.com/cnpm/cnpmcore/issues/891
|
|
11
|
+
*/
|
|
3
12
|
export declare class NwjsBinary extends BucketBinary {
|
|
4
|
-
private
|
|
13
|
+
private r2Endpoint;
|
|
14
|
+
private r2BucketName;
|
|
15
|
+
private r2AccessKeyId;
|
|
16
|
+
private r2SecretAccessKey;
|
|
17
|
+
private r2Region;
|
|
5
18
|
fetch(dir: string): Promise<FetchResult | undefined>;
|
|
19
|
+
private fetchRootDir;
|
|
20
|
+
private fetchSubDir;
|
|
21
|
+
/**
|
|
22
|
+
* Request R2 S3 ListObjectsV2 API with AWS Signature V4 authentication.
|
|
23
|
+
*/
|
|
24
|
+
private requestR2Xml;
|
|
25
|
+
private getSignatureKey;
|
|
6
26
|
}
|
|
@@ -4,56 +4,143 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
4
4
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
5
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
6
|
};
|
|
7
|
+
import crypto from 'node:crypto';
|
|
7
8
|
import { SingletonProto } from 'egg';
|
|
8
9
|
import binaries from "../../../../config/binaries.js";
|
|
9
10
|
import { BinaryType } from "../../enum/Binary.js";
|
|
10
11
|
import { BinaryAdapter } from "./AbstractBinary.js";
|
|
11
12
|
import { BucketBinary } from "./BucketBinary.js";
|
|
13
|
+
/**
|
|
14
|
+
* NW.js binary adapter.
|
|
15
|
+
*
|
|
16
|
+
* Root directory: scraped from https://dl.nwjs.io/ (HTML index)
|
|
17
|
+
* Sub directories: listed via Cloudflare R2 S3-compatible API (ListObjectsV2)
|
|
18
|
+
*
|
|
19
|
+
* The R2 bucket credentials are publicly available in the nwjs frontend page.
|
|
20
|
+
* @see https://github.com/cnpm/cnpmcore/issues/891
|
|
21
|
+
*/
|
|
12
22
|
let NwjsBinary = class NwjsBinary extends BucketBinary {
|
|
13
23
|
constructor() {
|
|
14
24
|
super(...arguments);
|
|
15
|
-
|
|
25
|
+
// Cloudflare R2 S3-compatible endpoint for nwjs bucket
|
|
26
|
+
this.r2Endpoint = 'https://6883a4a09c48918c64df1ec7ddb744ba.r2.cloudflarestorage.com';
|
|
27
|
+
this.r2BucketName = 'nwjs';
|
|
28
|
+
this.r2AccessKeyId = '90fdca5d031b05eed0ef896a56a9521a';
|
|
29
|
+
this.r2SecretAccessKey = '34eeb665b34bfb9b773a8ff763a15e76621f541fdbbadeca6ed23e6d99c878ad';
|
|
30
|
+
this.r2Region = 'auto';
|
|
16
31
|
}
|
|
17
32
|
async fetch(dir) {
|
|
18
33
|
const binaryConfig = binaries.nwjs;
|
|
19
34
|
const isRootDir = dir === '/';
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
35
|
+
if (isRootDir) {
|
|
36
|
+
return this.fetchRootDir(binaryConfig.distUrl);
|
|
37
|
+
}
|
|
38
|
+
return this.fetchSubDir(dir, binaryConfig);
|
|
39
|
+
}
|
|
40
|
+
async fetchRootDir(distUrl) {
|
|
41
|
+
const xml = await this.requestXml(distUrl);
|
|
24
42
|
if (!xml)
|
|
25
43
|
return;
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
//
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
name,
|
|
43
|
-
isDir: true,
|
|
44
|
-
url: '',
|
|
45
|
-
size: '-',
|
|
46
|
-
date,
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
return { items, nextParams: null };
|
|
44
|
+
const items = [];
|
|
45
|
+
const re = /<td><a [^>]+?>([^<]+?\/)<\/a><\/td><td [^>]+?>([^>]+?)<\/td>/gi;
|
|
46
|
+
const matchs = xml.matchAll(re);
|
|
47
|
+
for (const m of matchs) {
|
|
48
|
+
const name = m[1].trim();
|
|
49
|
+
// ignore live-build/ name
|
|
50
|
+
if (name === 'live-build/')
|
|
51
|
+
continue;
|
|
52
|
+
const date = m[2].trim();
|
|
53
|
+
items.push({
|
|
54
|
+
name,
|
|
55
|
+
isDir: true,
|
|
56
|
+
url: '',
|
|
57
|
+
size: '-',
|
|
58
|
+
date,
|
|
59
|
+
});
|
|
50
60
|
}
|
|
61
|
+
return { items, nextParams: null };
|
|
62
|
+
}
|
|
63
|
+
async fetchSubDir(dir, binaryConfig) {
|
|
64
|
+
// /foo/ => foo/
|
|
65
|
+
const prefix = dir.slice(1);
|
|
66
|
+
const xml = await this.requestR2Xml(prefix);
|
|
67
|
+
if (!xml)
|
|
68
|
+
return;
|
|
51
69
|
return { items: this.parseItems(xml, dir, binaryConfig), nextParams: null };
|
|
52
70
|
}
|
|
71
|
+
/**
|
|
72
|
+
* Request R2 S3 ListObjectsV2 API with AWS Signature V4 authentication.
|
|
73
|
+
*/
|
|
74
|
+
async requestR2Xml(prefix) {
|
|
75
|
+
const host = new URL(this.r2Endpoint).host;
|
|
76
|
+
const now = new Date();
|
|
77
|
+
const amzDate = now
|
|
78
|
+
.toISOString()
|
|
79
|
+
.replace(/[-:]/g, '')
|
|
80
|
+
.replace(/\.\d{3}/, '');
|
|
81
|
+
const dateStamp = amzDate.slice(0, 8);
|
|
82
|
+
const method = 'GET';
|
|
83
|
+
const canonicalUri = `/${this.r2BucketName}`;
|
|
84
|
+
// Build sorted query string
|
|
85
|
+
const queryParams = new URLSearchParams({
|
|
86
|
+
delimiter: '/',
|
|
87
|
+
'list-type': '2',
|
|
88
|
+
prefix: prefix,
|
|
89
|
+
});
|
|
90
|
+
queryParams.sort();
|
|
91
|
+
const canonicalQueryString = queryParams.toString();
|
|
92
|
+
// Build canonical request
|
|
93
|
+
const payloadHash = crypto.createHash('sha256').update('').digest('hex');
|
|
94
|
+
const canonicalHeaders = `host:${host}\nx-amz-content-sha256:${payloadHash}\nx-amz-date:${amzDate}\n`;
|
|
95
|
+
const signedHeaders = 'host;x-amz-content-sha256;x-amz-date';
|
|
96
|
+
const canonicalRequest = [
|
|
97
|
+
method,
|
|
98
|
+
canonicalUri,
|
|
99
|
+
canonicalQueryString,
|
|
100
|
+
canonicalHeaders,
|
|
101
|
+
signedHeaders,
|
|
102
|
+
payloadHash,
|
|
103
|
+
].join('\n');
|
|
104
|
+
// Build string to sign
|
|
105
|
+
const credentialScope = `${dateStamp}/${this.r2Region}/s3/aws4_request`;
|
|
106
|
+
const stringToSign = [
|
|
107
|
+
'AWS4-HMAC-SHA256',
|
|
108
|
+
amzDate,
|
|
109
|
+
credentialScope,
|
|
110
|
+
crypto.createHash('sha256').update(canonicalRequest).digest('hex'),
|
|
111
|
+
].join('\n');
|
|
112
|
+
// Calculate signature
|
|
113
|
+
const signingKey = this.getSignatureKey(this.r2SecretAccessKey, dateStamp, this.r2Region, 's3');
|
|
114
|
+
const signature = crypto.createHmac('sha256', signingKey).update(stringToSign).digest('hex');
|
|
115
|
+
const authorization = `AWS4-HMAC-SHA256 Credential=${this.r2AccessKeyId}/${credentialScope}, SignedHeaders=${signedHeaders}, Signature=${signature}`;
|
|
116
|
+
const url = `${this.r2Endpoint}/${this.r2BucketName}?${canonicalQueryString}`;
|
|
117
|
+
const { status, data, headers } = await this.httpclient.request(url, {
|
|
118
|
+
timeout: 30_000,
|
|
119
|
+
followRedirect: true,
|
|
120
|
+
gzip: true,
|
|
121
|
+
headers: {
|
|
122
|
+
Authorization: authorization,
|
|
123
|
+
'X-Amz-Content-Sha256': payloadHash,
|
|
124
|
+
'X-Amz-Date': amzDate,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const xml = data.toString();
|
|
128
|
+
if (status !== 200) {
|
|
129
|
+
this.logger.warn('[NwjsBinary.requestR2Xml:non-200-status] url: %s, status: %s, headers: %j, xml: %j', url, status, headers, xml);
|
|
130
|
+
return '';
|
|
131
|
+
}
|
|
132
|
+
return xml;
|
|
133
|
+
}
|
|
134
|
+
getSignatureKey(key, dateStamp, region, service) {
|
|
135
|
+
const kDate = crypto.createHmac('sha256', `AWS4${key}`).update(dateStamp).digest();
|
|
136
|
+
const kRegion = crypto.createHmac('sha256', kDate).update(region).digest();
|
|
137
|
+
const kService = crypto.createHmac('sha256', kRegion).update(service).digest();
|
|
138
|
+
return crypto.createHmac('sha256', kService).update('aws4_request').digest();
|
|
139
|
+
}
|
|
53
140
|
};
|
|
54
141
|
NwjsBinary = __decorate([
|
|
55
142
|
SingletonProto(),
|
|
56
143
|
BinaryAdapter(BinaryType.Nwjs)
|
|
57
144
|
], NwjsBinary);
|
|
58
145
|
export { NwjsBinary };
|
|
59
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
146
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiTndqc0JpbmFyeS5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uLy4uL2FwcC9jb21tb24vYWRhcHRlci9iaW5hcnkvTndqc0JpbmFyeS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxPQUFPLE1BQU0sTUFBTSxhQUFhLENBQUM7QUFFakMsT0FBTyxFQUFFLGNBQWMsRUFBRSxNQUFNLEtBQUssQ0FBQztBQUVyQyxPQUFPLFFBQVEsTUFBTSxnQ0FBZ0MsQ0FBQztBQUN0RCxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sc0JBQXNCLENBQUM7QUFDbEQsT0FBTyxFQUFFLGFBQWEsRUFBcUMsTUFBTSxxQkFBcUIsQ0FBQztBQUN2RixPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sbUJBQW1CLENBQUM7QUFFakQ7Ozs7Ozs7O0dBUUc7QUFHSSxJQUFNLFVBQVUsR0FBaEIsTUFBTSxVQUFXLFNBQVEsWUFBWTtJQUFyQzs7UUFDTCx1REFBdUQ7UUFDL0MsZUFBVSxHQUFHLG1FQUFtRSxDQUFDO1FBQ2pGLGlCQUFZLEdBQUcsTUFBTSxDQUFDO1FBQ3RCLGtCQUFhLEdBQUcsa0NBQWtDLENBQUM7UUFDbkQsc0JBQWlCLEdBQUcsa0VBQWtFLENBQUM7UUFDdkYsYUFBUSxHQUFHLE1BQU0sQ0FBQztJQWtJNUIsQ0FBQztJQWhJQyxLQUFLLENBQUMsS0FBSyxDQUFDLEdBQVc7UUFDckIsTUFBTSxZQUFZLEdBQUcsUUFBUSxDQUFDLElBQUksQ0FBQztRQUNuQyxNQUFNLFNBQVMsR0FBRyxHQUFHLEtBQUssR0FBRyxDQUFDO1FBRTlCLElBQUksU0FBUyxFQUFFLENBQUM7WUFDZCxPQUFPLElBQUksQ0FBQyxZQUFZLENBQUMsWUFBWSxDQUFDLE9BQU8sQ0FBQyxDQUFDO1FBQ2pELENBQUM7UUFFRCxPQUFPLElBQUksQ0FBQyxXQUFXLENBQUMsR0FBRyxFQUFFLFlBQVksQ0FBQyxDQUFDO0lBQzdDLENBQUM7SUFFTyxLQUFLLENBQUMs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
|