mirror of
https://github.com/tiddly-gittly/TidGi-Desktop.git
synced 2026-01-25 14:00:47 -08:00
feat: getTiddlerHtml
This commit is contained in:
parent
61892320f9
commit
232d0ce103
4 changed files with 81 additions and 41 deletions
|
|
@ -5,12 +5,11 @@ import { container } from '@services/container';
|
|||
import { logger } from '@services/libs/log';
|
||||
import serviceIdentifier from '@services/serviceIdentifier';
|
||||
import { IWikiService } from '@services/wiki/interface';
|
||||
import { IWikiServerRouteResponse } from '@services/wiki/ipcServerRoutes';
|
||||
import { IWorkspaceService } from '@services/workspaces/interface';
|
||||
import type { ITiddlerFields } from 'tiddlywiki';
|
||||
|
||||
export async function setupIpcServerRoutesHandlers(view: BrowserView, workspaceID: string) {
|
||||
const urlBase = `tidgi://${workspaceID}/`;
|
||||
const urlBase = `tidgi://${workspaceID}`;
|
||||
const workspaceService = container.get<IWorkspaceService>(serviceIdentifier.Workspace);
|
||||
const authService = container.get<IAuthenticationService>(serviceIdentifier.Authentication);
|
||||
const wikiService = container.get<IWikiService>(serviceIdentifier.Wiki);
|
||||
|
|
@ -18,73 +17,88 @@ export async function setupIpcServerRoutesHandlers(view: BrowserView, workspaceI
|
|||
{
|
||||
method: 'DELETE',
|
||||
path: /^\/bags\/default\/tiddlers\/(.+)$/,
|
||||
handler: async (title: string) => await wikiService.callWikiIpcServerRoute(workspaceID, 'deleteTiddler', title),
|
||||
name: 'deleteTiddler',
|
||||
handler: async (_request: GlobalRequest, parameters: RegExpMatchArray | null) =>
|
||||
await wikiService.callWikiIpcServerRoute(workspaceID, 'deleteTiddler', parameters?.[1] ?? ''),
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: /^\/favicon.ico$/,
|
||||
handler: async () => await wikiService.callWikiIpcServerRoute(workspaceID, 'getFavicon'),
|
||||
name: 'getFavicon',
|
||||
handler: async (_request: GlobalRequest, _parameters: RegExpMatchArray | null) => await wikiService.callWikiIpcServerRoute(workspaceID, 'getFavicon'),
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: /^\/files\/(.+)$/,
|
||||
handler: async (fileName: string) => await wikiService.callWikiIpcServerRoute(workspaceID, 'getFile', fileName),
|
||||
name: 'getFile',
|
||||
handler: async (_request: GlobalRequest, parameters: RegExpMatchArray | null) => await wikiService.callWikiIpcServerRoute(workspaceID, 'getFile', parameters?.[1] ?? ''),
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: /^\/$/,
|
||||
handler: async () => await wikiService.callWikiIpcServerRoute(workspaceID, 'getIndex', (await workspaceService.get(workspaceID))?.rootTiddler ?? '$:/core/save/lazy-images'),
|
||||
name: 'getIndex',
|
||||
handler: async (_request: GlobalRequest, _parameters: RegExpMatchArray | null) =>
|
||||
await wikiService.callWikiIpcServerRoute(workspaceID, 'getIndex', (await workspaceService.get(workspaceID))?.rootTiddler ?? '$:/core/save/lazy-images'),
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: /^\/status$/,
|
||||
handler: async () => {
|
||||
name: 'getStatus',
|
||||
handler: async (_request: GlobalRequest, _parameters: RegExpMatchArray | null) => {
|
||||
const workspace = await workspaceService.get(workspaceID);
|
||||
const userName = workspace === undefined ? '' : await authService.getUserName(workspace);
|
||||
await wikiService.callWikiIpcServerRoute(workspaceID, 'getStatus', userName);
|
||||
},
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: /^\/([^/]+)$/,
|
||||
name: 'getTiddlerHtml',
|
||||
handler: async (_request: GlobalRequest, parameters: RegExpMatchArray | null) =>
|
||||
await wikiService.callWikiIpcServerRoute(workspaceID, 'getTiddlerHtml', parameters?.[1] ?? ''),
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: /^\/recipes\/default\/tiddlers\/(.+)$/,
|
||||
handler: async (title: string) => await wikiService.callWikiIpcServerRoute(workspaceID, 'getTiddler', title),
|
||||
name: 'getTiddler',
|
||||
handler: async (_request: GlobalRequest, parameters: RegExpMatchArray | null) => await wikiService.callWikiIpcServerRoute(workspaceID, 'getTiddler', parameters?.[1] ?? ''),
|
||||
},
|
||||
{
|
||||
method: 'GET',
|
||||
path: /^\/recipes\/default\/tiddlers\/(.+)$/,
|
||||
name: 'getTiddlersJSON',
|
||||
handler: async (request: GlobalRequest, _parameters: RegExpMatchArray | null) =>
|
||||
await wikiService.callWikiIpcServerRoute(workspaceID, 'getTiddlersJSON', new URL(request.url).searchParams.get('filter') ?? ''),
|
||||
},
|
||||
{
|
||||
method: 'PUT',
|
||||
path: /^\/recipes\/default\/tiddlers\/(.+)$/,
|
||||
handler: async (title: string, fields: ITiddlerFields) => await wikiService.callWikiIpcServerRoute(workspaceID, 'putTiddler', title, fields),
|
||||
name: 'putTiddler',
|
||||
handler: async (request: GlobalRequest, parameters: RegExpMatchArray | null) => {
|
||||
const body = await request.json() as ITiddlerFields;
|
||||
await wikiService.callWikiIpcServerRoute(workspaceID, 'putTiddler', parameters?.[1] ?? '', body);
|
||||
},
|
||||
},
|
||||
];
|
||||
async function handlerCallback(request: GlobalRequest): Promise<GlobalResponse> {
|
||||
// Extracting methods and URLs from requests
|
||||
const { method, url } = request;
|
||||
const urlPath = url.replace(urlBase, '');
|
||||
// DEBUG: console urlPath
|
||||
console.log(`urlPath`, urlPath);
|
||||
|
||||
const parsedUrl = new URL(request.url);
|
||||
// Iterate through methods to find matching routes
|
||||
for (const route of methods) {
|
||||
if (method === route.method && route.path.test(urlPath)) {
|
||||
if (request.method === route.method && route.path.test(parsedUrl.pathname)) {
|
||||
// Get the parameters in the URL path
|
||||
const parameters = url.match(route.path);
|
||||
|
||||
const parameters = parsedUrl.pathname.match(route.path);
|
||||
logger.debug(`loadHTMLStringForView: ${route.name}`, { parsedUrl, parameters });
|
||||
// Call the handler of the route to process the request and return the result
|
||||
if (parameters === null) {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
|
||||
// @ts-ignore Expected 2 arguments, but got 0.
|
||||
const responseData: IWikiServerRouteResponse = await route.handler();
|
||||
return new Response(responseData.data, { status: responseData.statusCode, headers: responseData.headers });
|
||||
} else {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment, @typescript-eslint/prefer-ts-expect-error
|
||||
// @ts-ignore Expected 2 arguments, but got 0.
|
||||
const responseData: IWikiServerRouteResponse = await route.handler(...parameters.slice(1));
|
||||
return new Response(responseData.data, { status: responseData.statusCode, headers: responseData.headers });
|
||||
const responseData = await route.handler(request, parameters);
|
||||
if (responseData === undefined) {
|
||||
const statusText = `loadHTMLStringForView: responseData is undefined ${request.url}`;
|
||||
logger.warn(statusText);
|
||||
return new Response(undefined, { status: 404, statusText });
|
||||
}
|
||||
return new Response(responseData.data, { status: responseData.statusCode, headers: responseData.headers });
|
||||
}
|
||||
}
|
||||
|
||||
// 如果没有找到匹配的路由,返回404错误
|
||||
const statusText = `loadHTMLStringForView: tidgi protocol is not handled ${url}`;
|
||||
const statusText = `loadHTMLStringForView: tidgi protocol is not handled ${request.url}`;
|
||||
logger.warn(statusText);
|
||||
return new Response(undefined, { status: 404, statusText });
|
||||
}
|
||||
|
|
@ -95,7 +109,7 @@ export async function setupIpcServerRoutesHandlers(view: BrowserView, workspaceI
|
|||
if (!handled) {
|
||||
logger.warn(`loadHTMLStringForView: tidgi protocol is not handled`);
|
||||
}
|
||||
await view.webContents.loadURL(urlBase);
|
||||
await view.webContents.loadURL(`${urlBase}/`);
|
||||
// view.webContents.session.protocol.unhandle(`tidgi`);
|
||||
view.webContents.openDevTools({ mode: 'detach' });
|
||||
} catch (error) {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { ModuleThread } from 'threads';
|
|||
import { IWikiServerRouteResponse } from './ipcServerRoutes';
|
||||
import type { ISubWikiPluginContent } from './plugin/subWikiPlugin';
|
||||
import { IWikiOperations } from './wikiOperations';
|
||||
import type { IpcServerRouteMethods, WikiWorker } from './wikiWorker';
|
||||
import type { IpcServerRouteMethods, IpcServerRouteNames, WikiWorker } from './wikiWorker';
|
||||
|
||||
/**
|
||||
* Handle wiki worker startup and restart
|
||||
|
|
@ -16,7 +16,7 @@ export interface IWikiService {
|
|||
* Call wiki worker route methods, and return response.
|
||||
* Methods are copy from core/modules/server/routes , to support the IPC communication between renderer's browserView and main process and wiki worker.
|
||||
*/
|
||||
callWikiIpcServerRoute<NAME extends 'deleteTiddler' | 'getFavicon' | 'getIndex' | 'getStatus' | 'getTiddler' | 'getTiddlersJSON' | 'putTiddler' | 'getFile'>(
|
||||
callWikiIpcServerRoute<NAME extends IpcServerRouteNames>(
|
||||
workspaceID: string,
|
||||
route: NAME,
|
||||
...arguments_: Parameters<IpcServerRouteMethods[NAME]>
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ export class IpcServerRoutes {
|
|||
try {
|
||||
const data = await fs.readFile(filename);
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
const type = this.wikiInstance.config.fileExtensionInfo[extension] ? $tw.config.fileExtensionInfo[extension].type : 'application/octet-stream';
|
||||
const type = this.wikiInstance.config.fileExtensionInfo[extension] ? this.wikiInstance.config.fileExtensionInfo[extension].type : 'application/octet-stream';
|
||||
return ({ statusCode: 200, headers: { 'Content-Type': type }, data });
|
||||
} catch (error) {
|
||||
return { statusCode: 404, headers: { 'Content-Type': 'text/plain' }, data: `Error accessing file ${suppliedFilename} with error: ${(error as Error).toString()}` };
|
||||
|
|
@ -85,7 +85,7 @@ export class IpcServerRoutes {
|
|||
space: {
|
||||
recipe: 'default',
|
||||
},
|
||||
tiddlywiki_version: $tw.version,
|
||||
tiddlywiki_version: this.wikiInstance.version,
|
||||
});
|
||||
return { statusCode: 200, headers: { 'Content-Type': 'application/json' }, data: text };
|
||||
}
|
||||
|
|
@ -110,8 +110,9 @@ export class IpcServerRoutes {
|
|||
'type',
|
||||
'uri',
|
||||
]);
|
||||
// eslint-disable-next-line @typescript-eslint/strict-boolean-expressions
|
||||
if (tiddler) {
|
||||
if (tiddler === undefined) {
|
||||
return { statusCode: 404, headers: { 'Content-Type': 'text/plain' }, data: `Tiddler "${title}" not exist` };
|
||||
} else {
|
||||
Object.keys(tiddler.fields).forEach((name) => {
|
||||
const value = tiddler.getFieldStrings(name);
|
||||
if (knownFields.has(name)) {
|
||||
|
|
@ -127,8 +128,6 @@ export class IpcServerRoutes {
|
|||
tiddlerFields.bag = 'default';
|
||||
tiddlerFields.type = tiddlerFields.type ?? 'text/vnd.tiddlywiki';
|
||||
return { statusCode: 200, headers: { 'Content-Type': 'application/json; charset=utf8' }, data: JSON.stringify(tiddlerFields) };
|
||||
} else {
|
||||
return { statusCode: 404, headers: { 'Content-Type': 'text/plain' }, data: `Tiddler "${title}" not exist` };
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -177,8 +176,34 @@ export class IpcServerRoutes {
|
|||
tiddlerFieldsToPut.text = tiddler.fields.text;
|
||||
}
|
||||
}
|
||||
this.wikiInstance.wiki.addTiddler(new $tw.Tiddler(fields, { title }));
|
||||
this.wikiInstance.wiki.addTiddler(new this.wikiInstance.Tiddler(fields, { title }));
|
||||
const changeCount = this.wikiInstance.wiki.getChangeCount(title).toString();
|
||||
return { statusCode: 204, headers: { 'Content-Type': 'text/plain', Etag: `"default/${encodeURIComponent(title)}/${changeCount}:"` }, data: 'OK' };
|
||||
}
|
||||
|
||||
async getTiddlerHtml(title: string): Promise<IWikiServerRouteResponse> {
|
||||
await this.waitForIpcServerRoutesAvailable();
|
||||
const tiddler = this.wikiInstance.wiki.getTiddler(title);
|
||||
if (tiddler === undefined) {
|
||||
return { statusCode: 404, headers: { 'Content-Type': 'text/plain' }, data: `Tiddler "${title}" not exist` };
|
||||
} else {
|
||||
let renderType: string = tiddler.getFieldString('_render_type');
|
||||
let renderTemplate: string = tiddler.getFieldString('_render_template');
|
||||
// Tiddler fields '_render_type' and '_render_template' overwrite
|
||||
// system wide settings for render type and template
|
||||
if (this.wikiInstance.wiki.isSystemTiddler(title)) {
|
||||
renderType = renderType ?? this.wikiInstance.server.get('system-tiddler-render-type') ?? 'text/plain';
|
||||
renderTemplate = renderTemplate ?? this.wikiInstance.server.get('system-tiddler-render-template') ?? '$:/core/templates/wikified-tiddler';
|
||||
} else {
|
||||
renderType = renderType ?? this.wikiInstance.server.get('tiddler-render-type') ?? 'text/html';
|
||||
renderTemplate = renderTemplate ?? this.wikiInstance.server.get('tiddler-render-template') ?? '$:/core/templates/server/static.tiddler.html';
|
||||
}
|
||||
// DEBUG: console renderType
|
||||
console.log(`renderType`, renderType, 'renderTemplate', renderTemplate);
|
||||
const text = this.wikiInstance.wiki.renderTiddler(renderType, renderTemplate, { parseAsInline: true, variables: { currentTiddler: title } });
|
||||
|
||||
// Naughty not to set a content-type, but it's the easiest way to ensure the browser will see HTML pages as HTML, and accept plain text tiddlers as CSS or JS
|
||||
return { statusCode: 200, headers: { 'Content-Type': '; charset=utf8' }, data: text };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ const ipcServerRoutesMethods = {
|
|||
getIndex: ipcServerRoutes.getIndex.bind(ipcServerRoutes),
|
||||
getStatus: ipcServerRoutes.getStatus.bind(ipcServerRoutes),
|
||||
getTiddler: ipcServerRoutes.getTiddler.bind(ipcServerRoutes),
|
||||
getTiddlerHtml: ipcServerRoutes.getTiddlerHtml.bind(ipcServerRoutes),
|
||||
getTiddlersJSON: ipcServerRoutes.getTiddlersJSON.bind(ipcServerRoutes),
|
||||
putTiddler: ipcServerRoutes.putTiddler.bind(ipcServerRoutes),
|
||||
getFile: ipcServerRoutes.getFile.bind(ipcServerRoutes),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue