Browse Source

Merge branch 'support/typescript-go' into support/omit-jest

Yuki Takei 2 months ago
parent
commit
13eb3b3df8

+ 3 - 3
apps/app/src/server/crowi/index.ts

@@ -678,9 +678,9 @@ class Crowi {
    * setup GlobalNotificationService
    */
   async setUpGlobalNotification(): Promise<void> {
-    const GlobalNotificationService = (
-      await import('../service/global-notification')
-    ).default;
+    const { GlobalNotificationService } = await import(
+      '../service/global-notification'
+    );
     if (this.globalNotificationService == null) {
       this.globalNotificationService = new GlobalNotificationService(this);
     }

+ 0 - 38
apps/app/src/server/models/GlobalNotificationSetting.ts

@@ -1,38 +0,0 @@
-const mongoose = require('mongoose');
-
-const GlobalNotificationSetting = require('./GlobalNotificationSetting/index');
-
-const GlobalNotificationSettingClass = GlobalNotificationSetting.class;
-const GlobalNotificationSettingSchema = GlobalNotificationSetting.schema;
-
-/**
- * global notifcation event master
- */
-export const GlobalNotificationSettingEvent = {
-  PAGE_CREATE: 'pageCreate',
-  PAGE_EDIT: 'pageEdit',
-  PAGE_DELETE: 'pageDelete',
-  PAGE_MOVE: 'pageMove',
-  PAGE_LIKE: 'pageLike',
-  COMMENT: 'comment',
-};
-
-/**
- * global notifcation type master
- */
-export const GlobalNotificationSettingType = {
-  MAIL: 'mail',
-  SLACK: 'slack',
-};
-
-/** @param {import('~/server/crowi').default} crowi Crowi instance */
-const factory = (crowi) => {
-  GlobalNotificationSettingClass.crowi = crowi;
-  GlobalNotificationSettingSchema.loadClass(GlobalNotificationSettingClass);
-  return mongoose.model(
-    'GlobalNotificationSetting',
-    GlobalNotificationSettingSchema,
-  );
-};
-
-export default factory;

+ 0 - 35
apps/app/src/server/models/GlobalNotificationSetting/GlobalNotificationMailSetting.js

@@ -1,35 +0,0 @@
-import mongoose from 'mongoose';
-
-import { GlobalNotificationSettingType } from '../GlobalNotificationSetting';
-
-const GlobalNotificationSetting = require('./index');
-
-const GlobalNotificationSettingClass = GlobalNotificationSetting.class;
-const GlobalNotificationSettingSchema = GlobalNotificationSetting.schema;
-
-/** @param {import('~/server/crowi').default} crowi Crowi instance */
-const factory = (crowi) => {
-  GlobalNotificationSettingClass.crowi = crowi;
-  GlobalNotificationSettingSchema.loadClass(GlobalNotificationSettingClass);
-
-  const GlobalNotificationSettingModel = mongoose.model(
-    'GlobalNotificationSetting',
-    GlobalNotificationSettingSchema,
-  );
-  const GlobalNotificationMailSettingModel =
-    GlobalNotificationSettingModel.discriminator(
-      GlobalNotificationSettingType.MAIL,
-      new mongoose.Schema(
-        {
-          toEmail: String,
-        },
-        {
-          discriminatorKey: 'type',
-        },
-      ),
-    );
-
-  return GlobalNotificationMailSettingModel;
-};
-
-export default factory;

+ 44 - 0
apps/app/src/server/models/GlobalNotificationSetting/GlobalNotificationMailSetting.ts

@@ -0,0 +1,44 @@
+import mongoose from 'mongoose';
+
+import type Crowi from '~/server/crowi';
+
+import { GlobalNotificationSettingType } from './consts';
+import {
+  class as GlobalNotificationSettingClass,
+  schema as GlobalNotificationSettingSchema,
+} from './index';
+import type {
+  GlobalNotificationMailSettingModel,
+  GlobalNotificationSettingModel,
+  IGlobalNotificationMailSetting,
+  IGlobalNotificationSetting,
+} from './types';
+
+const factory = (crowi: Crowi): GlobalNotificationMailSettingModel => {
+  GlobalNotificationSettingClass.crowi = crowi;
+  GlobalNotificationSettingSchema.loadClass(GlobalNotificationSettingClass);
+
+  const GlobalNotificationSettingModel = mongoose.model<
+    IGlobalNotificationSetting,
+    GlobalNotificationSettingModel
+  >('GlobalNotificationSetting', GlobalNotificationSettingSchema);
+  const GlobalNotificationMailSettingModel =
+    GlobalNotificationSettingModel.discriminator<
+      IGlobalNotificationMailSetting,
+      GlobalNotificationMailSettingModel
+    >(
+      GlobalNotificationSettingType.MAIL,
+      new mongoose.Schema(
+        {
+          toEmail: String,
+        },
+        {
+          discriminatorKey: 'type',
+        },
+      ),
+    );
+
+  return GlobalNotificationMailSettingModel;
+};
+
+export default factory;

+ 0 - 35
apps/app/src/server/models/GlobalNotificationSetting/GlobalNotificationSlackSetting.js

@@ -1,35 +0,0 @@
-import mongoose from 'mongoose';
-
-import { GlobalNotificationSettingType } from '../GlobalNotificationSetting';
-
-const GlobalNotificationSetting = require('./index');
-
-const GlobalNotificationSettingClass = GlobalNotificationSetting.class;
-const GlobalNotificationSettingSchema = GlobalNotificationSetting.schema;
-
-/** @param {import('~/server/crowi').default} crowi Crowi instance */
-const factory = (crowi) => {
-  GlobalNotificationSettingClass.crowi = crowi;
-  GlobalNotificationSettingSchema.loadClass(GlobalNotificationSettingClass);
-
-  const GlobalNotificationSettingModel = mongoose.model(
-    'GlobalNotificationSetting',
-    GlobalNotificationSettingSchema,
-  );
-  const GlobalNotificationSlackSettingModel =
-    GlobalNotificationSettingModel.discriminator(
-      GlobalNotificationSettingType.SLACK,
-      new mongoose.Schema(
-        {
-          slackChannels: String,
-        },
-        {
-          discriminatorKey: 'type',
-        },
-      ),
-    );
-
-  return GlobalNotificationSlackSettingModel;
-};
-
-export default factory;

+ 44 - 0
apps/app/src/server/models/GlobalNotificationSetting/GlobalNotificationSlackSetting.ts

@@ -0,0 +1,44 @@
+import mongoose from 'mongoose';
+
+import type Crowi from '~/server/crowi';
+
+import { GlobalNotificationSettingType } from './consts';
+import {
+  class as GlobalNotificationSettingClass,
+  schema as GlobalNotificationSettingSchema,
+} from './index';
+import type {
+  GlobalNotificationSettingModel,
+  GlobalNotificationSlackSettingModel,
+  IGlobalNotificationSetting,
+  IGlobalNotificationSlackSetting,
+} from './types';
+
+const factory = (crowi: Crowi): GlobalNotificationSlackSettingModel => {
+  GlobalNotificationSettingClass.crowi = crowi;
+  GlobalNotificationSettingSchema.loadClass(GlobalNotificationSettingClass);
+
+  const GlobalNotificationSettingModel = mongoose.model<
+    IGlobalNotificationSetting,
+    GlobalNotificationSettingModel
+  >('GlobalNotificationSetting', GlobalNotificationSettingSchema);
+  const GlobalNotificationSlackSettingModel =
+    GlobalNotificationSettingModel.discriminator<
+      IGlobalNotificationSlackSetting,
+      GlobalNotificationSlackSettingModel
+    >(
+      GlobalNotificationSettingType.SLACK,
+      new mongoose.Schema(
+        {
+          slackChannels: String,
+        },
+        {
+          discriminatorKey: 'type',
+        },
+      ),
+    );
+
+  return GlobalNotificationSlackSettingModel;
+};
+
+export default factory;

+ 3 - 3
apps/app/src/server/models/GlobalNotificationSetting/consts.ts

@@ -8,12 +8,12 @@ export const GlobalNotificationSettingEvent = {
   PAGE_MOVE: 'pageMove',
   PAGE_LIKE: 'pageLike',
   COMMENT: 'comment',
-};
+} as const;
 
 /**
  * global notifcation type master
  */
-export const GlobalNotificationSettingEventType = {
+export const GlobalNotificationSettingType = {
   MAIL: 'mail',
   SLACK: 'slack',
-};
+} as const;

+ 0 - 122
apps/app/src/server/models/GlobalNotificationSetting/index.js

@@ -1,122 +0,0 @@
-const nodePath = require('path');
-
-const { pathUtils } = require('@growi/core/dist/utils');
-const mongoose = require('mongoose');
-
-/**
- * parent schema for GlobalNotificationSetting model
- */
-const globalNotificationSettingSchema = new mongoose.Schema({
-  isEnabled: { type: Boolean, required: true, default: true },
-  triggerPath: { type: String, required: true },
-  triggerEvents: { type: [String] },
-});
-
-/*
- * e.g. "/a/b/c" => ["/a/b/c", "/a/b", "/a", "/"]
- */
-const generatePathsOnTree = (path, pathList) => {
-  pathList.push(path);
-
-  if (path === '/') {
-    return pathList;
-  }
-
-  const newPath = nodePath.posix.dirname(path);
-
-  return generatePathsOnTree(newPath, pathList);
-};
-
-/*
- * e.g. "/a/b/c" => ["/a/b/c", "/a/b", "/a", "/"]
- */
-const generatePathsToMatch = (originalPath) => {
-  const pathList = generatePathsOnTree(originalPath, []);
-  return pathList.map((path) => {
-    // except for the original trigger path ("/a/b/c"), append "*" to find all matches
-    // e.g. ["/a/b/c", "/a/b", "/a", "/"] => ["/a/b/c", "/a/b/*", "/a/*", "/*"]
-    if (path !== originalPath) {
-      return `${pathUtils.addTrailingSlash(path)}*`;
-    }
-
-    return path;
-  });
-};
-
-/**
- * GlobalNotificationSetting Class
- * @class GlobalNotificationSetting
- */
-class GlobalNotificationSetting {
-  /** @type {import('~/server/crowi').default} Crowi instance */
-  crowi;
-
-  /** @param {import('~/server/crowi').default} crowi Crowi instance */
-  constructor(crowi) {
-    this.crowi = crowi;
-  }
-
-  /**
-   * enable notification setting
-   * @param {string} id
-   */
-  static async enable(id) {
-    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
-    const setting = await this.findOne({ _id: id });
-
-    setting.isEnabled = true;
-    setting.save();
-
-    return setting;
-  }
-
-  /**
-   * disable notification setting
-   * @param {string} id
-   */
-  static async disable(id) {
-    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
-    const setting = await this.findOne({ _id: id });
-
-    setting.isEnabled = false;
-    setting.save();
-
-    return setting;
-  }
-
-  /**
-   * find all notification settings
-   */
-  static async findAll() {
-    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
-    const settings = await this.find().sort({
-      triggerPath: 1,
-    });
-
-    return settings;
-  }
-
-  /**
-   * find a list of notification settings by path and a list of events
-   * @param {string} path
-   * @param {string} event
-   */
-  static async findSettingByPathAndEvent(event, path, type) {
-    const pathsToMatch = generatePathsToMatch(path);
-
-    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
-    const settings = await this.find({
-      triggerPath: { $in: pathsToMatch },
-      triggerEvents: event,
-      __t: type,
-      isEnabled: true,
-    }).sort({ triggerPath: 1 });
-
-    return settings;
-  }
-}
-
-module.exports = {
-  class: GlobalNotificationSetting,
-  schema: globalNotificationSettingSchema,
-};

+ 195 - 0
apps/app/src/server/models/GlobalNotificationSetting/index.ts

@@ -0,0 +1,195 @@
+import nodePath from 'node:path';
+import { pathUtils } from '@growi/core/dist/utils';
+import mongoose from 'mongoose';
+
+import type Crowi from '~/server/crowi';
+
+import type { GlobalNotificationSettingType } from './consts';
+import type {
+  GlobalNotificationSettingDocument,
+  GlobalNotificationSettingModel,
+  IGlobalNotificationMailSetting,
+  IGlobalNotificationSetting,
+  IGlobalNotificationSlackSetting,
+} from './types';
+
+/**
+ * parent schema for GlobalNotificationSetting model
+ */
+const globalNotificationSettingSchema = new mongoose.Schema<
+  IGlobalNotificationSetting,
+  GlobalNotificationSettingModel
+>({
+  isEnabled: { type: Boolean, required: true, default: true },
+  triggerPath: { type: String, required: true },
+  triggerEvents: { type: [String] },
+});
+
+/*
+ * e.g. "/a/b/c" => ["/a/b/c", "/a/b", "/a", "/"]
+ */
+const generatePathsOnTree = (path: string, pathList: string[]): string[] => {
+  pathList.push(path);
+
+  if (path === '/') {
+    return pathList;
+  }
+
+  const newPath = nodePath.posix.dirname(path);
+
+  return generatePathsOnTree(newPath, pathList);
+};
+
+/*
+ * e.g. "/a/b/c" => ["/a/b/c", "/a/b", "/a", "/"]
+ */
+const generatePathsToMatch = (originalPath: string): string[] => {
+  const pathList = generatePathsOnTree(originalPath, []);
+  return pathList.map((path) => {
+    // except for the original trigger path ("/a/b/c"), append "*" to find all matches
+    // e.g. ["/a/b/c", "/a/b", "/a", "/"] => ["/a/b/c", "/a/b/*", "/a/*", "/*"]
+    if (path !== originalPath) {
+      return `${pathUtils.addTrailingSlash(path)}*`;
+    }
+
+    return path;
+  });
+};
+
+/**
+ * GlobalNotificationSetting Class
+ * @class GlobalNotificationSetting
+ */
+class GlobalNotificationSetting {
+  static crowi: Crowi;
+
+  crowi: Crowi;
+
+  constructor(crowi: Crowi) {
+    this.crowi = crowi;
+  }
+
+  /**
+   * enable notification setting
+   */
+  static async enable(
+    this: GlobalNotificationSettingModel,
+    id: string,
+  ): Promise<GlobalNotificationSettingDocument> {
+    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
+    const setting = await this.findOne({ _id: id });
+
+    if (setting == null) {
+      throw new Error(`GlobalNotificationSetting with id ${id} not found`);
+    }
+
+    setting.isEnabled = true;
+    await setting.save();
+
+    return setting;
+  }
+
+  /**
+   * disable notification setting
+   */
+  static async disable(
+    this: GlobalNotificationSettingModel,
+    id: string,
+  ): Promise<GlobalNotificationSettingDocument> {
+    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
+    const setting = await this.findOne({ _id: id });
+
+    if (setting == null) {
+      throw new Error(`GlobalNotificationSetting with id ${id} not found`);
+    }
+
+    setting.isEnabled = false;
+    await setting.save();
+
+    return setting;
+  }
+
+  /**
+   * find all notification settings
+   */
+  static async findAll(
+    this: GlobalNotificationSettingModel,
+  ): Promise<GlobalNotificationSettingDocument[]> {
+    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
+    const settings = await this.find().sort({
+      triggerPath: 1,
+    });
+
+    return settings;
+  }
+
+  /**
+   * find a list of notification settings by path and a list of events
+   */
+  static async findSettingByPathAndEvent(
+    this: GlobalNotificationSettingModel,
+    event: string,
+    path: string,
+    type: typeof GlobalNotificationSettingType.SLACK,
+  ): Promise<
+    (GlobalNotificationSettingDocument & IGlobalNotificationSlackSetting)[]
+  >;
+  static async findSettingByPathAndEvent(
+    this: GlobalNotificationSettingModel,
+    event: string,
+    path: string,
+    type: typeof GlobalNotificationSettingType.MAIL,
+  ): Promise<
+    (GlobalNotificationSettingDocument & IGlobalNotificationMailSetting)[]
+  >;
+  static async findSettingByPathAndEvent(
+    this: GlobalNotificationSettingModel,
+    event: string,
+    path: string,
+    type: string,
+  ): Promise<GlobalNotificationSettingDocument[]> {
+    const pathsToMatch = generatePathsToMatch(path);
+
+    // biome-ignore lint/complexity/noThisInStatic: 'this' refers to the mongoose model here, not the class defined in this file
+    const settings = await this.find({
+      triggerPath: { $in: pathsToMatch },
+      triggerEvents: event,
+      __t: type,
+      isEnabled: true,
+    }).sort({ triggerPath: 1 });
+
+    return settings;
+  }
+}
+
+const factory = (crowi: Crowi): GlobalNotificationSettingModel => {
+  GlobalNotificationSetting.crowi = crowi;
+  globalNotificationSettingSchema.loadClass(GlobalNotificationSetting);
+  return mongoose.model<
+    IGlobalNotificationSetting,
+    GlobalNotificationSettingModel
+  >('GlobalNotificationSetting', globalNotificationSettingSchema);
+};
+
+export default factory;
+
+// Re-export types and constants for external use
+export {
+  GlobalNotificationSettingEvent,
+  GlobalNotificationSettingType,
+} from './consts';
+export type {
+  GlobalNotificationMailSettingModel,
+  GlobalNotificationSettingDocument,
+  GlobalNotificationSettingModel,
+  GlobalNotificationSlackSettingModel,
+  IGlobalNotificationMailSetting,
+  IGlobalNotificationSetting,
+  IGlobalNotificationSlackSetting,
+} from './types';
+
+// Internal use only
+export {
+  GlobalNotificationSetting as class,
+  globalNotificationSettingSchema as schema,
+};

+ 62 - 0
apps/app/src/server/models/GlobalNotificationSetting/types.d.ts

@@ -0,0 +1,62 @@
+import type { HydratedDocument, Model } from 'mongoose';
+
+import type Crowi from '~/server/crowi';
+
+import type { GlobalNotificationSettingType } from './consts';
+
+export interface IGlobalNotificationSetting {
+  isEnabled: boolean;
+  triggerPath: string;
+  triggerEvents: string[];
+}
+
+export type GlobalNotificationSettingDocument =
+  HydratedDocument<IGlobalNotificationSetting>;
+
+export interface GlobalNotificationSettingModel
+  extends Model<IGlobalNotificationSetting> {
+  enable(id: string): Promise<GlobalNotificationSettingDocument>;
+  disable(id: string): Promise<GlobalNotificationSettingDocument>;
+  findAll(): Promise<GlobalNotificationSettingDocument[]>;
+  findSettingByPathAndEvent(
+    event: string,
+    path: string,
+    type: typeof GlobalNotificationSettingType.SLACK,
+  ): Promise<
+    (GlobalNotificationSettingDocument & IGlobalNotificationSlackSetting)[]
+  >;
+  findSettingByPathAndEvent(
+    event: string,
+    path: string,
+    type: typeof GlobalNotificationSettingType.MAIL,
+  ): Promise<
+    (GlobalNotificationSettingDocument & IGlobalNotificationMailSetting)[]
+  >;
+}
+
+export interface IGlobalNotificationMailSetting
+  extends IGlobalNotificationSetting {
+  toEmail: string;
+}
+
+export type GlobalNotificationMailSettingModel =
+  Model<IGlobalNotificationMailSetting> & GlobalNotificationSettingModel;
+
+export interface IGlobalNotificationSlackSetting
+  extends IGlobalNotificationSetting {
+  slackChannels: string;
+}
+
+export type GlobalNotificationSlackSettingModel =
+  Model<IGlobalNotificationSlackSetting> & GlobalNotificationSettingModel;
+
+/**
+ * GlobalNotificationSetting Class
+ * @class GlobalNotificationSetting
+ */
+export declare class GlobalNotificationSetting {
+  static crowi: Crowi;
+  crowi: Crowi;
+
+  constructor(crowi: Crowi);
+}

+ 16 - 12
apps/app/src/server/service/global-notification/global-notification-mail.ts

@@ -1,9 +1,10 @@
+import nodePath from 'node:path';
 import type { IUser } from '@growi/core/dist/interfaces';
-import nodePath from 'path';
 
 import type Crowi from '~/server/crowi';
 import {
   GlobalNotificationSettingEvent,
+  type GlobalNotificationSettingModel,
   GlobalNotificationSettingType,
 } from '~/server/models/GlobalNotificationSetting';
 import type { PageDocument } from '~/server/models/page';
@@ -49,20 +50,23 @@ class GlobalNotificationMailService {
   ): Promise<void> {
     const { mailService } = this.crowi;
 
-    const { GlobalNotificationSetting } = this.crowi.models;
-    const notifications = await (
-      GlobalNotificationSetting as any
-    ).findSettingByPathAndEvent(
-      event,
-      page.path,
-      GlobalNotificationSettingType.MAIL,
-    );
+    const GlobalNotificationSetting = this.crowi.models
+      .GlobalNotificationSetting as GlobalNotificationSettingModel;
+    const notifications =
+      await GlobalNotificationSetting.findSettingByPathAndEvent(
+        event,
+        page.path,
+        GlobalNotificationSettingType.MAIL,
+      );
 
     const option = this.generateOption(event, page, triggeredBy, vars);
 
     await Promise.all(
-      notifications.map((notification: { toEmail: string }) => {
-        return mailService?.send({ ...option, to: notification.toEmail });
+      notifications.map((notification) => {
+        return mailService?.send({
+          ...option,
+          to: notification.toEmail,
+        });
       }),
     );
   }
@@ -171,4 +175,4 @@ class GlobalNotificationMailService {
   }
 }
 
-export default GlobalNotificationMailService;
+export { GlobalNotificationMailService };

+ 17 - 16
apps/app/src/server/service/global-notification/global-notification-slack.ts

@@ -1,10 +1,12 @@
 import type { IUser } from '@growi/core/dist/interfaces';
 import { pagePathUtils } from '@growi/core/dist/utils';
+import type { ChatPostMessageArguments } from '@slack/web-api';
 import urljoin from 'url-join';
 
 import type Crowi from '~/server/crowi';
 import {
   GlobalNotificationSettingEvent,
+  type GlobalNotificationSettingModel,
   GlobalNotificationSettingType,
 } from '~/server/models/GlobalNotificationSetting';
 import loggerFactory from '~/utils/logger';
@@ -17,10 +19,6 @@ const _logger = loggerFactory('growi:service:GlobalNotificationSlackService');
 
 const { encodeSpaces } = pagePathUtils;
 
-interface GlobalNotificationSlackSetting {
-  slackChannels: string;
-}
-
 /**
  * sub service class of GlobalNotificationSetting
  */
@@ -53,14 +51,14 @@ class GlobalNotificationSlackService {
   ): Promise<void> {
     const { appService, slackIntegrationService } = this.crowi;
 
-    const { GlobalNotificationSetting } = this.crowi.models;
-    const notifications = (
-      GlobalNotificationSetting as any
-    ).findSettingByPathAndEvent(
-      event,
-      path,
-      GlobalNotificationSettingType.SLACK,
-    );
+    const GlobalNotificationSetting = this.crowi.models
+      .GlobalNotificationSetting as GlobalNotificationSettingModel;
+    const notifications =
+      await GlobalNotificationSetting.findSettingByPathAndEvent(
+        event,
+        path,
+        GlobalNotificationSettingType.SLACK,
+      );
 
     const messageBody = this.generateMessageBody(
       event,
@@ -80,15 +78,16 @@ class GlobalNotificationSlackService {
     const appTitle = appService.getAppTitle();
 
     await Promise.all(
-      notifications.map((notification: GlobalNotificationSlackSetting) => {
+      notifications.map((notification) => {
         const messageObj = prepareSlackMessageForGlobalNotification(
           messageBody,
           attachmentBody,
           appTitle,
           notification.slackChannels,
         );
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any
-        return slackIntegrationService.postMessage(messageObj as any);
+        return slackIntegrationService.postMessage(
+          messageObj as unknown as ChatPostMessageArguments,
+        );
       }),
     );
   }
@@ -106,6 +105,7 @@ class GlobalNotificationSlackService {
    *
    * @return slack message body
    */
+  // biome-ignore lint/nursery/useMaxParams: event vars needed for different notification types
   generateMessageBody(
     event: string,
     id: string,
@@ -170,6 +170,7 @@ class GlobalNotificationSlackService {
    *
    * @return slack attachment body
    */
+  // biome-ignore lint/nursery/useMaxParams: event vars needed for different notification types
   generateAttachmentBody(
     _event: string,
     _id: string,
@@ -203,4 +204,4 @@ class GlobalNotificationSlackService {
   }
 }
 
-export default GlobalNotificationSlackService;
+export { GlobalNotificationSlackService };

+ 3 - 3
apps/app/src/server/service/global-notification/index.ts

@@ -5,8 +5,8 @@ import type Crowi from '~/server/crowi';
 import type { PageDocument } from '~/server/models/page';
 import loggerFactory from '~/utils/logger';
 
-import GlobalNotificationMailService from './global-notification-mail';
-import GlobalNotificationSlackService from './global-notification-slack';
+import { GlobalNotificationMailService } from './global-notification-mail';
+import { GlobalNotificationSlackService } from './global-notification-slack';
 import type { GlobalNotificationEventVars } from './types';
 
 const logger = loggerFactory('growi:service:GlobalNotificationService');
@@ -111,4 +111,4 @@ class GlobalNotificationService {
   }
 }
 
-export default GlobalNotificationService;
+export { GlobalNotificationService };