diff --git a/backend/locales/en.json b/backend/locales/en.json index 3e2d99809..e860d3109 100644 --- a/backend/locales/en.json +++ b/backend/locales/en.json @@ -612,6 +612,15 @@ "admin.mail.testRecipientHint": "Email address that should receive the test email.", "admin.mail.testSend": "Send Email", "admin.mail.title": "Mail", + "admin.mcp.disableButton": "Disable MCP", + "admin.mcp.disabled": "MCP Disabled", + "admin.mcp.docsButton": "MCP Docs", + "admin.mcp.enableButton": "Enable MCP", + "admin.mcp.enabled": "MCP Enabled", + "admin.mcp.newKeyButton": "New MCP Key", + "admin.mcp.notImplemented": "The MCP server is not implemented yet: this page is a placeholder, and MCP cannot be enabled so far.", + "admin.mcp.subtitle": "Manage the Model Context Protocol server", + "admin.mcp.title": "MCP", "admin.metrics.auth": "You must provide the {headerName} header with a {tokenType} token. Generate an API key with the {permission} permission and use it as the token.", "admin.metrics.disabled": "Endpoint Disabled", "admin.metrics.enabled": "Endpoint Enabled", @@ -811,35 +820,6 @@ "admin.sites.subtitle": "Manage your wiki sites", "admin.sites.title": "Sites", "admin.sites.updateSuccess": "Site updated successfully.", - "admin.ssl.currentState": "Current State", - "admin.ssl.domain": "Domain", - "admin.ssl.domainHint": "Enter the fully qualified domain pointing to your wiki. (e.g. wiki.example.com)", - "admin.ssl.expiration": "Certificate Expiration", - "admin.ssl.httpPort": "HTTP Port", - "admin.ssl.httpPortHint": "Non-SSL port the server will listen to for HTTP requests. Usually 80 or 3000.", - "admin.ssl.httpPortRedirect": "Redirect HTTP requests to HTTPS", - "admin.ssl.httpPortRedirectHint": "Will automatically redirect any requests on the HTTP port to HTTPS.", - "admin.ssl.httpPortRedirectSaveSuccess": "HTTP Redirection changed successfully.", - "admin.ssl.httpPortRedirectTurnOff": "Turn Off", - "admin.ssl.httpPortRedirectTurnOn": "Turn On", - "admin.ssl.httpsPort": "HTTPS Port", - "admin.ssl.httpsPortHint": "SSL port the server will listen to for HTTPS requests. Usually 443.", - "admin.ssl.ports": "Ports", - "admin.ssl.provider": "Provider", - "admin.ssl.providerCustomCertificate": "Custom Certificate", - "admin.ssl.providerDisabled": "Disabled", - "admin.ssl.providerHint": "Select Custom Certificate if you have your own certificate already.", - "admin.ssl.providerLetsEncrypt": "Let's Encrypt", - "admin.ssl.providerOptions": "Provider Options", - "admin.ssl.renewCertificate": "Renew Certificate", - "admin.ssl.renewCertificateLoadingSubtitle": "Do not leave this page.", - "admin.ssl.renewCertificateLoadingTitle": "Renewing Certificate...", - "admin.ssl.renewCertificateSuccess": "Certificate renewed successfully.", - "admin.ssl.status": "Certificate Status", - "admin.ssl.subscriberEmail": "Subscriber Email", - "admin.ssl.subtitle": "Manage SSL configuration", - "admin.ssl.title": "SSL", - "admin.ssl.writableConfigFileWarning": "Note that your config file must be writable in order to persist ports configuration.", "admin.stats.title": "Statistics", "admin.storage.actionFailed": "Failed to run {action}.", "admin.storage.actionSuccess": "{action} completed successfully.", diff --git a/backend/modules/storage/git/definition.yml b/backend/modules/storage/git/definition.yml index 8e3275441..20d8f4263 100644 --- a/backend/modules/storage/git/definition.yml +++ b/backend/modules/storage/git/definition.yml @@ -152,6 +152,6 @@ actions: icon: database-daily-import purge: label: Purge Local Repository - hint: Empty the local working copy and clone it again from the remote. A working copy that was replaced re-attaches itself on the next sync, so this is for when git can no longer make sense of at all. The remote is not touched and nothing is committed. + hint: Empty the local working copy and clone it again from the remote. A working copy that was replaced re-attaches itself on the next sync, so this is for one git can no longer make sense of at all. The remote is not touched and nothing is committed. warn: Any commit that exists only in the local repository and has never been pushed is lost. Run a Force Sync first if you are not sure. icon: trash diff --git a/backend/modules/storage/git/storage.ts b/backend/modules/storage/git/storage.ts index 3f7b0d9a6..fa97fe4f7 100644 --- a/backend/modules/storage/git/storage.ts +++ b/backend/modules/storage/git/storage.ts @@ -270,7 +270,7 @@ async function ensureRemote(repo: Repo, target: StorageTarget): Promise<{ onRemo const remoteBranches = branches.all.filter((b) => b.startsWith('remotes/origin/')) if (remoteBranches.length > 0) { throw new Error( - `The branch "${branch}" does not exist locally or on the remote, which has ${remoteBranches + `(STORAGE/GIT) The branch "${branch}" does not exist locally or on the remote, which has ${remoteBranches .map((b) => b.replace('remotes/origin/', '')) .join(', ')}. Check the branch name, or create it on the remote first.` ) @@ -383,7 +383,7 @@ async function pullRebase(repo: Repo, branch: string): Promise { throw retryErr } throw new Error( - `${unsettled.slice(0, 5).join(', ')}${unsettled.length > 5 ? ', ...' : ''} changed here and were deleted on the remote, or the other way round, which nothing can settle on its own. Nothing has been changed either side. Decide which of the two is right - Purge Local Repository takes the remote's answer, Force Sync in Push mode takes the wiki's.` + `(STORAGE/GIT) ${unsettled.slice(0, 5).join(', ')}${unsettled.length > 5 ? ', ...' : ''} changed here and were deleted on the remote, or the other way round, which nothing can settle on its own. Nothing has been changed either side. Decide which of the two is right - Purge Local Repository takes the remote's answer, Force Sync in Push mode takes the wiki's.` ) } return conflicted @@ -442,7 +442,7 @@ async function reattach(repo: Repo, branch: string): Promise { } catch (err: any) { await abortInterrupted(repo).catch(() => null) throw new Error( - `This working copy has no history in common with origin/${branch}, and taking the remote's up failed: ${err.message}. Purge Local Repository starts again from the remote's copy.` + `(STORAGE/GIT) This working copy has no history in common with origin/${branch}, and taking the remote's up failed: ${err.message}. Purge Local Repository starts again from the remote's copy.` ) } return `The working copy had no history in common with origin/${branch} - which is what a container replaced without a volume for it leaves behind - so the remote's history was taken up and this wiki's own files kept on top of it. Nothing in the wiki itself was changed: run Import Everything if the repository holds content this wiki does not.` @@ -770,7 +770,7 @@ const gitStorage: StorageModule = { const relPath = assetRelPath(target, ref) if (!relPath) { throw new Error( - `${target.title} has no path for ${ref.locale} content, so ${ref.fileName} cannot be stored there.` + `(STORAGE/GIT) ${target.title} has no path for ${ref.locale} content, so ${ref.fileName} cannot be stored there.` ) } await withRepo(target, async (repo) => { @@ -909,7 +909,7 @@ const gitStorage: StorageModule = { const branch = target.config.branch || 'main' return withRepo(target, async (repo) => { if (!target.config.repoUrl) { - return 'No repository URI is configured, so there is nothing to sync with. Commits are being made locally.' + return '(STORAGE/GIT) No repository URI is configured, so there is nothing to sync with. Commits are being made locally.' } // -> Before anything else touches the repository, since git refuses to move a branch or make a // commit while an earlier operation is unfinished — `ensureRemote`'s checkout is the first @@ -1119,7 +1119,7 @@ async function applyIncoming( } catch (err: any) { // -> One entry the wiki could not let go of must not stop the rest of the commit being applied WIKI.logger.warn(`(STORAGE/GIT) Could not delete ${segments.join('/')} [ SKIPPED ]`) - WIKI.logger.warn(err.message) + WIKI.logger.warn(`(STORAGE/GIT) ${err.message}`) } } diff --git a/dev/build/Dockerfile b/dev/build/Dockerfile index 9968b0ef0..a8a3b5c2a 100644 --- a/dev/build/Dockerfile +++ b/dev/build/Dockerfile @@ -15,6 +15,7 @@ RUN DEBIAN_FRONTEND=noninteractive apt-get update && apt-get install -qy --no-in RUN mkdir -p /wiki && \ mkdir -p /logs && \ mkdir -p /wiki/data/content && \ + mkdir -p /wiki/data/repo && \ chown -R node:node /wiki /logs WORKDIR /wiki @@ -54,6 +55,7 @@ RUN PUPPETEER_VERSION="$(sed -n 's/^installVersion: *//p' modules/extensions/pup WORKDIR /wiki VOLUME ["/wiki/data/content"] +VOLUME ["/wiki/data/repo"] EXPOSE 3000 EXPOSE 3443 diff --git a/frontend/public/_assets/icons/fluent-ai.svg b/frontend/public/_assets/icons/fluent-ai.svg new file mode 100644 index 000000000..14a151717 --- /dev/null +++ b/frontend/public/_assets/icons/fluent-ai.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/layouts/AdminLayout.vue b/frontend/src/layouts/AdminLayout.vue index 3931b23f5..84db143bd 100644 --- a/frontend/src/layouts/AdminLayout.vue +++ b/frontend/src/layouts/AdminLayout.vue @@ -343,6 +343,16 @@ :pulse="!adminStore.info.isMailConfigured" /> + + + + + {{ t('admin.mcp.title') }} + + + + @@ -384,16 +394,6 @@ {{ t('admin.security.title') }} - - - - - {{ t('admin.ssl.title') }} - diff --git a/frontend/src/pages/AdminMcp.vue b/frontend/src/pages/AdminMcp.vue new file mode 100644 index 000000000..83d67665e --- /dev/null +++ b/frontend/src/pages/AdminMcp.vue @@ -0,0 +1,129 @@ + + + + + diff --git a/frontend/src/pages/AdminSsl.vue b/frontend/src/pages/AdminSsl.vue deleted file mode 100644 index b266bb85f..000000000 --- a/frontend/src/pages/AdminSsl.vue +++ /dev/null @@ -1,269 +0,0 @@ - - - - - diff --git a/frontend/src/router/routes.js b/frontend/src/router/routes.js index 66820a854..9cd68abe9 100644 --- a/frontend/src/router/routes.js +++ b/frontend/src/router/routes.js @@ -76,6 +76,7 @@ const routes = [ { path: 'icons', component: () => import('@/pages/AdminIcons.vue') }, { path: 'instances', component: () => import('@/pages/AdminInstances.vue') }, { path: 'mail', component: () => import('@/pages/AdminMail.vue') }, + { path: 'mcp', component: () => import('@/pages/AdminMcp.vue') }, { path: 'metrics', component: () => import('@/pages/AdminMetrics.vue') }, { path: 'rendering', component: () => import('@/pages/AdminRendering.vue') }, { path: 'scheduler', component: () => import('@/pages/AdminScheduler.vue') }, diff --git a/frontend/src/stores/admin.js b/frontend/src/stores/admin.js index 7d88d4582..9b2ad754b 100644 --- a/frontend/src/stores/admin.js +++ b/frontend/src/stores/admin.js @@ -19,6 +19,7 @@ export const useAdminStore = defineStore('admin', { webhooksTotal: 0, loginsPastDay: 0, isApiEnabled: false, + isMCPEnabled: false, isMailConfigured: false, isMetricsEnabled: false, isSchedulerHealthy: false