initial
This commit is contained in:
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
.nogit/
|
||||||
|
|
||||||
|
# artifacts
|
||||||
|
coverage/
|
||||||
|
public/
|
||||||
|
|
||||||
|
# installs
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.yarn/
|
||||||
|
.cache/
|
||||||
|
.rpt2_cache
|
||||||
|
|
||||||
|
# builds
|
||||||
|
dist/
|
||||||
|
dist_*/
|
||||||
|
|
||||||
|
#------# custom
|
||||||
30
.smartconfig.json
Normal file
30
.smartconfig.json
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
{
|
||||||
|
"@git.zone/cli": {
|
||||||
|
"projectType": "npm",
|
||||||
|
"module": {
|
||||||
|
"githost": "code.foss.global",
|
||||||
|
"gitscope": "apiclient.xyz",
|
||||||
|
"gitrepo": "bookstack",
|
||||||
|
"description": "A TypeScript API client for BookStack, providing easy access to books, chapters, pages, shelves, and more.",
|
||||||
|
"npmPackagename": "@apiclient.xyz/bookstack",
|
||||||
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"bookstack",
|
||||||
|
"api client",
|
||||||
|
"TypeScript",
|
||||||
|
"wiki",
|
||||||
|
"documentation"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"release": {
|
||||||
|
"registries": [
|
||||||
|
"https://verdaccio.lossless.digital",
|
||||||
|
"https://registry.npmjs.org"
|
||||||
|
],
|
||||||
|
"accessLevel": "public"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@ship.zone/szci": {
|
||||||
|
"npmGlobalTools": []
|
||||||
|
}
|
||||||
|
}
|
||||||
21
license
Normal file
21
license
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Lossless GmbH (https://lossless.com)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
55
package.json
Normal file
55
package.json
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
{
|
||||||
|
"name": "@apiclient.xyz/bookstack",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"private": false,
|
||||||
|
"description": "A TypeScript API client for BookStack, providing easy access to books, chapters, pages, shelves, and more.",
|
||||||
|
"main": "dist_ts/index.js",
|
||||||
|
"typings": "dist_ts/index.d.ts",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"test": "(tstest test/ --verbose --timeout 600)",
|
||||||
|
"build": "(tsbuild --web --allowimplicitany)"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://code.foss.global/apiclient.xyz/bookstack.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"bookstack",
|
||||||
|
"api client",
|
||||||
|
"TypeScript",
|
||||||
|
"wiki",
|
||||||
|
"documentation",
|
||||||
|
"books",
|
||||||
|
"pages",
|
||||||
|
"chapters"
|
||||||
|
],
|
||||||
|
"author": "Lossless GmbH",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"@push.rocks/smartrequest": "^5.0.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@git.zone/tsbuild": "^3.1.0",
|
||||||
|
"@git.zone/tsrun": "^2.0.0",
|
||||||
|
"@git.zone/tstest": "^2.8.2",
|
||||||
|
"@push.rocks/qenv": "^6.1.3",
|
||||||
|
"@push.rocks/tapbundle": "^5.6.0",
|
||||||
|
"@types/node": "^22.15.3"
|
||||||
|
},
|
||||||
|
"files": [
|
||||||
|
"ts/**/*",
|
||||||
|
"ts_web/**/*",
|
||||||
|
"dist/**/*",
|
||||||
|
"dist_*/**/*",
|
||||||
|
"dist_ts/**/*",
|
||||||
|
"dist_ts_web/**/*",
|
||||||
|
"assets/**/*",
|
||||||
|
"cli.js",
|
||||||
|
"npmextra.json",
|
||||||
|
"readme.md"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
"last 1 chrome versions"
|
||||||
|
]
|
||||||
|
}
|
||||||
10808
pnpm-lock.yaml
generated
Normal file
10808
pnpm-lock.yaml
generated
Normal file
File diff suppressed because it is too large
Load Diff
4
qenv.yml
Normal file
4
qenv.yml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
required:
|
||||||
|
- BOOKSTACK_URL
|
||||||
|
- BOOKSTACK_TOKENID
|
||||||
|
- BOOKSTACK_TOKENSECRET
|
||||||
59
test/test.node.ts
Normal file
59
test/test.node.ts
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
import { tap, expect } from '@push.rocks/tapbundle';
|
||||||
|
import * as qenv from '@push.rocks/qenv';
|
||||||
|
import { BookStackAccount } from '../ts/index.js';
|
||||||
|
|
||||||
|
const testQenv = new qenv.Qenv('./', '.nogit/');
|
||||||
|
|
||||||
|
let bookstackAccount: BookStackAccount;
|
||||||
|
|
||||||
|
tap.test('should create a BookStackAccount instance', async () => {
|
||||||
|
const baseUrl = await testQenv.getEnvVarOnDemand('BOOKSTACK_URL');
|
||||||
|
const tokenId = await testQenv.getEnvVarOnDemand('BOOKSTACK_TOKENID');
|
||||||
|
const tokenSecret = await testQenv.getEnvVarOnDemand('BOOKSTACK_TOKENSECRET');
|
||||||
|
bookstackAccount = new BookStackAccount(baseUrl, tokenId, tokenSecret);
|
||||||
|
expect(bookstackAccount).toBeInstanceOf(BookStackAccount);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should test connection', async () => {
|
||||||
|
const result = await bookstackAccount.testConnection();
|
||||||
|
expect(result).toHaveProperty('ok');
|
||||||
|
console.log('Connection test:', result);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should get system info', async () => {
|
||||||
|
const info = await bookstackAccount.getSystemInfo();
|
||||||
|
expect(info).toHaveProperty('version');
|
||||||
|
console.log('System info:', info);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should list books', async () => {
|
||||||
|
const books = await bookstackAccount.getBooks();
|
||||||
|
expect(books).toBeArray();
|
||||||
|
console.log(`Found ${books.length} books`);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should list shelves', async () => {
|
||||||
|
const shelves = await bookstackAccount.getShelves();
|
||||||
|
expect(shelves).toBeArray();
|
||||||
|
console.log(`Found ${shelves.length} shelves`);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should list chapters', async () => {
|
||||||
|
const chapters = await bookstackAccount.getChapters();
|
||||||
|
expect(chapters).toBeArray();
|
||||||
|
console.log(`Found ${chapters.length} chapters`);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should list pages', async () => {
|
||||||
|
const pages = await bookstackAccount.getPages();
|
||||||
|
expect(pages).toBeArray();
|
||||||
|
console.log(`Found ${pages.length} pages`);
|
||||||
|
});
|
||||||
|
|
||||||
|
tap.test('should search content', async () => {
|
||||||
|
const results = await bookstackAccount.search('test');
|
||||||
|
expect(results).toBeArray();
|
||||||
|
console.log(`Found ${results.length} search results`);
|
||||||
|
});
|
||||||
|
|
||||||
|
export default tap.start();
|
||||||
5
ts/00_commitinfo_data.ts
Normal file
5
ts/00_commitinfo_data.ts
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
export const commitinfo = {
|
||||||
|
name: '@apiclient.xyz/bookstack',
|
||||||
|
version: '1.0.0',
|
||||||
|
description: 'A TypeScript API client for BookStack, providing easy access to books, chapters, pages, shelves, and more.',
|
||||||
|
};
|
||||||
592
ts/bookstack.classes.account.ts
Normal file
592
ts/bookstack.classes.account.ts
Normal file
@@ -0,0 +1,592 @@
|
|||||||
|
import * as plugins from './bookstack.plugins.js';
|
||||||
|
import type {
|
||||||
|
ITestConnectionResult,
|
||||||
|
IBookStackListParams,
|
||||||
|
IBookStackListResponse,
|
||||||
|
IBookStackBook,
|
||||||
|
IBookStackChapter,
|
||||||
|
IBookStackPage,
|
||||||
|
IBookStackShelf,
|
||||||
|
IBookStackAttachment,
|
||||||
|
IBookStackComment,
|
||||||
|
IBookStackImage,
|
||||||
|
IBookStackUser,
|
||||||
|
IBookStackRole,
|
||||||
|
IBookStackSearchResult,
|
||||||
|
IBookStackAuditLogEntry,
|
||||||
|
IBookStackRecycleBinItem,
|
||||||
|
IBookStackContentPermission,
|
||||||
|
IBookStackSystemInfo,
|
||||||
|
IBookStackTag,
|
||||||
|
} from './bookstack.interfaces.js';
|
||||||
|
import { BookStackBook } from './bookstack.classes.book.js';
|
||||||
|
import { BookStackChapter } from './bookstack.classes.chapter.js';
|
||||||
|
import { BookStackPage } from './bookstack.classes.page.js';
|
||||||
|
import { BookStackShelf } from './bookstack.classes.shelf.js';
|
||||||
|
import { autoPaginate } from './bookstack.helpers.js';
|
||||||
|
|
||||||
|
export class BookStackAccount {
|
||||||
|
private baseUrl: string;
|
||||||
|
private tokenId: string;
|
||||||
|
private tokenSecret: string;
|
||||||
|
|
||||||
|
constructor(baseUrl: string, tokenId: string, tokenSecret: string) {
|
||||||
|
this.baseUrl = baseUrl.replace(/\/+$/, '');
|
||||||
|
this.tokenId = tokenId;
|
||||||
|
this.tokenSecret = tokenSecret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// HTTP helpers (internal)
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
async request<T = any>(
|
||||||
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||||
|
path: string,
|
||||||
|
data?: any,
|
||||||
|
): Promise<T> {
|
||||||
|
const url = `${this.baseUrl}/api${path}`;
|
||||||
|
|
||||||
|
let builder = plugins.smartrequest.SmartRequest.create()
|
||||||
|
.url(url)
|
||||||
|
.header('Authorization', `Token ${this.tokenId}:${this.tokenSecret}`)
|
||||||
|
.header('Content-Type', 'application/json');
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
builder = builder.json(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
let response: Awaited<ReturnType<typeof builder.get>>;
|
||||||
|
switch (method) {
|
||||||
|
case 'GET':
|
||||||
|
response = await builder.get();
|
||||||
|
break;
|
||||||
|
case 'POST':
|
||||||
|
response = await builder.post();
|
||||||
|
break;
|
||||||
|
case 'PUT':
|
||||||
|
response = await builder.put();
|
||||||
|
break;
|
||||||
|
case 'DELETE':
|
||||||
|
response = await builder.delete();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`${method} ${path}: ${response.status} ${response.statusText} - ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
return (await response.json()) as T;
|
||||||
|
} catch {
|
||||||
|
return undefined as unknown as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
async requestText(
|
||||||
|
method: 'GET' | 'POST' | 'PUT' | 'DELETE',
|
||||||
|
path: string,
|
||||||
|
): Promise<string> {
|
||||||
|
const url = `${this.baseUrl}/api${path}`;
|
||||||
|
|
||||||
|
let builder = plugins.smartrequest.SmartRequest.create()
|
||||||
|
.url(url)
|
||||||
|
.header('Authorization', `Token ${this.tokenId}:${this.tokenSecret}`)
|
||||||
|
.header('Accept', 'text/plain');
|
||||||
|
|
||||||
|
let response: Awaited<ReturnType<typeof builder.get>>;
|
||||||
|
switch (method) {
|
||||||
|
case 'GET':
|
||||||
|
response = await builder.get();
|
||||||
|
break;
|
||||||
|
case 'POST':
|
||||||
|
response = await builder.post();
|
||||||
|
break;
|
||||||
|
case 'PUT':
|
||||||
|
response = await builder.put();
|
||||||
|
break;
|
||||||
|
case 'DELETE':
|
||||||
|
response = await builder.delete();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!response.ok) {
|
||||||
|
const errorText = await response.text();
|
||||||
|
throw new Error(`${method} ${path}: ${response.status} ${response.statusText} - ${errorText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response.text();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
async requestBinary(path: string): Promise<Uint8Array> {
|
||||||
|
const url = `${this.baseUrl}/api${path}`;
|
||||||
|
const response = await fetch(url, {
|
||||||
|
headers: {
|
||||||
|
Authorization: `Token ${this.tokenId}:${this.tokenSecret}`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error(`GET ${path}: ${response.status} ${response.statusText}`);
|
||||||
|
}
|
||||||
|
const buf = await response.arrayBuffer();
|
||||||
|
return new Uint8Array(buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @internal — build a URL with list query params */
|
||||||
|
buildListUrl(basePath: string, opts?: IBookStackListParams): string {
|
||||||
|
const params: string[] = [];
|
||||||
|
if (opts?.count !== undefined) params.push(`count=${opts.count}`);
|
||||||
|
if (opts?.offset !== undefined) params.push(`offset=${opts.offset}`);
|
||||||
|
if (opts?.sort) params.push(`sort=${encodeURIComponent(opts.sort)}`);
|
||||||
|
if (opts?.filter) {
|
||||||
|
for (const [key, value] of Object.entries(opts.filter)) {
|
||||||
|
params.push(`filter[${encodeURIComponent(key)}]=${encodeURIComponent(value)}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params.length > 0 ? `${basePath}?${params.join('&')}` : basePath;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Connection
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async testConnection(): Promise<ITestConnectionResult> {
|
||||||
|
try {
|
||||||
|
await this.request<IBookStackListResponse<IBookStackBook>>('GET', '/books?count=1');
|
||||||
|
return { ok: true };
|
||||||
|
} catch (err) {
|
||||||
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Books
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getBooks(opts?: IBookStackListParams): Promise<BookStackBook[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackBook>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/books', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
).then((books) => books.map((b) => new BookStackBook(this, b)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getBook(id: number): Promise<BookStackBook> {
|
||||||
|
const raw = await this.request<IBookStackBook>('GET', `/books/${id}`);
|
||||||
|
return new BookStackBook(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createBook(data: {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
description_html?: string;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
default_template_id?: number | null;
|
||||||
|
}): Promise<BookStackBook> {
|
||||||
|
const raw = await this.request<IBookStackBook>('POST', '/books', data);
|
||||||
|
return new BookStackBook(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Chapters
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getChapters(opts?: IBookStackListParams): Promise<BookStackChapter[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackChapter>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/chapters', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
).then((chapters) => chapters.map((c) => new BookStackChapter(this, c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getChapter(id: number): Promise<BookStackChapter> {
|
||||||
|
const raw = await this.request<IBookStackChapter>('GET', `/chapters/${id}`);
|
||||||
|
return new BookStackChapter(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createChapter(data: {
|
||||||
|
book_id: number;
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
description_html?: string;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
priority?: number;
|
||||||
|
default_template_id?: number | null;
|
||||||
|
}): Promise<BookStackChapter> {
|
||||||
|
const raw = await this.request<IBookStackChapter>('POST', '/chapters', data);
|
||||||
|
return new BookStackChapter(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Pages
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getPages(opts?: IBookStackListParams): Promise<BookStackPage[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackPage>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/pages', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
).then((pages) => pages.map((p) => new BookStackPage(this, p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getPage(id: number): Promise<BookStackPage> {
|
||||||
|
const raw = await this.request<IBookStackPage>('GET', `/pages/${id}`);
|
||||||
|
return new BookStackPage(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createPage(data: {
|
||||||
|
name: string;
|
||||||
|
book_id?: number;
|
||||||
|
chapter_id?: number;
|
||||||
|
html?: string;
|
||||||
|
markdown?: string;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
priority?: number;
|
||||||
|
}): Promise<BookStackPage> {
|
||||||
|
const raw = await this.request<IBookStackPage>('POST', '/pages', data);
|
||||||
|
return new BookStackPage(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Shelves
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getShelves(opts?: IBookStackListParams): Promise<BookStackShelf[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackShelf>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/shelves', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
).then((shelves) => shelves.map((s) => new BookStackShelf(this, s)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getShelf(id: number): Promise<BookStackShelf> {
|
||||||
|
const raw = await this.request<IBookStackShelf>('GET', `/shelves/${id}`);
|
||||||
|
return new BookStackShelf(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createShelf(data: {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
description_html?: string;
|
||||||
|
books?: number[];
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
}): Promise<BookStackShelf> {
|
||||||
|
const raw = await this.request<IBookStackShelf>('POST', '/shelves', data);
|
||||||
|
return new BookStackShelf(this, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Attachments
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getAttachments(opts?: IBookStackListParams): Promise<IBookStackAttachment[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackAttachment>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/attachments', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getAttachment(id: number): Promise<IBookStackAttachment> {
|
||||||
|
return this.request<IBookStackAttachment>('GET', `/attachments/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createAttachment(data: {
|
||||||
|
name: string;
|
||||||
|
uploaded_to: number;
|
||||||
|
link?: string;
|
||||||
|
}): Promise<IBookStackAttachment> {
|
||||||
|
return this.request<IBookStackAttachment>('POST', '/attachments', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateAttachment(id: number, data: {
|
||||||
|
name?: string;
|
||||||
|
uploaded_to?: number;
|
||||||
|
link?: string;
|
||||||
|
}): Promise<IBookStackAttachment> {
|
||||||
|
return this.request<IBookStackAttachment>('PUT', `/attachments/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteAttachment(id: number): Promise<void> {
|
||||||
|
await this.request('DELETE', `/attachments/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Comments
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getComments(opts?: IBookStackListParams): Promise<IBookStackComment[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackComment>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/comments', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getComment(id: number): Promise<IBookStackComment> {
|
||||||
|
return this.request<IBookStackComment>('GET', `/comments/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createComment(data: {
|
||||||
|
page_id: number;
|
||||||
|
html: string;
|
||||||
|
reply_to?: number;
|
||||||
|
content_ref?: string;
|
||||||
|
}): Promise<IBookStackComment> {
|
||||||
|
return this.request<IBookStackComment>('POST', '/comments', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateComment(id: number, data: {
|
||||||
|
html?: string;
|
||||||
|
archived?: boolean;
|
||||||
|
}): Promise<IBookStackComment> {
|
||||||
|
return this.request<IBookStackComment>('PUT', `/comments/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteComment(id: number): Promise<void> {
|
||||||
|
await this.request('DELETE', `/comments/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Image Gallery
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getImages(opts?: IBookStackListParams): Promise<IBookStackImage[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackImage>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/image-gallery', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getImage(id: number): Promise<IBookStackImage> {
|
||||||
|
return this.request<IBookStackImage>('GET', `/image-gallery/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateImage(id: number, data: {
|
||||||
|
name?: string;
|
||||||
|
}): Promise<IBookStackImage> {
|
||||||
|
return this.request<IBookStackImage>('PUT', `/image-gallery/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteImage(id: number): Promise<void> {
|
||||||
|
await this.request('DELETE', `/image-gallery/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Users
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getUsers(opts?: IBookStackListParams): Promise<IBookStackUser[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackUser>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/users', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getUser(id: number): Promise<IBookStackUser> {
|
||||||
|
return this.request<IBookStackUser>('GET', `/users/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createUser(data: {
|
||||||
|
name: string;
|
||||||
|
email: string;
|
||||||
|
password?: string;
|
||||||
|
roles?: number[];
|
||||||
|
send_invite?: boolean;
|
||||||
|
external_auth_id?: string;
|
||||||
|
language?: string;
|
||||||
|
}): Promise<IBookStackUser> {
|
||||||
|
return this.request<IBookStackUser>('POST', '/users', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateUser(id: number, data: {
|
||||||
|
name?: string;
|
||||||
|
email?: string;
|
||||||
|
password?: string;
|
||||||
|
roles?: number[];
|
||||||
|
external_auth_id?: string;
|
||||||
|
language?: string;
|
||||||
|
}): Promise<IBookStackUser> {
|
||||||
|
return this.request<IBookStackUser>('PUT', `/users/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteUser(id: number, opts?: { migrate_ownership_id?: number }): Promise<void> {
|
||||||
|
const path = opts?.migrate_ownership_id
|
||||||
|
? `/users/${id}?migrate_ownership_id=${opts.migrate_ownership_id}`
|
||||||
|
: `/users/${id}`;
|
||||||
|
await this.request('DELETE', path);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Roles
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getRoles(opts?: IBookStackListParams): Promise<IBookStackRole[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackRole>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/roles', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async getRole(id: number): Promise<IBookStackRole> {
|
||||||
|
return this.request<IBookStackRole>('GET', `/roles/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async createRole(data: {
|
||||||
|
display_name: string;
|
||||||
|
description?: string;
|
||||||
|
mfa_enforced?: boolean;
|
||||||
|
external_auth_id?: string;
|
||||||
|
permissions?: string[];
|
||||||
|
}): Promise<IBookStackRole> {
|
||||||
|
return this.request<IBookStackRole>('POST', '/roles', data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateRole(id: number, data: {
|
||||||
|
display_name?: string;
|
||||||
|
description?: string;
|
||||||
|
mfa_enforced?: boolean;
|
||||||
|
external_auth_id?: string;
|
||||||
|
permissions?: string[];
|
||||||
|
}): Promise<IBookStackRole> {
|
||||||
|
return this.request<IBookStackRole>('PUT', `/roles/${id}`, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async deleteRole(id: number): Promise<void> {
|
||||||
|
await this.request('DELETE', `/roles/${id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Search
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async search(query: string, opts?: { page?: number; count?: number }): Promise<IBookStackSearchResult[]> {
|
||||||
|
let url = `/search?query=${encodeURIComponent(query)}`;
|
||||||
|
if (opts?.page) url += `&page=${opts.page}`;
|
||||||
|
if (opts?.count) url += `&count=${opts.count}`;
|
||||||
|
const result = await this.request<IBookStackListResponse<IBookStackSearchResult>>('GET', url);
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Audit Log
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getAuditLog(opts?: IBookStackListParams): Promise<IBookStackAuditLogEntry[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackAuditLogEntry>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/audit-log', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Recycle Bin
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getRecycleBinItems(opts?: IBookStackListParams): Promise<IBookStackRecycleBinItem[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.request<IBookStackListResponse<IBookStackRecycleBinItem>>(
|
||||||
|
'GET',
|
||||||
|
this.buildListUrl('/recycle-bin', { ...opts, offset, count }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async restoreRecycleBinItem(deletionId: number): Promise<{ restore_count: number }> {
|
||||||
|
return this.request<{ restore_count: number }>('PUT', `/recycle-bin/${deletionId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async destroyRecycleBinItem(deletionId: number): Promise<{ delete_count: number }> {
|
||||||
|
return this.request<{ delete_count: number }>('DELETE', `/recycle-bin/${deletionId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// Content Permissions
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getContentPermissions(
|
||||||
|
contentType: 'page' | 'book' | 'chapter' | 'bookshelf',
|
||||||
|
contentId: number,
|
||||||
|
): Promise<IBookStackContentPermission> {
|
||||||
|
return this.request<IBookStackContentPermission>(
|
||||||
|
'GET',
|
||||||
|
`/content-permissions/${contentType}/${contentId}`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async updateContentPermissions(
|
||||||
|
contentType: 'page' | 'book' | 'chapter' | 'bookshelf',
|
||||||
|
contentId: number,
|
||||||
|
data: {
|
||||||
|
owner_id?: number;
|
||||||
|
role_permissions?: {
|
||||||
|
role_id: number;
|
||||||
|
view: boolean;
|
||||||
|
create: boolean;
|
||||||
|
update: boolean;
|
||||||
|
delete: boolean;
|
||||||
|
}[];
|
||||||
|
fallback_permissions?: {
|
||||||
|
inheriting: boolean;
|
||||||
|
view?: boolean;
|
||||||
|
create?: boolean;
|
||||||
|
update?: boolean;
|
||||||
|
delete?: boolean;
|
||||||
|
};
|
||||||
|
},
|
||||||
|
): Promise<IBookStackContentPermission> {
|
||||||
|
return this.request<IBookStackContentPermission>(
|
||||||
|
'PUT',
|
||||||
|
`/content-permissions/${contentType}/${contentId}`,
|
||||||
|
data,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===========================================================================
|
||||||
|
// System
|
||||||
|
// ===========================================================================
|
||||||
|
|
||||||
|
public async getSystemInfo(): Promise<IBookStackSystemInfo> {
|
||||||
|
return this.request<IBookStackSystemInfo>('GET', '/system');
|
||||||
|
}
|
||||||
|
}
|
||||||
155
ts/bookstack.classes.book.ts
Normal file
155
ts/bookstack.classes.book.ts
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
import type { BookStackAccount } from './bookstack.classes.account.js';
|
||||||
|
import type {
|
||||||
|
IBookStackBook,
|
||||||
|
IBookStackChapter,
|
||||||
|
IBookStackPage,
|
||||||
|
IBookStackTag,
|
||||||
|
IBookStackListParams,
|
||||||
|
IBookStackListResponse,
|
||||||
|
TBookStackExportFormat,
|
||||||
|
} from './bookstack.interfaces.js';
|
||||||
|
import { BookStackChapter } from './bookstack.classes.chapter.js';
|
||||||
|
import { BookStackPage } from './bookstack.classes.page.js';
|
||||||
|
import { autoPaginate } from './bookstack.helpers.js';
|
||||||
|
|
||||||
|
export class BookStackBook {
|
||||||
|
public readonly id: number;
|
||||||
|
public readonly name: string;
|
||||||
|
public readonly slug: string;
|
||||||
|
public readonly description: string;
|
||||||
|
public readonly createdAt: string;
|
||||||
|
public readonly updatedAt: string;
|
||||||
|
public readonly createdBy: number;
|
||||||
|
public readonly updatedBy: number;
|
||||||
|
public readonly ownedBy: number;
|
||||||
|
public readonly defaultTemplateId: number | null;
|
||||||
|
public readonly tags: IBookStackTag[];
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
constructor(
|
||||||
|
private accountRef: BookStackAccount,
|
||||||
|
raw: IBookStackBook,
|
||||||
|
) {
|
||||||
|
this.id = raw.id;
|
||||||
|
this.name = raw.name || '';
|
||||||
|
this.slug = raw.slug || '';
|
||||||
|
this.description = raw.description || '';
|
||||||
|
this.createdAt = raw.created_at || '';
|
||||||
|
this.updatedAt = raw.updated_at || '';
|
||||||
|
this.createdBy = raw.created_by;
|
||||||
|
this.updatedBy = raw.updated_by;
|
||||||
|
this.ownedBy = raw.owned_by;
|
||||||
|
this.defaultTemplateId = raw.default_template_id ?? null;
|
||||||
|
this.tags = raw.tags || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CRUD
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async update(data: {
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
description_html?: string;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
default_template_id?: number | null;
|
||||||
|
}): Promise<BookStackBook> {
|
||||||
|
const raw = await this.accountRef.request<IBookStackBook>('PUT', `/books/${this.id}`, data);
|
||||||
|
return new BookStackBook(this.accountRef, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(): Promise<void> {
|
||||||
|
await this.accountRef.request('DELETE', `/books/${this.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Export
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async export(format: TBookStackExportFormat): Promise<string | Uint8Array> {
|
||||||
|
if (format === 'pdf') {
|
||||||
|
return this.accountRef.requestBinary(`/books/${this.id}/export/${format}`);
|
||||||
|
}
|
||||||
|
return this.accountRef.requestText('GET', `/books/${this.id}/export/${format}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Navigation — Chapters
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getChapters(opts?: IBookStackListParams): Promise<BookStackChapter[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.accountRef.request<IBookStackListResponse<IBookStackChapter>>(
|
||||||
|
'GET',
|
||||||
|
this.accountRef.buildListUrl(`/chapters`, { ...opts, offset, count, filter: { ...opts?.filter, book_id: String(this.id) } }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
).then((chapters) => chapters.map((c) => new BookStackChapter(this.accountRef, c)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async createChapter(data: {
|
||||||
|
name: string;
|
||||||
|
description?: string;
|
||||||
|
description_html?: string;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
priority?: number;
|
||||||
|
default_template_id?: number | null;
|
||||||
|
}): Promise<BookStackChapter> {
|
||||||
|
const raw = await this.accountRef.request<IBookStackChapter>('POST', '/chapters', {
|
||||||
|
book_id: this.id,
|
||||||
|
...data,
|
||||||
|
});
|
||||||
|
return new BookStackChapter(this.accountRef, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Navigation — Pages
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getPages(opts?: IBookStackListParams): Promise<BookStackPage[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.accountRef.request<IBookStackListResponse<IBookStackPage>>(
|
||||||
|
'GET',
|
||||||
|
this.accountRef.buildListUrl(`/pages`, { ...opts, offset, count, filter: { ...opts?.filter, book_id: String(this.id) } }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
).then((pages) => pages.map((p) => new BookStackPage(this.accountRef, p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPage(data: {
|
||||||
|
name: string;
|
||||||
|
html?: string;
|
||||||
|
markdown?: string;
|
||||||
|
chapter_id?: number;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
priority?: number;
|
||||||
|
}): Promise<BookStackPage> {
|
||||||
|
const raw = await this.accountRef.request<IBookStackPage>('POST', '/pages', {
|
||||||
|
book_id: this.id,
|
||||||
|
...data,
|
||||||
|
});
|
||||||
|
return new BookStackPage(this.accountRef, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Serialization
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
toJSON(): IBookStackBook {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
name: this.name,
|
||||||
|
slug: this.slug,
|
||||||
|
description: this.description,
|
||||||
|
created_at: this.createdAt,
|
||||||
|
updated_at: this.updatedAt,
|
||||||
|
created_by: this.createdBy,
|
||||||
|
updated_by: this.updatedBy,
|
||||||
|
owned_by: this.ownedBy,
|
||||||
|
default_template_id: this.defaultTemplateId,
|
||||||
|
tags: this.tags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
127
ts/bookstack.classes.chapter.ts
Normal file
127
ts/bookstack.classes.chapter.ts
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
import type { BookStackAccount } from './bookstack.classes.account.js';
|
||||||
|
import type {
|
||||||
|
IBookStackChapter,
|
||||||
|
IBookStackPage,
|
||||||
|
IBookStackTag,
|
||||||
|
IBookStackListParams,
|
||||||
|
IBookStackListResponse,
|
||||||
|
TBookStackExportFormat,
|
||||||
|
} from './bookstack.interfaces.js';
|
||||||
|
import { BookStackPage } from './bookstack.classes.page.js';
|
||||||
|
import { autoPaginate } from './bookstack.helpers.js';
|
||||||
|
|
||||||
|
export class BookStackChapter {
|
||||||
|
public readonly id: number;
|
||||||
|
public readonly bookId: number;
|
||||||
|
public readonly name: string;
|
||||||
|
public readonly slug: string;
|
||||||
|
public readonly description: string;
|
||||||
|
public readonly priority: number;
|
||||||
|
public readonly createdAt: string;
|
||||||
|
public readonly updatedAt: string;
|
||||||
|
public readonly createdBy: number;
|
||||||
|
public readonly updatedBy: number;
|
||||||
|
public readonly ownedBy: number;
|
||||||
|
public readonly tags: IBookStackTag[];
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
constructor(
|
||||||
|
private accountRef: BookStackAccount,
|
||||||
|
raw: IBookStackChapter,
|
||||||
|
) {
|
||||||
|
this.id = raw.id;
|
||||||
|
this.bookId = raw.book_id;
|
||||||
|
this.name = raw.name || '';
|
||||||
|
this.slug = raw.slug || '';
|
||||||
|
this.description = raw.description || '';
|
||||||
|
this.priority = raw.priority || 0;
|
||||||
|
this.createdAt = raw.created_at || '';
|
||||||
|
this.updatedAt = raw.updated_at || '';
|
||||||
|
this.createdBy = raw.created_by;
|
||||||
|
this.updatedBy = raw.updated_by;
|
||||||
|
this.ownedBy = raw.owned_by;
|
||||||
|
this.tags = raw.tags || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CRUD
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async update(data: {
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
description_html?: string;
|
||||||
|
book_id?: number;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
priority?: number;
|
||||||
|
default_template_id?: number | null;
|
||||||
|
}): Promise<BookStackChapter> {
|
||||||
|
const raw = await this.accountRef.request<IBookStackChapter>('PUT', `/chapters/${this.id}`, data);
|
||||||
|
return new BookStackChapter(this.accountRef, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(): Promise<void> {
|
||||||
|
await this.accountRef.request('DELETE', `/chapters/${this.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Export
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async export(format: TBookStackExportFormat): Promise<string | Uint8Array> {
|
||||||
|
if (format === 'pdf') {
|
||||||
|
return this.accountRef.requestBinary(`/chapters/${this.id}/export/${format}`);
|
||||||
|
}
|
||||||
|
return this.accountRef.requestText('GET', `/chapters/${this.id}/export/${format}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Navigation — Pages
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getPages(opts?: IBookStackListParams): Promise<BookStackPage[]> {
|
||||||
|
return autoPaginate(
|
||||||
|
(offset, count) =>
|
||||||
|
this.accountRef.request<IBookStackListResponse<IBookStackPage>>(
|
||||||
|
'GET',
|
||||||
|
this.accountRef.buildListUrl(`/pages`, { ...opts, offset, count, filter: { ...opts?.filter, chapter_id: String(this.id) } }),
|
||||||
|
),
|
||||||
|
opts,
|
||||||
|
).then((pages) => pages.map((p) => new BookStackPage(this.accountRef, p)));
|
||||||
|
}
|
||||||
|
|
||||||
|
async createPage(data: {
|
||||||
|
name: string;
|
||||||
|
html?: string;
|
||||||
|
markdown?: string;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
priority?: number;
|
||||||
|
}): Promise<BookStackPage> {
|
||||||
|
const raw = await this.accountRef.request<IBookStackPage>('POST', '/pages', {
|
||||||
|
chapter_id: this.id,
|
||||||
|
...data,
|
||||||
|
});
|
||||||
|
return new BookStackPage(this.accountRef, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Serialization
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
toJSON(): IBookStackChapter {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
book_id: this.bookId,
|
||||||
|
name: this.name,
|
||||||
|
slug: this.slug,
|
||||||
|
description: this.description,
|
||||||
|
priority: this.priority,
|
||||||
|
created_at: this.createdAt,
|
||||||
|
updated_at: this.updatedAt,
|
||||||
|
created_by: this.createdBy,
|
||||||
|
updated_by: this.updatedBy,
|
||||||
|
owned_by: this.ownedBy,
|
||||||
|
tags: this.tags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
135
ts/bookstack.classes.page.ts
Normal file
135
ts/bookstack.classes.page.ts
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
import type { BookStackAccount } from './bookstack.classes.account.js';
|
||||||
|
import type {
|
||||||
|
IBookStackPage,
|
||||||
|
IBookStackTag,
|
||||||
|
IBookStackComment,
|
||||||
|
TBookStackExportFormat,
|
||||||
|
} from './bookstack.interfaces.js';
|
||||||
|
|
||||||
|
export class BookStackPage {
|
||||||
|
public readonly id: number;
|
||||||
|
public readonly bookId: number;
|
||||||
|
public readonly chapterId: number;
|
||||||
|
public readonly name: string;
|
||||||
|
public readonly slug: string;
|
||||||
|
public readonly html: string;
|
||||||
|
public readonly markdown: string;
|
||||||
|
public readonly priority: number;
|
||||||
|
public readonly draft: boolean;
|
||||||
|
public readonly template: boolean;
|
||||||
|
public readonly revisionCount: number;
|
||||||
|
public readonly editor: string;
|
||||||
|
public readonly createdAt: string;
|
||||||
|
public readonly updatedAt: string;
|
||||||
|
public readonly createdBy: number;
|
||||||
|
public readonly updatedBy: number;
|
||||||
|
public readonly ownedBy: number;
|
||||||
|
public readonly tags: IBookStackTag[];
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
constructor(
|
||||||
|
private accountRef: BookStackAccount,
|
||||||
|
raw: IBookStackPage,
|
||||||
|
) {
|
||||||
|
this.id = raw.id;
|
||||||
|
this.bookId = raw.book_id;
|
||||||
|
this.chapterId = raw.chapter_id;
|
||||||
|
this.name = raw.name || '';
|
||||||
|
this.slug = raw.slug || '';
|
||||||
|
this.html = raw.html || '';
|
||||||
|
this.markdown = raw.markdown || '';
|
||||||
|
this.priority = raw.priority || 0;
|
||||||
|
this.draft = raw.draft || false;
|
||||||
|
this.template = raw.template || false;
|
||||||
|
this.revisionCount = raw.revision_count || 0;
|
||||||
|
this.editor = raw.editor || '';
|
||||||
|
this.createdAt = raw.created_at || '';
|
||||||
|
this.updatedAt = raw.updated_at || '';
|
||||||
|
this.createdBy = raw.created_by;
|
||||||
|
this.updatedBy = raw.updated_by;
|
||||||
|
this.ownedBy = raw.owned_by;
|
||||||
|
this.tags = raw.tags || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CRUD
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async update(data: {
|
||||||
|
name?: string;
|
||||||
|
html?: string;
|
||||||
|
markdown?: string;
|
||||||
|
book_id?: number;
|
||||||
|
chapter_id?: number;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
priority?: number;
|
||||||
|
}): Promise<BookStackPage> {
|
||||||
|
const raw = await this.accountRef.request<IBookStackPage>('PUT', `/pages/${this.id}`, data);
|
||||||
|
return new BookStackPage(this.accountRef, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(): Promise<void> {
|
||||||
|
await this.accountRef.request('DELETE', `/pages/${this.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Export
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async export(format: TBookStackExportFormat): Promise<string | Uint8Array> {
|
||||||
|
if (format === 'pdf') {
|
||||||
|
return this.accountRef.requestBinary(`/pages/${this.id}/export/${format}`);
|
||||||
|
}
|
||||||
|
return this.accountRef.requestText('GET', `/pages/${this.id}/export/${format}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Comments
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getComments(): Promise<IBookStackComment[]> {
|
||||||
|
const result = await this.accountRef.request<{ data: IBookStackComment[] }>(
|
||||||
|
'GET',
|
||||||
|
`/comments?filter[page_id]=${this.id}`,
|
||||||
|
);
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addComment(data: {
|
||||||
|
html: string;
|
||||||
|
reply_to?: number;
|
||||||
|
content_ref?: string;
|
||||||
|
}): Promise<IBookStackComment> {
|
||||||
|
return this.accountRef.request<IBookStackComment>('POST', '/comments', {
|
||||||
|
page_id: this.id,
|
||||||
|
...data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Serialization
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
toJSON(): IBookStackPage {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
book_id: this.bookId,
|
||||||
|
chapter_id: this.chapterId,
|
||||||
|
name: this.name,
|
||||||
|
slug: this.slug,
|
||||||
|
html: this.html,
|
||||||
|
markdown: this.markdown,
|
||||||
|
priority: this.priority,
|
||||||
|
draft: this.draft,
|
||||||
|
template: this.template,
|
||||||
|
revision_count: this.revisionCount,
|
||||||
|
editor: this.editor,
|
||||||
|
created_at: this.createdAt,
|
||||||
|
updated_at: this.updatedAt,
|
||||||
|
created_by: this.createdBy,
|
||||||
|
updated_by: this.updatedBy,
|
||||||
|
owned_by: this.ownedBy,
|
||||||
|
tags: this.tags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
91
ts/bookstack.classes.shelf.ts
Normal file
91
ts/bookstack.classes.shelf.ts
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
import type { BookStackAccount } from './bookstack.classes.account.js';
|
||||||
|
import type {
|
||||||
|
IBookStackShelf,
|
||||||
|
IBookStackBook,
|
||||||
|
IBookStackTag,
|
||||||
|
IBookStackListParams,
|
||||||
|
IBookStackListResponse,
|
||||||
|
} from './bookstack.interfaces.js';
|
||||||
|
import { BookStackBook } from './bookstack.classes.book.js';
|
||||||
|
import { autoPaginate } from './bookstack.helpers.js';
|
||||||
|
|
||||||
|
export class BookStackShelf {
|
||||||
|
public readonly id: number;
|
||||||
|
public readonly name: string;
|
||||||
|
public readonly slug: string;
|
||||||
|
public readonly description: string;
|
||||||
|
public readonly createdAt: string;
|
||||||
|
public readonly updatedAt: string;
|
||||||
|
public readonly createdBy: number;
|
||||||
|
public readonly updatedBy: number;
|
||||||
|
public readonly ownedBy: number;
|
||||||
|
public readonly tags: IBookStackTag[];
|
||||||
|
|
||||||
|
/** @internal */
|
||||||
|
constructor(
|
||||||
|
private accountRef: BookStackAccount,
|
||||||
|
raw: IBookStackShelf,
|
||||||
|
) {
|
||||||
|
this.id = raw.id;
|
||||||
|
this.name = raw.name || '';
|
||||||
|
this.slug = raw.slug || '';
|
||||||
|
this.description = raw.description || '';
|
||||||
|
this.createdAt = raw.created_at || '';
|
||||||
|
this.updatedAt = raw.updated_at || '';
|
||||||
|
this.createdBy = raw.created_by;
|
||||||
|
this.updatedBy = raw.updated_by;
|
||||||
|
this.ownedBy = raw.owned_by;
|
||||||
|
this.tags = raw.tags || [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// CRUD
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async update(data: {
|
||||||
|
name?: string;
|
||||||
|
description?: string;
|
||||||
|
description_html?: string;
|
||||||
|
books?: number[];
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
}): Promise<BookStackShelf> {
|
||||||
|
const raw = await this.accountRef.request<IBookStackShelf>('PUT', `/shelves/${this.id}`, data);
|
||||||
|
return new BookStackShelf(this.accountRef, raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
async delete(): Promise<void> {
|
||||||
|
await this.accountRef.request('DELETE', `/shelves/${this.id}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Navigation — Books
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
async getBooks(opts?: IBookStackListParams): Promise<BookStackBook[]> {
|
||||||
|
// The shelf detail endpoint includes books inline
|
||||||
|
const detail = await this.accountRef.request<IBookStackShelf>('GET', `/shelves/${this.id}`);
|
||||||
|
if (detail.books) {
|
||||||
|
return detail.books.map((b) => new BookStackBook(this.accountRef, b));
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Serialization
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
toJSON(): IBookStackShelf {
|
||||||
|
return {
|
||||||
|
id: this.id,
|
||||||
|
name: this.name,
|
||||||
|
slug: this.slug,
|
||||||
|
description: this.description,
|
||||||
|
created_at: this.createdAt,
|
||||||
|
updated_at: this.updatedAt,
|
||||||
|
created_by: this.createdBy,
|
||||||
|
updated_by: this.updatedBy,
|
||||||
|
owned_by: this.ownedBy,
|
||||||
|
tags: this.tags,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
29
ts/bookstack.helpers.ts
Normal file
29
ts/bookstack.helpers.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import type { IBookStackListResponse } from './bookstack.interfaces.js';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Auto-paginate a BookStack list endpoint using offset/count.
|
||||||
|
* If opts includes a specific offset, returns just that single page (no auto-pagination).
|
||||||
|
*/
|
||||||
|
export async function autoPaginate<T>(
|
||||||
|
fetchPage: (offset: number, count: number) => Promise<IBookStackListResponse<T>>,
|
||||||
|
opts?: { offset?: number; count?: number },
|
||||||
|
): Promise<T[]> {
|
||||||
|
const count = opts?.count || 100;
|
||||||
|
|
||||||
|
// If caller requests a specific offset, return just that page
|
||||||
|
if (opts?.offset !== undefined) {
|
||||||
|
const result = await fetchPage(opts.offset, count);
|
||||||
|
return result.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise auto-paginate through all pages
|
||||||
|
const all: T[] = [];
|
||||||
|
let offset = 0;
|
||||||
|
while (true) {
|
||||||
|
const result = await fetchPage(offset, count);
|
||||||
|
all.push(...result.data);
|
||||||
|
offset += count;
|
||||||
|
if (offset >= result.total) break;
|
||||||
|
}
|
||||||
|
return all;
|
||||||
|
}
|
||||||
322
ts/bookstack.interfaces.ts
Normal file
322
ts/bookstack.interfaces.ts
Normal file
@@ -0,0 +1,322 @@
|
|||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Common
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface ITestConnectionResult {
|
||||||
|
ok: boolean;
|
||||||
|
error?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBookStackListParams {
|
||||||
|
count?: number;
|
||||||
|
offset?: number;
|
||||||
|
sort?: string;
|
||||||
|
filter?: Record<string, string>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBookStackListResponse<T> {
|
||||||
|
data: T[];
|
||||||
|
total: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBookStackErrorResponse {
|
||||||
|
error: {
|
||||||
|
code: number;
|
||||||
|
message: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBookStackTag {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
order?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type TBookStackExportFormat = 'html' | 'pdf' | 'plaintext' | 'markdown';
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Books
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackBook {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
description: string;
|
||||||
|
description_html?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: number;
|
||||||
|
updated_by: number;
|
||||||
|
owned_by: number;
|
||||||
|
default_template_id: number | null;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
cover?: { id: number; name: string; url: string } | null;
|
||||||
|
contents?: IBookStackBookContent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface IBookStackBookContent {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
type: 'chapter' | 'page';
|
||||||
|
book_id: number;
|
||||||
|
priority: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
url: string;
|
||||||
|
pages?: IBookStackBookContent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Chapters
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackChapter {
|
||||||
|
id: number;
|
||||||
|
book_id: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
description: string;
|
||||||
|
description_html?: string;
|
||||||
|
priority: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: number;
|
||||||
|
updated_by: number;
|
||||||
|
owned_by: number;
|
||||||
|
default_template_id?: number | null;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
pages?: IBookStackPage[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Pages
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackPage {
|
||||||
|
id: number;
|
||||||
|
book_id: number;
|
||||||
|
chapter_id: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
html: string;
|
||||||
|
raw_html?: string;
|
||||||
|
markdown: string;
|
||||||
|
priority: number;
|
||||||
|
draft: boolean;
|
||||||
|
template: boolean;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: number;
|
||||||
|
updated_by: number;
|
||||||
|
owned_by: number;
|
||||||
|
revision_count: number;
|
||||||
|
editor: string;
|
||||||
|
book_slug?: string;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Shelves
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackShelf {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
description: string;
|
||||||
|
description_html?: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: number;
|
||||||
|
updated_by: number;
|
||||||
|
owned_by: number;
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
cover?: { id: number; name: string; url: string } | null;
|
||||||
|
books?: IBookStackBook[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Attachments
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackAttachment {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
extension: string;
|
||||||
|
uploaded_to: number;
|
||||||
|
external: boolean;
|
||||||
|
order: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: number;
|
||||||
|
updated_by: number;
|
||||||
|
content?: string;
|
||||||
|
links?: {
|
||||||
|
html: string;
|
||||||
|
markdown: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Comments
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackComment {
|
||||||
|
id: number;
|
||||||
|
html: string;
|
||||||
|
parent_id: number | null;
|
||||||
|
local_id: number;
|
||||||
|
content_ref: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: number;
|
||||||
|
updated_by: number;
|
||||||
|
archived?: boolean;
|
||||||
|
replies?: IBookStackComment[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Image Gallery
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackImage {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
path: string;
|
||||||
|
type: string;
|
||||||
|
uploaded_to: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
created_by: number;
|
||||||
|
updated_by: number;
|
||||||
|
thumbs?: {
|
||||||
|
gallery: string;
|
||||||
|
display: string;
|
||||||
|
};
|
||||||
|
content?: {
|
||||||
|
html: string;
|
||||||
|
markdown: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Users
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackUser {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
email: string;
|
||||||
|
profile_url: string;
|
||||||
|
edit_url?: string;
|
||||||
|
avatar_url: string;
|
||||||
|
external_auth_id: string;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
last_activity_at?: string;
|
||||||
|
roles?: { id: number; display_name: string }[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Roles
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackRole {
|
||||||
|
id: number;
|
||||||
|
display_name: string;
|
||||||
|
description: string;
|
||||||
|
mfa_enforced: boolean;
|
||||||
|
external_auth_id: string;
|
||||||
|
permissions_count: number;
|
||||||
|
users_count?: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
permissions?: string[];
|
||||||
|
users?: IBookStackUser[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Search
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackSearchResult {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
slug: string;
|
||||||
|
book_id?: number;
|
||||||
|
chapter_id?: number;
|
||||||
|
type: string;
|
||||||
|
url: string;
|
||||||
|
preview_html?: { name: string; content: string };
|
||||||
|
tags?: IBookStackTag[];
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Audit Log
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackAuditLogEntry {
|
||||||
|
id: number;
|
||||||
|
type: string;
|
||||||
|
detail: string;
|
||||||
|
user_id: number;
|
||||||
|
loggable_id: number;
|
||||||
|
loggable_type: string;
|
||||||
|
ip: string;
|
||||||
|
created_at: string;
|
||||||
|
user?: IBookStackUser;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Recycle Bin
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackRecycleBinItem {
|
||||||
|
id: number;
|
||||||
|
deleted_by: number;
|
||||||
|
created_at: string;
|
||||||
|
updated_at: string;
|
||||||
|
deletable_type: string;
|
||||||
|
deletable_id: number;
|
||||||
|
deletable?: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// Content Permissions
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackContentPermission {
|
||||||
|
owner: IBookStackUser;
|
||||||
|
role_permissions: {
|
||||||
|
role_id: number;
|
||||||
|
view: boolean;
|
||||||
|
create: boolean;
|
||||||
|
update: boolean;
|
||||||
|
delete: boolean;
|
||||||
|
}[];
|
||||||
|
fallback_permissions: {
|
||||||
|
inheriting: boolean;
|
||||||
|
view: boolean;
|
||||||
|
create: boolean;
|
||||||
|
update: boolean;
|
||||||
|
delete: boolean;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
// System
|
||||||
|
// ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
export interface IBookStackSystemInfo {
|
||||||
|
version: string;
|
||||||
|
instance_id: string;
|
||||||
|
app_name: string;
|
||||||
|
app_logo: string | null;
|
||||||
|
base_url: string;
|
||||||
|
}
|
||||||
3
ts/bookstack.plugins.ts
Normal file
3
ts/bookstack.plugins.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
import * as smartrequest from '@push.rocks/smartrequest';
|
||||||
|
|
||||||
|
export { smartrequest };
|
||||||
38
ts/index.ts
Normal file
38
ts/index.ts
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Main client
|
||||||
|
export { BookStackAccount } from './bookstack.classes.account.js';
|
||||||
|
|
||||||
|
// Domain classes
|
||||||
|
export { BookStackBook } from './bookstack.classes.book.js';
|
||||||
|
export { BookStackChapter } from './bookstack.classes.chapter.js';
|
||||||
|
export { BookStackPage } from './bookstack.classes.page.js';
|
||||||
|
export { BookStackShelf } from './bookstack.classes.shelf.js';
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
export { autoPaginate } from './bookstack.helpers.js';
|
||||||
|
|
||||||
|
// Interfaces (raw API types)
|
||||||
|
export type {
|
||||||
|
IBookStackBook,
|
||||||
|
IBookStackChapter,
|
||||||
|
IBookStackPage,
|
||||||
|
IBookStackShelf,
|
||||||
|
IBookStackAttachment,
|
||||||
|
IBookStackComment,
|
||||||
|
IBookStackImage,
|
||||||
|
IBookStackUser,
|
||||||
|
IBookStackRole,
|
||||||
|
IBookStackSearchResult,
|
||||||
|
IBookStackAuditLogEntry,
|
||||||
|
IBookStackRecycleBinItem,
|
||||||
|
IBookStackContentPermission,
|
||||||
|
IBookStackSystemInfo,
|
||||||
|
IBookStackListResponse,
|
||||||
|
IBookStackListParams,
|
||||||
|
IBookStackErrorResponse,
|
||||||
|
IBookStackTag,
|
||||||
|
TBookStackExportFormat,
|
||||||
|
ITestConnectionResult,
|
||||||
|
} from './bookstack.interfaces.js';
|
||||||
|
|
||||||
|
// Commit info
|
||||||
|
export { commitinfo } from './00_commitinfo_data.js';
|
||||||
16
tsconfig.json
Normal file
16
tsconfig.json
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"useDefineForClassFields": false,
|
||||||
|
"target": "ES2022",
|
||||||
|
"module": "NodeNext",
|
||||||
|
"moduleResolution": "NodeNext",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"baseUrl": ".",
|
||||||
|
"paths": {}
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"dist_*/**/*.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user