106 lines
4.1 KiB
JavaScript
106 lines
4.1 KiB
JavaScript
import memoryDriver from "unstorage/drivers/memory";
|
|
import { createStorage, prefixStorage } from "unstorage";
|
|
import { withBase } from "ufo";
|
|
import { createQuery } from "../query/query.js";
|
|
import { createNav } from "../server/navigation.js";
|
|
import { createPipelineFetcher } from "../query/match/pipeline.js";
|
|
import { useContentPreview } from "./preview.js";
|
|
import { useRuntimeConfig, useNuxtApp } from "#imports";
|
|
const withContentBase = (url) => withBase(url, useRuntimeConfig().public.content.api.baseURL);
|
|
export const contentStorage = prefixStorage(createStorage({ driver: memoryDriver() }), "@content");
|
|
export function createDB(storage) {
|
|
async function getItems() {
|
|
const keys = new Set(await storage.getKeys("cache:"));
|
|
const previewToken = useContentPreview().getPreviewToken();
|
|
if (previewToken) {
|
|
const previewMeta = await storage.getItem(`${previewToken}$`).then((data) => data || {});
|
|
if (Array.isArray(previewMeta.ignoreSources)) {
|
|
const sources = previewMeta.ignoreSources.map((s) => `cache:${s.trim()}:`);
|
|
for (const key of keys) {
|
|
if (sources.some((s) => key.startsWith(s))) {
|
|
keys.delete(key);
|
|
}
|
|
}
|
|
}
|
|
const previewKeys = await storage.getKeys(`${previewToken}:`);
|
|
const previewContents = await Promise.all(previewKeys.map((key) => storage.getItem(key)));
|
|
for (const pItem of previewContents) {
|
|
keys.delete(`cache:${pItem._id}`);
|
|
if (!pItem.__deleted) {
|
|
keys.add(`${previewToken}:${pItem._id}`);
|
|
}
|
|
}
|
|
}
|
|
const items = await Promise.all(Array.from(keys).map((key) => storage.getItem(key)));
|
|
return items;
|
|
}
|
|
return {
|
|
storage,
|
|
fetch: createPipelineFetcher(getItems),
|
|
query: (initialParams) => createQuery(
|
|
createPipelineFetcher(getItems),
|
|
{ initialParams, legacy: false }
|
|
)
|
|
};
|
|
}
|
|
let contentDatabase = null;
|
|
let contentDatabaseInitPromise = null;
|
|
export async function useContentDatabase() {
|
|
if (contentDatabaseInitPromise) {
|
|
await contentDatabaseInitPromise;
|
|
} else if (!contentDatabase) {
|
|
contentDatabaseInitPromise = initContentDatabase();
|
|
contentDatabase = await contentDatabaseInitPromise;
|
|
}
|
|
return contentDatabase;
|
|
}
|
|
async function initContentDatabase() {
|
|
const nuxtApp = useNuxtApp();
|
|
const { content } = useRuntimeConfig().public;
|
|
const _contentDatabase = createDB(contentStorage);
|
|
const integrity = await _contentDatabase.storage.getItem("integrity");
|
|
if (content.integrity !== +(integrity || 0)) {
|
|
const { contents, navigation } = await $fetch(withContentBase(content.integrity ? `cache.${content.integrity}.json` : "cache.json"));
|
|
await Promise.all(
|
|
contents.map((content2) => _contentDatabase.storage.setItem(`cache:${content2._id}`, content2))
|
|
);
|
|
await _contentDatabase.storage.setItem("navigation", navigation);
|
|
await _contentDatabase.storage.setItem("integrity", content.integrity);
|
|
}
|
|
await nuxtApp.callHook("content:storage", _contentDatabase.storage);
|
|
return _contentDatabase;
|
|
}
|
|
export async function generateNavigation(query) {
|
|
const db = await useContentDatabase();
|
|
if (!useContentPreview().getPreviewToken() && Object.keys(query || {}).length === 0) {
|
|
return db.storage.getItem("navigation");
|
|
}
|
|
const contents = await db.query(query).where({
|
|
/**
|
|
* Partial contents are not included in the navigation
|
|
* A partial content is a content that has `_` prefix in its path
|
|
*/
|
|
_partial: false,
|
|
/**
|
|
* Exclude any pages which have opted out of navigation via frontmatter.
|
|
*/
|
|
navigation: {
|
|
$ne: false
|
|
}
|
|
}).find();
|
|
const dirConfigs = await db.query().where({ _path: /\/_dir$/i, _partial: true }).find();
|
|
const configs = dirConfigs.result.reduce((configs2, conf) => {
|
|
if (conf.title?.toLowerCase() === "dir") {
|
|
conf.title = void 0;
|
|
}
|
|
const key = conf._path.split("/").slice(0, -1).join("/") || "/";
|
|
configs2[key] = {
|
|
...conf,
|
|
// Extract meta from body. (non MD files)
|
|
...conf.body
|
|
};
|
|
return configs2;
|
|
}, {});
|
|
return createNav(contents?.result || contents, configs);
|
|
}
|