feat: allow disable http api

This commit is contained in:
lin onetwo 2023-06-19 11:59:59 +08:00
parent ba535d8c8c
commit 926b0c29c0
10 changed files with 75 additions and 36 deletions

View file

@ -5,6 +5,7 @@
"dugite",
"fullscreenable",
"gitee",
"HTTPAPI",
"IIFE",
"maximizable",
"minimizable",

View file

@ -181,7 +181,7 @@
"TokenAuth": "Token Authenticate",
"TokenAuthDescription": "Cannot work with read-only mode. After enabled, other ones need to bring credentials in the request header to read write the content of the wiki, preventing other people in the same LAN from accessing your notes. TidGi will automatically bring the certificate, so turning on will not affect the normal use of TidGi app, it only increases security, so it is recommended to turn it on.",
"TokenAuthAutoFillUserNameDescription": "This feature requires userName to be filled in global setting or workspace setting, if its empty, a default one will be auto filled into workspace setting, you can change it later.",
"BlogOptions": "Blog Server Options",
"ServerOptions": "Blog & Server Options",
"EnableHTTPS": "Enable HTTPS",
"EnableHTTPSDescription": "To provide secure TLS encrypted access, you need to have your own HTTPS certificate, which can be downloaded from the domain name provider, or you can search for free HTTPS certificate application methods.",
"HTTPSUploadCert": "Add Cert file",
@ -198,7 +198,9 @@
"HTTPSCertPath": "Cert file path",
"HTTPSKeyPath": "Key file path",
"HTTPSKeyPathDescription": "The location of the private key file with the suffix .key.",
"LastNodeJSArgv": "Command line arguments from the latest startup"
"LastNodeJSArgv": "Command line arguments from the latest startup",
"EnableHTTPAPI": "Enable HTTP APIs",
"EnableHTTPAPIDescription": "Allow third-party programs such as Tiddloid, MaoXianWebclipper clipper, etc. to read and modify your notes through the HTTP network interface."
},
"Dialog": {
"CantFindWorkspaceFolderRemoveWorkspace": "Cannot find the workspace folder that was still there before! \nThe folders that should have existed here may have been removed, or there is no wiki in this folder! \nDo you want to remove the workspace?",

View file

@ -216,7 +216,9 @@
"TokenAuthDescription": "无法与只读模式同时开启。开启后需要在请求中带上凭证才能读写知识库内容,防止同一局域网下其他人访问笔记。太记会自动带上凭证,因此开启后不会影响太记应用正常使用,仅增加安全性,建议开启。",
"TokenAuthAutoFillUserNameDescription": "此功能需要在全局设置或工作区设置里填写用户名,不然不会生效。若你未填,将自动在工作区设置里填一个默认值,你可自行修改。",
"TokenAuthCurrentHeader": "凭证鉴权当前请求头",
"BlogOptions": "博客服务器设置",
"ServerOptions": "博客和服务器设置",
"EnableHTTPAPI": "启用 HTTP API",
"EnableHTTPAPIDescription": "允许第三方程序如 Tiddloid、MaoXianWebclipper剪藏等等通过 HTTP 网络接口读取和修改你的笔记。",
"EnableHTTPS": "启用HTTPS",
"EnableHTTPSDescription": "提供安全的TLS加密访问需要你有自己的HTTPS证书可以从域名提供商那下载也可以搜索免费的HTTPS证书申请方式。",
"HTTPSUploadCert": "添加Cert文件",

View file

@ -181,6 +181,7 @@ export async function workspaceConfigFromForm(form: INewWikiRequiredFormData, is
tokenAuth: true,
userName: userNameIsEmpty ? DEFAULT_USER_NAME : undefined,
excludedPlugins: [],
enableHTTPAPI: false,
lastNodeJSArgv: [],
};
}

View file

@ -26,7 +26,7 @@ import { SupportedStorageServices } from '@services/types';
import { isEqual } from 'lodash';
import { SyncedWikiDescription } from '../AddWorkspace/Description';
import { GitRepoUrlForm } from '../AddWorkspace/GitRepoUrlForm';
import { BlogOptions } from './blog';
import { ServerOptions } from './server';
const Root = styled(Paper)`
height: 100%;
@ -294,7 +294,7 @@ export default function EditWorkspace(): JSX.Element {
}}
/>
)}
<BlogOptions actualIP={actualIP} workspace={workspace} workspaceSetter={workspaceSetter} />
<ServerOptions actualIP={actualIP} workspace={workspace} workspaceSetter={workspaceSetter} />
</>
)}
{isSubWiki && (

View file

@ -26,11 +26,11 @@ import { defaultServerIP } from '@/constants/urls';
import { usePromiseValue } from '@/helpers/useServiceValue';
import { IWorkspace } from '@services/workspaces/interface';
const ABlogOptionsAccordion = styled(Accordion)`
const AServerOptionsAccordion = styled(Accordion)`
box-shadow: unset;
background-color: unset;
`;
const ABlogOptionsAccordionSummary = styled(AccordionSummary)`
const AServerOptionsAccordionSummary = styled(AccordionSummary)`
padding: 0;
`;
const HttpsCertKeyListItem: typeof ListItem = styled(ListItem)`
@ -48,12 +48,12 @@ const ChipContainer = styled.div`
margin-bottom: 10px;
`;
export interface IBlogOptionsProps {
export interface IServerOptionsProps {
actualIP: string | undefined;
workspace: IWorkspace;
workspaceSetter: (newValue: IWorkspace, requestSaveAndRestart?: boolean | undefined) => void;
}
export function BlogOptions(props: IBlogOptionsProps) {
export function ServerOptions(props: IServerOptionsProps) {
const { t } = useTranslation();
const { workspace, actualIP, workspaceSetter } = props;
const {
@ -64,14 +64,29 @@ export function BlogOptions(props: IBlogOptionsProps) {
rootTiddler,
tokenAuth,
lastNodeJSArgv,
enableHTTPAPI,
} = (workspace ?? {}) as unknown as IWorkspace;
const alreadyEnableSomeBlogOptions = readOnlyMode;
const alreadyEnableSomeServerOptions = readOnlyMode;
return (
<ABlogOptionsAccordion defaultExpanded={alreadyEnableSomeBlogOptions}>
<ABlogOptionsAccordionSummary expandIcon={<ExpandMoreIcon />}>{t('EditWorkspace.BlogOptions')}</ABlogOptionsAccordionSummary>
<AServerOptionsAccordion defaultExpanded={alreadyEnableSomeServerOptions}>
<AServerOptionsAccordionSummary expandIcon={<ExpandMoreIcon />}>{t('EditWorkspace.ServerOptions')}</AServerOptionsAccordionSummary>
<AccordionDetails>
<List>
<ListItem disableGutters>
<ListItemText primary={t('EditWorkspace.EnableHTTPAPI')} secondary={t('EditWorkspace.EnableHTTPAPIDescription')} />
<ListItemSecondaryAction>
<Switch
edge='end'
color='primary'
checked={enableHTTPAPI}
onChange={(event) => {
workspaceSetter({ ...workspace, enableHTTPAPI: event.target.checked, tokenAuth: event.target.checked ? false : tokenAuth }, true);
}}
/>
</ListItemSecondaryAction>
</ListItem>
<ListItem disableGutters>
<TextField
id='outlined-full-width'
@ -247,7 +262,7 @@ export function BlogOptions(props: IBlogOptionsProps) {
renderInput={(parameters) => <TextField {...parameters} label={t('EditWorkspace.WikiRootTiddler')} helperText={t('EditWorkspace.WikiRootTiddlerDescription')} />}
/>
</AccordionDetails>
</ABlogOptionsAccordion>
</AServerOptionsAccordion>
);
}

View file

@ -117,7 +117,7 @@ export class Wiki implements IWikiService {
logger.error('Try to start wiki, but workspace not found', { workspace, workspaceID });
return;
}
const { port, rootTiddler, readOnlyMode, tokenAuth, homeUrl, lastUrl, https, excludedPlugins, isSubWiki, wikiFolderLocation, name } = workspace;
const { port, rootTiddler, readOnlyMode, tokenAuth, homeUrl, lastUrl, https, excludedPlugins, isSubWiki, wikiFolderLocation, name, enableHTTPAPI } = workspace;
if (isSubWiki) {
logger.error('Try to start wiki, but workspace is sub wiki', { workspace, workspaceID });
return;
@ -130,6 +130,7 @@ export class Wiki implements IWikiService {
adminToken = this.authService.getOrGenerateOneTimeAdminAuthTokenForWorkspace(workspaceID);
}
const workerData: IStartNodeJSWikiConfigs = {
enableHTTPAPI,
adminToken,
constants: { TIDDLYWIKI_PACKAGE_FOLDER, EXTRA_TIDGI_PLUGINS_PATH },
excludedPlugins,
@ -190,7 +191,7 @@ export class Wiki implements IWikiService {
case WikiControlActions.booted: {
setTimeout(async () => {
if (message.message !== undefined) {
logger.info('WikiControlActions.booted', { 'message.message': message.message, ...loggerMeta });
logger.info('WikiControlActions.booted ' + message.message, loggerMeta);
}
logger.info(`startWiki() resolved with message.type === 'control' and WikiControlActions.booted`, loggerMeta);
resolve();
@ -203,6 +204,13 @@ export class Wiki implements IWikiService {
}
break;
}
case WikiControlActions.listening: {
// API server started, but we are using IPC to serve content now, so do nothing here.
if (message.message !== undefined) {
logger.info('WikiControlActions.listening ' + message.message, loggerMeta);
}
break;
}
case WikiControlActions.error: {
const errorMessage = message.message ?? 'get WikiControlActions.error without message';
logger.error(errorMessage, { ...loggerMeta, message });

View file

@ -130,11 +130,12 @@ export enum WikiControlActions {
/** wiki is booted */
booted = 'tw-booted',
error = 'tw-error',
listening = 'tw-listening',
/** means worker is just started */
start = 'tw-start',
}
export interface IWikiControlMessage {
actions: WikiControlActions.booted | WikiControlActions.error | WikiControlActions.start;
actions: WikiControlActions;
argv: string[];
message?: string;
/** where this bug rise, helps debug */

View file

@ -27,6 +27,7 @@ import { startNodeJSWiki } from './startNodeJSWiki';
export interface IStartNodeJSWikiConfigs {
adminToken?: string;
constants: { EXTRA_TIDGI_PLUGINS_PATH: string; TIDDLYWIKI_PACKAGE_FOLDER: string };
enableHTTPAPI: boolean;
excludedPlugins: string[];
homePath: string;
https?: {

View file

@ -13,6 +13,7 @@ import { ipcServerRoutes } from './ipcServerRoutes';
import { adminTokenIsProvided } from './wikiWorkerUtils';
export function startNodeJSWiki({
enableHTTPAPI,
adminToken,
constants: { TIDDLYWIKI_PACKAGE_FOLDER, EXTRA_TIDGI_PLUGINS_PATH },
excludedPlugins = [],
@ -56,16 +57,17 @@ export function startNodeJSWiki({
}
process.env.TIDDLYWIKI_PLUGIN_PATH = path.resolve(homePath, 'plugins');
process.env.TIDDLYWIKI_THEME_PATH = path.resolve(homePath, 'themes');
const builtInPluginArguments = [
// don't add `+` prefix to plugin name here. `+` only used in args[0], but we are not prepend this list to the args list.
wikiInstance.boot.extraPlugins = [
// add tiddly filesystem back if is not readonly https://github.com/Jermolene/TiddlyWiki5/issues/4484#issuecomment-596779416
readOnlyMode === true ? undefined : '+plugins/tiddlywiki/filesystem',
readOnlyMode === true ? undefined : 'plugins/tiddlywiki/filesystem',
/**
* Install $:/plugins/linonetwo/tidgi instead of +plugins/tiddlywiki/tiddlyweb to speedup (without JSON.parse) and fix http errors when network change.
* See scripts/compilePlugins.mjs for how it is built.
*/
'+plugins/linonetwo/tidgi',
// '+plugins/tiddlywiki/tiddlyweb', // we use $:/plugins/linonetwo/tidgi instead
// '+plugins/linonetwo/watch-fs',
'plugins/linonetwo/tidgi',
enableHTTPAPI ? 'plugins/tiddlywiki/tiddlyweb' : undefined, // we use $:/plugins/linonetwo/tidgi instead
// 'plugins/linonetwo/watch-fs',
].filter(Boolean) as string[];
/**
* Make wiki readonly if readonly is true. This is normally used for server mode, so also enable gzip.
@ -114,19 +116,19 @@ export function startNodeJSWiki({
]
: [];
fullBootArgv = [
...builtInPluginArguments,
homePath,
'--listen',
`port=${tiddlyWikiPort}`,
`host=${tiddlyWikiHost}`,
`root-tiddler=${rootTiddler}`,
...httpsArguments,
...readonlyArguments,
...tokenAuthenticateArguments,
...excludePluginsArguments,
// `debug-level=${isDev ? 'full' : 'none'}`,
];
fullBootArgv = enableHTTPAPI
? [
homePath,
'--listen',
`port=${tiddlyWikiPort}`,
`host=${tiddlyWikiHost}`,
`root-tiddler=${rootTiddler}`,
...httpsArguments,
...readonlyArguments,
...tokenAuthenticateArguments,
...excludePluginsArguments,
]
: [homePath, '--version'];
wikiInstance.boot.argv = [...fullBootArgv];
wikiInstance.hooks.addHook('th-server-command-post-start', function(listenCommand, server) {
@ -136,9 +138,9 @@ export function startNodeJSWiki({
server.on('listening', function() {
observer.next({
type: 'control',
actions: WikiControlActions.booted,
actions: WikiControlActions.listening,
message:
`Tiddlywiki booted at http://${tiddlyWikiHost}:${tiddlyWikiPort} (webview uri ip may be different, being nativeService.getLocalHostUrlWithActualInfo(appUrl, workspace.id)) with args ${
`Tiddlywiki listening at http://${tiddlyWikiHost}:${tiddlyWikiPort} (webview uri ip may be different, being nativeService.getLocalHostUrlWithActualInfo(appUrl, workspace.id)) with args ${
wikiInstance === undefined ? '(wikiInstance is undefined)' : fullBootArgv.join(' ')
}`,
argv: fullBootArgv,
@ -148,6 +150,12 @@ export function startNodeJSWiki({
wikiInstance.boot.startup({ bootPath: TIDDLYWIKI_PACKAGE_FOLDER });
// after setWikiInstance, ipc server routes will start serving content
ipcServerRoutes.setWikiInstance(wikiInstance);
observer.next({
type: 'control',
actions: WikiControlActions.booted,
message: `Tiddlywiki booted with args ${wikiInstance === undefined ? '(wikiInstance is undefined)' : fullBootArgv.join(' ')}`,
argv: fullBootArgv,
});
} catch (error) {
const message = `Tiddlywiki booted failed with error ${(error as Error).message} ${(error as Error).stack ?? ''}`;
observer.next({ type: 'control', source: 'try catch', actions: WikiControlActions.error, message, argv: fullBootArgv });