index.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812
  1. /* eslint-disable @typescript-eslint/no-this-alias */
  2. import http from 'http';
  3. import path from 'path';
  4. import { createTerminus } from '@godaddy/terminus';
  5. import attachmentRoutes from '@growi/remark-attachment-refs/dist/server';
  6. import lsxRoutes from '@growi/remark-lsx/dist/server/index.cjs';
  7. import mongoose from 'mongoose';
  8. import next from 'next';
  9. import { KeycloakUserGroupSyncService } from '~/features/external-user-group/server/service/keycloak-user-group-sync';
  10. import { LdapUserGroupSyncService } from '~/features/external-user-group/server/service/ldap-user-group-sync';
  11. import { startCronIfEnabled as startOpenaiCronIfEnabled } from '~/features/openai/server/services/cron';
  12. import { initializeOpenaiService } from '~/features/openai/server/services/openai';
  13. import { checkPageBulkExportJobInProgressCronService } from '~/features/page-bulk-export/server/service/check-page-bulk-export-job-in-progress-cron';
  14. import instanciatePageBulkExportJobCleanUpCronService, {
  15. pageBulkExportJobCleanUpCronService,
  16. } from '~/features/page-bulk-export/server/service/page-bulk-export-job-clean-up-cron';
  17. import instanciatePageBulkExportJobCronService from '~/features/page-bulk-export/server/service/page-bulk-export-job-cron';
  18. import { startCron as startAccessTokenCron } from '~/server/service/access-token';
  19. import { projectRoot } from '~/server/util/project-dir-utils';
  20. import { getGrowiVersion } from '~/utils/growi-version';
  21. import loggerFactory from '~/utils/logger';
  22. import UserEvent from '../events/user';
  23. import { accessTokenParser } from '../middlewares/access-token-parser';
  24. import { aclService as aclServiceSingletonInstance } from '../service/acl';
  25. import AppService from '../service/app';
  26. import AttachmentService from '../service/attachment';
  27. import { configManager as configManagerSingletonInstance } from '../service/config-manager';
  28. import instanciateExportService from '../service/export';
  29. import instanciateExternalAccountService from '../service/external-account';
  30. import { FileUploader, getUploader } from '../service/file-uploader'; // eslint-disable-line no-unused-vars
  31. import { G2GTransferPusherService, G2GTransferReceiverService } from '../service/g2g-transfer';
  32. import { GrowiBridgeService } from '../service/growi-bridge';
  33. import { initializeImportService } from '../service/import';
  34. import { InstallerService } from '../service/installer';
  35. import { normalizeData } from '../service/normalize-data';
  36. import PageService from '../service/page';
  37. import PageGrantService from '../service/page-grant';
  38. import PageOperationService from '../service/page-operation';
  39. import PassportService from '../service/passport';
  40. import SearchService from '../service/search';
  41. import { SlackIntegrationService } from '../service/slack-integration';
  42. import { SocketIoService } from '../service/socket-io';
  43. import UserGroupService from '../service/user-group';
  44. import { UserNotificationService } from '../service/user-notification';
  45. import { initializeYjsService } from '../service/yjs';
  46. import { getModelSafely, getMongoUri, mongoOptions } from '../util/mongoose-utils';
  47. import { setupModelsDependentOnCrowi } from './setup-models';
  48. const logger = loggerFactory('growi:crowi');
  49. const httpErrorHandler = require('../middlewares/http-error-handler');
  50. const sep = path.sep;
  51. class Crowi {
  52. /**
  53. * For retrieving other packages
  54. * @type {import('~/server/middlewares/access-token-parser').AccessTokenParser}
  55. */
  56. accessTokenParser;
  57. /** @type {ReturnType<typeof next>} */
  58. nextApp;
  59. /** @type {import('../service/config-manager').IConfigManagerForApp} */
  60. configManager;
  61. /** @type {import('../service/acl').AclService} */
  62. aclService;
  63. /** @type {AppService} */
  64. appService;
  65. /** @type {FileUploader} */
  66. fileUploadService;
  67. /** @type {import('../service/growi-info').GrowiInfoService} */
  68. growiInfoService;
  69. /** @type {import('../service/growi-bridge').GrowiBridgeService} */
  70. growiBridgeService;
  71. /** @type {import('../service/page').IPageService} */
  72. pageService;
  73. /** @type {import('../service/page-grant').default} */
  74. pageGrantService;
  75. /** @type {import('../service/page-operation').default} */
  76. pageOperationService;
  77. /** @type {PassportService} */
  78. passportService;
  79. /** @type {import('../service/rest-qiita-API')} */
  80. restQiitaAPIService;
  81. /** @type {SearchService} */
  82. searchService;
  83. /** @type {SlackIntegrationService} */
  84. slackIntegrationService;
  85. /** @type {SocketIoService} */
  86. socketIoService;
  87. /** @type UserNotificationService */
  88. userNotificationService;
  89. constructor() {
  90. this.version = getGrowiVersion();
  91. this.publicDir = path.join(projectRoot, 'public') + sep;
  92. this.resourceDir = path.join(projectRoot, 'resource') + sep;
  93. this.localeDir = path.join(this.resourceDir, 'locales') + sep;
  94. this.viewsDir = path.resolve(__dirname, '../views') + sep;
  95. this.tmpDir = path.join(projectRoot, 'tmp') + sep;
  96. this.cacheDir = path.join(this.tmpDir, 'cache');
  97. this.express = null;
  98. this.accessTokenParser = accessTokenParser;
  99. this.config = {};
  100. this.configManager = null;
  101. this.s2sMessagingService = null;
  102. this.g2gTransferPusherService = null;
  103. this.g2gTransferReceiverService = null;
  104. this.mailService = null;
  105. this.passportService = null;
  106. this.globalNotificationService = null;
  107. this.aclService = null;
  108. this.appService = null;
  109. this.fileUploadService = null;
  110. this.pluginService = null;
  111. this.searchService = null;
  112. this.socketIoService = null;
  113. this.syncPageStatusService = null;
  114. this.slackIntegrationService = null;
  115. this.inAppNotificationService = null;
  116. this.activityService = null;
  117. this.commentService = null;
  118. this.openaiThreadDeletionCronService = null;
  119. this.openaiVectorStoreFileDeletionCronService = null;
  120. this.tokens = null;
  121. /** @type {import('./setup-models').ModelsMapDependentOnCrowi} */
  122. this.models = {};
  123. this.env = process.env;
  124. this.node_env = this.env.NODE_ENV || 'development';
  125. this.port = this.env.PORT || 3000;
  126. this.events = {
  127. user: new UserEvent(this),
  128. page: new (require('../events/page'))(this),
  129. activity: new (require('../events/activity'))(this),
  130. bookmark: new (require('../events/bookmark'))(this),
  131. tag: new (require('../events/tag'))(this),
  132. admin: new (require('../events/admin'))(this),
  133. };
  134. }
  135. }
  136. Crowi.prototype.init = async function() {
  137. await this.setupDatabase();
  138. this.models = await setupModelsDependentOnCrowi(this);
  139. await this.setupConfigManager();
  140. await this.setupSessionConfig();
  141. // setup messaging services
  142. await this.setupS2sMessagingService();
  143. await this.setupSocketIoService();
  144. // customizeService depends on AppService
  145. // passportService depends on appService
  146. // export and import depends on setUpGrowiBridge
  147. await Promise.all([
  148. this.setUpApp(),
  149. this.setUpGrowiBridge(),
  150. ]);
  151. await Promise.all([
  152. this.setupGrowiInfoService(),
  153. this.setupPassport(),
  154. this.setupSearcher(),
  155. this.setupMailer(),
  156. this.setupSlackIntegrationService(),
  157. this.setupG2GTransferService(),
  158. this.setUpFileUpload(),
  159. this.setUpFileUploaderSwitchService(),
  160. this.setupAttachmentService(),
  161. this.setUpAcl(),
  162. this.setUpRestQiitaAPI(),
  163. this.setupUserGroupService(),
  164. this.setupExport(),
  165. this.setupImport(),
  166. this.setupGrowiPluginService(),
  167. this.setupPageService(),
  168. this.setupInAppNotificationService(),
  169. this.setupActivityService(),
  170. this.setupCommentService(),
  171. this.setupSyncPageStatusService(),
  172. this.setUpCustomize(), // depends on pluginService
  173. ]);
  174. await Promise.all([
  175. // globalNotification depends on slack and mailer
  176. this.setUpGlobalNotification(),
  177. this.setUpUserNotification(),
  178. // depends on passport service
  179. this.setupExternalAccountService(),
  180. this.setupExternalUserGroupSyncService(),
  181. // depends on AttachmentService
  182. this.setupOpenaiService(),
  183. ]);
  184. this.setupCron();
  185. await normalizeData();
  186. };
  187. /**
  188. * Execute functions that should be run after the express server is ready.
  189. */
  190. Crowi.prototype.asyncAfterExpressServerReady = async function() {
  191. if (this.pageOperationService != null) {
  192. await this.pageOperationService.afterExpressServerReady();
  193. }
  194. };
  195. Crowi.prototype.isPageId = function(pageId) {
  196. if (!pageId) {
  197. return false;
  198. }
  199. if (typeof pageId === 'string' && pageId.match(/^[\da-f]{24}$/)) {
  200. return true;
  201. }
  202. return false;
  203. };
  204. Crowi.prototype.setConfig = function(config) {
  205. this.config = config;
  206. };
  207. Crowi.prototype.getConfig = function() {
  208. return this.config;
  209. };
  210. Crowi.prototype.getEnv = function() {
  211. return this.env;
  212. };
  213. /**
  214. * Wrapper function of mongoose.model()
  215. * @param {string} modelName
  216. * @returns {mongoose.Model}
  217. */
  218. Crowi.prototype.model = function(modelName) {
  219. return getModelSafely(modelName);
  220. };
  221. // getter/setter of event instance
  222. Crowi.prototype.event = function(name, event) {
  223. if (event) {
  224. this.events[name] = event;
  225. }
  226. return this.events[name];
  227. };
  228. Crowi.prototype.setupDatabase = function() {
  229. mongoose.Promise = global.Promise;
  230. // mongoUri = mongodb://user:password@host/dbname
  231. const mongoUri = getMongoUri();
  232. return mongoose.connect(mongoUri, mongoOptions);
  233. };
  234. Crowi.prototype.setupSessionConfig = async function() {
  235. const session = require('express-session');
  236. const sessionMaxAge = this.configManager.getConfig('security:sessionMaxAge') || 2592000000; // default: 30days
  237. const redisUrl = this.env.REDISTOGO_URL || this.env.REDIS_URI || this.env.REDIS_URL || null;
  238. const uid = require('uid-safe').sync;
  239. // generate pre-defined uid for healthcheck
  240. const healthcheckUid = uid(24);
  241. const sessionConfig = {
  242. rolling: true,
  243. secret: this.env.SECRET_TOKEN || 'this is default session secret',
  244. resave: false,
  245. saveUninitialized: true,
  246. cookie: {
  247. maxAge: sessionMaxAge,
  248. },
  249. genid(req) {
  250. // return pre-defined uid when healthcheck
  251. if (req.path === '/_api/v3/healthcheck') {
  252. return healthcheckUid;
  253. }
  254. return uid(24);
  255. },
  256. };
  257. if (this.env.SESSION_NAME) {
  258. sessionConfig.name = this.env.SESSION_NAME;
  259. }
  260. // use Redis for session store
  261. if (redisUrl) {
  262. const redis = require('redis');
  263. const redisClient = redis.createClient({ url: redisUrl });
  264. const RedisStore = require('connect-redis')(session);
  265. sessionConfig.store = new RedisStore({ client: redisClient });
  266. }
  267. // use MongoDB for session store
  268. else {
  269. const MongoStore = require('connect-mongo');
  270. sessionConfig.store = MongoStore.create({ client: mongoose.connection.getClient() });
  271. }
  272. this.sessionConfig = sessionConfig;
  273. };
  274. Crowi.prototype.setupConfigManager = async function() {
  275. this.configManager = configManagerSingletonInstance;
  276. return this.configManager.loadConfigs();
  277. };
  278. Crowi.prototype.setupS2sMessagingService = async function() {
  279. const s2sMessagingService = require('../service/s2s-messaging')(this);
  280. if (s2sMessagingService != null) {
  281. s2sMessagingService.subscribe();
  282. this.configManager.setS2sMessagingService(s2sMessagingService);
  283. // add as a message handler
  284. s2sMessagingService.addMessageHandler(this.configManager);
  285. this.s2sMessagingService = s2sMessagingService;
  286. }
  287. };
  288. Crowi.prototype.setupSocketIoService = async function() {
  289. this.socketIoService = new SocketIoService(this);
  290. };
  291. Crowi.prototype.setupCron = function() {
  292. instanciatePageBulkExportJobCronService(this);
  293. checkPageBulkExportJobInProgressCronService.startCron();
  294. instanciatePageBulkExportJobCleanUpCronService(this);
  295. pageBulkExportJobCleanUpCronService.startCron();
  296. startOpenaiCronIfEnabled();
  297. startAccessTokenCron();
  298. };
  299. Crowi.prototype.getSlack = function() {
  300. return this.slack;
  301. };
  302. Crowi.prototype.getSlackLegacy = function() {
  303. return this.slackLegacy;
  304. };
  305. Crowi.prototype.getGlobalNotificationService = function() {
  306. return this.globalNotificationService;
  307. };
  308. Crowi.prototype.getUserNotificationService = function() {
  309. return this.userNotificationService;
  310. };
  311. Crowi.prototype.setupPassport = async function() {
  312. logger.debug('Passport is enabled');
  313. // initialize service
  314. if (this.passportService == null) {
  315. this.passportService = new PassportService(this);
  316. }
  317. this.passportService.setupSerializer();
  318. // setup strategies
  319. try {
  320. this.passportService.setupStrategyById('local');
  321. this.passportService.setupStrategyById('ldap');
  322. this.passportService.setupStrategyById('saml');
  323. this.passportService.setupStrategyById('oidc');
  324. this.passportService.setupStrategyById('google');
  325. this.passportService.setupStrategyById('github');
  326. }
  327. catch (err) {
  328. logger.error(err);
  329. }
  330. // add as a message handler
  331. if (this.s2sMessagingService != null) {
  332. this.s2sMessagingService.addMessageHandler(this.passportService);
  333. }
  334. return Promise.resolve();
  335. };
  336. Crowi.prototype.setupSearcher = async function() {
  337. this.searchService = new SearchService(this);
  338. };
  339. Crowi.prototype.setupMailer = async function() {
  340. const MailService = require('~/server/service/mail');
  341. this.mailService = new MailService(this);
  342. // add as a message handler
  343. if (this.s2sMessagingService != null) {
  344. this.s2sMessagingService.addMessageHandler(this.mailService);
  345. }
  346. };
  347. Crowi.prototype.autoInstall = async function() {
  348. const isInstalled = this.configManager.getConfig('app:installed');
  349. const username = this.configManager.getConfig('autoInstall:adminUsername');
  350. if (isInstalled || username == null) {
  351. return;
  352. }
  353. logger.info('Start automatic installation');
  354. const firstAdminUserToSave = {
  355. username,
  356. name: this.configManager.getConfig('autoInstall:adminName'),
  357. email: this.configManager.getConfig('autoInstall:adminEmail'),
  358. password: this.configManager.getConfig('autoInstall:adminPassword'),
  359. admin: true,
  360. };
  361. const globalLang = this.configManager.getConfig('autoInstall:globalLang');
  362. const allowGuestMode = this.configManager.getConfig('autoInstall:allowGuestMode');
  363. const serverDate = this.configManager.getConfig('autoInstall:serverDate');
  364. const installerService = new InstallerService(this);
  365. try {
  366. await installerService.install(firstAdminUserToSave, globalLang ?? 'en_US', {
  367. allowGuestMode,
  368. serverDate,
  369. });
  370. }
  371. catch (err) {
  372. logger.warn('Automatic installation failed.', err);
  373. }
  374. };
  375. Crowi.prototype.getTokens = function() {
  376. return this.tokens;
  377. };
  378. Crowi.prototype.start = async function() {
  379. const dev = process.env.NODE_ENV !== 'production';
  380. await this.init();
  381. await this.buildServer();
  382. // setup Next.js
  383. this.nextApp = next({ dev });
  384. await this.nextApp.prepare();
  385. // setup CrowiDev
  386. if (dev) {
  387. const CrowiDev = require('./dev');
  388. this.crowiDev = new CrowiDev(this);
  389. this.crowiDev.init();
  390. }
  391. const { express } = this;
  392. const app = (this.node_env === 'development') ? this.crowiDev.setupServer(express) : express;
  393. const httpServer = http.createServer(app);
  394. // setup terminus
  395. this.setupTerminus(httpServer);
  396. // attach to socket.io
  397. this.socketIoService.attachServer(httpServer);
  398. // Initialization YjsService
  399. initializeYjsService(this.socketIoService.io);
  400. await this.autoInstall();
  401. // listen
  402. const serverListening = httpServer.listen(this.port, () => {
  403. logger.info(`[${this.node_env}] Express server is listening on port ${this.port}`);
  404. if (this.node_env === 'development') {
  405. this.crowiDev.setupExpressAfterListening(express);
  406. }
  407. });
  408. // setup Express Routes
  409. this.setupRoutesForPlugins();
  410. this.setupRoutesAtLast();
  411. // setup Global Error Handlers
  412. this.setupGlobalErrorHandlers();
  413. // Execute this asynchronously after the express server is ready so it does not block the ongoing process
  414. this.asyncAfterExpressServerReady();
  415. return serverListening;
  416. };
  417. Crowi.prototype.buildServer = async function() {
  418. const env = this.node_env;
  419. const express = require('express')();
  420. require('./express-init')(this, express);
  421. // use bunyan
  422. if (env === 'production') {
  423. const expressBunyanLogger = require('express-bunyan-logger');
  424. const logger = loggerFactory('express');
  425. express.use(expressBunyanLogger({
  426. logger,
  427. excludes: ['*'],
  428. }));
  429. }
  430. // use morgan
  431. else {
  432. const morgan = require('morgan');
  433. express.use(morgan('dev'));
  434. }
  435. this.express = express;
  436. };
  437. Crowi.prototype.setupTerminus = function(server) {
  438. createTerminus(server, {
  439. signals: ['SIGINT', 'SIGTERM'],
  440. onSignal: async() => {
  441. logger.info('Server is starting cleanup');
  442. await mongoose.disconnect();
  443. return;
  444. },
  445. onShutdown: async() => {
  446. logger.info('Cleanup finished, server is shutting down');
  447. },
  448. });
  449. };
  450. Crowi.prototype.setupRoutesForPlugins = function() {
  451. lsxRoutes(this, this.express);
  452. attachmentRoutes(this, this.express);
  453. };
  454. /**
  455. * setup Express Routes
  456. * !! this must be at last because it includes '/*' route !!
  457. */
  458. Crowi.prototype.setupRoutesAtLast = function() {
  459. require('../routes')(this, this.express);
  460. };
  461. /**
  462. * setup global error handlers
  463. * !! this must be after the Routes setup !!
  464. */
  465. Crowi.prototype.setupGlobalErrorHandlers = function() {
  466. this.express.use(httpErrorHandler);
  467. };
  468. /**
  469. * require API for plugins
  470. *
  471. * @param {string} modulePath relative path from /lib/crowi/index.js
  472. * @return {module}
  473. *
  474. * @memberof Crowi
  475. */
  476. Crowi.prototype.require = function(modulePath) {
  477. return require(modulePath);
  478. };
  479. /**
  480. * setup GlobalNotificationService
  481. */
  482. Crowi.prototype.setUpGlobalNotification = async function() {
  483. const GlobalNotificationService = require('../service/global-notification');
  484. if (this.globalNotificationService == null) {
  485. this.globalNotificationService = new GlobalNotificationService(this);
  486. }
  487. };
  488. /**
  489. * setup UserNotificationService
  490. */
  491. Crowi.prototype.setUpUserNotification = async function() {
  492. if (this.userNotificationService == null) {
  493. this.userNotificationService = new UserNotificationService(this);
  494. }
  495. };
  496. /**
  497. * setup AclService
  498. */
  499. Crowi.prototype.setUpAcl = async function() {
  500. this.aclService = aclServiceSingletonInstance;
  501. };
  502. /**
  503. * setup CustomizeService
  504. */
  505. Crowi.prototype.setUpCustomize = async function() {
  506. const CustomizeService = require('../service/customize');
  507. if (this.customizeService == null) {
  508. this.customizeService = new CustomizeService(this);
  509. this.customizeService.initCustomCss();
  510. this.customizeService.initCustomTitle();
  511. this.customizeService.initGrowiTheme();
  512. // add as a message handler
  513. if (this.s2sMessagingService != null) {
  514. this.s2sMessagingService.addMessageHandler(this.customizeService);
  515. }
  516. }
  517. };
  518. /**
  519. * setup AppService
  520. */
  521. Crowi.prototype.setUpApp = async function() {
  522. if (this.appService == null) {
  523. this.appService = new AppService(this);
  524. // add as a message handler
  525. const isInstalled = this.configManager.getConfig('app:installed');
  526. if (this.s2sMessagingService != null && !isInstalled) {
  527. this.s2sMessagingService.addMessageHandler(this.appService);
  528. }
  529. }
  530. };
  531. /**
  532. * setup FileUploadService
  533. */
  534. Crowi.prototype.setUpFileUpload = async function(isForceUpdate = false) {
  535. if (this.fileUploadService == null || isForceUpdate) {
  536. this.fileUploadService = getUploader(this);
  537. }
  538. };
  539. /**
  540. * setup FileUploaderSwitchService
  541. */
  542. Crowi.prototype.setUpFileUploaderSwitchService = async function() {
  543. const FileUploaderSwitchService = require('../service/file-uploader-switch');
  544. this.fileUploaderSwitchService = new FileUploaderSwitchService(this);
  545. // add as a message handler
  546. if (this.s2sMessagingService != null) {
  547. this.s2sMessagingService.addMessageHandler(this.fileUploaderSwitchService);
  548. }
  549. };
  550. Crowi.prototype.setupGrowiInfoService = async function() {
  551. const { growiInfoService } = await import('../service/growi-info');
  552. this.growiInfoService = growiInfoService;
  553. };
  554. /**
  555. * setup AttachmentService
  556. */
  557. Crowi.prototype.setupAttachmentService = async function() {
  558. if (this.attachmentService == null) {
  559. this.attachmentService = new AttachmentService(this);
  560. }
  561. };
  562. /**
  563. * setup RestQiitaAPIService
  564. */
  565. Crowi.prototype.setUpRestQiitaAPI = async function() {
  566. const RestQiitaAPIService = require('../service/rest-qiita-API');
  567. if (this.restQiitaAPIService == null) {
  568. this.restQiitaAPIService = new RestQiitaAPIService(this);
  569. }
  570. };
  571. Crowi.prototype.setupUserGroupService = async function() {
  572. if (this.userGroupService == null) {
  573. this.userGroupService = new UserGroupService(this);
  574. return this.userGroupService.init();
  575. }
  576. };
  577. Crowi.prototype.setUpGrowiBridge = async function() {
  578. if (this.growiBridgeService == null) {
  579. this.growiBridgeService = new GrowiBridgeService(this);
  580. }
  581. };
  582. Crowi.prototype.setupExport = async function() {
  583. instanciateExportService(this);
  584. };
  585. Crowi.prototype.setupImport = async function() {
  586. initializeImportService(this);
  587. };
  588. Crowi.prototype.setupGrowiPluginService = async function() {
  589. const growiPluginService = await import('~/features/growi-plugin/server/services').then(mod => mod.growiPluginService);
  590. // download plugin repositories, if document exists but there is no repository
  591. // TODO: Cannot download unless connected to the Internet at setup.
  592. await growiPluginService.downloadNotExistPluginRepositories();
  593. };
  594. Crowi.prototype.setupPageService = async function() {
  595. if (this.pageGrantService == null) {
  596. this.pageGrantService = new PageGrantService(this);
  597. }
  598. // initialize after pageGrantService since pageService uses pageGrantService in constructor
  599. if (this.pageService == null) {
  600. this.pageService = new PageService(this);
  601. await this.pageService.createTtlIndex();
  602. }
  603. if (this.pageOperationService == null) {
  604. this.pageOperationService = new PageOperationService(this);
  605. await this.pageOperationService.init();
  606. }
  607. };
  608. Crowi.prototype.setupInAppNotificationService = async function() {
  609. const InAppNotificationService = require('../service/in-app-notification');
  610. if (this.inAppNotificationService == null) {
  611. this.inAppNotificationService = new InAppNotificationService(this);
  612. }
  613. };
  614. Crowi.prototype.setupActivityService = async function() {
  615. const ActivityService = require('../service/activity');
  616. if (this.activityService == null) {
  617. this.activityService = new ActivityService(this);
  618. await this.activityService.createTtlIndex();
  619. }
  620. };
  621. Crowi.prototype.setupCommentService = async function() {
  622. const CommentService = require('../service/comment');
  623. if (this.commentService == null) {
  624. this.commentService = new CommentService(this);
  625. }
  626. };
  627. Crowi.prototype.setupSyncPageStatusService = async function() {
  628. const SyncPageStatusService = require('../service/system-events/sync-page-status');
  629. if (this.syncPageStatusService == null) {
  630. this.syncPageStatusService = new SyncPageStatusService(this, this.s2sMessagingService, this.socketIoService);
  631. // add as a message handler
  632. if (this.s2sMessagingService != null) {
  633. this.s2sMessagingService.addMessageHandler(this.syncPageStatusService);
  634. }
  635. }
  636. };
  637. Crowi.prototype.setupSlackIntegrationService = async function() {
  638. if (this.slackIntegrationService == null) {
  639. this.slackIntegrationService = new SlackIntegrationService(this);
  640. }
  641. // add as a message handler
  642. if (this.s2sMessagingService != null) {
  643. this.s2sMessagingService.addMessageHandler(this.slackIntegrationService);
  644. }
  645. };
  646. Crowi.prototype.setupG2GTransferService = async function() {
  647. if (this.g2gTransferPusherService == null) {
  648. this.g2gTransferPusherService = new G2GTransferPusherService(this);
  649. }
  650. if (this.g2gTransferReceiverService == null) {
  651. this.g2gTransferReceiverService = new G2GTransferReceiverService(this);
  652. }
  653. };
  654. // execute after setupPassport
  655. Crowi.prototype.setupExternalAccountService = function() {
  656. instanciateExternalAccountService(this.passportService);
  657. };
  658. // execute after setupPassport, s2sMessagingService, socketIoService
  659. Crowi.prototype.setupExternalUserGroupSyncService = function() {
  660. this.ldapUserGroupSyncService = new LdapUserGroupSyncService(this.passportService, this.s2sMessagingService, this.socketIoService);
  661. this.keycloakUserGroupSyncService = new KeycloakUserGroupSyncService(this.s2sMessagingService, this.socketIoService);
  662. };
  663. Crowi.prototype.setupOpenaiService = function() {
  664. initializeOpenaiService(this);
  665. };
  666. export default Crowi;