@hydra-acp/archiver 0.1.8 → 0.1.10
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/backend/s3.js +155 -46
- package/dist/backend/s3.js.map +1 -1
- package/dist/util/aws-credentials.js +82 -0
- package/dist/util/aws-credentials.js.map +1 -0
- package/package.json +1 -2
package/dist/backend/s3.js
CHANGED
|
@@ -1,80 +1,189 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createHash, createHmac } from "node:crypto";
|
|
2
|
+
import { loadAwsCredentials, resolveRegion } from "../util/aws-credentials.js";
|
|
2
3
|
import { logger } from "../util/log.js";
|
|
3
4
|
const log = logger("backend.s3");
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
// ── Signature V4 ─────────────────────────────────────────────────────────────
|
|
6
|
+
function sha256hex(data) {
|
|
7
|
+
return createHash("sha256").update(data).digest("hex");
|
|
8
|
+
}
|
|
9
|
+
function hmac(key, data) {
|
|
10
|
+
return createHmac("sha256", key).update(data).digest();
|
|
11
|
+
}
|
|
12
|
+
function signingKey(secret, date, region) {
|
|
13
|
+
return hmac(hmac(hmac(hmac(`AWS4${secret}`, date), region), "s3"), "aws4_request");
|
|
14
|
+
}
|
|
15
|
+
function isoDateTime() {
|
|
16
|
+
const now = new Date();
|
|
17
|
+
const datetime = now.toISOString().replace(/[-:]/g, "").slice(0, 15) + "Z";
|
|
18
|
+
return { datetime, date: datetime.slice(0, 8) };
|
|
19
|
+
}
|
|
20
|
+
function buildAuthHeaders(method, url, body, creds, region) {
|
|
21
|
+
const { datetime, date } = isoDateTime();
|
|
22
|
+
const bodyHash = sha256hex(body ?? Buffer.alloc(0));
|
|
23
|
+
const toSign = [
|
|
24
|
+
["host", url.host],
|
|
25
|
+
["x-amz-content-sha256", bodyHash],
|
|
26
|
+
["x-amz-date", datetime],
|
|
27
|
+
];
|
|
28
|
+
if (creds.sessionToken)
|
|
29
|
+
toSign.push(["x-amz-security-token", creds.sessionToken]);
|
|
30
|
+
toSign.sort((a, b) => a[0].localeCompare(b[0]));
|
|
31
|
+
const canonicalHeaders = toSign.map(([k, v]) => `${k}:${v}`).join("\n") + "\n";
|
|
32
|
+
const signedHeaderNames = toSign.map(([k]) => k).join(";");
|
|
33
|
+
const canonicalQuery = [...url.searchParams.entries()]
|
|
34
|
+
.sort((a, b) => a[0].localeCompare(b[0]))
|
|
35
|
+
.map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
|
|
36
|
+
.join("&");
|
|
37
|
+
const canonicalRequest = [
|
|
38
|
+
method,
|
|
39
|
+
url.pathname,
|
|
40
|
+
canonicalQuery,
|
|
41
|
+
canonicalHeaders,
|
|
42
|
+
signedHeaderNames,
|
|
43
|
+
bodyHash,
|
|
44
|
+
].join("\n");
|
|
45
|
+
const credentialScope = `${date}/${region}/s3/aws4_request`;
|
|
46
|
+
const stringToSign = [
|
|
47
|
+
"AWS4-HMAC-SHA256",
|
|
48
|
+
datetime,
|
|
49
|
+
credentialScope,
|
|
50
|
+
sha256hex(canonicalRequest),
|
|
51
|
+
].join("\n");
|
|
52
|
+
const signature = createHmac("sha256", signingKey(creds.secretAccessKey, date, region))
|
|
53
|
+
.update(stringToSign)
|
|
54
|
+
.digest("hex");
|
|
55
|
+
const result = {
|
|
56
|
+
authorization: `AWS4-HMAC-SHA256 Credential=${creds.accessKeyId}/${credentialScope}, SignedHeaders=${signedHeaderNames}, Signature=${signature}`,
|
|
57
|
+
"x-amz-content-sha256": bodyHash,
|
|
58
|
+
"x-amz-date": datetime,
|
|
59
|
+
};
|
|
60
|
+
if (creds.sessionToken)
|
|
61
|
+
result["x-amz-security-token"] = creds.sessionToken;
|
|
62
|
+
return result;
|
|
63
|
+
}
|
|
64
|
+
// ── URL helpers ───────────────────────────────────────────────────────────────
|
|
65
|
+
// Encode each path segment individually, preserving slashes between them.
|
|
66
|
+
function encodePath(path) {
|
|
67
|
+
return path.split("/").map(encodeURIComponent).join("/");
|
|
68
|
+
}
|
|
69
|
+
function buildUrl(bucket, keyPath, // leading slash, empty for bucket root ("/" or "/key")
|
|
70
|
+
query, region, endpoint) {
|
|
71
|
+
const base = endpoint !== undefined
|
|
72
|
+
? `${endpoint.replace(/\/$/, "")}/${bucket}` // path-style for custom endpoints
|
|
73
|
+
: `https://${bucket}.s3.${region}.amazonaws.com`; // virtual-hosted for AWS
|
|
74
|
+
const url = new URL(`${base}${keyPath}`);
|
|
75
|
+
for (const [k, v] of Object.entries(query))
|
|
76
|
+
url.searchParams.set(k, v);
|
|
77
|
+
return url;
|
|
78
|
+
}
|
|
79
|
+
// ── XML helpers ───────────────────────────────────────────────────────────────
|
|
80
|
+
function xmlFirst(xml, tag) {
|
|
81
|
+
const m = new RegExp(`<${tag}(?:\\s[^>]*)?>([\\s\\S]*?)</${tag}>`).exec(xml);
|
|
82
|
+
if (!m || m[1] === undefined)
|
|
83
|
+
return undefined;
|
|
84
|
+
return m[1]
|
|
85
|
+
.replace(/&/g, "&")
|
|
86
|
+
.replace(/</g, "<")
|
|
87
|
+
.replace(/>/g, ">")
|
|
88
|
+
.replace(/"/g, '"')
|
|
89
|
+
.replace(/'/g, "'");
|
|
90
|
+
}
|
|
91
|
+
function parseContents(xml) {
|
|
92
|
+
const out = [];
|
|
93
|
+
const re = /<Contents>([\s\S]*?)<\/Contents>/g;
|
|
94
|
+
let m;
|
|
95
|
+
while ((m = re.exec(xml)) !== null) {
|
|
96
|
+
const block = m[1];
|
|
97
|
+
if (block === undefined)
|
|
98
|
+
continue;
|
|
99
|
+
const key = xmlFirst(block, "Key");
|
|
100
|
+
if (!key)
|
|
101
|
+
continue;
|
|
102
|
+
out.push({
|
|
103
|
+
key,
|
|
104
|
+
size: Number.parseInt(xmlFirst(block, "Size") ?? "0", 10),
|
|
105
|
+
modifiedAt: xmlFirst(block, "LastModified") ?? new Date(0).toISOString(),
|
|
106
|
+
});
|
|
8
107
|
}
|
|
9
|
-
return
|
|
108
|
+
return out;
|
|
10
109
|
}
|
|
110
|
+
// ── HTTP request ─────────────────────────────────────────────────────────────
|
|
111
|
+
async function s3Fetch(method, url, body, creds, region) {
|
|
112
|
+
const headers = buildAuthHeaders(method, url, body, creds, region);
|
|
113
|
+
if (body !== undefined)
|
|
114
|
+
headers["content-length"] = String(body.length);
|
|
115
|
+
const res = await fetch(url, {
|
|
116
|
+
method,
|
|
117
|
+
headers,
|
|
118
|
+
...(body !== undefined ? { body } : {}),
|
|
119
|
+
});
|
|
120
|
+
if (!res.ok) {
|
|
121
|
+
const text = await res.text().catch(() => "");
|
|
122
|
+
const code = xmlFirst(text, "Code") ?? String(res.status);
|
|
123
|
+
const msg = xmlFirst(text, "Message") ?? res.statusText;
|
|
124
|
+
throw new Error(`s3: ${method} ${url.pathname} — ${code}: ${msg}`);
|
|
125
|
+
}
|
|
126
|
+
return res;
|
|
127
|
+
}
|
|
128
|
+
// ── Backend ───────────────────────────────────────────────────────────────────
|
|
11
129
|
export class S3Backend {
|
|
12
130
|
opts;
|
|
13
|
-
|
|
131
|
+
credentials;
|
|
132
|
+
region;
|
|
14
133
|
prefix;
|
|
15
134
|
constructor(opts) {
|
|
16
135
|
this.opts = opts;
|
|
136
|
+
this.region = resolveRegion(opts.region);
|
|
17
137
|
this.prefix = opts.prefix;
|
|
18
138
|
}
|
|
19
139
|
async init() {
|
|
20
|
-
this.
|
|
21
|
-
|
|
22
|
-
...(this.opts.endpoint !== undefined
|
|
23
|
-
? { endpoint: this.opts.endpoint, forcePathStyle: true }
|
|
24
|
-
: {}),
|
|
25
|
-
});
|
|
26
|
-
await this.client.send(new HeadBucketCommand({ Bucket: this.opts.bucket }));
|
|
140
|
+
this.credentials = loadAwsCredentials();
|
|
141
|
+
await s3Fetch("HEAD", buildUrl(this.opts.bucket, "/", {}, this.region, this.opts.endpoint), undefined, this.credentials, this.region);
|
|
27
142
|
log.info(`s3 backend ready: bucket=${this.opts.bucket}${this.prefix !== "" ? ` prefix="${this.prefix}"` : ""}`);
|
|
28
143
|
}
|
|
29
144
|
async list() {
|
|
30
|
-
const
|
|
145
|
+
const creds = this.requireCredentials();
|
|
31
146
|
const entries = [];
|
|
32
147
|
let continuationToken;
|
|
33
148
|
do {
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
for (const obj of
|
|
42
|
-
if (!obj.Key)
|
|
43
|
-
continue;
|
|
149
|
+
const query = { "list-type": "2" };
|
|
150
|
+
if (this.prefix !== "")
|
|
151
|
+
query["prefix"] = this.prefix;
|
|
152
|
+
if (continuationToken !== undefined)
|
|
153
|
+
query["continuation-token"] = continuationToken;
|
|
154
|
+
const url = buildUrl(this.opts.bucket, "/", query, this.region, this.opts.endpoint);
|
|
155
|
+
const xml = await (await s3Fetch("GET", url, undefined, creds, this.region)).text();
|
|
156
|
+
for (const obj of parseContents(xml)) {
|
|
44
157
|
entries.push({
|
|
45
|
-
key: this.prefix !== "" ? obj.
|
|
46
|
-
size: obj.
|
|
47
|
-
modifiedAt:
|
|
158
|
+
key: this.prefix !== "" ? obj.key.slice(this.prefix.length) : obj.key,
|
|
159
|
+
size: obj.size,
|
|
160
|
+
modifiedAt: obj.modifiedAt,
|
|
48
161
|
});
|
|
49
162
|
}
|
|
50
|
-
continuationToken =
|
|
163
|
+
continuationToken =
|
|
164
|
+
xmlFirst(xml, "IsTruncated") === "true"
|
|
165
|
+
? xmlFirst(xml, "NextContinuationToken")
|
|
166
|
+
: undefined;
|
|
51
167
|
} while (continuationToken !== undefined);
|
|
52
168
|
return entries;
|
|
53
169
|
}
|
|
54
170
|
async get(key) {
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
throw new Error(`s3: empty body for key ${key}`);
|
|
59
|
-
return streamToBuffer(res.Body);
|
|
171
|
+
const creds = this.requireCredentials();
|
|
172
|
+
const url = buildUrl(this.opts.bucket, `/${encodePath(this.prefix + key)}`, {}, this.region, this.opts.endpoint);
|
|
173
|
+
return Buffer.from(await (await s3Fetch("GET", url, undefined, creds, this.region)).arrayBuffer());
|
|
60
174
|
}
|
|
61
175
|
async put(key, data) {
|
|
62
|
-
const
|
|
63
|
-
await
|
|
64
|
-
Bucket: this.opts.bucket,
|
|
65
|
-
Key: this.prefix + key,
|
|
66
|
-
Body: data,
|
|
67
|
-
ContentType: "application/octet-stream",
|
|
68
|
-
}));
|
|
176
|
+
const creds = this.requireCredentials();
|
|
177
|
+
await s3Fetch("PUT", buildUrl(this.opts.bucket, `/${encodePath(this.prefix + key)}`, {}, this.region, this.opts.endpoint), data, creds, this.region);
|
|
69
178
|
}
|
|
70
179
|
async delete(key) {
|
|
71
|
-
const
|
|
72
|
-
await
|
|
180
|
+
const creds = this.requireCredentials();
|
|
181
|
+
await s3Fetch("DELETE", buildUrl(this.opts.bucket, `/${encodePath(this.prefix + key)}`, {}, this.region, this.opts.endpoint), undefined, creds, this.region);
|
|
73
182
|
}
|
|
74
|
-
|
|
75
|
-
if (!this.
|
|
183
|
+
requireCredentials() {
|
|
184
|
+
if (!this.credentials)
|
|
76
185
|
throw new Error("S3Backend used before init()");
|
|
77
|
-
return this.
|
|
186
|
+
return this.credentials;
|
|
78
187
|
}
|
|
79
188
|
}
|
|
80
189
|
//# sourceMappingURL=s3.js.map
|
package/dist/backend/s3.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"s3.js","sourceRoot":"","sources":["../../src/backend/s3.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"s3.js","sourceRoot":"","sources":["../../src/backend/s3.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
function parseIni(text) {
|
|
5
|
+
const sections = new Map();
|
|
6
|
+
let current;
|
|
7
|
+
for (const raw of text.split(/\r?\n/)) {
|
|
8
|
+
const line = raw.trim();
|
|
9
|
+
if (!line || line.startsWith("#") || line.startsWith(";"))
|
|
10
|
+
continue;
|
|
11
|
+
if (line.startsWith("[") && line.endsWith("]")) {
|
|
12
|
+
// config file uses "[profile name]", credentials file uses "[name]"
|
|
13
|
+
const name = line.slice(1, -1).trim().replace(/^profile\s+/, "");
|
|
14
|
+
current = new Map();
|
|
15
|
+
sections.set(name, current);
|
|
16
|
+
}
|
|
17
|
+
else if (current) {
|
|
18
|
+
const eq = line.indexOf("=");
|
|
19
|
+
if (eq >= 0)
|
|
20
|
+
current.set(line.slice(0, eq).trim(), line.slice(eq + 1).trim());
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return sections;
|
|
24
|
+
}
|
|
25
|
+
function readSection(path, profile) {
|
|
26
|
+
try {
|
|
27
|
+
return parseIni(readFileSync(path, "utf8")).get(profile);
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
// Credential chain: env vars > ~/.aws/credentials > ~/.aws/config
|
|
34
|
+
export function loadAwsCredentials(profile) {
|
|
35
|
+
const profileName = profile ??
|
|
36
|
+
process.env.AWS_PROFILE ??
|
|
37
|
+
process.env.AWS_DEFAULT_PROFILE ??
|
|
38
|
+
"default";
|
|
39
|
+
const envKey = process.env.AWS_ACCESS_KEY_ID;
|
|
40
|
+
const envSecret = process.env.AWS_SECRET_ACCESS_KEY;
|
|
41
|
+
if (envKey && envSecret) {
|
|
42
|
+
return {
|
|
43
|
+
accessKeyId: envKey,
|
|
44
|
+
secretAccessKey: envSecret,
|
|
45
|
+
sessionToken: process.env.AWS_SESSION_TOKEN,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
const home = homedir();
|
|
49
|
+
const credsPath = process.env.AWS_SHARED_CREDENTIALS_FILE ??
|
|
50
|
+
resolve(home, ".aws", "credentials");
|
|
51
|
+
const cfgPath = process.env.AWS_CONFIG_FILE ?? resolve(home, ".aws", "config");
|
|
52
|
+
const creds = readSection(credsPath, profileName);
|
|
53
|
+
const cfg = readSection(cfgPath, profileName);
|
|
54
|
+
const accessKeyId = creds?.get("aws_access_key_id") ?? cfg?.get("aws_access_key_id");
|
|
55
|
+
const secretAccessKey = creds?.get("aws_secret_access_key") ?? cfg?.get("aws_secret_access_key");
|
|
56
|
+
if (!accessKeyId || !secretAccessKey) {
|
|
57
|
+
throw new Error(`No AWS credentials found for profile "${profileName}". ` +
|
|
58
|
+
`Set AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY, or configure ~/.aws/credentials.`);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
accessKeyId,
|
|
62
|
+
secretAccessKey,
|
|
63
|
+
sessionToken: creds?.get("aws_session_token") ?? cfg?.get("aws_session_token"),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Region resolution: explicit > env vars > ~/.aws/config > us-east-1
|
|
67
|
+
export function resolveRegion(explicit, profile) {
|
|
68
|
+
if (explicit)
|
|
69
|
+
return explicit;
|
|
70
|
+
if (process.env.AWS_REGION)
|
|
71
|
+
return process.env.AWS_REGION;
|
|
72
|
+
if (process.env.AWS_DEFAULT_REGION)
|
|
73
|
+
return process.env.AWS_DEFAULT_REGION;
|
|
74
|
+
const profileName = profile ??
|
|
75
|
+
process.env.AWS_PROFILE ??
|
|
76
|
+
process.env.AWS_DEFAULT_PROFILE ??
|
|
77
|
+
"default";
|
|
78
|
+
const cfgPath = process.env.AWS_CONFIG_FILE ??
|
|
79
|
+
resolve(homedir(), ".aws", "config");
|
|
80
|
+
return readSection(cfgPath, profileName)?.get("region") ?? "us-east-1";
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=aws-credentials.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"aws-credentials.js","sourceRoot":"","sources":["../../src/util/aws-credentials.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hydra-acp/archiver",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.10",
|
|
4
4
|
"description": "Session sync extension for hydra-acp — uploads session bundles to a shared backend (Google Drive, fs) and imports peers' bundles.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -30,7 +30,6 @@
|
|
|
30
30
|
"node": ">=20"
|
|
31
31
|
},
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@aws-sdk/client-s3": "^3.700.0",
|
|
34
33
|
"googleapis": "^171.0.0",
|
|
35
34
|
"ws": "^8.20.0"
|
|
36
35
|
},
|