Files
Trilium/apps/script-deployer/src/dev.ts
T
2026-04-22 19:38:46 +03:00

110 lines
3.7 KiB
TypeScript

/**
* Starts a dedicated Trilium instance for script development.
*
* On first run it initializes a fresh database (no demo content),
* creates an ETAPI token and persists it to data/.etapi-token.
* On subsequent runs it just boots the server.
*/
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { resolve, join } from "node:path";
// ── Environment — must be set before any server module is imported ──────────
const DATA_DIR = resolve(__dirname, "../data");
const TOKEN_PATH = join(DATA_DIR, ".etapi-token");
const PORT = 37842;
mkdirSync(DATA_DIR, { recursive: true });
process.env.TRILIUM_DATA_DIR = DATA_DIR;
process.env.TRILIUM_PORT = String(PORT);
process.env.TRILIUM_RESOURCE_DIR = resolve(__dirname, "../../server/src");
process.env.NODE_ENV = "development";
process.env.TRILIUM_ENV = "dev";
// ── Constants ───────────────────────────────────────────────────────────────
const SCRIPTS_NOTE_ID = "_scripts";
const needsInit = !existsSync(join(DATA_DIR, "document.db"));
async function ensureTranslations() {
const i18n = await import("@triliumnext/server/src/services/i18n.js");
await i18n.initializeTranslations();
}
async function ensureDatabase() {
if (!needsInit) return;
console.log("No database found — creating a fresh instance…");
const cls = (await import("@triliumnext/server/src/services/cls.js")).default;
const sqlInit = (await import("@triliumnext/server/src/services/sql_init.js")).default;
// createInitialDatabase must run inside CLS (it touches becca).
await cls.init(async () => {
await sqlInit.createInitialDatabase(/* skipDemoDb */ true);
});
console.log("Database created.");
}
async function ensureEtapiToken() {
if (existsSync(TOKEN_PATH)) {
const token = readFileSync(TOKEN_PATH, "utf-8").trim();
console.log(`ETAPI token: ${token}`);
return;
}
const cls = (await import("@triliumnext/server/src/services/cls.js")).default;
const etapiTokens = (await import("@triliumnext/server/src/services/etapi_tokens.js")).default;
const authToken: string = cls.init(() => {
const { authToken } = etapiTokens.createToken("script-deployer");
return authToken;
});
writeFileSync(TOKEN_PATH, authToken + "\n");
console.log(`ETAPI token created and saved to ${TOKEN_PATH}`);
console.log(`ETAPI token: ${authToken}`);
}
async function ensureScriptsFolder() {
const becca = (await import("@triliumnext/server/src/becca/becca.js")).default;
if (becca.notes[SCRIPTS_NOTE_ID]) return;
const cls = (await import("@triliumnext/server/src/services/cls.js")).default;
const notesService = (await import("@triliumnext/server/src/services/notes.js")).default;
cls.init(() => {
notesService.createNewNote({
noteId: SCRIPTS_NOTE_ID,
parentNoteId: "root",
title: "Scripts",
type: "doc",
content: "",
});
});
console.log("Created 'Scripts' folder note.");
}
async function main() {
await ensureTranslations();
await ensureDatabase();
await ensureEtapiToken();
// Start the full HTTP server — this loads becca and makes the note
// tree available for subsequent setup steps.
const startTriliumServer = (await import("@triliumnext/server/src/www.js")).default;
await startTriliumServer();
await ensureScriptsFolder();
console.log(`\nScript-deployer Trilium instance running on http://localhost:${PORT}`);
console.log(`Token file: ${TOKEN_PATH}\n`);
}
main().catch((err) => {
console.error("Fatal:", err);
process.exit(1);
});