feat: s3-compatible vault support

This commit is contained in:
break27 2024-10-30 01:23:55 +08:00
parent 520040a6bf
commit ed12a9e0f2
7 changed files with 712 additions and 96 deletions

1
.gitignore vendored
View File

@ -10,6 +10,7 @@ lerna-debug.log*
node_modules node_modules
dist dist
dist-ssr dist-ssr
dist-vault
*.local *.local
# Editor directories and files # Editor directories and files

View File

@ -3,8 +3,8 @@ description: an example web application
language: en language: en
vault: vault:
root: public type: file
cleanup: false path: /path/to/vault
metadata: metadata:
hidden: hidden:

721
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build", "build": "vite build",
"clean": "rm -rf ./dist", "clean": "rm -rf ./dist*",
"update": "node ./src/scripts/sprachbund.js", "update": "node ./src/scripts/sprachbund.js",
"preview": "vite preview" "preview": "vite preview"
}, },
@ -14,6 +14,7 @@
"@tailwindcss/typography": "^0.5.15", "@tailwindcss/typography": "^0.5.15",
"autoprefixer": "^10.4.20", "autoprefixer": "^10.4.20",
"front-matter": "^4.0.2", "front-matter": "^4.0.2",
"minio": "^8.0.2",
"mustache": "^4.2.0", "mustache": "^4.2.0",
"postcss": "^8.4.45", "postcss": "^8.4.45",
"tailwindcss": "^3.4.10", "tailwindcss": "^3.4.10",

2
public/.gitignore vendored
View File

@ -1,2 +0,0 @@
*
!.gitignore

View File

@ -6,6 +6,7 @@ export const PATH = {};
PATH.CONFIG = 'config.yaml'; PATH.CONFIG = 'config.yaml';
PATH.OUTPUT = 'dist'; PATH.OUTPUT = 'dist';
PATH.S3TEMP = 'dist-vault';
PATH.OBJECT = `${NATIVE ? PATH.OUTPUT : ''}/_site`; PATH.OBJECT = `${NATIVE ? PATH.OUTPUT : ''}/_site`;
PATH.INDEX = `${PATH.OBJECT}/index`; PATH.INDEX = `${PATH.OBJECT}/index`;
PATH.METADATA = `${PATH.OBJECT}/metadata`; PATH.METADATA = `${PATH.OBJECT}/metadata`;

View File

@ -2,12 +2,13 @@ import { PATH } from '../paths.js'
import { transform } from './zettelkasten.js' import { transform } from './zettelkasten.js'
import { Index, Attachment, Document } from './object.js' import { Index, Attachment, Document } from './object.js'
import { createHash } from 'node:crypto' import { Client } from 'minio'
import { createHash } from 'crypto'
import { encode, decode } from '@msgpack/msgpack' import { encode, decode } from '@msgpack/msgpack'
import Mustache from 'mustache' import Mustache from 'mustache'
import YAML from 'yaml' import YAML from 'yaml'
import fs from 'node:fs' import fs from 'fs'
import fm from'front-matter' import fm from'front-matter'
@ -139,8 +140,7 @@ function buildObject(index, ctx) /* always return falsy values */ {
fs.writeFileSync(path, content); fs.writeFileSync(path, content);
} }
function buildIndex(config) { function buildIndex(metadata, vault) {
let metadata = Index.Metadata(config["metadata"]);
let index = new Index(metadata, {}); let index = new Index(metadata, {});
if (! fs.existsSync(PATH.INDEX)) { if (! fs.existsSync(PATH.INDEX)) {
@ -148,26 +148,17 @@ function buildIndex(config) {
// or reuse it // or reuse it
fs.mkdirSync(PATH.OBJECT, { recursive: true }); fs.mkdirSync(PATH.OBJECT, { recursive: true });
walkSourceDir([config.vault.root], ctx => { walkSourceDir([vault], ctx => {
buildObject(index, ctx); buildObject(index, ctx);
}); });
if (config.vault.cleanup) {
fs.readdirSync(config.vault.root).forEach(name => {
if (name !== '.gitignore') {
let path = [config.vault.root, name].join('/');
fs.rmSync(path, { recursive: true, force: true });
}
});
}
return index; return index;
} }
let buffer = fs.readFileSync(PATH.INDEX); let buffer = fs.readFileSync(PATH.INDEX);
let legacy = decode(buffer); let legacy = decode(buffer);
walkSourceDir([config.vault.root], ctx => { walkSourceDir([vault], ctx => {
let path = ctx.path; let path = ctx.path;
let entry = legacy.object[path]; let entry = legacy.object[path];
// if no such legacy entry found, continue // if no such legacy entry found, continue
@ -197,6 +188,51 @@ function buildIndex(config) {
return index; return index;
} }
function prepareVault(config) {
switch (config["type"]) {
case "minio":
fs.rmSync(PATH.S3TEMP, { recursive: true, force: true });
fs.mkdirSync(PATH.S3TEMP);
let bucket = config["bucket"];
let prefix = config["prefix"];
let client = new Client(config);
let stream = client.listObjectsV2(bucket, prefix, true);
stream.on('data', item => {
let name = item.name.replace(prefix, '');
let path = `${PATH.S3TEMP}/${name}`;
if (name.endsWith('/')) {
if (! fs.existsSync(path)) fs.mkdirSync(path);
return;
}
stream.pause();
console.log(item.name);
client.fGetObject(bucket, item.name, path)
.catch(err => { throw err })
.finally(() => stream.resume());
});
return new Promise((resolve, reject) => {
stream.on('close', () => resolve(PATH.S3TEMP));
stream.on('error', er => reject(er));
});
case "file":
let path = config["path"];
if (fs.existsSync(path) && fs.lstatSync(path).isDirectory) {
return new Promise((resolve) => resolve(path));
}
throw new Error(path + ": not a valid vault");
default:
throw new Error("invalid vault type");
}
}
if (process.argv[1] === import.meta.filename) { if (process.argv[1] === import.meta.filename) {
// when running under node.js solely // when running under node.js solely
sprachbund().writeBundle(); sprachbund().writeBundle();
@ -213,11 +249,11 @@ export default function sprachbund() {
language: CONFIG?.language, language: CONFIG?.language,
}); });
}, },
writeBundle() { async writeBundle() {
let index = buildIndex(CONFIG); let vault = await prepareVault(CONFIG.vault);
let data = encode(index); let index = buildIndex(CONFIG.metadata, vault);
fs.writeFileSync(PATH.INDEX, data); fs.writeFileSync(PATH.INDEX, encode(index));
}, },
} }
} }