@s-hirano-ist/s-scripts 4.0.1 → 4.2.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/dist/{chunk-6ADTNHCK.js → chunk-3INTMHPH.js} +1 -1
- package/dist/{chunk-Q6QUV5VR.js → chunk-BYEEIQ7O.js} +1 -1
- package/dist/chunk-IYGLKY27.js +11 -0
- package/dist/chunk-M6LFKOUH.js +160 -0
- package/dist/{chunk-XG6LAGZO.js → chunk-R5EI7JXR.js} +26 -16
- package/dist/{chunk-M275YZQP.js → chunk-RLAYINCA.js} +17 -14
- package/dist/{chunk-AUD3PD6K.js → chunk-RO5QUZFR.js} +1 -1
- package/dist/{chunk-TK456CR2.js → chunk-ZMQUO6OA.js} +1 -1
- package/dist/cleanup-minio-images.js +26 -36
- package/dist/fetch-articles.js +1 -1
- package/dist/fetch-books.js +7 -12
- package/dist/fetch-images.js +7 -12
- package/dist/fetch-notes.js +1 -1
- package/dist/ingest-articles.js +7 -4
- package/dist/ingest-books.js +3 -3
- package/dist/ingest-images.js +3 -3
- package/dist/ingest-notes.js +3 -3
- package/dist/reset-articles.js +2 -2
- package/dist/reset-books.js +2 -2
- package/dist/reset-images.js +2 -2
- package/dist/reset-notes.js +2 -2
- package/dist/revert-articles.js +2 -2
- package/dist/revert-books.js +2 -2
- package/dist/revert-images.js +2 -2
- package/dist/revert-notes.js +2 -2
- package/dist/update-json-articles.js +4 -1
- package/package.json +10 -10
- package/dist/chunk-F4J6JBR4.js +0 -115
|
@@ -2,7 +2,7 @@ import { createRequire as __cliCreateRequire } from "node:module"; const require
|
|
|
2
2
|
import {
|
|
3
3
|
makeExportedAt,
|
|
4
4
|
withTransactionRetry
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RLAYINCA.js";
|
|
6
6
|
|
|
7
7
|
// ../application/dist/batch/notes-batch-service.js
|
|
8
8
|
var NotesBatchApplicationService = class {
|
|
@@ -2,7 +2,7 @@ import { createRequire as __cliCreateRequire } from "node:module"; const require
|
|
|
2
2
|
import {
|
|
3
3
|
makeExportedAt,
|
|
4
4
|
withTransactionRetry
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-RLAYINCA.js";
|
|
6
6
|
|
|
7
7
|
// ../application/dist/batch/articles-batch-service.js
|
|
8
8
|
var ArticlesBatchApplicationService = class {
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/lib/article-og-image-url.ts
|
|
4
|
+
function resolveArticleOgImageUrl(value, articleUrl) {
|
|
5
|
+
if (value === null || value === void 0 || value.trim() === "") return null;
|
|
6
|
+
return URL.canParse(value) ? value : new URL(value, articleUrl).href;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export {
|
|
10
|
+
resolveArticleOgImageUrl
|
|
11
|
+
};
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { createRequire as __cliCreateRequire } from "node:module"; const require = __cliCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// ../storage/dist/src/client.js
|
|
4
|
+
import { S3Client, CreateBucketCommand, DeleteBucketCommand, PutObjectCommand, GetObjectCommand, HeadObjectCommand, DeleteObjectCommand, paginateListObjectsV2 } from "@aws-sdk/client-s3";
|
|
5
|
+
import { createWriteStream } from "node:fs";
|
|
6
|
+
import { Readable } from "node:stream";
|
|
7
|
+
import { pipeline } from "node:stream/promises";
|
|
8
|
+
function loadStorageConfig(env = process.env) {
|
|
9
|
+
const required = (key) => {
|
|
10
|
+
const value = env[key];
|
|
11
|
+
if (!value)
|
|
12
|
+
throw new Error(`${key} is required`);
|
|
13
|
+
return value;
|
|
14
|
+
};
|
|
15
|
+
const endpoint = required("S3_ENDPOINT");
|
|
16
|
+
const url = new URL(endpoint);
|
|
17
|
+
if (!["http:", "https:"].includes(url.protocol) || url.username || url.password || url.search || url.hash || url.pathname !== "/")
|
|
18
|
+
throw new Error("S3_ENDPOINT must be an HTTP(S) origin");
|
|
19
|
+
const pathStyle = env.S3_FORCE_PATH_STYLE ?? "false";
|
|
20
|
+
if (pathStyle !== "true" && pathStyle !== "false")
|
|
21
|
+
throw new Error("S3_FORCE_PATH_STYLE must be true or false");
|
|
22
|
+
return {
|
|
23
|
+
endpoint,
|
|
24
|
+
region: required("S3_REGION"),
|
|
25
|
+
accessKeyId: required("S3_ACCESS_KEY_ID"),
|
|
26
|
+
secretAccessKey: required("S3_SECRET_ACCESS_KEY"),
|
|
27
|
+
forcePathStyle: pathStyle === "true"
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
function isObjectNotFound(error) {
|
|
31
|
+
if (typeof error !== "object" || error === null)
|
|
32
|
+
return false;
|
|
33
|
+
const value = error;
|
|
34
|
+
return value.$metadata?.httpStatusCode === 404 && (value.name === "NotFound" || value.name === "NoSuchKey");
|
|
35
|
+
}
|
|
36
|
+
function createObjectStorageClient(config = loadStorageConfig()) {
|
|
37
|
+
const client = new S3Client({
|
|
38
|
+
endpoint: config.endpoint,
|
|
39
|
+
region: config.region,
|
|
40
|
+
credentials: {
|
|
41
|
+
accessKeyId: config.accessKeyId,
|
|
42
|
+
secretAccessKey: config.secretAccessKey
|
|
43
|
+
},
|
|
44
|
+
forcePathStyle: config.forcePathStyle
|
|
45
|
+
});
|
|
46
|
+
return {
|
|
47
|
+
async createBucket(bucket) {
|
|
48
|
+
await client.send(new CreateBucketCommand({ Bucket: bucket }));
|
|
49
|
+
},
|
|
50
|
+
async deleteBucket(bucket) {
|
|
51
|
+
await client.send(new DeleteBucketCommand({ Bucket: bucket }));
|
|
52
|
+
},
|
|
53
|
+
async putObject(bucket, key, body) {
|
|
54
|
+
const result = await client.send(new PutObjectCommand({ Bucket: bucket, Key: key, Body: body }));
|
|
55
|
+
return { etag: result.ETag, versionId: result.VersionId };
|
|
56
|
+
},
|
|
57
|
+
async getObject(bucket, key) {
|
|
58
|
+
const result = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
59
|
+
if (!(result.Body instanceof Readable))
|
|
60
|
+
throw new Error("Object storage returned no Node readable body");
|
|
61
|
+
return result.Body;
|
|
62
|
+
},
|
|
63
|
+
async downloadObject(bucket, key, destination) {
|
|
64
|
+
const result = await client.send(new GetObjectCommand({ Bucket: bucket, Key: key }));
|
|
65
|
+
if (!(result.Body instanceof Readable))
|
|
66
|
+
throw new Error("Object storage returned no Node readable body");
|
|
67
|
+
await pipeline(result.Body, createWriteStream(destination));
|
|
68
|
+
},
|
|
69
|
+
async statObject(bucket, key) {
|
|
70
|
+
const result = await client.send(new HeadObjectCommand({ Bucket: bucket, Key: key }));
|
|
71
|
+
return {
|
|
72
|
+
size: result.ContentLength,
|
|
73
|
+
etag: result.ETag,
|
|
74
|
+
lastModified: result.LastModified,
|
|
75
|
+
metaData: result.Metadata ?? {}
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
async removeObject(bucket, key) {
|
|
79
|
+
await client.send(new DeleteObjectCommand({ Bucket: bucket, Key: key }));
|
|
80
|
+
},
|
|
81
|
+
async listObjects(bucket, prefix) {
|
|
82
|
+
const keys = [];
|
|
83
|
+
for await (const page of paginateListObjectsV2({ client }, { Bucket: bucket, Prefix: prefix })) {
|
|
84
|
+
for (const object of page.Contents ?? [])
|
|
85
|
+
if (object.Key)
|
|
86
|
+
keys.push(object.Key);
|
|
87
|
+
}
|
|
88
|
+
return keys;
|
|
89
|
+
},
|
|
90
|
+
destroy() {
|
|
91
|
+
client.destroy();
|
|
92
|
+
}
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// ../storage/dist/index.js
|
|
97
|
+
import { S3ServiceException } from "@aws-sdk/client-s3";
|
|
98
|
+
|
|
99
|
+
// ../storage/dist/src/errors.js
|
|
100
|
+
var StorageOperationError = class extends Error {
|
|
101
|
+
context;
|
|
102
|
+
constructor(context, cause) {
|
|
103
|
+
super(`Storage ${context.operation} failed for ${context.objectKey}`, {
|
|
104
|
+
cause
|
|
105
|
+
});
|
|
106
|
+
this.name = "StorageOperationError";
|
|
107
|
+
this.context = context;
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
// ../storage/dist/src/storage-service.js
|
|
112
|
+
function createStorageService(client, bucketName, pathConfig) {
|
|
113
|
+
function buildObjectKey(path, isThumbnail) {
|
|
114
|
+
return `${isThumbnail ? pathConfig.thumbnailPrefix : pathConfig.originalPrefix}/${path}`;
|
|
115
|
+
}
|
|
116
|
+
function wrapStorageError(operation, objectKey, isThumbnail, error) {
|
|
117
|
+
throw new StorageOperationError({ operation, objectKey, bucketName, isThumbnail }, error);
|
|
118
|
+
}
|
|
119
|
+
return {
|
|
120
|
+
async uploadImage(path, bytes, isThumbnail) {
|
|
121
|
+
const objKey = buildObjectKey(path, isThumbnail);
|
|
122
|
+
try {
|
|
123
|
+
await client.putObject(bucketName, objKey, Buffer.from(bytes));
|
|
124
|
+
} catch (error) {
|
|
125
|
+
wrapStorageError("uploadImage", objKey, isThumbnail, error);
|
|
126
|
+
}
|
|
127
|
+
},
|
|
128
|
+
async getImage(path, isThumbnail) {
|
|
129
|
+
const objKey = buildObjectKey(path, isThumbnail);
|
|
130
|
+
try {
|
|
131
|
+
return await client.getObject(bucketName, objKey);
|
|
132
|
+
} catch (error) {
|
|
133
|
+
return wrapStorageError("getImage", objKey, isThumbnail, error);
|
|
134
|
+
}
|
|
135
|
+
},
|
|
136
|
+
async getImageOrThrow(path, isThumbnail) {
|
|
137
|
+
const objKey = buildObjectKey(path, isThumbnail);
|
|
138
|
+
try {
|
|
139
|
+
await client.statObject(bucketName, objKey);
|
|
140
|
+
} catch (error) {
|
|
141
|
+
wrapStorageError("getImageOrThrow", objKey, isThumbnail, error);
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
async deleteImage(path, isThumbnail) {
|
|
145
|
+
const objKey = buildObjectKey(path, isThumbnail);
|
|
146
|
+
try {
|
|
147
|
+
await client.removeObject(bucketName, objKey);
|
|
148
|
+
} catch (error) {
|
|
149
|
+
wrapStorageError("deleteImage", objKey, isThumbnail, error);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
export {
|
|
156
|
+
isObjectNotFound,
|
|
157
|
+
createObjectStorageClient,
|
|
158
|
+
StorageOperationError,
|
|
159
|
+
createStorageService
|
|
160
|
+
};
|
|
@@ -4,9 +4,11 @@ import {
|
|
|
4
4
|
readImageMetadata
|
|
5
5
|
} from "./chunk-KKZP7ZF3.js";
|
|
6
6
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
StorageOperationError,
|
|
8
|
+
createObjectStorageClient,
|
|
9
|
+
createStorageService,
|
|
10
|
+
isObjectNotFound
|
|
11
|
+
} from "./chunk-M6LFKOUH.js";
|
|
10
12
|
import {
|
|
11
13
|
CreatedAt,
|
|
12
14
|
ExportedStatus,
|
|
@@ -18,7 +20,7 @@ import {
|
|
|
18
20
|
import_generated,
|
|
19
21
|
makeId,
|
|
20
22
|
withTransactionRetry
|
|
21
|
-
} from "./chunk-
|
|
23
|
+
} from "./chunk-RLAYINCA.js";
|
|
22
24
|
import {
|
|
23
25
|
createPushoverService
|
|
24
26
|
} from "./chunk-7PVU2Z32.js";
|
|
@@ -222,7 +224,7 @@ var Quote = z4.string().max(512, { message: "tooLong" }).nullable().optional().b
|
|
|
222
224
|
var Url = z4.url({ message: "invalidFormat" }).min(1, { message: "required" }).max(1024, { message: "tooLong" }).refine(isValidHttpUrl, { message: "invalidFormat" }).brand();
|
|
223
225
|
var OgTitle = z4.string().max(512, { message: "tooLong" }).nullable().optional().brand();
|
|
224
226
|
var OgDescription = z4.string().max(1024, { message: "tooLong" }).nullable().optional().brand();
|
|
225
|
-
var OgImageUrl = z4.string().max(
|
|
227
|
+
var OgImageUrl = z4.string().max(4096, { message: "tooLong" }).refine(isValidHttpUrl, { message: "invalidFormat" }).nullable().optional().brand();
|
|
226
228
|
var Base2 = z4.object({
|
|
227
229
|
id: Id,
|
|
228
230
|
userId: UserId,
|
|
@@ -497,7 +499,9 @@ function createImportMediaStore(storage, processor) {
|
|
|
497
499
|
const target = storage[domain];
|
|
498
500
|
try {
|
|
499
501
|
await target.getImageOrThrow(path, false);
|
|
500
|
-
} catch {
|
|
502
|
+
} catch (error) {
|
|
503
|
+
if (!isObjectNotFound(error instanceof StorageOperationError ? error.cause : error))
|
|
504
|
+
throw error;
|
|
501
505
|
await target.uploadImage(path, bytes, false);
|
|
502
506
|
}
|
|
503
507
|
await target.uploadImage(path, await processor.createThumbnail(bytes, 192, 192), true);
|
|
@@ -512,16 +516,16 @@ function createImportMediaStore(storage, processor) {
|
|
|
512
516
|
// ../infrastructure/dist/shared/books-storage-service.js
|
|
513
517
|
var ORIGINAL_BOOK_IMAGE_PATH = "books/original";
|
|
514
518
|
var THUMBNAIL_BOOK_IMAGE_PATH = "books/thumbnail";
|
|
515
|
-
function createBooksStorageService(
|
|
516
|
-
return createStorageService(
|
|
519
|
+
function createBooksStorageService(storageClient, bucketName) {
|
|
520
|
+
return createStorageService(storageClient, bucketName, {
|
|
517
521
|
originalPrefix: ORIGINAL_BOOK_IMAGE_PATH,
|
|
518
522
|
thumbnailPrefix: THUMBNAIL_BOOK_IMAGE_PATH
|
|
519
523
|
});
|
|
520
524
|
}
|
|
521
525
|
|
|
522
|
-
// ../infrastructure/dist/shared/
|
|
523
|
-
function createImagesStorageService(
|
|
524
|
-
return createStorageService(
|
|
526
|
+
// ../infrastructure/dist/shared/images-storage-service.js
|
|
527
|
+
function createImagesStorageService(storageClient, bucketName) {
|
|
528
|
+
return createStorageService(storageClient, bucketName, {
|
|
525
529
|
originalPrefix: "images/original",
|
|
526
530
|
thumbnailPrefix: "images/thumbnail"
|
|
527
531
|
});
|
|
@@ -545,10 +549,10 @@ async function runContentImport(domain, source) {
|
|
|
545
549
|
});
|
|
546
550
|
try {
|
|
547
551
|
const mediaDomain = domain === "images" || domain === "books";
|
|
548
|
-
const bucket = process.env.
|
|
552
|
+
const bucket = process.env.S3_BUCKET_NAME;
|
|
549
553
|
if (mediaDomain && !dryRun && !bucket)
|
|
550
|
-
throw new Error("
|
|
551
|
-
const client = mediaDomain && !dryRun ?
|
|
554
|
+
throw new Error("S3_BUCKET_NAME is required.");
|
|
555
|
+
const client = mediaDomain && !dryRun ? createObjectStorageClient() : void 0;
|
|
552
556
|
const media = client && bucket ? createImportMediaStore(
|
|
553
557
|
{
|
|
554
558
|
images: createImagesStorageService(client, bucket),
|
|
@@ -573,11 +577,17 @@ async function runContentImport(domain, source) {
|
|
|
573
577
|
}
|
|
574
578
|
);
|
|
575
579
|
console.log(summary);
|
|
576
|
-
|
|
580
|
+
if (!dryRun)
|
|
581
|
+
await notification.notifyInfo(`ingest-${domain} completed`, {
|
|
582
|
+
caller: `ingest-${domain}`
|
|
583
|
+
}).catch((error) => console.error("\u901A\u77E5\u5931\u6557:", error));
|
|
577
584
|
} catch (error) {
|
|
578
585
|
console.error(error);
|
|
579
586
|
process.exitCode = 1;
|
|
580
|
-
|
|
587
|
+
if (!dryRun)
|
|
588
|
+
await notification.notifyError(`ingest-${domain} failed`, { caller: `ingest-${domain}` }).catch(
|
|
589
|
+
(notifyError) => console.error("\u901A\u77E5\u5931\u6557:", notifyError)
|
|
590
|
+
);
|
|
581
591
|
} finally {
|
|
582
592
|
await prisma.$disconnect();
|
|
583
593
|
}
|