110 lines
3.7 KiB
TypeScript
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);
|
|
});
|