Yuki Takei 3 лет назад
Родитель
Сommit
42d4d2ddfd

+ 0 - 56
packages/app/src/client/services/activate-plugin.ts

@@ -1,12 +1,5 @@
-import { readFileSync } from 'fs';
-import path from 'path';
-
-import { GrowiThemeMetadata, ViteManifest } from '@growi/core';
-
-import { GrowiPlugin, GrowiPluginResourceType, GrowiThemePluginMeta } from '~/interfaces/plugin';
 import { initializeGrowiFacade } from '~/utils/growi-facade';
 import loggerFactory from '~/utils/logger';
-import { resolveFromRoot } from '~/utils/project-dir-utils';
 
 
 declare global {
@@ -21,58 +14,9 @@ declare global {
 
 const logger = loggerFactory('growi:cli:ActivatePluginService');
 
-export type GrowiPluginManifestEntries = [growiPlugin: GrowiPlugin, manifest: ViteManifest][];
-
-
-async function retrievePluginManifest(growiPlugin: GrowiPlugin): Promise<ViteManifest> {
-  const manifestPath = resolveFromRoot(path.join('tmp/plugins', growiPlugin.installedPath, 'dist/manifest.json'));
-  const manifestStr: string = await readFileSync(manifestPath, 'utf-8');
-  return JSON.parse(manifestStr);
-}
 
 export class ActivatePluginService {
 
-  static async retrieveThemeHref(growiPlugins: GrowiPlugin[], theme: string): Promise<string | undefined> {
-
-    let matchedPlugin: GrowiPlugin | undefined;
-    let matchedThemeMetadata: GrowiThemeMetadata | undefined;
-
-    growiPlugins
-      .filter(p => p.meta.types.includes(GrowiPluginResourceType.Theme))
-      .forEach(async(growiPlugin) => {
-        const themeMetadatas = (growiPlugin.meta as GrowiThemePluginMeta).themes;
-        const themeMetadata = themeMetadatas.find(t => t.name === theme);
-
-        // found
-        if (themeMetadata != null) {
-          matchedPlugin = growiPlugin;
-          matchedThemeMetadata = themeMetadata;
-        }
-      });
-
-    if (matchedPlugin != null && matchedThemeMetadata != null) {
-      const manifest = await retrievePluginManifest(matchedPlugin);
-      return '/static/plugins' // configured by express.static
-        + `/${matchedPlugin.installedPath}/dist/${manifest[matchedThemeMetadata.manifestKey].file}`;
-    }
-  }
-
-  static async retrievePluginManifestEntries(growiPlugins: GrowiPlugin[]): Promise<GrowiPluginManifestEntries> {
-    const entries: GrowiPluginManifestEntries = [];
-
-    growiPlugins.forEach(async(growiPlugin) => {
-      try {
-        const manifest = await retrievePluginManifest(growiPlugin);
-        entries.push([growiPlugin, manifest]);
-      }
-      catch (e) {
-        logger.warn(e);
-      }
-    });
-
-    return entries;
-  }
-
   static activateAll(): void {
     initializeGrowiFacade();
 

+ 7 - 14
packages/app/src/pages/_document.page.tsx

@@ -3,15 +3,14 @@ import React from 'react';
 
 import type { ViteManifest } from '@growi/core';
 import { DefaultThemeMetadata, PresetThemesMetadatas } from '@growi/preset-themes';
-import mongoose from 'mongoose';
 import Document, {
   DocumentContext, DocumentInitialProps,
   Html, Head, Main, NextScript,
 } from 'next/document';
 
-import { ActivatePluginService, GrowiPluginManifestEntries } from '~/client/services/activate-plugin';
 import type { CrowiRequest } from '~/interfaces/crowi-request';
-import { GrowiPlugin, GrowiPluginResourceType } from '~/interfaces/plugin';
+import { GrowiPluginResourceType } from '~/interfaces/plugin';
+import type { IPluginService, GrowiPluginManifestEntries } from '~/server/service/plugin';
 import loggerFactory from '~/utils/logger';
 
 const logger = loggerFactory('growi:page:_document');
@@ -96,32 +95,26 @@ declare type GrowiDocumentInitialProps = DocumentInitialProps & GrowiDocumentPro
 
 class GrowiDocument extends Document<GrowiDocumentInitialProps> {
 
-  static presetThemesManifest: ViteManifest;
-
   static override async getInitialProps(ctx: DocumentContext): Promise<GrowiDocumentInitialProps> {
     const initialProps: DocumentInitialProps = await Document.getInitialProps(ctx);
     const { crowi } = ctx.req as CrowiRequest<any>;
-    const { configManager, customizeService } = crowi;
+    const { configManager, customizeService, pluginService } = crowi;
 
     const theme = configManager.getConfig('crowi', 'customize:theme');
     const customCss: string = customizeService.getCustomCss();
 
     // import preset-themes manifest
-    if (this.presetThemesManifest == null) {
-      this.presetThemesManifest = await import('@growi/preset-themes/dist/themes/manifest.json').then(imported => imported.default);
-    }
+    const presetThemesManifest = await import('@growi/preset-themes/dist/themes/manifest.json').then(imported => imported.default);
 
     // retrieve plugin manifests
-    const GrowiPlugin = mongoose.model<GrowiPlugin>('GrowiPlugin');
-    const growiPlugins = await GrowiPlugin.find({ isEnabled: true });
-    const pluginManifestEntries = await ActivatePluginService.retrievePluginManifestEntries(growiPlugins);
-    const pluginThemeHref = await ActivatePluginService.retrieveThemeHref(growiPlugins, theme);
+    const pluginManifestEntries = await (pluginService as IPluginService).retrieveAllPluginManifestEntries();
+    const pluginThemeHref = await (pluginService as IPluginService).retrieveThemeHref(theme);
 
     return {
       ...initialProps,
       theme,
       customCss,
-      presetThemesManifest: this.presetThemesManifest,
+      presetThemesManifest,
       pluginThemeHref,
       pluginManifestEntries,
     };

+ 1 - 1
packages/app/src/server/models/growi-plugin.ts

@@ -13,7 +13,7 @@ export interface GrowiPluginDocument extends GrowiPlugin, Document {
 }
 export interface GrowiPluginModel extends Model<GrowiPluginDocument> {
   findEnabledPlugins(): Promise<GrowiPlugin[]>
-  findEnabledPluginsIncludingTypes(includingTypes: GrowiPluginResourceType[]): Promise<GrowiPlugin[]>
+  findEnabledPluginsIncludingAnyTypes(includingTypes: GrowiPluginResourceType[]): Promise<GrowiPlugin[]>
 }
 
 const growiThemeMetadataSchema = new Schema<GrowiThemeMetadata>({

+ 90 - 6
packages/app/src/server/service/plugin.ts

@@ -1,6 +1,7 @@
-import fs from 'fs';
+import fs, { readFileSync } from 'fs';
 import path from 'path';
 
+import { GrowiThemeMetadata, ViteManifest } from '@growi/core';
 // eslint-disable-next-line no-restricted-imports
 import axios from 'axios';
 import mongoose from 'mongoose';
@@ -13,6 +14,8 @@ import {
 import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 
+import type { GrowiPluginModel } from '../models/growi-plugin';
+
 const logger = loggerFactory('growi:plugins:plugin-utils');
 
 const pluginStoringPath = resolveFromRoot('tmp/plugins');
@@ -21,7 +24,22 @@ const pluginStoringPath = resolveFromRoot('tmp/plugins');
 const githubReposIdPattern = new RegExp(/^\/([^/]+)\/([^/]+)$/);
 
 
-export class PluginService {
+export type GrowiPluginManifestEntries = [growiPlugin: GrowiPlugin, manifest: ViteManifest][];
+
+
+function retrievePluginManifest(growiPlugin: GrowiPlugin): ViteManifest {
+  const manifestPath = resolveFromRoot(path.join('tmp/plugins', growiPlugin.installedPath, 'dist/manifest.json'));
+  const manifestStr: string = readFileSync(manifestPath, 'utf-8');
+  return JSON.parse(manifestStr);
+}
+
+export interface IPluginService {
+  install(origin: GrowiPluginOrigin): Promise<void>
+  retrieveThemeHref(theme: string): Promise<string | undefined>
+  retrieveAllPluginManifestEntries(): Promise<GrowiPluginManifestEntries>
+}
+
+export class PluginService implements IPluginService {
 
   async install(origin: GrowiPluginOrigin): Promise<void> {
     // download
@@ -50,7 +68,7 @@ export class PluginService {
     return;
   }
 
-  async download(requestUrl: string, ghOrganizationName: string, ghReposName: string, ghBranch: string): Promise<void> {
+  private async download(requestUrl: string, ghOrganizationName: string, ghReposName: string, ghBranch: string): Promise<void> {
 
     const zipFilePath = path.join(pluginStoringPath, `${ghBranch}.zip`);
     const unzippedPath = path.join(pluginStoringPath, ghOrganizationName);
@@ -111,15 +129,15 @@ export class PluginService {
     return;
   }
 
-  async savePluginMetaData(plugins: GrowiPlugin[]): Promise<void> {
+  private async savePluginMetaData(plugins: GrowiPlugin[]): Promise<void> {
     const GrowiPlugin = mongoose.model('GrowiPlugin');
     await GrowiPlugin.insertMany(plugins);
   }
 
   // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
-  static async detectPlugins(origin: GrowiPluginOrigin, installedPath: string, parentPackageJson?: any): Promise<GrowiPlugin[]> {
+  private static async detectPlugins(origin: GrowiPluginOrigin, installedPath: string, parentPackageJson?: any): Promise<GrowiPlugin[]> {
     const packageJsonPath = path.resolve(pluginStoringPath, installedPath, 'package.json');
-    const packageJson = await import(packageJsonPath);
+    const packageJson = JSON.parse(readFileSync(packageJsonPath, 'utf-8'));
 
     const { growiPlugin } = packageJson;
     const {
@@ -174,4 +192,70 @@ export class PluginService {
     return [];
   }
 
+
+  async retrieveThemeHref(theme: string): Promise<string | undefined> {
+
+    const GrowiPlugin = mongoose.model('GrowiPlugin') as GrowiPluginModel;
+
+    let matchedPlugin: GrowiPlugin | undefined;
+    let matchedThemeMetadata: GrowiThemeMetadata | undefined;
+
+    try {
+      // retrieve plugin manifests
+      const growiPlugins = await GrowiPlugin.findEnabledPluginsIncludingAnyTypes([GrowiPluginResourceType.Theme]) as GrowiPlugin<GrowiThemePluginMeta>[];
+
+      growiPlugins
+        .forEach(async(growiPlugin) => {
+          const themeMetadatas = growiPlugin.meta.themes;
+          const themeMetadata = themeMetadatas.find(t => t.name === theme);
+
+          // found
+          if (themeMetadata != null) {
+            matchedPlugin = growiPlugin;
+            matchedThemeMetadata = themeMetadata;
+          }
+        });
+    }
+    catch (e) {
+      logger.error(`Could not find the theme '${theme}' from GrowiPlugin documents.`, e);
+    }
+
+    try {
+      if (matchedPlugin != null && matchedThemeMetadata != null) {
+        const manifest = await retrievePluginManifest(matchedPlugin);
+        return '/static/plugins' // configured by express.static
+          + `/${matchedPlugin.installedPath}/dist/${manifest[matchedThemeMetadata.manifestKey].file}`;
+      }
+    }
+    catch (e) {
+      logger.error(`Could not read manifest file for the theme '${theme}'`, e);
+    }
+  }
+
+  async retrieveAllPluginManifestEntries(): Promise<GrowiPluginManifestEntries> {
+
+    const GrowiPlugin = mongoose.model('GrowiPlugin') as GrowiPluginModel;
+
+    const entries: GrowiPluginManifestEntries = [];
+
+    try {
+      const growiPlugins = await GrowiPlugin.findEnabledPlugins();
+
+      growiPlugins.forEach(async(growiPlugin) => {
+        try {
+          const manifest = await retrievePluginManifest(growiPlugin);
+          entries.push([growiPlugin, manifest]);
+        }
+        catch (e) {
+          logger.warn(e);
+        }
+      });
+    }
+    catch (e) {
+      logger.error('Could not retrieve GrowiPlugin documents.', e);
+    }
+
+    return entries;
+  }
+
 }