Преглед изворни кода

Merge branch 'master' into feat/page-bulk-export-pnpm

Futa Arai пре 1 година
родитељ
комит
2ed6cc39e7
100 измењених фајлова са 869 додато и 1225 уклоњено
  1. 5 2
      .changeset/config.json
  2. 0 59
      .devcontainer/Dockerfile
  3. 3 22
      .devcontainer/compose.yml
  4. 34 33
      .devcontainer/devcontainer.json
  5. 19 0
      .devcontainer/postCreateCommand.sh
  6. 2 3
      .github/workflows/ci-app-prod.yml
  7. 16 47
      .github/workflows/ci-app.yml
  8. 22 50
      .github/workflows/ci-slackbot-proxy.yml
  9. 6 6
      .github/workflows/release-slackbot-proxy.yml
  10. 14 34
      .github/workflows/release-subpackages.yml
  11. 15 15
      .github/workflows/release.yml
  12. 23 50
      .github/workflows/reusable-app-prod.yml
  13. 5 15
      .github/workflows/reusable-app-reg-suit.yml
  14. 1 0
      .gitignore
  15. 0 0
      .npmrc
  16. 2 1
      .vscode/launch.json
  17. 8 1
      CHANGELOG.md
  18. 7 7
      README.md
  19. 7 7
      README_JP.md
  20. 28 0
      apps/app/bin/swagger-jsdoc/definition-apiv1.js
  21. 93 0
      apps/app/bin/swagger-jsdoc/definition-apiv3.js
  22. 15 0
      apps/app/bin/swagger-jsdoc/generate-spec-apiv1.sh
  23. 14 0
      apps/app/bin/swagger-jsdoc/generate-spec-apiv3.sh
  24. 0 37
      apps/app/config/swagger-definition.js
  25. 27 70
      apps/app/docker/Dockerfile
  26. 1 0
      apps/app/docker/Dockerfile.dockerignore
  27. 1 1
      apps/app/docker/README.md
  28. 1 3
      apps/app/docker/codebuild/buildspec.yml
  29. 4 0
      apps/app/nodemon.json
  30. 70 51
      apps/app/package.json
  31. 1 1
      apps/app/playwright.config.ts
  32. 4 2
      apps/app/public/static/locales/en_US/translation.json
  33. 4 2
      apps/app/public/static/locales/fr_FR/translation.json
  34. 4 2
      apps/app/public/static/locales/ja_JP/translation.json
  35. 4 2
      apps/app/public/static/locales/zh_CN/translation.json
  36. 4 4
      apps/app/src/client/components/Admin/AdminHome/SystemInfomationTable.tsx
  37. 0 1
      apps/app/src/client/components/Admin/UserGroup/UserGroupDeleteModal.tsx
  38. 12 11
      apps/app/src/client/components/Bookmarks/DragAndDropWrapper.tsx
  39. 1 1
      apps/app/src/client/components/InfiniteScroll.tsx
  40. 3 2
      apps/app/src/client/components/Me/InAppNotificationSettings.tsx
  41. 1 2
      apps/app/src/client/components/PageComment/CommentEditor.tsx
  42. 0 1
      apps/app/src/client/components/PageEditor/MarkdownTableDataImportForm.tsx
  43. 3 3
      apps/app/src/client/components/PageEditor/PageEditor.tsx
  44. 1 1
      apps/app/src/client/components/PageEditor/markdown-drawio-util-for-editor.ts
  45. 1 1
      apps/app/src/client/components/PageEditor/markdown-table-util-for-editor.ts
  46. 2 3
      apps/app/src/client/components/SearchPage/SearchResultList.tsx
  47. 3 3
      apps/app/src/client/services/AdminHomeContainer.js
  48. 1 1
      apps/app/src/features/callout/services/callout.ts
  49. 34 6
      apps/app/src/features/openai/chat/components/AiChatModal/AiChatModal.tsx
  50. 1 1
      apps/app/src/features/openai/interfaces/message-error.ts
  51. 8 5
      apps/app/src/features/openai/server/routes/message.ts
  52. 13 0
      apps/app/src/features/openai/server/services/getStreamErrorCode.ts
  53. 4 4
      apps/app/src/features/templates/server/routes/apiv3/index.ts
  54. 1 13
      apps/app/src/pages/admin/index.page.tsx
  55. 2 3
      apps/app/src/server/crowi/express-init.js
  56. 1 2
      apps/app/src/server/events/bookmark.js
  57. 10 7
      apps/app/src/server/events/page.js
  58. 3 2
      apps/app/src/server/middlewares/access-token-parser.js
  59. 10 7
      apps/app/src/server/models/bookmark.js
  60. 13 12
      apps/app/src/server/models/external-account.ts
  61. 5 6
      apps/app/src/server/models/obsolete-page.js
  62. 0 0
      apps/app/src/server/models/openapi/error-v3.ts
  63. 79 0
      apps/app/src/server/models/openapi/page.ts
  64. 39 0
      apps/app/src/server/models/openapi/revision.ts
  65. 2 2
      apps/app/src/server/models/revision.ts
  66. 10 7
      apps/app/src/server/models/user-group-relation.ts
  67. 4 10
      apps/app/src/server/routes/apiv3/admin-home.js
  68. 2 9
      apps/app/src/server/routes/apiv3/app-settings.js
  69. 1 8
      apps/app/src/server/routes/apiv3/attachment.js
  70. 0 6
      apps/app/src/server/routes/apiv3/bookmarks.js
  71. 0 6
      apps/app/src/server/routes/apiv3/customize-setting.js
  72. 0 43
      apps/app/src/server/routes/apiv3/docs.js
  73. 0 6
      apps/app/src/server/routes/apiv3/export.js
  74. 0 6
      apps/app/src/server/routes/apiv3/healthcheck.ts
  75. 0 6
      apps/app/src/server/routes/apiv3/import.js
  76. 6 3
      apps/app/src/server/routes/apiv3/invited.ts
  77. 0 6
      apps/app/src/server/routes/apiv3/markdown-setting.js
  78. 1 7
      apps/app/src/server/routes/apiv3/mongo.js
  79. 0 6
      apps/app/src/server/routes/apiv3/notification-setting.js
  80. 4 83
      apps/app/src/server/routes/apiv3/page/index.ts
  81. 0 87
      apps/app/src/server/routes/apiv3/pages/index.js
  82. 11 17
      apps/app/src/server/routes/apiv3/personal-setting.js
  83. 0 45
      apps/app/src/server/routes/apiv3/revisions.js
  84. 3 8
      apps/app/src/server/routes/apiv3/search.js
  85. 26 32
      apps/app/src/server/routes/apiv3/security-settings/index.js
  86. 1 7
      apps/app/src/server/routes/apiv3/share-links.js
  87. 0 6
      apps/app/src/server/routes/apiv3/slack-integration-legacy-settings.js
  88. 13 19
      apps/app/src/server/routes/apiv3/slack-integration-settings.js
  89. 0 5
      apps/app/src/server/routes/apiv3/statistics.js
  90. 1 7
      apps/app/src/server/routes/apiv3/user-group-relation.js
  91. 14 20
      apps/app/src/server/routes/apiv3/user-group.js
  92. 14 20
      apps/app/src/server/routes/apiv3/users.js
  93. 4 3
      apps/app/src/server/routes/attachment/api.js
  94. 4 4
      apps/app/src/server/routes/comment.js
  95. 0 2
      apps/app/src/server/routes/index.js
  96. 19 21
      apps/app/src/server/routes/login-passport.js
  97. 1 2
      apps/app/src/server/routes/login.js
  98. 5 71
      apps/app/src/server/routes/page.js
  99. 1 2
      apps/app/src/server/routes/search.ts
  100. 7 8
      apps/app/src/server/service/page/index.ts

+ 5 - 2
.changeset/config.json

@@ -18,8 +18,11 @@
     "@growi/markdown-splitter",
     "@growi/editor",
     "@growi/presentation",
-    "@growi/preset-*",
-    "@growi/remark-*",
+    "@growi/preset-templates",
+    "@growi/preset-themes",
+    "@growi/remark-attachment-refs",
+    "@growi/remark-drawio",
+    "@growi/remark-lsx",
     "@growi/slack",
     "@growi/ui"
   ]

+ 0 - 59
.devcontainer/Dockerfile

@@ -1,59 +0,0 @@
-#-------------------------------------------------------------------------------------------------------------
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
-#-------------------------------------------------------------------------------------------------------------
-
-FROM mcr.microsoft.com/vscode/devcontainers/javascript-node:0-20
-
-# The node image includes a non-root user with sudo access. Use the
-# "remoteUser" property in devcontainer.json to use it. On Linux, update
-# these values to ensure the container user's UID/GID matches your local values.
-# See https://aka.ms/vscode-remote/containers/non-root-user for details.
-ARG USERNAME=node
-ARG USER_UID=1000
-ARG USER_GID=$USER_UID
-
-RUN mkdir -p /workspace/growi/node_modules
-RUN mkdir -p /workspace/growi/apps/app/node_modules
-RUN mkdir -p /workspace/growi/apps/slackbot-proxy/node_modules
-RUN mkdir -p /workspace/growi/apps/app/.next
-
-# [Optional] Update UID/GID if needed
-RUN if [ "$USER_GID" != "1000" ] || [ "$USER_UID" != "1000" ]; then \
-        groupmod --gid $USER_GID $USERNAME \
-        && usermod --uid $USER_UID --gid $USER_GID $USERNAME; \
-    fi
-RUN chown -R $USER_UID:$USER_GID /home/$USERNAME /workspace;
-
-# *************************************************************
-# * Uncomment this section to use RUN instructions to install *
-# * any needed dependencies after executing "apt-get update". *
-# * See https://docs.docker.com/engine/reference/builder/#run *
-# *************************************************************
-ENV DEBIAN_FRONTEND=noninteractive
-
-# Prepare to install Chrome for VRT
-RUN sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
-RUN wget -q -O - https://dl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
-
-RUN apt-get update \
-    && apt-get -y install --no-install-recommends \
-      git-lfs \
-      iputils-ping net-tools dnsutils \
-
-    # Uncomment below lines to install Chromium
-    # --- works only on AMD64 ---
-    # && apt-get -y install --no-install-recommends chromium \
-    #    libgtk2.0-0 libgtk-3-0 libgbm-dev libnotify-dev libgconf-2-4 libnss3 libxss1 libasound2 libxtst6 xauth xvfb fonts-noto-cjk \
-
-    # Clean up
-    && apt-get autoremove -y \
-    && apt-get clean -y \
-    && rm -rf /var/lib/apt/lists/*
-ENV DEBIAN_FRONTEND=dialog
-
-RUN yarn global add turbo
-RUN yarn global add node-gyp
-
-# Uncomment to default to non-root user
-# USER $USER_UID

+ 3 - 22
.devcontainer/docker-compose.yml → .devcontainer/compose.yml

@@ -1,30 +1,12 @@
-#-------------------------------------------------------------------------------------------------------------
-# Copyright (c) Microsoft Corporation. All rights reserved.
-# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information.
-#-------------------------------------------------------------------------------------------------------------
-
-version: '3'
 services:
   node:
-    # Uncomment the next line to use a non-root user for all processes. You can also
-    # simply use the "remoteUser" property in devcontainer.json if you just want VS Code
-    # and its sub-processes (terminals, tasks, debugging) to execute as the user. On Linux,
-    # you may need to update USER_UID and USER_GID in .devcontainer/Dockerfile to match your
-    # user if not 1000. See https://aka.ms/vscode-remote/containers/non-root for details.
-    user: node
-
-    build:
-      context: .
-      dockerfile: Dockerfile
-
+    image: mcr.microsoft.com/devcontainers/base:ubuntu
     volumes:
       - ..:/workspace/growi:delegated
+      - pnpm-store:/workspace/growi/.pnpm-store
       - node_modules:/workspace/growi/node_modules
-      - node_modules_app:/workspace/growi/apps/app/node_modules
-      - node_modules_slackbot-proxy:/workspace/growi/apps/slackbot-proxy/node_modules
       - buildcache_app:/workspace/growi/apps/app/.next
       - ../../growi-docker-compose:/workspace/growi-docker-compose:delegated
-
     tty: true
 
   mongo:
@@ -59,7 +41,6 @@ services:
       - ../../growi-docker-compose/elasticsearch/v8/config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
 
 volumes:
+  pnpm-store:
   node_modules:
-  node_modules_app:
-  node_modules_slackbot-proxy:
   buildcache_app:

+ 34 - 33
.devcontainer/devcontainer.json

@@ -1,44 +1,45 @@
-// For format details, see https://aka.ms/vscode-remote/devcontainer.json or this file's README at:
-// https://github.com/microsoft/vscode-dev-containers/tree/v0.117.1/containers/javascript-node-12-mongo
-// If you want to run as a non-root user in the container, see .devcontainer/docker-compose.yml.
+// For format details, see https://aka.ms/devcontainer.json. For config options, see the
+// README at: https://github.com/devcontainers/templates/tree/main/src/ubuntu
 {
   "name": "GROWI-Dev",
-  "dockerComposeFile": "docker-compose.yml",
+  "dockerComposeFile": "compose.yml",
   "service": "node",
   "workspaceFolder": "/workspace/growi",
 
-  // Set *default* container specific settings.json values on container create.
-  "settings": {
-    "terminal.integrated.defaultProfile.linux": "bash"
+  "features": {
+    "ghcr.io/devcontainers/features/node:1": {
+      "version": "20.18.0"
+    }
   },
 
-  // Add the IDs of extensions you want installed when the container is created.
-  "extensions": [
-    "dbaeumer.vscode-eslint",
-    "mhutchie.git-graph",
-    "eamodio.gitlens",
-    "github.vscode-pull-request-github",
-    "cschleiden.vscode-github-actions",
-    "cweijan.vscode-database-client2",
-    "mongodb.mongodb-vscode",
-    "msjsdiag.debugger-for-chrome",
-    "firefox-devtools.vscode-firefox-debug",
-    "editorconfig.editorconfig",
-    "shinnn.stylelint",
-    "stylelint.vscode-stylelint",
-    "vitest.explorer",
-    "ms-playwright.playwright"
-  ],
-
-  // Uncomment the next line if you want start specific services in your Docker Compose config.
-  // "runServices": [],
-
-  // Uncomment the line below if you want to keep your containers running after VS Code shuts down.
-  // "shutdownAction": "none",
+  // Use 'forwardPorts' to make a list of ports inside the container available locally.
+  // "forwardPorts": [],
 
   // Use 'postCreateCommand' to run commands after the container is created.
-  "postCreateCommand": "git-lfs pull & turbo run bootstrap",
+  "postCreateCommand": "/bin/bash ./.devcontainer/postCreateCommand.sh",
+
+  // Configure tool-specific properties.
+  "customizations": {
+    "vscode": {
+      "extensions": [
+        "dbaeumer.vscode-eslint",
+        "mhutchie.git-graph",
+        "eamodio.gitlens",
+        "github.vscode-pull-request-github",
+        "cschleiden.vscode-github-actions",
+        "cweijan.vscode-database-client2",
+        "mongodb.mongodb-vscode",
+        "msjsdiag.debugger-for-chrome",
+        "firefox-devtools.vscode-firefox-debug",
+        "editorconfig.editorconfig",
+        "shinnn.stylelint",
+        "stylelint.vscode-stylelint",
+        "vitest.explorer",
+        "ms-playwright.playwright"
+      ],
+    }
+  },
 
-  // Uncomment to connect as a non-root user. See https://aka.ms/vscode-remote/containers/non-root.
-  "remoteUser": "node"
+  // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
+  // "remoteUser": "root"
 }

+ 19 - 0
.devcontainer/postCreateCommand.sh

@@ -0,0 +1,19 @@
+sudo chown -R vscode:vscode /workspace;
+
+# Instal additional packages
+sudo apt update
+sudo apt-get install -y --no-install-recommends \
+  git-lfs \
+  iputils-ping net-tools dnsutils
+sudo apt-get clean -y
+
+# Setup pnpm
+SHELL=bash pnpm setup
+eval "$(cat /home/vscode/.bashrc)"
+
+# Install turbo
+pnpm install turbo --global
+
+# Install dependencies
+git-lfs pull
+turbo run bootstrap

+ 2 - 3
.github/workflows/ci-app-prod.yml

@@ -5,7 +5,6 @@ on:
     branches:
       - master
       - dev/7.*.x
-      - dev/6.*.x
     paths:
       - .github/mergify.yml
       - .github/workflows/ci-app-prod.yml
@@ -13,7 +12,7 @@ on:
       - .github/workflows/reusable-app-reg-suit.yml
       - tsconfig.base.json
       - turbo.json
-      - yarn.lock
+      - pnpm-lock.yaml
       - package.json
       - apps/app/**
       - '!apps/app/docker/**'
@@ -26,7 +25,7 @@ on:
       - .github/workflows/reusable-app-prod.yml
       - .github/workflows/reusable-app-reg-suit.yml
       - tsconfig.base.json
-      - yarn.lock
+      - pnpm-lock.yaml
       - turbo.json
       - package.json
       - apps/app/**

+ 16 - 47
.github/workflows/ci-app.yml

@@ -12,7 +12,7 @@ on:
       - .eslint*
       - tsconfig.base.json
       - turbo.json
-      - yarn.lock
+      - pnpm-lock.yaml
       - package.json
       - apps/app/**
       - '!apps/app/docker/**'
@@ -34,21 +34,12 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
+      - uses: pnpm/action-setup@v4
+
       - uses: actions/setup-node@v4
         with:
           node-version: ${{ matrix.node-version }}
-          cache: 'yarn'
-          cache-dependency-path: '**/yarn.lock'
-
-      - name: Cache/Restore node_modules
-        uses: actions/cache@v4
-        with:
-          path: |
-            **/node_modules
-            !**/node_modules/.cache/turbo
-          key: node_modules-app-devdependencies-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-          restore-keys: |
-            node_modules-app-devdependencies-${{ runner.OS }}-node${{ matrix.node-version }}-
+          cache: 'pnpm'
 
       - name: Cache/Restore dist
         uses: actions/cache@v4
@@ -63,9 +54,8 @@ jobs:
 
       - name: Install dependencies
         run: |
-          yarn global add turbo
-          yarn global add node-gyp
-          yarn --frozen-lockfile
+          pnpm add turbo --global
+          pnpm install
 
       - name: Lint
         run: |
@@ -98,21 +88,12 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
+      - uses: pnpm/action-setup@v4
+
       - uses: actions/setup-node@v4
         with:
           node-version: ${{ matrix.node-version }}
-          cache: 'yarn'
-          cache-dependency-path: '**/yarn.lock'
-
-      - name: Cache/Restore node_modules
-        uses: actions/cache@v4
-        with:
-          path: |
-            **/node_modules
-            !**/node_modules/.cache/turbo
-          key: node_modules-app-devdependencies-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-          restore-keys: |
-            node_modules-app-devdependencies-${{ runner.OS }}-node${{ matrix.node-version }}-
+          cache: 'pnpm'
 
       - name: Cache/Restore dist
         uses: actions/cache@v4
@@ -127,9 +108,8 @@ jobs:
 
       - name: Install dependencies
         run: |
-          yarn global add turbo
-          yarn global add node-gyp
-          yarn --frozen-lockfile
+          pnpm add turbo --global
+          pnpm install
 
       - name: Test
         run: |
@@ -172,22 +152,12 @@ jobs:
     steps:
       - uses: actions/checkout@v4
 
+      - uses: pnpm/action-setup@v4
+
       - uses: actions/setup-node@v4
         with:
           node-version: ${{ matrix.node-version }}
-          cache: 'yarn'
-          cache-dependency-path: '**/yarn.lock'
-
-      - name: Cache/Restore node_modules
-        uses: actions/cache@v4
-        with:
-          path: |
-            **/node_modules
-            !**/node_modules/.cache/turbo
-          key: node_modules-app-devdependencies-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}
-          restore-keys: |
-            node_modules-app-devdependencies-${{ runner.OS }}-node${{ matrix.node-version }}-
-            node_modules-app-devdependencies-${{ runner.OS }}-node${{ matrix.node-version }}-
+          cache: 'pnpm'
 
       - name: Cache/Restore dist
         uses: actions/cache@v4
@@ -202,9 +172,8 @@ jobs:
 
       - name: Install dependencies
         run: |
-          yarn global add turbo
-          yarn global add node-gyp
-          yarn --frozen-lockfile
+          pnpm add turbo --global
+          pnpm install
 
       - name: turbo run launch-dev:ci
         working-directory: ./apps/app

+ 22 - 50
.github/workflows/ci-slackbot-proxy.yml

@@ -12,7 +12,7 @@ on:
       - .eslint*
       - tsconfig.base.json
       - turbo.json
-      - yarn.lock
+      - pnpm-lock.yaml
       - package.json
       - apps/slackbot-proxy/**
       - '!apps/slackbot-proxy/docker/**'
@@ -35,21 +35,12 @@ jobs:
     steps:
     - uses: actions/checkout@v4
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: ${{ matrix.node-version }}
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
-
-    - name: Cache/Restore node_modules
-      uses: actions/cache@v4
-      with:
-        path: |
-          **/node_modules
-        key: node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/slackbot-proxy/package.json') }}
-        restore-keys: |
-          node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
-          node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-
+        cache: 'pnpm'
 
     - name: Restore dist
       uses: actions/cache/restore@v4
@@ -63,9 +54,8 @@ jobs:
 
     - name: Install dependencies
       run: |
-        yarn global add turbo
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm add turbo --global
+        pnpm install
 
     - name: Lint
       run: |
@@ -110,21 +100,12 @@ jobs:
     steps:
     - uses: actions/checkout@v4
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: ${{ matrix.node-version }}
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
-
-    - name: Cache/Restore node_modules
-      uses: actions/cache@v4
-      with:
-        path: |
-          **/node_modules
-        key: node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-${{ hashFiles('apps/slackbot-proxy/package.json') }}
-        restore-keys: |
-          node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-${{ hashFiles('**/yarn.lock') }}-
-          node_modules-${{ runner.OS }}-node${{ matrix.node-version }}-
+        cache: 'pnpm'
 
     - name: Restore dist
       uses: actions/cache/restore@v4
@@ -138,11 +119,10 @@ jobs:
 
     - name: Install dependencies
       run: |
-        yarn global add turbo
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm add turbo --global
+        pnpm install
 
-    - name: yarn dev:ci
+    - name: turbo run dev:ci
       working-directory: ./apps/slackbot-proxy
       run: |
         cp config/ci/.env.local.for-ci .env.development.local
@@ -198,15 +178,16 @@ jobs:
     steps:
     - uses: actions/checkout@v4
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: ${{ matrix.node-version }}
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
+        cache: 'pnpm'
 
     - name: Install turbo
       run: |
-        yarn global add turbo
+        pnpm add turbo --global
 
     - name: Prune repositories
       run: |
@@ -214,20 +195,10 @@ jobs:
         rm -rf apps packages
         mv out/* .
 
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v4
-      with:
-        path: |
-          **/node_modules
-        key: node_modules-slackbot-prxy-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          node_modules-slackbot-proxy-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
-
     - name: Install dependencies
+      # Run pnpm install with `--no-frozen-lockfile` option after `turbo prune` to avoid ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY
       run: |
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm install --no-frozen-lockfile
 
     - name: Restore dist
       uses: actions/cache/restore@v4
@@ -245,14 +216,15 @@ jobs:
         turbo run build
 
     - name: Install dependencies for production
+      # Run pnpm install with `--no-frozen-lockfile` option after `turbo prune` to avoid ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY
       run: |
-        yarn --production
+        pnpm install --no-frozen-lockfile --prod
 
-    - name: yarn start:prod:ci
+    - name: pnpm run start:prod:ci
       working-directory: ./apps/slackbot-proxy
       run: |
         cp config/ci/.env.local.for-ci .env.production.local
-        yarn start:prod:ci
+        pnpm run start:prod:ci
       env:
         SERVER_URI: http://localhost:8080
         TYPEORM_CONNECTION: mysql

+ 6 - 6
.github/workflows/release-slackbot-proxy.yml

@@ -93,21 +93,21 @@ jobs:
       with:
         ref: ${{ github.event.pull_request.base.ref }}
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: '18'
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
+        cache: 'pnpm'
 
     - name: Install dependencies
       run: |
-        yarn global add turbo
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm add turbo --global
+        pnpm install
 
     - name: Bump versions for next RC
       run: |
-        turbo run version --filter=@growi/slackbot-proxy -- --prerelease
+        turbo run version:prerelease --filter=@growi/slackbot-proxy
 
     - name: Retrieve information from package.json
       uses: myrotvorets/info-from-package-json-action@2.0.1

+ 14 - 34
.github/workflows/release-subpackages.yml

@@ -28,27 +28,17 @@ jobs:
     steps:
     - uses: actions/checkout@v4
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: '20'
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
-
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v4
-      with:
-        path: |
-          **/node_modules
-        key: node_modules-release-subpackages-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          node_modules-release-subpackages-${{ runner.OS }}-node${{ inputs.node-version }}-
+        cache: 'pnpm'
 
     - name: Install dependencies
       run: |
-        yarn global add turbo
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm add turbo --global
+        pnpm install
 
     - name: Setup .npmrc
       run: |
@@ -61,14 +51,14 @@ jobs:
     - name: Retrieve changesets information
       id: changesets-status
       run: |
-        yarn changeset status --output status.json
+        pnpm changeset status --output status.json
         echo "CHANGESETS_LENGTH=$(jq -r '.changesets | length' status.json)" >> $GITHUB_OUTPUT
         rm status.json
 
     - name: Snapshot release to npm
       if: steps.changesets-status.outputs.CHANGESETS_LENGTH > 0
       run: |
-        yarn release-subpackages:snapshot
+        pnpm run release-subpackages:snapshot
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
@@ -81,35 +71,25 @@ jobs:
     steps:
     - uses: actions/checkout@v4
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: '20'
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
-
-    - name: Cache/Restore node_modules
-      id: cache-dependencies
-      uses: actions/cache@v4
-      with:
-        path: |
-          **/node_modules
-        key: node_modules-release-subpackages-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          node_modules-release-subpackages-${{ runner.OS }}-node${{ inputs.node-version }}-
+        cache: 'pnpm'
 
     - name: Install dependencies
       run: |
-        yarn global add turbo
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm add turbo --global
+        pnpm install
 
     - name: Create Release Pull Request or Publish to npm
       id: changesets
       uses: changesets/action@v1
       with:
         title: Release Subpackages
-        version: yarn version-subpackages
-        publish: yarn release-subpackages
+        version: pnpm run version-subpackages
+        publish: pnpm run release-subpackages
       env:
         GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
         NPM_TOKEN: ${{ secrets.NPM_TOKEN }}

+ 15 - 15
.github/workflows/release.yml

@@ -22,22 +22,22 @@ jobs:
       with:
         ref: ${{ github.event.pull_request.base.ref }}
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: '20'
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
+        cache: 'pnpm'
 
     - name: Install dependencies
       run: |
-        yarn global add turbo
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm add turbo --global
+        pnpm install
 
     - name: Bump versions
       run: |
-        turbo run version --filter=@growi/app -- --patch
-        yarn upgrade --scope=@growi
+        turbo run version:patch --filter=@growi/app
+        pnpm upgrade --scope=@growi
         sh ./apps/app/bin/github-actions/update-readme.sh
 
     - name: Retrieve information from package.json
@@ -162,23 +162,23 @@ jobs:
       with:
         ref: v${{ needs.create-github-release.outputs.RELEASED_VERSION }}
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: '20'
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
+        cache: 'pnpm'
 
     - name: Install dependencies
       run: |
-        yarn global add turbo
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm add turbo --global
+        pnpm install
 
     - name: Bump versions for next RC
       run: |
-        turbo run version --filter=@growi/app -- --prepatch
-        turbo run version --filter=@growi/slackbot-proxy -- --prepatch
-        yarn upgrade --scope=@growi
+        turbo run version:prepatch --filter=@growi/app
+        turbo run version:prepatch --filter=@growi/slackbot-proxy
+        pnpm upgrade --scope=@growi
 
     - name: Retrieve information from package.json
       uses: myrotvorets/info-from-package-json-action@2.0.1

+ 23 - 50
.github/workflows/reusable-app-prod.yml

@@ -26,15 +26,16 @@ jobs:
         # retrieve local font files
         lfs: true
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: ${{ inputs.node-version }}
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
+        cache: 'pnpm'
 
     - name: Install turbo
       run: |
-        yarn global add turbo
+        pnpm add turbo --global
 
     - name: Prune repositories
       run: |
@@ -42,20 +43,10 @@ jobs:
         rm -rf apps packages
         mv out/* .
 
-    - name: Cache/Restore node_modules
-      uses: actions/cache@v4
-      with:
-        path: |
-          **/node_modules
-          !**/node_modules/.cache/turbo
-        key: node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
-
     - name: Install dependencies
+      # Run pnpm install with `--no-frozen-lockfile` option after `turbo prune` to avoid ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY
       run: |
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm install --no-frozen-lockfile
 
     - name: Cache/Restore dist
       uses: actions/cache@v4
@@ -79,7 +70,7 @@ jobs:
     - name: Archive production files
       id: archive-prod-files
       run: |
-        tar -zcf production.tar.gz \
+        tar -zcf production.tar.gz --exclude ./apps/app/.next/cache \
           package.json \
           apps/app/.next \
           apps/app/config \
@@ -137,15 +128,16 @@ jobs:
     steps:
     - uses: actions/checkout@v4
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: ${{ inputs.node-version }}
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
+        cache: 'pnpm'
 
     - name: Install turbo
       run: |
-        yarn global add turbo
+        pnpm add turbo --global
 
     - name: Prune repositories
       run: |
@@ -153,19 +145,10 @@ jobs:
         rm -rf apps packages
         mv out/* .
 
-    - name: Restore node_modules
-      uses: actions/cache/restore@v4
-      with:
-        path: |
-          **/node_modules
-        # shared key with build-prod
-        key: node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
-
     - name: Install dependencies
+      # Run pnpm install with `--no-frozen-lockfile` option after `turbo prune` to avoid ERR_PNPM_LOCKFILE_MISSING_DEPENDENCY
       run: |
-        yarn --production
+        pnpm install --no-frozen-lockfile --prod
 
     - name: Download production files artifact
       uses: actions/download-artifact@v4
@@ -176,11 +159,11 @@ jobs:
       run: |
         tar -xf ${{ needs.build-prod.outputs.PROD_FILES }}
 
-    - name: yarn server:ci
+    - name: pnpm run server:ci
       working-directory: ./apps/app
       run: |
         cp config/ci/.env.local.for-ci .env.production.local
-        yarn server:ci
+        pnpm run server:ci
       env:
         MONGO_URI: mongodb://localhost:${{ job.services.mongodb.ports['27017'] }}/growi
         ELASTICSEARCH_URI: http://localhost:${{ job.services.elasticsearch.ports['9200'] }}/growi
@@ -228,30 +211,20 @@ jobs:
     steps:
     - uses: actions/checkout@v4
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: ${{ inputs.node-version }}
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
-
-    - name: Restore node_modules
-      uses: actions/cache/restore@v4
-      with:
-        path: |
-          **/node_modules
-        # saved key by build-prod
-        key: node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          node_modules-app-build-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
+        cache: 'pnpm'
 
     - name: Install dependencies
       run: |
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm install
 
     - name: Install Playwright browsers
       run: |
-        yarn playwright install --with-deps ${{ matrix.browser }}
+        pnpm playwright install --with-deps ${{ matrix.browser }}
 
     - name: Download production files artifact
       uses: actions/download-artifact@v4
@@ -271,7 +244,7 @@ jobs:
       if: ${{ matrix.browser == 'chromium' }}
       working-directory: ./apps/app
       run: |
-        yarn playwright test --project=chromium/installer
+        pnpm playwright test --project=chromium/installer
       env:
         HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500
         MONGO_URI: mongodb://mongodb:27017/growi-playwright-installer
@@ -285,7 +258,7 @@ jobs:
     - name: Playwright Run
       working-directory: ./apps/app
       run: |
-        yarn playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }}
+        pnpm playwright test --project=${{ matrix.browser }} --shard=${{ matrix.shard }}
       env:
         HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500
         MONGO_URI: mongodb://mongodb:27017/growi-playwright
@@ -299,7 +272,7 @@ jobs:
     - name: Playwright Run (--project=${browser}/guest-mode)
       working-directory: ./apps/app
       run: |
-        yarn playwright test --project=${{ matrix.browser }}/guest-mode --shard=${{ matrix.shard }}
+        pnpm playwright test --project=${{ matrix.browser }}/guest-mode --shard=${{ matrix.shard }}
       env:
         HOME: /root # ref: https://github.com/microsoft/playwright/issues/6500
         MONGO_URI: mongodb://mongodb:27017/growi-playwright-guest-mode

+ 5 - 15
.github/workflows/reusable-app-reg-suit.yml

@@ -54,26 +54,16 @@ jobs:
         ref: ${{ inputs.checkout-ref }}
         fetch-depth: 0
 
+    - uses: pnpm/action-setup@v4
+
     - uses: actions/setup-node@v4
       with:
         node-version: ${{ inputs.node-version }}
-        cache: 'yarn'
-        cache-dependency-path: '**/yarn.lock'
-
-    - name: Restore node_modules
-      uses: actions/cache/restore@v4
-      with:
-        path: |
-          **/node_modules
-        # saved key by launch-prod
-        key: node_modules-app-launch-prod-${{ runner.OS }}-node${{ inputs.node-version }}-${{ hashFiles('**/yarn.lock') }}
-        restore-keys: |
-          node_modules-app-launch-prod-${{ runner.OS }}-node${{ inputs.node-version }}-
+        cache: 'pnpm'
 
     - name: Install dependencies
       run: |
-        yarn global add node-gyp
-        yarn --frozen-lockfile
+        pnpm install
 
     - name: Download screenshots taken by cypress
       uses: actions/download-artifact@v4
@@ -85,7 +75,7 @@ jobs:
     - name: Run reg-suit
       working-directory: ./apps/app
       run: |
-        yarn reg:run
+        pnpm run reg:run
 
     - name: Slack Notification
       uses: weseek/ghaction-slack-notification@master

+ 1 - 0
.gitignore

@@ -4,6 +4,7 @@
 node_modules
 /.pnp
 .pnp.js
+.pnpm-store
 
 # testing
 coverage


+ 2 - 1
.vscode/launch.json

@@ -31,8 +31,9 @@
         "request": "launch",
         "name": "Debug: Server",
         "cwd": "${workspaceFolder}/apps/app",
-        "runtimeExecutable": "yarn",
+        "runtimeExecutable": "pnpm",
         "runtimeArgs": [
+          "run",
           "dev"
         ],
         "skipFiles": [

+ 8 - 1
CHANGELOG.md

@@ -1,9 +1,16 @@
 # Changelog
 
-## [Unreleased](https://github.com/weseek/growi/compare/v7.0.21...HEAD)
+## [Unreleased](https://github.com/weseek/growi/compare/v7.0.22...HEAD)
 
 *Please do not manually update this file. We've automated the process.*
 
+## [v7.0.22](https://github.com/weseek/growi/compare/v7.0.21...v7.0.22) - 2024-10-21
+
+### 🐛 Bug Fixes
+
+* fix: Edit button appear for the side of header (#9270) @yuki-takei
+* fix: Collaborative editing occurs unstable behavior (#9267) @yuki-takei
+
 ## [v7.0.21](https://github.com/weseek/growi/compare/v7.0.20...v7.0.21) - 2024-10-15
 
 ### 🚀 Improvement

+ 7 - 7
README.md

@@ -81,9 +81,9 @@ See [GROWI Docs: Environment Variables](https://docs.growi.org/en/admin-guide/ad
 
 - Node.js v18.x or v20.x
 - npm 6.x
-- yarn
+- pnpm 9.x
 - [Turborepo](https://turbo.build/repo)
-- MongoDB 4.4 or above
+- MongoDB 6.0 or above
 
 ### Optional Dependencies
 
@@ -95,11 +95,11 @@ See [GROWI Docs: Environment Variables](https://docs.growi.org/en/admin-guide/ad
 
 ## Command details
 
-| command           | desc                                                    |
-| ------------------| ------------------------------------------------------- |
-| `yarn app:build`  | Build GROWI app client                                  |
-| `yarn app:server` | Launch GROWI app server                                 |
-| `yarn start`      | Invoke `yarn app:build` and `yarn app:server`           |
+| command               | desc                                                    |
+| --------------------- | ------------------------------------------------------- |
+| `pnpm run app:build`  | Build GROWI app client                                  |
+| `pnpm run app:server` | Launch GROWI app server                                 |
+| `pnpm run start`      | Invoke `pnpm run app:build` and `pnpm run app:server`   |
 
 For more info, see [GROWI Docs: List of npm Scripts](https://docs.growi.org/en/dev/startup-v5/start-development.html#list-of-npm-scripts).
 

+ 7 - 7
README_JP.md

@@ -80,9 +80,9 @@ Crowi からの移行は **[こちら](https://docs.growi.org/en/admin-guide/mig
 
 - Node.js v18.x or v20.x
 - npm 6.x
-- yarn
+- pnpm 9.x
 - [Turborepo](https://turbo.build/repo)
-- MongoDB 4.4 以上
+- MongoDB 6.0 以上
 
 ### オプションの依存関係
 
@@ -94,11 +94,11 @@ Crowi からの移行は **[こちら](https://docs.growi.org/en/admin-guide/mig
 
 ## コマンド詳細
 
-| コマンド          | 説明                                                    |
-| ------------------| ------------------------------------------------------- |
-| `yarn app:build`  | GROWI app クライアントをビルドします。                  |
-| `yarn app:server` | GROWI app サーバーを起動します。                        |
-| `yarn start`      | `yarn app:build` と `yarn app:server` を呼び出します。  |
+| コマンド              | 説明                                                            |
+| --------------------- | --------------------------------------------------------------- |
+| `pnpm run app:build`  | GROWI app クライアントをビルドします。                          |
+| `pnpm run app:server` | GROWI app サーバーを起動します。                                |
+| `pnpm run start`      | `pnpm run app:build` と `pnpm run app:server` を呼び出します。  |
 
 詳しくは [GROWI Docs: npm スクリプトリスト](https://docs.growi.org/ja/dev/startup-v5/start-development.html#npm-%E3%82%B9%E3%82%AF%E3%83%AA%E3%83%95%E3%82%9A%E3%83%88%E3%83%AA%E3%82%B9%E3%83%88)をご覧ください。
 

+ 28 - 0
apps/app/bin/swagger-jsdoc/definition-apiv1.js

@@ -0,0 +1,28 @@
+const pkg = require('../../package.json');
+
+module.exports = {
+  openapi: '3.0.1',
+  info: {
+    title: 'GROWI REST API v1',
+    version: pkg.version,
+  },
+  servers: [
+    {
+      url: 'https://demo.growi.org/_api',
+    },
+  ],
+  security: [
+    {
+      api_key: [],
+    },
+  ],
+  components: {
+    securitySchemes: {
+      api_key: {
+        type: 'apiKey',
+        name: 'access_token',
+        in: 'query',
+      },
+    },
+  },
+};

+ 93 - 0
apps/app/bin/swagger-jsdoc/definition-apiv3.js

@@ -0,0 +1,93 @@
+const pkg = require('../../package.json');
+
+module.exports = {
+  openapi: '3.0.1',
+  info: {
+    title: 'GROWI REST API v3',
+    version: pkg.version,
+  },
+  servers: [
+    {
+      url: 'https://demo.growi.org/_api/v3',
+    },
+  ],
+  security: [
+    {
+      api_key: [],
+    },
+  ],
+  components: {
+    securitySchemes: {
+      api_key: {
+        type: 'apiKey',
+        name: 'access_token',
+        in: 'query',
+      },
+    },
+  },
+  'x-tagGroups': [
+    {
+      name: 'User API',
+      tags: [
+        'Attachment',
+        'Bookmarks',
+        'Page',
+        'Pages',
+        'Revisions',
+        'ShareLinks',
+        'Users',
+        '',
+        '',
+      ],
+    },
+    {
+      name: 'User Personal Settings API',
+      tags: [
+        'GeneralSetting',
+        'EditorSetting',
+        'InAppNotificationSettings',
+        '',
+        '',
+        '',
+        '',
+        '',
+      ],
+    },
+    {
+      name: 'System Management API',
+      tags: [
+        'Home',
+        'AppSettings',
+        'SecuritySetting',
+        'MarkDownSetting',
+        'CustomizeSetting',
+        'Import',
+        'Export',
+        'MongoDB',
+        'NotificationSetting',
+        'SlackIntegrationSettings',
+        'SlackIntegrationSettings (with proxy)',
+        'SlackIntegrationSettings (without proxy)',
+        'SlackIntegrationLegacySetting',
+        'ShareLink Management',
+        'UserGroupRelations',
+        'UserGroups',
+        'Users Management',
+        'FullTextSearch Management',
+      ],
+    },
+    {
+      name: 'Public API',
+      tags: [
+        'Healthcheck',
+        'Statistics',
+        '',
+        '',
+        '',
+        '',
+        '',
+        '',
+      ],
+    },
+  ],
+};

+ 15 - 0
apps/app/bin/swagger-jsdoc/generate-spec-apiv1.sh

@@ -0,0 +1,15 @@
+# USAGE:
+#   cd apps/app && sh bin/swagger-jsdoc/generate-spec-apiv1.sh
+#   APP_PATH=/path/to/apps/app sh bin/swagger-jsdoc/generate-spec-apiv1.sh
+#   APP_PATH=/path/to/apps/app OUT=/path/to/output sh bin/swagger-jsdoc/generate-spec-apiv1.sh
+
+APP_PATH=${APP_PATH:-"."}
+
+OUT=${OUT:-"${APP_PATH}/tmp/openapi-spec-apiv1.json"}
+
+swagger-jsdoc \
+  -o "${OUT}" \
+  -d "${APP_PATH}/bin/swagger-jsdoc/definition-apiv1.js" \
+  "${APP_PATH}/src/server/routes/*.{js,ts}" \
+  "${APP_PATH}/src/server/routes/attachment/**/*.{js,ts}" \
+  "${APP_PATH}/src/server/models/openapi/**/*.{js,ts}"

+ 14 - 0
apps/app/bin/swagger-jsdoc/generate-spec-apiv3.sh

@@ -0,0 +1,14 @@
+# USAGE:
+#   cd apps/app && sh bin/swagger-jsdoc/generate-spec-apiv3.sh
+#   APP_PATH=/path/to/apps/app sh bin/swagger-jsdoc/generate-spec-apiv3.sh
+#   APP_PATH=/path/to/apps/app OUT=/path/to/output sh bin/swagger-jsdoc/generate-spec-apiv3.sh
+
+APP_PATH=${APP_PATH:-"."}
+
+OUT=${OUT:-"${APP_PATH}/tmp/openapi-spec-apiv3.json"}
+
+swagger-jsdoc \
+  -o "${OUT}" \
+  -d "${APP_PATH}/bin/swagger-jsdoc/definition-apiv3.js" \
+  "${APP_PATH}/src/server/routes/apiv3/**/*.{js,ts}" \
+  "${APP_PATH}/src/server/models/openapi/**/*.{js,ts}"

+ 0 - 37
apps/app/config/swagger-definition.js

@@ -1,37 +0,0 @@
-const pkg = require('../package.json');
-
-const apiVersion = process.env.API_VERSION || '3';
-const basePath = (apiVersion === '1' ? '/_api' : `/_api/v${apiVersion}`);
-
-module.exports = {
-  openapi: '3.0.1',
-  info: {
-    title: `GROWI REST API v${apiVersion}`,
-    version: pkg.version,
-  },
-  servers: [
-    {
-      url: 'https://demo.growi.org{basePath}',
-      variables: {
-        basePath: {
-          default: basePath,
-          description: 'base path',
-        },
-      },
-    },
-  ],
-  security: [
-    {
-      api_key: [],
-    },
-  ],
-  components: {
-    securitySchemes: {
-      api_key: {
-        type: 'apiKey',
-        name: 'access_token',
-        in: 'query',
-      },
-    },
-  },
-};

+ 27 - 70
apps/app/docker/Dockerfile

@@ -10,85 +10,40 @@ ENV optDir /opt
 
 WORKDIR ${optDir}
 
-RUN yarn global add turbo
-COPY . .
-RUN turbo prune @growi/app --docker
-
-
-##
-## deps-resolver
-##
-FROM node:20-slim AS deps-resolver
-
-ENV optDir /opt
-
-WORKDIR ${optDir}
-
-RUN set -eux; \
-	apt-get update; \
-	apt-get install -y python3 build-essential;
-
-# copy files
-COPY --from=base ${optDir}/out/json/ .
-COPY --from=base ${optDir}/out/yarn.lock ./yarn.lock
+# install pnpm
+RUN apt-get update && apt-get install -y ca-certificates wget --no-install-recommends \
+  && wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sh -
+ENV PNPM_HOME "/root/.local/share/pnpm"
+ENV PATH "$PNPM_HOME:$PATH"
 
-# setup (with network-timeout = 1 hour)
-RUN yarn config set network-timeout 3600000
-RUN yarn global add node-gyp
-RUN yarn --frozen-lockfile
-
-# make artifacts
-RUN tar -cf node_modules.tar \
-  node_modules \
-  apps/*/node_modules \
-  packages/*/node_modules
-
-
-
-##
-## deps-resolver-prod
-##
-FROM deps-resolver AS deps-resolver-prod
-
-RUN yarn --production
-# make artifacts
-RUN tar -cf node_modules.tar \
-  node_modules \
-  apps/*/node_modules \
-  packages/*/node_modules
+# install turbo
+RUN pnpm add turbo --global
 
 
 
 ##
 ## builder
 ##
-FROM node:20-slim AS builder
+FROM base AS builder
 
 ENV optDir /opt
 
 WORKDIR ${optDir}
 
-RUN yarn global add turbo
-
-# copy files
-COPY --from=base ${optDir}/out/full/ .
-COPY --from=base ${optDir}/out/yarn.lock ./yarn.lock
-COPY ["tsconfig.base.json", "./"]
-
-# copy dependent packages
-COPY --from=deps-resolver \
-  ${optDir}/node_modules.tar ${optDir}/
+COPY . .
 
-# extract node_modules.tar
-RUN tar -xf node_modules.tar
-RUN rm node_modules.tar
+RUN pnpm add node-gyp --global
+RUN pnpm install ---frozen-lockfile
 
 # build
 RUN turbo run clean
-RUN turbo run build
+RUN turbo run build --filter @growi/app
 
 # make artifacts
-RUN tar -cf packages.tar \
+RUN pnpm --filter @growi/app --prod deploy pruned
+RUN rm -rf apps/app/node_modules && mv pruned/node_modules apps/app/node_modules
+RUN rm -rf apps/app/.next/cache
+RUN tar -zcf packages.tar.gz \
   package.json \
   apps/app/.next \
   apps/app/config \
@@ -99,8 +54,7 @@ RUN tar -cf packages.tar \
   apps/app/.env.production* \
   apps/app/next.config.js \
   apps/app/package.json \
-  packages/*/package.json \
-  packages/*/dist
+  apps/app/node_modules
 
 
 
@@ -124,17 +78,20 @@ RUN set -eux; \
 # verify that the binary works
 	gosu nobody true
 
-COPY --from=deps-resolver-prod --chown=node:node \
-  ${optDir}/node_modules.tar ${appDir}/
+# Add pnpm for 'node' user
+RUN apt-get update && apt-get install -y sudo ca-certificates wget --no-install-recommends \
+  && wget -qO- https://get.pnpm.io/install.sh | ENV="$HOME/.shrc" SHELL="$(which sh)" sudo -u node sh - \
+  && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false
+ENV PNPM_HOME="/home/node/.local/share/pnpm"
+ENV PATH "$PNPM_HOME:$PATH"
+
 COPY --from=builder --chown=node:node \
-  ${optDir}/packages.tar ${appDir}/
+  ${optDir}/packages.tar.gz ${appDir}/
 
 # extract artifacts as 'node' user
 USER node
 WORKDIR ${appDir}
-RUN tar -xf node_modules.tar
-RUN tar -xf packages.tar
-RUN rm node_modules.tar packages.tar
+RUN tar -zxf packages.tar.gz && rm packages.tar.gz
 
 COPY --chown=node:node --chmod=700 apps/app/docker/docker-entrypoint.sh /
 
@@ -145,4 +102,4 @@ VOLUME /data
 EXPOSE 3000
 
 ENTRYPOINT ["/docker-entrypoint.sh"]
-CMD ["yarn migrate && node -r dotenv-flow/config --expose_gc dist/server/app.js"]
+CMD ["pnpm run migrate && node -r dotenv-flow/config --expose_gc dist/server/app.js"]

+ 1 - 0
apps/app/docker/Dockerfile.dockerignore

@@ -2,6 +2,7 @@
 **/coverage
 **/Dockerfile
 **/*.dockerignore
+**/.pnpm-store
 **/.next
 **/.turbo
 out

+ 1 - 1
apps/app/docker/README.md

@@ -10,7 +10,7 @@ GROWI Official docker image
 Supported tags and respective Dockerfile links
 ------------------------------------------------
 
-* [`7.0.21`, `7.0`, `7`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v7.0.21/apps/app/docker/Dockerfile)
+* [`7.0.22`, `7.0`, `7`, `latest` (Dockerfile)](https://github.com/weseek/growi/blob/v7.0.22/apps/app/docker/Dockerfile)
 * [`6.3.2`, `6.3`, `6` (Dockerfile)](https://github.com/weseek/growi/blob/v6.3.2/apps/app/docker/Dockerfile)
 * [`6.2.4`, `6.2` (Dockerfile)](https://github.com/weseek/growi/blob/v6.2.4/apps/app/docker/Dockerfile)
 * [`6.1.15`, `6.1` (Dockerfile)](https://github.com/weseek/growi/blob/v6.1.15/apps/app/docker/Dockerfile)

+ 1 - 3
apps/app/docker/codebuild/buildspec.yml

@@ -27,6 +27,4 @@ phases:
 
 cache:
   paths:
-    - node_modules/**/*
-    - apps/*/node_modules/**/*
-    - packages/*/node_modules/**/*
+    - .pnpm-store/**/*

+ 4 - 0
apps/app/nodemon.json

@@ -1,5 +1,9 @@
 {
   "ext": "js,ts,json",
+  "watch": [
+    ".",
+    "../../packages/**/dist"
+  ],
   "ignore": [
     ".next",
     "public/static",

+ 70 - 51
apps/app/package.json

@@ -6,33 +6,35 @@
   "scripts": {
     "//// for production": "",
     "build": "run-p build:*",
-    "start": "yarn next start",
-    "build:client": "yarn next build",
-    "build:server": "yarn cross-env NODE_ENV=production tspc -p tsconfig.build.server.json",
+    "start": "next start",
+    "build:client": "next build",
+    "build:server": "cross-env NODE_ENV=production tspc -p tsconfig.build.server.json",
     "postbuild:server": "shx echo \"Listing files under transpiled\" && shx ls transpiled && shx rm -rf dist && shx mv transpiled/src dist && shx rm -rf transpiled",
     "clean": "shx rm -rf dist transpiled",
-    "server": "yarn cross-env NODE_ENV=production node -r dotenv-flow/config dist/server/app.js",
-    "server:ci": "yarn server --ci",
-    "preserver": "yarn cross-env NODE_ENV=production yarn migrate",
+    "server": "cross-env NODE_ENV=production node -r dotenv-flow/config dist/server/app.js",
+    "server:ci": "pnpm run server --ci",
+    "preserver": "cross-env NODE_ENV=production pnpm run migrate",
     "styles-prebuilt": "vite build -c vite.styles-prebuilt.config.ts",
-    "migrate": "node -r dotenv-flow/config node_modules/.bin/migrate-mongo up -f config/migrate-mongo-config.js",
+    "migrate": "node -r dotenv-flow/config node_modules/migrate-mongo/bin/migrate-mongo up -f config/migrate-mongo-config.js",
     "//// for development": "",
-    "dev": "yarn cross-env NODE_ENV=development nodemon --exec yarn ts-node --inspect src/server/app.ts",
-    "dev:styles-prebuilt": "yarn styles-prebuilt --mode dev",
-    "dev:migrate-mongo": "yarn cross-env NODE_ENV=development yarn ts-node node_modules/.bin/migrate-mongo",
-    "dev:migrate": "yarn dev:migrate:status > tmp/cache/migration-status.out && yarn dev:migrate:up",
-    "dev:migrate:create": "yarn dev:migrate-mongo create -f config/migrate-mongo-config.js",
-    "dev:migrate:status": "yarn dev:migrate-mongo status -f config/migrate-mongo-config.js",
-    "dev:migrate:up": "yarn dev:migrate-mongo up -f config/migrate-mongo-config.js",
-    "dev:migrate:down": "yarn dev:migrate-mongo down -f config/migrate-mongo-config.js",
+    "dev": "cross-env NODE_ENV=development nodemon --exec pnpm run ts-node --inspect src/server/app.ts",
+    "dev:styles-prebuilt": "pnpm run styles-prebuilt --mode dev",
+    "dev:migrate-mongo": "cross-env NODE_ENV=development pnpm run ts-node node_modules/migrate-mongo/bin/migrate-mongo",
+    "dev:migrate": "pnpm run dev:migrate:status > tmp/cache/migration-status.out && pnpm run dev:migrate:up",
+    "dev:migrate:create": "pnpm run dev:migrate-mongo create -f config/migrate-mongo-config.js",
+    "dev:migrate:status": "pnpm run dev:migrate-mongo status -f config/migrate-mongo-config.js",
+    "dev:migrate:up": "pnpm run dev:migrate-mongo up -f config/migrate-mongo-config.js",
+    "dev:migrate:down": "pnpm run dev:migrate-mongo down -f config/migrate-mongo-config.js",
     "//// for CI": "",
-    "launch-dev:ci": "yarn cross-env NODE_ENV=development yarn dev:migrate && yarn ts-node src/server/app.ts --ci",
+    "launch-dev:ci": "cross-env NODE_ENV=development pnpm run dev:migrate && pnpm run ts-node src/server/app.ts --ci",
     "lint:typecheck": "npx -y tspc",
-    "lint:eslint": "yarn eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
+    "lint:eslint": "eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
     "lint:styles": "stylelint \"src/**/*.scss\"",
-    "lint:swagger2openapi": "node node_modules/.bin/oas-validate tmp/swagger.json",
+    "lint:swagger2openapi:apiv3": "node node_modules/swagger2openapi/oas-validate tmp/openapi-spec-apiv3.json",
+    "lint:swagger2openapi:apiv1": "node node_modules/swagger2openapi/oas-validate tmp/openapi-spec-apiv1.json",
     "lint": "run-p lint:*",
-    "prelint:swagger2openapi": "yarn openapi:v3",
+    "prelint:swagger2openapi:apiv3": "pnpm run swagger2openapi:apiv3",
+    "prelint:swagger2openapi:apiv1": "pnpm run swagger2openapi:apiv1",
     "test": "run-p test:*",
     "test:jest": "cross-env NODE_ENV=test TS_NODE_PROJECT=test/integration/tsconfig.json jest",
     "test:vitest": "vitest run --coverage",
@@ -40,20 +42,21 @@
     "reg:run": "reg-suit run",
     "previtest:run:integ": "vitest run -c test-with-vite/download-mongo-binary/vitest.config.ts test-with-vite/download-mongo-binary",
     "//// misc": "",
-    "console": "yarn repl",
-    "repl": "yarn cross-env NODE_ENV=development yarn ts-node src/server/repl.ts",
-    "swagger-jsdoc": "swagger-jsdoc -o tmp/swagger.json -d config/swagger-definition.js",
-    "openapi:v3": "yarn cross-env API_VERSION=3 yarn swagger-jsdoc -- \"src/server/routes/apiv3/**/*.js\" \"src/server/models/**/*.js\"",
-    "openapi:v1": "yarn cross-env API_VERSION=1 yarn swagger-jsdoc -- \"src/server/*/*.js\" \"src/server/models/**/*.js\"",
+    "console": "cross-env NODE_ENV=development pnpm run ts-node --experimental-repl-await src/server/console.js",
+    "swagger2openapi:apiv3": "sh bin/swagger-jsdoc/generate-spec-apiv3.sh",
+    "swagger2openapi:apiv1": "sh bin/swagger-jsdoc/generate-spec-apiv1.sh",
     "ts-node": "node -r ts-node/register/transpile-only -r tsconfig-paths/register -r dotenv-flow/config",
-    "version": "yarn version --no-git-tag-version --non-interactive --preid=RC"
+    "version:patch": "pnpm version patch",
+    "version:prerelease": "pnpm version prerelease --preid=RC",
+    "version:prepatch": "pnpm version prepatch --preid=RC",
+    "version:preminor": "pnpm version preminor --preid=RC",
+    "version:premajor": "pnpm version premajor --preid=RC"
   },
   "// comments for dependencies": {
     "@aws-skd/*": "fix version above 3.186.0 that is required by mongodb@4.16.0",
     "@keycloak/keycloak-admin-client": "19.0.0 or above exports only ESM.",
     "escape-string-regexp": "5.0.0 or above exports only ESM",
     "next-themes": "0.3.0 causes a type error: https://github.com/pacocoursey/next-themes/issues/122",
-    "remark-wiki-link": "!!DO NOT REMOVE!! including 'mdast-util-wiki-link' and 'micromark-extension-wiki-link' required by pukiwiki-like-linker",
     "string-width": "5.0.0 or above exports only ESM."
   },
   "dependencies": {
@@ -68,22 +71,20 @@
     "@elastic/elasticsearch8": "npm:@elastic/elasticsearch@^8.7.0",
     "@godaddy/terminus": "^4.9.0",
     "@google-cloud/storage": "^5.8.5",
-    "@growi/core": "link:../../packages/core",
-    "@growi/pluginkit": "link:../../packages/pluginkit",
-    "@growi/presentation": "link:../../packages/presentation",
-    "@growi/preset-templates": "link:../../packages/preset-templates",
-    "@growi/preset-themes": "link:../../packages/preset-themes",
-    "@growi/remark-attachment-refs": "link:../../packages/remark-attachment-refs",
-    "@growi/remark-drawio": "link:../../packages/remark-drawio",
-    "@growi/remark-growi-directive": "link:../../packages/remark-growi-directive",
-    "@growi/remark-lsx": "link:../../packages/remark-lsx",
-    "@growi/slack": "link:../../packages/slack",
+    "@growi/core": "workspace:^",
+    "@growi/pluginkit": "workspace:^",
+    "@growi/presentation": "workspace:^",
+    "@growi/preset-templates": "workspace:^",
+    "@growi/preset-themes": "workspace:^",
+    "@growi/remark-attachment-refs": "workspace:^",
+    "@growi/remark-drawio": "workspace:^",
+    "@growi/remark-growi-directive": "workspace:^",
+    "@growi/remark-lsx": "workspace:^",
+    "@growi/slack": "workspace:^",
     "@keycloak/keycloak-admin-client": "^18.0.0",
     "@slack/web-api": "^6.2.4",
     "@slack/webhook": "^6.0.0",
     "@types/async": "^3.2.24",
-    "@types/jest": "^29.5.2",
-    "@types/ldapjs": "^2.2.5",
     "JSONStream": "^1.3.5",
     "archiver": "^5.3.0",
     "array.prototype.flatmap": "^1.2.2",
@@ -94,7 +95,7 @@
     "browser-bunyan": "^1.8.0",
     "bson-objectid": "^2.0.4",
     "bunyan": "^1.8.15",
-    "check-node-version": "^4.1.0",
+    "check-node-version": "^4.2.1",
     "compression": "^1.7.4",
     "connect-flash": "~0.1.1",
     "connect-mongo": "^4.6.0",
@@ -120,6 +121,7 @@
     "extensible-custom-error": "^0.0.7",
     "form-data": "^4.0.0",
     "graceful-fs": "^4.1.11",
+    "hast-util-sanitize": "^5.0.1",
     "hast-util-select": "^6.0.2",
     "helmet": "^4.6.0",
     "http-errors": "^2.0.0",
@@ -127,14 +129,21 @@
     "i18next-resources-to-backend": "^1.2.1",
     "is-absolute-url": "^4.0.1",
     "is-iso-date": "^0.0.1",
+    "katex": "^0.16.11",
     "ldapjs": "^3.0.2",
     "lucene-query-parser": "^1.2.0",
     "markdown-table": "^3.0.3",
+    "mdast-util-from-markdown": "^2.0.1",
+    "mdast-util-gfm-table": "^2.0.0",
+    "mdast-util-wiki-link": "^0.1.2",
     "md5": "^2.2.1",
     "mermaid": "^11.2.0",
     "method-override": "^3.0.0",
+    "micromark-extension-gfm-table": "^2.1.0",
+    "micromark-extension-wiki-link": "^0.0.4",
     "migrate-mongo": "^11.0.0",
     "mkdirp": "^1.0.3",
+    "mongodb": "^4.17.2",
     "mongoose": "^6.11.3",
     "mongoose-gridfs": "^1.2.42",
     "mongoose-paginate-v2": "^1.3.9",
@@ -160,6 +169,7 @@
     "passport-ldapauth": "^3.0.1",
     "passport-local": "^1.0.0",
     "passport-saml": "^3.2.0",
+    "prop-types": "^15.8.1",
     "qs": "^6.11.1",
     "rate-limiter-flexible": "^2.3.7",
     "react": "^18.2.0",
@@ -180,27 +190,32 @@
     "reactstrap": "^9.2.2",
     "reconnecting-websocket": "^4.4.0",
     "redis": "^3.0.2",
-    "rehype-katex": "^7.0.0",
+    "rehype-katex": "^7.0.1",
     "rehype-raw": "^7.0.0",
     "rehype-sanitize": "^6.0.0",
     "rehype-slug": "^6.0.0",
     "rehype-toc": "^3.0.2",
     "remark-breaks": "^4.0.0",
+    "remark-directive": "^3.0.0",
     "remark-emoji": "^5.0.0",
     "remark-frontmatter": "^5.0.0",
     "remark-gfm": "^4.0.0",
     "remark-math": "^6.0.0",
+    "remark-parse": "^11.0.0",
+    "remark-rehype": "^11.1.1",
     "remark-toc": "^9.0.0",
-    "remark-wiki-link": "^2.0.1",
     "sanitize-filename": "^1.6.3",
     "socket.io": "^4.7.5",
     "stream-to-promise": "^3.0.0",
     "string-width": "=4.2.2",
     "superjson": "^1.9.1",
-    "swagger-jsdoc": "^6.1.0",
+    "swagger-jsdoc": "^6.2.8",
     "swr": "^2.2.2",
     "throttle-debounce": "^5.0.0",
+    "uid-safe": "^2.1.5",
     "uglifycss": "^0.0.29",
+    "unified": "^11.0.0",
+    "unist-util-visit": "^5.0.0",
     "universal-bunyan": "^0.9.2",
     "unstated": "^2.1.1",
     "unzip-stream": "^0.3.2",
@@ -220,11 +235,11 @@
     "mongodb": "mongoose which is used requires mongo@4.16.0."
   },
   "devDependencies": {
-    "@growi/core-styles": "link:../../packages/core-styles",
-    "@growi/custom-icons": "link:../../packages/custom-icons",
-    "@growi/editor": "link:../../packages/editor",
-    "@growi/markdown-splitter": "link:../../packages/markdown-splitter",
-    "@growi/ui": "link:../../packages/ui",
+    "@growi/core-styles": "workspace:^",
+    "@growi/custom-icons": "workspace:^",
+    "@growi/editor": "workspace:^",
+    "@growi/markdown-splitter": "workspace:^",
+    "@growi/ui": "workspace:^",
     "@handsontable/react": "=2.1.0",
     "@next/bundle-analyzer": "^14.1.3",
     "@popperjs/core": "^2.11.8",
@@ -236,13 +251,19 @@
     "@testing-library/user-event": "^14.5.2",
     "@types/archiver": "^6.0.2",
     "@types/express": "^4.17.21",
+    "@types/hast": "^3.0.4",
     "@types/jest": "^29.5.2",
+    "@types/ldapjs": "^2.2.5",
+    "@types/mdast": "^4.0.4",
     "@types/node-cron": "^3.0.11",
+    "@types/react": "^18.2.14",
+    "@types/react-dom": "^18.2.6",
     "@types/react-input-autosize": "^2.2.4",
     "@types/react-scroll": "^1.8.4",
     "@types/react-stickynode": "^4.0.3",
     "@types/testing-library__dom": "^7.5.0",
     "@types/throttle-debounce": "^5.0.1",
+    "@types/unist": "^3.0.3",
     "@types/unzip-stream": "^0.3.4",
     "@types/url-join": "^4.0.2",
     "babel-loader": "^8.2.5",
@@ -265,24 +286,22 @@
     "jest-localstorage-mock": "^2.4.14",
     "load-css-file": "^1.0.0",
     "material-icons": "^1.11.3",
-    "mongodb": "4.16.0",
+    "mdast-util-directive": "^3.0.0",
     "mongodb-memory-server-core": "^9.1.1",
     "morgan": "^1.10.0",
     "null-loader": "^4.0.1",
-    "plantuml-encoder": "^1.2.5",
     "pretty-bytes": "^6.1.1",
     "react-copy-to-clipboard": "^5.0.1",
     "react-dnd": "^14.0.5",
     "react-dnd-html5-backend": "^14.1.0",
     "react-dropzone": "^14.2.3",
+    "react-hook-form": "^7.45.4",
     "react-hotkeys": "^2.0.0",
     "react-input-autosize": "^3.0.0",
     "react-toastify": "^9.1.3",
     "rehype-rewrite": "^4.0.2",
     "remark-github-admonitions-to-directives": "^2.0.0",
-    "replacestream": "^4.0.3",
     "sass": "^1.53.0",
-    "simple-load-script": "^1.0.2",
     "simplebar-react": "^2.3.6",
     "socket.io-client": "^4.7.5",
     "source-map-loader": "^4.0.1",

+ 1 - 1
apps/app/playwright.config.ts

@@ -51,7 +51,7 @@ export default defineConfig({
   reporter: process.env.CI ? 'github' : 'list',
 
   webServer: {
-    command: 'yarn server',
+    command: 'pnpm run server',
     url: 'http://localhost:3000',
     reuseExistingServer: !process.env.CI,
     stdout: 'ignore',

+ 4 - 2
apps/app/public/static/locales/en_US/translation.json

@@ -492,8 +492,10 @@
     "caution_against_hallucination": "Please verify the information and check the sources.",
     "progress_label": "Generating answers",
     "failed_to_create_or_retrieve_thread": "Failed to create or retrieve thread",
-    "rate_limit_exceeded": "You have reached your usage limit for OpenAI's API. To use the Knowledge Assistant again, please add credits from the OpenAI billing page.",
-    "rate_limit_exceeded_for_growi_cloud": "You have reached your OpenAI API usage limit. To use the Knowledge Assistant again, please add credits from the GROWI.cloud admin page for Hosted users or from the OpenAI billing page for Owned users."
+    "budget_exceeded": "You have reached your usage limit for OpenAI's API. To use the Knowledge Assistant again, please add credits from the OpenAI billing page.",
+    "budget_exceeded_for_growi_cloud": "You have reached your OpenAI API usage limit. To use the Knowledge Assistant again, please add credits from the GROWI.cloud admin page for Hosted users or from the OpenAI billing page for Owned users.",
+    "error_message": "An error has occurred",
+    "show_error_detail": "Show error details"
 
   },
   "link_edit": {

+ 4 - 2
apps/app/public/static/locales/fr_FR/translation.json

@@ -486,8 +486,10 @@
     "caution_against_hallucination": "Veuillez vérifier les informations et consulter les sources.",
     "progress_label": "Génération des réponses",
     "failed_to_create_or_retrieve_thread": "Échec de la création ou de la récupération du fil de discussion",
-    "rate_limit_exceeded": "Vous avez atteint votre limite d'utilisation de l'API de l'OpenAI. Pour utiliser à nouveau l'assistant de connaissance, veuillez ajouter des crédits à partir de la page de facturation d'OpenAI.",
-    "rate_limit_exceeded_for_growi_cloud": "Vous avez atteint votre limite d'utilisation de l'API de l'OpenAI. Pour utiliser à nouveau l'assistant de connaissance, veuillez ajouter des crédits à partir de la page d'administration de GROWI.cloud pour les utilisateurs hébergés ou à partir de la page de facturation de l'OpenAI pour les utilisateurs propriétaires."
+    "budget_exceeded": "Vous avez atteint votre limite d'utilisation de l'API de l'OpenAI. Pour utiliser à nouveau l'assistant de connaissance, veuillez ajouter des crédits à partir de la page de facturation d'OpenAI.",
+    "budget_exceeded_for_growi_cloud": "Vous avez atteint votre limite d'utilisation de l'API de l'OpenAI. Pour utiliser à nouveau l'assistant de connaissance, veuillez ajouter des crédits à partir de la page d'administration de GROWI.cloud pour les utilisateurs hébergés ou à partir de la page de facturation de l'OpenAI pour les utilisateurs propriétaires.",
+    "error_message": "Erreur",
+    "show_error_detail": "Détails de l'exposition"
   },
   "link_edit": {
     "edit_link": "Modifier lien",

+ 4 - 2
apps/app/public/static/locales/ja_JP/translation.json

@@ -525,8 +525,10 @@
     "caution_against_hallucination": "情報が正しいか出典を確認しましょう",
     "progress_label": "回答を生成しています",
     "failed_to_create_or_retrieve_thread": "スレッドの作成または取得に失敗しました",
-    "rate_limit_exceeded": "OpenAI の API の利用上限に達しました。ナレッジアシスタントを再度利用するには OpenAI の請求ページからクレジットを追加してください。",
-    "rate_limit_exceeded_for_growi_cloud": "OpenAI の API の利用上限に達しました。ナレッジアシスタントを再度利用するには Hosted の場合は GROWI.cloud の管理画面から Owned の場合は OpenAI の請求ページからクレジットを追加してください。"
+    "budget_exceeded": "OpenAI の API の利用上限に達しました。ナレッジアシスタントを再度利用するには OpenAI の請求ページからクレジットを追加してください。",
+    "budget_exceeded_for_growi_cloud": "OpenAI の API の利用上限に達しました。ナレッジアシスタントを再度利用するには Hosted の場合は GROWI.cloud の管理画面から Owned の場合は OpenAI の請求ページからクレジットを追加してください。",
+    "error_message": "エラーが発生しました",
+    "show_error_detail": "詳細を表示"
   },
   "link_edit": {
     "edit_link": "リンク編集",

+ 4 - 2
apps/app/public/static/locales/zh_CN/translation.json

@@ -481,8 +481,10 @@
     "caution_against_hallucination": "请核实信息并检查来源。",
     "progress_label": "生成答案中",
     "failed_to_create_or_retrieve_thread": "创建或获取线程失败",
-    "rate_limit_exceeded": "您已达到 OpenAI API 的使用上限。要再次使用知识助手,请从 OpenAI 账单页面添加点数。",
-    "rate_limit_exceeded_for_growi_cloud": "您已达到 OpenAI API 使用上限。如需再次使用知识助手,请从GROWI.cloud管理页面为托管用户添加点数,或从OpenAI计费页面为自有用户添加点数。"
+    "budget_exceeded": "您已达到 OpenAI API 的使用上限。要再次使用知识助手,请从 OpenAI 账单页面添加点数。",
+    "budget_exceeded_for_growi_cloud": "您已达到 OpenAI API 使用上限。如需再次使用知识助手,请从GROWI.cloud管理页面为托管用户添加点数,或从OpenAI计费页面为自有用户添加点数。",
+    "error_message": "错误",
+    "show_error_detail": "显示详情"
   },
   "link_edit": {
     "edit_link": "Edit Link",

+ 4 - 4
apps/app/src/client/components/Admin/AdminHome/SystemInfomationTable.tsx

@@ -13,10 +13,10 @@ const SystemInformationTable = (props: Props) => {
   const { adminHomeContainer } = props;
 
   const {
-    growiVersion, nodeVersion, npmVersion, yarnVersion,
+    growiVersion, nodeVersion, npmVersion, pnpmVersion,
   } = adminHomeContainer.state;
 
-  if (growiVersion == null || nodeVersion == null || npmVersion == null || yarnVersion == null) {
+  if (growiVersion == null || nodeVersion == null || npmVersion == null || pnpmVersion == null) {
     return <></>;
   }
 
@@ -36,8 +36,8 @@ const SystemInformationTable = (props: Props) => {
           <td>{ npmVersion }</td>
         </tr>
         <tr>
-          <th>yarn</th>
-          <td>{ yarnVersion }</td>
+          <th>pnpm</th>
+          <td>{ pnpmVersion }</td>
         </tr>
       </tbody>
     </table>

+ 0 - 1
apps/app/src/client/components/Admin/UserGroup/UserGroupDeleteModal.tsx

@@ -120,7 +120,6 @@ export const UserGroupDeleteModal: FC<Props> = (props: Props) => {
       <select
         name="actionName"
         className="form-control"
-        placeholder="select"
         value={actionName ?? ''}
         onChange={handleActionChange}
       >

+ 12 - 11
apps/app/src/client/components/Bookmarks/DragAndDropWrapper.tsx

@@ -1,8 +1,8 @@
-import React, { ReactNode } from 'react';
+import type { ReactNode } from 'react';
 
 import { useDrag, useDrop } from 'react-dnd';
 
-import { DragItemDataType } from '~/interfaces/bookmark-info';
+import type { DragItemDataType } from '~/interfaces/bookmark-info';
 
 type DragAndDropWrapperProps = {
   item?: Partial<DragItemDataType>
@@ -53,20 +53,21 @@ export const DragAndDropWrapper = (props: DragAndDropWrapperProps): JSX.Element
     }),
   }));
 
-
-  const getRef = (c: HTMLDivElement | null) => {
+  const getCallback = (c: HTMLDivElement | null) => {
     if (useDragMode && useDropMode) {
-      return [dragRef(c), dropRef(c)];
-    } if (useDragMode) {
-      return dragRef(c);
-    } if (useDropMode) {
-      return dropRef(c);
+      dragRef(c);
+      dropRef(c);
+    }
+    else if (useDragMode) {
+      dragRef(c);
+    }
+    else if (useDropMode) {
+      dropRef(c);
     }
-    return null;
   };
 
   return (
-    <div ref={c => getRef(c)} className={`grw-drag-drop-container ${isOver ? 'grw-accept-drop-item' : ''}`}>
+    <div ref={getCallback} className={`grw-drag-drop-container ${isOver ? 'grw-accept-drop-item' : ''}`}>
       {children}
     </div>
   );

+ 1 - 1
apps/app/src/client/components/InfiniteScroll.tsx

@@ -27,7 +27,7 @@ const useIntersection = <E extends HTMLElement>(): [boolean, Ref<E>] => {
     }
     return;
   }, [element]);
-  return [intersecting, el => el && setElement(el)];
+  return [intersecting, (el) => { if (el != null) setElement(el); }];
 };
 
 const LoadingIndicator = (): React.ReactElement => {

+ 3 - 2
apps/app/src/client/components/Me/InAppNotificationSettings.tsx

@@ -1,7 +1,6 @@
 import type { FC } from 'react';
 import React, { useState, useEffect, useCallback } from 'react';
 
-import pullAllBy from 'lodash/pullAllBy';
 import { useTranslation } from 'next-i18next';
 
 import { apiv3Get, apiv3Put } from '~/client/util/apiv3-client';
@@ -28,7 +27,9 @@ const isCheckedRule = (ruleName: string, subscribeRules: SubscribeRule[]) => (
 
 const updateIsEnabled = (subscribeRules: SubscribeRule[], ruleName: string, isChecked: boolean) => {
   const target = [{ name: ruleName, isEnabled: isChecked }];
-  return pullAllBy(subscribeRules, target, 'name').concat(target);
+  return subscribeRules
+    .filter(rule => rule.name !== ruleName)
+    .concat(target);
 };
 
 

+ 1 - 2
apps/app/src/client/components/PageComment/CommentEditor.tsx

@@ -9,7 +9,6 @@ import { CodeMirrorEditorComment } from '@growi/editor/dist/client/components/Co
 import { useCodeMirrorEditorIsolated } from '@growi/editor/dist/client/stores/codemirror-editor';
 import { useResolvedThemeForEditor } from '@growi/editor/dist/client/stores/use-resolved-theme';
 import { UserPicture } from '@growi/ui/dist/components';
-import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
 import { useTranslation } from 'next-i18next';
 import dynamic from 'next/dynamic';
 import {
@@ -209,7 +208,7 @@ export const CommentEditor = (props: CommentEditorProps): JSX.Element => {
     });
   }, [codeMirrorEditor, pageId]);
 
-  const cmProps = useMemo<ReactCodeMirrorProps>(() => ({
+  const cmProps = useMemo(() => ({
     onChange: async(value: string) => {
       const dirtyNum = await evaluateEditorDirtyMap(editorKey, value);
       mutateIsEnabledUnsavedWarning(dirtyNum > 0);

+ 0 - 1
apps/app/src/client/components/PageEditor/MarkdownTableDataImportForm.tsx

@@ -57,7 +57,6 @@ export const MarkdownTableDataImportForm = (props: MarkdownTableDataImportFormPr
         <select
           id="data-import-form-type-select"
           className="form-select"
-          placeholder="select"
           value={dataFormat}
           onChange={(e) => { return setDataFormat(e.target.value) }}
         >

+ 3 - 3
apps/app/src/client/components/PageEditor/PageEditor.tsx

@@ -6,14 +6,14 @@ import React, {
 import type EventEmitter from 'events';
 import nodePath from 'path';
 
-import { type IPageHasId, Origin } from '@growi/core';
+import { Origin } from '@growi/core';
+import type { IPageHasId } from '@growi/core/dist/interfaces';
 import { pathUtils } from '@growi/core/dist/utils';
 import { GlobalCodeMirrorEditorKey } from '@growi/editor';
 import { CodeMirrorEditorMain } from '@growi/editor/dist/client/components/CodeMirrorEditorMain';
 import { useCodeMirrorEditorIsolated } from '@growi/editor/dist/client/stores/codemirror-editor';
 import { useResolvedThemeForEditor } from '@growi/editor/dist/client/stores/use-resolved-theme';
 import { useRect } from '@growi/ui/dist/utils';
-import type { ReactCodeMirrorProps } from '@uiw/react-codemirror';
 import detectIndent from 'detect-indent';
 import { useTranslation } from 'next-i18next';
 import { throttle, debounce } from 'throttle-debounce';
@@ -267,7 +267,7 @@ export const PageEditor = React.memo((props: Props): JSX.Element => {
   }, [codeMirrorEditor, pageId]);
 
 
-  const cmProps = useMemo<ReactCodeMirrorProps>(() => ({
+  const cmProps = useMemo(() => ({
     onChange: (value: string) => {
       setMarkdownPreviewWithDebounce(value);
     },

+ 1 - 1
apps/app/src/client/components/PageEditor/markdown-drawio-util-for-editor.ts

@@ -1,4 +1,4 @@
-import { EditorView } from '@codemirror/view';
+import type { EditorView } from '@growi/editor';
 
 const lineBeginPartOfDrawioRE = /^```(\s.*)drawio$/;
 const lineEndPartOfDrawioRE = /^```$/;

+ 1 - 1
apps/app/src/client/components/PageEditor/markdown-table-util-for-editor.ts

@@ -1,4 +1,4 @@
-import type { EditorView } from '@codemirror/view';
+import type { EditorView } from '@growi/editor';
 import { MarkdownTable } from '@growi/editor';
 
 // https://regex101.com/r/7BN2fR/10

+ 2 - 3
apps/app/src/client/components/SearchPage/SearchResultList.tsx

@@ -5,7 +5,7 @@ import React, {
 
 import {
   type IPageInfoForListing, type IPageWithMeta, isIPageInfoForListing,
-} from '@growi/core';
+} from '@growi/core/dist/interfaces';
 import { useTranslation } from 'next-i18next';
 
 import type { ISelectable, ISelectableAll } from '~/client/interfaces/selectable-all';
@@ -130,8 +130,7 @@ const SearchResultListSubstance: ForwardRefRenderFunction<ISelectableAll, Props>
         return (
           <PageListItemL
             key={page.data._id}
-            // eslint-disable-next-line no-return-assign
-            ref={c => itemsRef.current[i] = c}
+            ref={(c) => { itemsRef.current[i] = c }}
             page={page}
             isEnableActions={!isGuestUser}
             isReadOnlyUser={!!isReadOnlyUser}

+ 3 - 3
apps/app/src/client/services/AdminHomeContainer.js

@@ -31,7 +31,7 @@ export default class AdminHomeContainer extends Container {
       growiVersion: null,
       nodeVersion: null,
       npmVersion: null,
-      yarnVersion: null,
+      pnpmVersion: null,
       copyState: this.copyStateValues.DEFAULT,
       installedPlugins: null,
       isV5Compatible: null,
@@ -64,7 +64,7 @@ export default class AdminHomeContainer extends Container {
         growiVersion: adminHomeParams.growiVersion,
         nodeVersion: adminHomeParams.nodeVersion,
         npmVersion: adminHomeParams.npmVersion,
-        yarnVersion: adminHomeParams.yarnVersion,
+        pnpmVersion: adminHomeParams.pnpmVersion,
         envVars: adminHomeParams.envVars,
         isV5Compatible: adminHomeParams.isV5Compatible,
         isMaintenanceMode: adminHomeParams.isMaintenanceMode,
@@ -103,7 +103,7 @@ export default class AdminHomeContainer extends Container {
 |GROWI     |${this.state.growiVersion}|
 |node.js   |${this.state.nodeVersion}|
 |npm       |${this.state.npmVersion}|
-|yarn      |${this.state.yarnVersion}|
+|pnpm      |${this.state.pnpmVersion}|
 |Using Docker|yes/no|
 |Using [growi-docker-compose][growi-docker-compose]|yes/no|
 

+ 1 - 1
apps/app/src/features/callout/services/callout.ts

@@ -11,7 +11,7 @@ export const remarkPlugin: Plugin = () => {
         const data = node.data ?? (node.data = {});
         data.hName = 'callout';
         data.hProperties = {
-          name: node.name,
+          name: node.name.toLocaleLowerCase(),
         };
       }
     });

+ 34 - 6
apps/app/src/features/openai/chat/components/AiChatModal/AiChatModal.tsx

@@ -4,6 +4,7 @@ import React, { useCallback, useEffect, useState } from 'react';
 import { useForm, Controller } from 'react-hook-form';
 import { useTranslation } from 'react-i18next';
 import {
+  Collapse,
   Modal, ModalBody, ModalFooter, ModalHeader,
 } from 'reactstrap';
 
@@ -48,6 +49,8 @@ const AiChatModalSubstance = (): JSX.Element => {
   const [threadId, setThreadId] = useState<string | undefined>();
   const [messageLogs, setMessageLogs] = useState<Message[]>([]);
   const [generatingAnswerMessage, setGeneratingAnswerMessage] = useState<Message>();
+  const [errorMessage, setErrorMessage] = useState<string | undefined>();
+  const [isErrorDetailCollapsed, setIsErrorDetailCollapsed] = useState<boolean>(false);
 
   const { data: growiCloudUri } = useGrowiCloudUri();
 
@@ -95,6 +98,7 @@ const AiChatModalSubstance = (): JSX.Element => {
 
     // reset form
     form.reset();
+    setErrorMessage(undefined);
 
     // add an empty assistant message
     const newAnswerMessage = { id: (logLength + 1).toString(), content: '' };
@@ -157,11 +161,8 @@ const AiChatModalSubstance = (): JSX.Element => {
             logger.error(error.errorMessage);
             form.setError('input', { type: 'manual', message: error.message });
 
-            if (error.code === StreamErrorCode.RATE_LIMIT_EXCEEDED) {
-              const toastErrorMessage = growiCloudUri != null
-                ? 'modal_aichat.rate_limit_exceeded_for_growi_cloud'
-                : 'modal_aichat.rate_limit_exceeded';
-              toastError(t(toastErrorMessage));
+            if (error.code === StreamErrorCode.BUDGET_EXCEEDED) {
+              setErrorMessage(growiCloudUri != null ? 'modal_aichat.budget_exceeded_for_growi_cloud' : 'modal_aichat.budget_exceeded');
             }
           }
         });
@@ -241,7 +242,34 @@ const AiChatModalSubstance = (): JSX.Element => {
         </form>
 
         {form.formState.errors.input != null && (
-          <span className="text-danger small">{form.formState.errors.input?.message}</span>
+          <div className="mt-4 bg-danger bg-opacity-10 rounded-3 p-2 w-100">
+            <div>
+              <span className="material-symbols-outlined text-danger me-2">error</span>
+              <span className="text-danger">{ errorMessage != null ? t(errorMessage) : t('modal_aichat.error_message') }</span>
+            </div>
+
+            <button
+              type="button"
+              className="btn btn-link text-secondary p-0"
+              aria-expanded={isErrorDetailCollapsed}
+              onClick={() => setIsErrorDetailCollapsed(!isErrorDetailCollapsed)}
+            >
+              <span className={`material-symbols-outlined mt-2 me-1 ${isErrorDetailCollapsed ? 'rotate-90' : ''}`}>
+                chevron_right
+              </span>
+              <span className="small">{t('modal_aichat.show_error_detail')}</span>
+            </button>
+
+            <Collapse isOpen={isErrorDetailCollapsed}>
+              <div className="ms-2">
+                <div className="">
+                  <div className="text-secondary small">
+                    {form.formState.errors.input?.message}
+                  </div>
+                </div>
+              </div>
+            </Collapse>
+          </div>
         )}
       </ModalFooter>
     </>

+ 1 - 1
apps/app/src/features/openai/interfaces/message-error.ts

@@ -3,7 +3,7 @@ export const MessageErrorCode = {
 } as const;
 
 export const StreamErrorCode = {
-  RATE_LIMIT_EXCEEDED: 'rate_limit_exceeded',
+  BUDGET_EXCEEDED: 'budget-exceeded',
 } as const;
 
 export type StreamErrorCode = typeof StreamErrorCode[keyof typeof StreamErrorCode];

+ 8 - 5
apps/app/src/features/openai/server/routes/message.ts

@@ -11,8 +11,9 @@ import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
 import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import loggerFactory from '~/utils/logger';
 
-import { MessageErrorCode, StreamErrorCode } from '../../interfaces/message-error';
+import { MessageErrorCode, type StreamErrorCode } from '../../interfaces/message-error';
 import { openaiClient } from '../services';
+import { getStreamErrorCode } from '../services/getStreamErrorCode';
 
 import { certifyAiService } from './middlewares/certify-ai-service';
 
@@ -80,16 +81,18 @@ export const postMessageHandlersFactory: PostMessageHandlersFactory = (crowi) =>
         res.write(`data: ${JSON.stringify(delta)}\n\n`);
       };
 
-      const sendError = (code: StreamErrorCode, message: string) => {
+      const sendError = (message: string, code?: StreamErrorCode) => {
         res.write(`error: ${JSON.stringify({ code, message })}\n\n`);
       };
 
       stream.on('event', (delta) => {
         if (delta.event === 'thread.run.failed') {
-          if (delta.data.last_error?.code === StreamErrorCode.RATE_LIMIT_EXCEEDED) {
-            logger.error(delta.data.last_error.message);
-            sendError(StreamErrorCode.RATE_LIMIT_EXCEEDED, delta.data.last_error.message);
+          const errorMessage = delta.data.last_error?.message;
+          if (errorMessage == null) {
+            return;
           }
+          logger.error(errorMessage);
+          sendError(errorMessage, getStreamErrorCode(errorMessage));
         }
       });
       stream.on('messageDelta', messageDeltaHandler);

+ 13 - 0
apps/app/src/features/openai/server/services/getStreamErrorCode.ts

@@ -0,0 +1,13 @@
+import { StreamErrorCode } from '../../interfaces/message-error';
+
+const OpenaiStreamErrorMessageRegExp = {
+  BUDGET_EXCEEDED: /exceeded your current quota/i, // stream-error-message: "You exceeded your current quota, please check your plan and billing details. For more information on this error, read the docs: https://platform.openai.com/docs/guides/error-codes/api-errors."
+} as const;
+
+export const getStreamErrorCode = (errorMessage: string): StreamErrorCode | undefined => {
+  for (const [code, regExp] of Object.entries(OpenaiStreamErrorMessageRegExp)) {
+    if (regExp.test(errorMessage)) {
+      return StreamErrorCode[code];
+    }
+  }
+};

+ 4 - 4
apps/app/src/features/templates/server/routes/apiv3/index.ts

@@ -1,7 +1,7 @@
 import path from 'path';
 
 import { GrowiPluginType } from '@growi/core';
-import { TemplateSummary } from '@growi/pluginkit/dist/v4';
+import type { TemplateSummary } from '@growi/pluginkit/dist/v4';
 import { scanAllTemplates, getMarkdown } from '@growi/pluginkit/dist/v4/server/index.cjs';
 import express from 'express';
 import { param, query } from 'express-validator';
@@ -9,7 +9,7 @@ import { param, query } from 'express-validator';
 import { PLUGIN_STORING_PATH } from '~/features/growi-plugin/server/consts';
 import { GrowiPlugin } from '~/features/growi-plugin/server/models';
 import { apiV3FormValidator } from '~/server/middlewares/apiv3-form-validator';
-import { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
+import type { ApiV3Response } from '~/server/routes/apiv3/interfaces/apiv3-response';
 import loggerFactory from '~/utils/logger';
 import { resolveFromRoot } from '~/utils/project-dir-utils';
 
@@ -40,7 +40,7 @@ module.exports = (crowi) => {
 
     // scan preset templates
     if (presetTemplateSummaries == null) {
-      const presetTemplatesRoot = resolveFromRoot('../../node_modules/@growi/preset-templates');
+      const presetTemplatesRoot = resolveFromRoot('node_modules/@growi/preset-templates');
 
       try {
         presetTemplateSummaries = await scanAllTemplates(presetTemplatesRoot, {
@@ -76,7 +76,7 @@ module.exports = (crowi) => {
       templateId, locale,
     } = req.params;
 
-    const presetTemplatesRoot = resolveFromRoot('../../node_modules/@growi/preset-templates');
+    const presetTemplatesRoot = resolveFromRoot('node_modules/@growi/preset-templates');
 
     try {
       const markdown = await getMarkdown(presetTemplatesRoot, templateId, locale);

+ 1 - 13
apps/app/src/pages/admin/index.page.tsx

@@ -25,10 +25,6 @@ const ForbiddenPage = dynamic(() => import('~/client/components/Admin/ForbiddenP
 
 
 type Props = CommonProps & {
-  nodeVersion: string,
-  npmVersion: string,
-  yarnVersion: string,
-  installedPlugins: any,
   growiCloudUri: string,
   growiAppIdForGrowiCloud: number,
 };
@@ -64,12 +60,7 @@ const AdminHomepage: NextPage<Props> = (props) => {
         <Head>
           <title>{title}</title>
         </Head>
-        <AdminHome
-          nodeVersion={props.nodeVersion}
-          npmVersion={props.npmVersion}
-          yarnVersion={props.yarnVersion}
-          installedPlugins={props.installedPlugins}
-        />
+        <AdminHome />
       </AdminLayout>
     </Provider>
   );
@@ -80,9 +71,6 @@ const injectServerConfigurations = async(context: GetServerSidePropsContext, pro
   const req: CrowiRequest = context.req as CrowiRequest;
   const { crowi } = req;
 
-  props.nodeVersion = crowi.runtimeVersions.versions.node ? crowi.runtimeVersions.versions.node.version.version : null;
-  props.npmVersion = crowi.runtimeVersions.versions.npm ? crowi.runtimeVersions.versions.npm.version.version : null;
-  props.yarnVersion = crowi.runtimeVersions.versions.yarn ? crowi.runtimeVersions.versions.yarn.version.version : null;
   props.growiCloudUri = await crowi.configManager.getConfig('crowi', 'app:growiCloudUri');
   props.growiAppIdForGrowiCloud = await crowi.configManager.getConfig('crowi', 'app:growiAppIdForCloud');
 };

+ 2 - 3
apps/app/src/server/crowi/express-init.js

@@ -11,7 +11,6 @@ import registerSafeRedirectFactory from '../middlewares/safe-redirect';
 const logger = loggerFactory('growi:crowi:express-init');
 
 module.exports = function(crowi, app) {
-  const debug = require('debug')('growi:crowi:express-init');
   const express = require('express');
   const compression = require('compression');
   const helmet = require('helmet');
@@ -85,7 +84,7 @@ module.exports = function(crowi, app) {
   const staticOption = (crowi.node_env === 'production') ? { maxAge: '30d' } : {};
   app.use(express.static(crowi.publicDir, staticOption));
   app.use('/static/preset-themes', express.static(
-    resolveFromRoot(`../../node_modules/@growi/preset-themes/${presetThemesRootPath}`),
+    resolveFromRoot(`node_modules/@growi/preset-themes/${presetThemesRootPath}`),
   ));
   app.use(PLUGIN_EXPRESS_STATIC_DIR, express.static(PLUGIN_STORING_PATH));
 
@@ -124,7 +123,7 @@ module.exports = function(crowi, app) {
   app.use(csrf({ ignoreMethods: ['GET', 'HEAD', 'OPTIONS', 'PUT', 'POST', 'DELETE'], cookie: false }));
 
   // passport
-  debug('initialize Passport');
+  logger.debug('initialize Passport');
   app.use(passport.initialize());
   app.use(passport.session());
 

+ 1 - 2
apps/app/src/server/events/bookmark.js

@@ -1,6 +1,5 @@
-// var debug = require('debug')('crowi:events:page')
-const util = require('util');
 const events = require('events');
+const util = require('util');
 
 function BookmarkEvent(crowi) {
   this.crowi = crowi;

+ 10 - 7
apps/app/src/server/events/page.js

@@ -1,6 +1,9 @@
-const debug = require('debug')('growi:events:page');
-const util = require('util');
-const events = require('events');
+import events from 'events';
+import util from 'util';
+
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('growi:events:page');
 
 function PageEvent(crowi) {
   this.crowi = crowi;
@@ -10,15 +13,15 @@ function PageEvent(crowi) {
 util.inherits(PageEvent, events.EventEmitter);
 
 PageEvent.prototype.onCreate = function(page, user) {
-  debug('onCreate event fired');
+  logger.debug('onCreate event fired');
 };
 PageEvent.prototype.onUpdate = function(page, user) {
-  debug('onUpdate event fired');
+  logger.debug('onUpdate event fired');
 };
 PageEvent.prototype.onCreateMany = function(pages, user) {
-  debug('onCreateMany event fired');
+  logger.debug('onCreateMany event fired');
 };
 PageEvent.prototype.onAddSeenUsers = function(pages, user) {
-  debug('onAddSeenUsers event fired');
+  logger.debug('onAddSeenUsers event fired');
 };
 module.exports = PageEvent;

+ 3 - 2
apps/app/src/server/middlewares/access-token-parser.js

@@ -1,4 +1,5 @@
 import { serializeUserSecurely } from '@growi/core/dist/models/serializers';
+import mongoose from 'mongoose';
 
 import loggerFactory from '~/utils/logger';
 
@@ -14,11 +15,11 @@ module.exports = (crowi) => {
       return next();
     }
 
-    const User = crowi.model('User');
+    const User = mongoose.model('User');
 
     logger.debug('accessToken is', accessToken);
 
-    const user = await User.findUserByApiToken(accessToken);
+    const user = await User.findUserByApiToken(accessToken).lean();
 
     if (user == null) {
       logger.debug('The access token is invalid');

+ 10 - 7
apps/app/src/server/models/bookmark.js

@@ -1,9 +1,12 @@
 /* eslint-disable no-return-await */
 
-const debug = require('debug')('growi:models:bookmark');
-const mongoose = require('mongoose');
-const mongoosePaginate = require('mongoose-paginate-v2');
-const uniqueValidator = require('mongoose-unique-validator');
+import mongoose from 'mongoose';
+import mongoosePaginate from 'mongoose-paginate-v2';
+import uniqueValidator from 'mongoose-unique-validator';
+
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('growi:models:bookmark');
 
 const ObjectId = mongoose.Schema.Types.ObjectId;
 
@@ -74,7 +77,7 @@ const factory = (crowi) => {
         // duplicate key (dummy response of new object)
         return newBookmark;
       }
-      debug('Bookmark.save failed', err);
+      logger.debug('Bookmark.save failed', err);
       throw err;
     }
   };
@@ -93,7 +96,7 @@ const factory = (crowi) => {
       return data;
     }
     catch (err) {
-      debug('Bookmark.remove failed (removeBookmarkByPage)', err);
+      logger.debug('Bookmark.remove failed (removeBookmarkByPage)', err);
       throw err;
     }
   };
@@ -107,7 +110,7 @@ const factory = (crowi) => {
       return data;
     }
     catch (err) {
-      debug('Bookmark.findOneAndRemove failed', err);
+      logger.debug('Bookmark.findOneAndRemove failed', err);
       throw err;
     }
   };

+ 13 - 12
apps/app/src/server/models/external-account.ts

@@ -1,17 +1,18 @@
 // disable no-return-await for model functions
 /* eslint-disable no-return-await */
-import type { IExternalAccount, IExternalAccountHasId, IUserHasId } from '@growi/core';
-import type { Model, Document } from 'mongoose';
-import { Schema } from 'mongoose';
+import type { IUser } from '@growi/core/dist/interfaces';
+import { type IExternalAccount, type IExternalAccountHasId, type IUserHasId } from '@growi/core/dist/interfaces';
+import type { Model, Document, HydratedDocument } from 'mongoose';
+import mongoose, { Schema } from 'mongoose';
+import mongoosePaginate from 'mongoose-paginate-v2';
+import uniqueValidator from 'mongoose-unique-validator';
 
 import { NullUsernameToBeRegisteredError } from '~/server/models/errors';
+import loggerFactory from '~/utils/logger';
 
 import { getOrCreateModel } from '../util/mongoose-utils';
 
-const debug = require('debug')('growi:models:external-account');
-const mongoose = require('mongoose');
-const mongoosePaginate = require('mongoose-paginate-v2');
-const uniqueValidator = require('mongoose-unique-validator');
+const logger = loggerFactory('growi:models:external-account');
 
 
 export interface ExternalAccountDocument extends IExternalAccount, Document {}
@@ -75,7 +76,7 @@ schema.statics.findOrRegister = function(
     .then((account) => {
     // ExternalAccount is found
       if (account != null) {
-        debug(`ExternalAccount '${accountId}' is found `, account);
+        logger.debug(`ExternalAccount '${accountId}' is found `, account);
         return account;
       }
 
@@ -83,9 +84,9 @@ schema.statics.findOrRegister = function(
         throw new NullUsernameToBeRegisteredError('username_should_not_be_null');
       }
 
-      const User = mongoose.model('User');
+      const User = mongoose.model<HydratedDocument<IUser>, Model<IUser> & { createUser, STATUS_ACTIVE }>('User');
 
-      let promise = User.findOne({ username: usernameToBeRegistered });
+      let promise = User.findOne({ username: usernameToBeRegistered }).exec();
       if (isSameUsernameTreatedAsIdenticalUser && isSameEmailTreatedAsIdenticalUser) {
         promise = promise
           .then((user) => {
@@ -94,7 +95,7 @@ schema.statics.findOrRegister = function(
           });
       }
       else if (isSameEmailTreatedAsIdenticalUser) {
-        promise = User.findOne({ email: mailToBeRegistered });
+        promise = User.findOne({ email: mailToBeRegistered }).exec();
       }
 
       return promise
@@ -109,7 +110,7 @@ schema.statics.findOrRegister = function(
           }
 
           // create a new User with STATUS_ACTIVE
-          debug(`ExternalAccount '${accountId}' is not found, it is going to be registered.`);
+          logger.debug(`ExternalAccount '${accountId}' is not found, it is going to be registered.`);
           return User.createUser(nameToBeRegistered, usernameToBeRegistered, mailToBeRegistered, undefined, undefined, User.STATUS_ACTIVE);
         })
         .then((newUser) => {

+ 5 - 6
apps/app/src/server/models/obsolete-page.js

@@ -11,6 +11,8 @@ import loggerFactory from '~/utils/logger';
 import UserGroup from './user-group';
 import UserGroupRelation from './user-group-relation';
 
+const logger = loggerFactory('growi:models:page');
+
 
 // disable no-return-await for model functions
 /* eslint-disable no-return-await */
@@ -19,15 +21,12 @@ import UserGroupRelation from './user-group-relation';
 
 const nodePath = require('path');
 
-const debug = require('debug')('growi:models:page');
 const mongoose = require('mongoose');
 const urljoin = require('url-join');
 
 const { isTopPage, isTrashPage } = pagePathUtils;
 const { checkTemplatePath } = templateChecker;
 
-const logger = loggerFactory('growi:models:page');
-
 const GRANT_PUBLIC = 1;
 const GRANT_RESTRICTED = 2;
 const GRANT_SPECIFIED = 3;
@@ -216,7 +215,7 @@ export const getPageSchema = (crowi) => {
 
   pageSchema.methods.seen = async function(userData) {
     if (this.isSeenUser(userData)) {
-      debug('seenUsers not updated');
+      logger.debug('seenUsers not updated');
       return this;
     }
 
@@ -227,7 +226,7 @@ export const getPageSchema = (crowi) => {
     const added = this.seenUsers.addToSet(userData._id);
     const saved = await this.save();
 
-    debug('seenUsers updated!', added);
+    logger.debug('seenUsers updated!', added);
     pageEvent.emit('addSeenUsers', saved);
 
     return saved;
@@ -290,7 +289,7 @@ export const getPageSchema = (crowi) => {
       .then((count) => {
         self.update({ _id: pageId }, { commentCount: count }, {}, (err, data) => {
           if (err) {
-            debug('Update commentCount Error', err);
+            logger.debug('Update commentCount Error', err);
             throw err;
           }
 

+ 0 - 0
apps/app/src/server/models/vo/error-v3.js → apps/app/src/server/models/openapi/error-v3.ts


+ 79 - 0
apps/app/src/server/models/openapi/page.ts

@@ -0,0 +1,79 @@
+/**
+ * @swagger
+ *
+ *  components:
+ *    schemas:
+ *      Page:
+ *        description: Page
+ *        type: object
+ *        properties:
+ *          _id:
+ *            type: string
+ *            description: page ID
+ *            example: 5e07345972560e001761fa63
+ *          __v:
+ *            type: number
+ *            description: DB record version
+ *            example: 0
+ *          commentCount:
+ *            type: number
+ *            description: count of comments
+ *            example: 3
+ *          createdAt:
+ *            type: string
+ *            description: date created at
+ *            example: 2010-01-01T00:00:00.000Z
+ *          creator:
+ *            $ref: '#/components/schemas/User'
+ *          extended:
+ *            type: object
+ *            description: extend data
+ *            example: {}
+ *          grant:
+ *            type: number
+ *            description: grant
+ *            example: 1
+ *          grantedUsers:
+ *            type: array
+ *            description: granted users
+ *            items:
+ *              type: string
+ *              description: user ID
+ *            example: ["5ae5fccfc5577b0004dbd8ab"]
+ *          lastUpdateUser:
+ *            $ref: '#/components/schemas/User'
+ *          liker:
+ *            type: array
+ *            description: granted users
+ *            items:
+ *              type: string
+ *              description: user ID
+ *            example: []
+ *          path:
+ *            type: string
+ *            description: page path
+ *            example: /
+ *          revision:
+ *            type: string
+ *            description: page revision
+ *          seenUsers:
+ *            type: array
+ *            description: granted users
+ *            items:
+ *              type: string
+ *              description: user ID
+ *            example: ["5ae5fccfc5577b0004dbd8ab"]
+ *          status:
+ *            type: string
+ *            description: status
+ *            enum:
+ *              - 'wip'
+ *              - 'published'
+ *              - 'deleted'
+ *              - 'deprecated'
+ *            example: published
+ *          updatedAt:
+ *            type: string
+ *            description: date updated at
+ *            example: 2010-01-01T00:00:00.000Z
+ */

+ 39 - 0
apps/app/src/server/models/openapi/revision.ts

@@ -0,0 +1,39 @@
+/**
+ * @swagger
+ *
+ *  components:
+ *    schemas:
+ *      Revision:
+ *        description: Revision
+ *        type: object
+ *        properties:
+ *          _id:
+ *            type: string
+ *            description: revision ID
+ *            example: 5e0734e472560e001761fa68
+ *          __v:
+ *            type: number
+ *            description: DB record version
+ *            example: 0
+ *          author:
+ *            $ref: '#/components/schemas/User/properties/_id'
+ *          body:
+ *            type: string
+ *            description: content body
+ *            example: |
+ *              # test
+ *
+ *              test
+ *          format:
+ *            type: string
+ *            description: format
+ *            example: markdown
+ *          path:
+ *            type: string
+ *            description: path
+ *            example: /user/alice/test
+ *          createdAt:
+ *            type: string
+ *            description: date created at
+ *            example: 2010-01-01T00:00:00.000Z
+ */

+ 2 - 2
apps/app/src/server/models/revision.ts

@@ -1,9 +1,9 @@
+import { allOrigin } from '@growi/core';
 import type {
   HasObjectId,
   IRevision,
   Origin,
-} from '@growi/core';
-import { allOrigin } from '@growi/core';
+} from '@growi/core/dist/interfaces';
 import type { Types } from 'mongoose';
 import {
   Schema, type Document, type Model,

+ 10 - 7
apps/app/src/server/models/user-group-relation.ts

@@ -1,17 +1,20 @@
 import {
-  getIdForRef, isPopulated, type IUserGroupRelation,
+  getIdForRef, isPopulated,
 } from '@growi/core';
+import type { IUserGroupRelation } from '@growi/core/dist/interfaces';
 import type { Model, Document } from 'mongoose';
 import mongoose, { Schema } from 'mongoose';
+import mongoosePaginate from 'mongoose-paginate-v2';
+import uniqueValidator from 'mongoose-unique-validator';
+
+import loggerFactory from '~/utils/logger';
 
 import type { ObjectIdLike } from '../interfaces/mongoose-utils';
 import { getOrCreateModel } from '../util/mongoose-utils';
 
 import type { UserGroupDocument } from './user-group';
 
-const debug = require('debug')('growi:models:userGroupRelation');
-const mongoosePaginate = require('mongoose-paginate-v2');
-const uniqueValidator = require('mongoose-unique-validator');
+const logger = loggerFactory('growi:models:userGroupRelation');
 
 
 export interface UserGroupRelationDocument extends IUserGroupRelation, Document {}
@@ -87,7 +90,7 @@ schema.statics.findAllRelation = function() {
  * @memberof UserGroupRelation
  */
 schema.statics.findAllRelationForUserGroup = function(userGroup) {
-  debug('findAllRelationForUserGroup is called', userGroup);
+  logger.debug('findAllRelationForUserGroup is called', userGroup);
   return this
     .find({ relatedGroup: userGroup })
     .populate('relatedUser')
@@ -130,7 +133,7 @@ schema.statics.findAllRelationForUserGroups = function(userGroups) {
 schema.statics.findAllGroupsForUser = async function(user): Promise<UserGroupDocument[]> {
   const userGroupRelations = await this.find({ relatedUser: user._id }).populate('relatedGroup');
   const userGroups = userGroupRelations.map((relation) => {
-    return isPopulated(relation.relatedGroup) ? relation.relatedGroup as UserGroupDocument : null;
+    return isPopulated(relation.relatedGroup) ? relation.relatedGroup as unknown as UserGroupDocument : null;
   });
   return userGroups.filter((group): group is NonNullable<UserGroupDocument> => group != null);
 };
@@ -203,7 +206,7 @@ schema.statics.findUserByNotRelatedGroup = function(userGroup, queryOptions) {
         $or: searthField,
       };
 
-      debug('findUserByNotRelatedGroup ', query);
+      logger.debug('findUserByNotRelatedGroup ', query);
       return User.find(query).exec();
     });
 };

+ 4 - 10
apps/app/src/server/routes/apiv3/admin-home.js

@@ -4,12 +4,6 @@ const express = require('express');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: adminHome
- */
-
 /**
  * @swagger
  *
@@ -27,9 +21,9 @@ const router = express.Router();
  *          npmVersion:
  *            type: string
  *            description: version of npm
- *          yarnVersion:
+ *          pnpmVersion:
  *            type: string
- *            description: version of yarn
+ *            description: version of pnpm
  *      InstalledPluginsParams:
  *        type: object
  *        properties:
@@ -47,7 +41,7 @@ module.exports = (crowi) => {
    *
    *    /admin-home/:
    *      get:
-   *        tags: [AdminHome]
+   *        tags: [Admin]
    *        operationId: getAdminHome
    *        summary: /admin-home
    *        description: Get adminHome parameters
@@ -67,7 +61,7 @@ module.exports = (crowi) => {
       growiVersion: crowi.version,
       nodeVersion: crowi.runtimeVersions.versions.node ? crowi.runtimeVersions.versions.node.version.version : '-',
       npmVersion: crowi.runtimeVersions.versions.npm ? crowi.runtimeVersions.versions.npm.version.version : '-',
-      yarnVersion: crowi.runtimeVersions.versions.yarn ? crowi.runtimeVersions.versions.yarn.version.version : '-',
+      pnpmVersion: crowi.runtimeVersions.versions.pnpm ? crowi.runtimeVersions.versions.pnpm.version.version : '-',
       envVars: await ConfigLoader.getEnvVarsForDisplay(true),
       isV5Compatible: crowi.configManager.getConfig('crowi', 'app:isV5Compatible'),
       isMaintenanceMode: crowi.configManager.getConfig('crowi', 'app:isMaintenanceMode'),

+ 2 - 9
apps/app/src/server/routes/apiv3/app-settings.js

@@ -13,17 +13,10 @@ import { apiV3FormValidator } from '../../middlewares/apiv3-form-validator';
 const logger = loggerFactory('growi:routes:apiv3:app-settings');
 
 const { pathUtils } = require('@growi/core/dist/utils');
-const debug = require('debug')('growi:routes:admin');
 const express = require('express');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: AppSettings
- */
-
 /**
  * @swagger
  *
@@ -462,7 +455,7 @@ module.exports = (crowi) => {
     }
 
     const smtpClient = mailService.createSMTPClient(option);
-    debug('mailer setup for validate SMTP setting', smtpClient);
+    logger.debug('mailer setup for validate SMTP setting', smtpClient);
 
     const mailOptions = {
       from: fromAddress,
@@ -567,7 +560,7 @@ module.exports = (crowi) => {
     catch (err) {
       const msg = req.t('validation.failed_to_send_a_test_email');
       logger.error('Error', err);
-      debug('Error validate mail setting: ', err);
+      logger.debug('Error validate mail setting: ', err);
       return res.apiv3Err(new ErrorV3(msg, 'send-email-with-smtp-failed'));
     }
   });

+ 1 - 8
apps/app/src/server/routes/apiv3/attachment.js

@@ -24,13 +24,6 @@ const {
 } = require('express-validator');
 
 
-/**
- * @swagger
- *  tags:
- *    name: Attachment
- */
-
-
 /**
  * @swagger
  *
@@ -231,7 +224,7 @@ module.exports = (crowi) => {
    *
    *    /attachment:
    *      post:
-   *        tags: [Attachment, CrowiCompatibles]
+   *        tags: [Attachment]
    *        operationId: addAttachment
    *        summary: /attachment
    *        description: Add attachment to the page

+ 0 - 6
apps/app/src/server/routes/apiv3/bookmarks.js

@@ -17,12 +17,6 @@ const { body, query, param } = require('express-validator');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: Bookmarks
- */
-
 /**
  * @swagger
  *

+ 0 - 6
apps/app/src/server/routes/apiv3/customize-setting.js

@@ -21,12 +21,6 @@ const logger = loggerFactory('growi:routes:apiv3:customize-setting');
 const router = express.Router();
 
 
-/**
- * @swagger
- *  tags:
- *    name: CustomizeSetting
- */
-
 /**
  * @swagger
  *

+ 0 - 43
apps/app/src/server/routes/apiv3/docs.js

@@ -1,43 +0,0 @@
-import loggerFactory from '~/utils/logger';
-import swaggerDefinition from '^/config/swagger-definition';
-
-const express = require('express');
-const swaggerJSDoc = require('swagger-jsdoc');
-
-const logger = loggerFactory('growi:routes:apiv3:docs'); // eslint-disable-line no-unused-vars
-
-const router = express.Router();
-
-// paths to scan
-const APIS = [
-  'src/server/routes/apiv3/**/*.js',
-  'src/server/models/**/*.js',
-];
-
-module.exports = (crowi) => {
-
-  // skip if disabled
-  if (!crowi.configManager.getConfig('crowi', 'app:publishOpenAPI')) {
-    return router;
-  }
-
-  // generate swagger spec
-  const options = {
-    swaggerDefinition,
-    apis: APIS,
-  };
-  const swaggerSpec = swaggerJSDoc(options);
-
-  // publish swagger spec
-  router.get('/swagger-spec.json', (req, res) => {
-    res.setHeader('Content-Type', 'application/json');
-    res.send(swaggerSpec);
-  });
-
-  // publish redoc
-  router.get('/', (req, res) => {
-    res.render('redoc');
-  });
-
-  return router;
-};

+ 0 - 6
apps/app/src/server/routes/apiv3/export.js

@@ -14,12 +14,6 @@ const { param } = require('express-validator');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: Export
- */
-
 /**
  * @swagger
  *

+ 0 - 6
apps/app/src/server/routes/apiv3/healthcheck.ts

@@ -13,12 +13,6 @@ const logger = loggerFactory('growi:routes:apiv3:healthcheck');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: Healthcheck
- */
-
 /**
  * @swagger
  *

+ 0 - 6
apps/app/src/server/routes/apiv3/import.js

@@ -17,12 +17,6 @@ const multer = require('multer');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: Import
- */
-
 /**
  * @swagger
  *

+ 6 - 3
apps/app/src/server/routes/apiv3/invited.ts

@@ -1,18 +1,21 @@
-import type { IUser } from '@growi/core';
+import type { IUser } from '@growi/core/dist/interfaces';
 import type { Request, Router } from 'express';
 import express from 'express';
 import mongoose from 'mongoose';
 
+import loggerFactory from '~/utils/logger';
+
 import type Crowi from '../../crowi';
 import { invitedRules, invitedValidation } from '../../middlewares/invited-form-validator';
 
 import type { ApiV3Response } from './interfaces/apiv3-response';
 
+const logger = loggerFactory('growi:routes:login');
+
 type InvitedFormRequest = Request & { form: any, user: any };
 
 module.exports = (crowi: Crowi): Router => {
   const applicationInstalled = require('../../middlewares/application-installed')(crowi);
-  const debug = require('debug')('growi:routes:login');
   const router = express.Router();
 
   router.post('/', applicationInstalled, invitedRules(), invitedValidation, async(req: InvitedFormRequest, res: ApiV3Response) => {
@@ -41,7 +44,7 @@ module.exports = (crowi: Crowi): Router => {
 
     const creatable = await User.isRegisterableUsername(username);
     if (!creatable) {
-      debug('username', username);
+      logger.debug('username', username);
       return res.apiv3Err('message.unable_to_use_this_user', 403);
     }
 

+ 0 - 6
apps/app/src/server/routes/apiv3/markdown-setting.js

@@ -32,12 +32,6 @@ const validator = {
 };
 
 
-/**
- * @swagger
- *  tags:
- *    name: MarkDownSetting
- */
-
 /**
  * @swagger
  *

+ 1 - 7
apps/app/src/server/routes/apiv3/mongo.js

@@ -7,12 +7,6 @@ const mongoose = require('mongoose');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: Mongo
- */
-
 module.exports = (crowi) => {
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
   const adminRequired = require('../../middlewares/admin-required')(crowi);
@@ -22,7 +16,7 @@ module.exports = (crowi) => {
    *
    *  /mongo/collections:
    *    get:
-   *      tags: [Mongo]
+   *      tags: [MongoDB]
    *      operationId: getMongoCollections
    *      summary: /mongo/collections
    *      description: get mongodb collections names

+ 0 - 6
apps/app/src/server/routes/apiv3/notification-setting.js

@@ -41,12 +41,6 @@ const validator = {
   ],
 };
 
-/**
- * @swagger
- *  tags:
- *    name: NotificationSetting
- */
-
 /**
  * @swagger
  *

+ 4 - 83
apps/app/src/server/routes/apiv3/page/index.ts

@@ -47,91 +47,12 @@ const { body, query, param } = require('express-validator');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: Page
- */
 
 /**
  * @swagger
  *
- *  components:
- *    schemas:
- *      Page:
- *        description: Page
- *        type: object
- *        properties:
- *          _id:
- *            type: string
- *            description: page ID
- *            example: 5e07345972560e001761fa63
- *          __v:
- *            type: number
- *            description: DB record version
- *            example: 0
- *          commentCount:
- *            type: number
- *            description: count of comments
- *            example: 3
- *          createdAt:
- *            type: string
- *            description: date created at
- *            example: 2010-01-01T00:00:00.000Z
- *          creator:
- *            $ref: '#/components/schemas/User'
- *          extended:
- *            type: object
- *            description: extend data
- *            example: {}
- *          grant:
- *            type: number
- *            description: grant
- *            example: 1
- *          grantedUsers:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: ["5ae5fccfc5577b0004dbd8ab"]
- *          lastUpdateUser:
- *            $ref: '#/components/schemas/User'
- *          liker:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: []
- *          path:
- *            type: string
- *            description: page path
- *            example: /
- *          revision:
- *            type: string
- *            description: page revision
- *          seenUsers:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: ["5ae5fccfc5577b0004dbd8ab"]
- *          status:
- *            type: string
- *            description: status
- *            enum:
- *              - 'wip'
- *              - 'published'
- *              - 'deleted'
- *              - 'deprecated'
- *            example: published
- *          updatedAt:
- *            type: string
- *            description: date updated at
- *            example: 2010-01-01T00:00:00.000Z
- *
+ * components:
+ *   schemas:
  *      LikeParams:
  *        description: LikeParams
  *        type: object
@@ -745,9 +666,9 @@ module.exports = (crowi) => {
   /**
   * @swagger
   *
-  *    /pages/export:
+  *    /page/export:
   *      get:
-  *        tags: [Export]
+  *        tags: [Page]
   *        description: return page's markdown
   *        responses:
   *          200:

+ 0 - 87
apps/app/src/server/routes/apiv3/pages/index.js

@@ -27,12 +27,6 @@ const router = express.Router();
 const LIMIT_FOR_LIST = 10;
 const LIMIT_FOR_MULTIPLE_PAGE_OP = 20;
 
-/**
- * @swagger
- *  tags:
- *    name: Pages
- */
-
 /**
  * @swagger
  *
@@ -63,87 +57,6 @@ const LIMIT_FOR_MULTIPLE_PAGE_OP = 20;
  *            example: 3
  */
 
-/**
- * @swagger
- *
- *  components:
- *    schemas:
- *      Page:
- *        description: Page
- *        type: object
- *        properties:
- *          _id:
- *            type: string
- *            description: page ID
- *            example: 5e07345972560e001761fa63
- *          __v:
- *            type: number
- *            description: DB record version
- *            example: 0
- *          commentCount:
- *            type: number
- *            description: count of comments
- *            example: 3
- *          createdAt:
- *            type: string
- *            description: date created at
- *            example: 2010-01-01T00:00:00.000Z
- *          creator:
- *            $ref: '#/components/schemas/User'
- *          extended:
- *            type: object
- *            description: extend data
- *            example: {}
- *          grant:
- *            type: number
- *            description: grant
- *            example: 1
- *          grantedUsers:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: ["5ae5fccfc5577b0004dbd8ab"]
- *          lastUpdateUser:
- *            $ref: '#/components/schemas/User'
- *          liker:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: []
- *          path:
- *            type: string
- *            description: page path
- *            example: /Sandbox/Math
- *          revision:
- *            type: string
- *            description: revision ID
- *            example: ["5ae5fccfc5577b0004dbd8ab"]
- *          seenUsers:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: ["5ae5fccfc5577b0004dbd8ab"]
- *          status:
- *            type: string
- *            description: status
- *            enum:
- *              - 'wip'
- *              - 'published'
- *              - 'deleted'
- *              - 'deprecated'
- *            example: published
- *          updatedAt:
- *            type: string
- *            description: date updated at
- *            example: 2010-01-01T00:00:00.000Z
- */
-
 module.exports = (crowi) => {
   const accessTokenParser = require('../../../middlewares/access-token-parser')(crowi);
   const loginRequired = require('../../../middlewares/login-required')(crowi, true);

+ 11 - 17
apps/app/src/server/routes/apiv3/personal-setting.js

@@ -21,12 +21,6 @@ const passport = require('passport');
 
 const router = express.Router();
 
-/**
- * @swagger
- *  tags:
- *    name: PersonalSetting
- */
-
 /**
  * @swagger
  *
@@ -136,7 +130,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting:
    *      get:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: getPersonalSetting
    *        summary: /personal-setting
    *        description: Get personal parameters
@@ -175,7 +169,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/is-password-set:
    *      get:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: getIsPasswordSet
    *        summary: /personal-setting
    *        description: Get whether a password has been set
@@ -210,7 +204,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting:
    *      put:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: updatePersonalSetting
    *        summary: /personal-setting
    *        description: Update personal setting
@@ -267,7 +261,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/image-type:
    *      put:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: putUserImageType
    *        summary: /personal-setting/image-type
    *        description: Update user image type
@@ -304,7 +298,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/external-accounts:
    *      get:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: getExternalAccounts
    *        summary: /personal-setting/external-accounts
    *        description: Get external accounts that linked current user
@@ -338,7 +332,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/password:
    *      put:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: putUserPassword
    *        summary: /personal-setting/password
    *        description: Update user password
@@ -386,7 +380,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/api-token:
    *      put:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: putUserApiToken
    *        summary: /personal-setting/api-token
    *        description: Update user api token
@@ -424,7 +418,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/associate-ldap:
    *      put:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: associateLdapAccount
    *        summary: /personal-setting/associate-ldap
    *        description: associate Ldap account
@@ -476,7 +470,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/disassociate-ldap:
    *      put:
-   *        tags: [PersonalSetting]
+   *        tags: [GeneralSetting]
    *        operationId: disassociateLdapAccount
    *        summary: /personal-setting/disassociate-ldap
    *        description: disassociate Ldap account
@@ -609,7 +603,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/in-app-notification-settings:
    *      put:
-   *        tags: [in-app-notification-settings]
+   *        tags: [InAppNotificationSettings]
    *        operationId: putInAppNotificationSettings
    *        summary: personal-setting/in-app-notification-settings
    *        description: Put InAppNotificationSettings
@@ -653,7 +647,7 @@ module.exports = (crowi) => {
    *
    *    /personal-setting/in-app-notification-settings:
    *      get:
-   *        tags: [in-app-notification-settings]
+   *        tags: [InAppNotificationSettings]
    *        operationId: getInAppNotificationSettings
    *        summary: personal-setting/in-app-notification-settings
    *        description: Get InAppNotificationSettings

+ 0 - 45
apps/app/src/server/routes/apiv3/revisions.js

@@ -17,51 +17,6 @@ const router = express.Router();
 
 const MIGRATION_FILE_NAME = '20211227060705-revision-path-to-page-id-schema-migration--fixed-7549';
 
-/**
- * @swagger
- *  tags:
- *    name: Revisions
- */
-
-/**
- * @swagger
- *
- *  components:
- *    schemas:
- *      Revision:
- *        description: Revision
- *        type: object
- *        properties:
- *          _id:
- *            type: string
- *            description: revision ID
- *            example: 5e0734e472560e001761fa68
- *          __v:
- *            type: number
- *            description: DB record version
- *            example: 0
- *          author:
- *            $ref: '#/components/schemas/User/properties/_id'
- *          body:
- *            type: string
- *            description: content body
- *            example: |
- *              # test
- *
- *              test
- *          format:
- *            type: string
- *            description: format
- *            example: markdown
- *          path:
- *            type: string
- *            description: path
- *            example: /user/alice/test
- *          createdAt:
- *            type: string
- *            description: date created at
- *            example: 2010-01-01T00:00:00.000Z
- */
 module.exports = (crowi) => {
   const certifySharedPage = require('../../middlewares/certify-shared-page')(crowi);
   const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);

+ 3 - 8
apps/app/src/server/routes/apiv3/search.js

@@ -16,11 +16,6 @@ const router = express.Router();
 
 const noCache = require('nocache');
 
-/**
- * @swagger
- *  tags:
- *    name: Search
- */
 module.exports = (crowi) => {
   const accessTokenParser = require('../../middlewares/access-token-parser')(crowi);
   const loginRequired = require('../../middlewares/login-required')(crowi);
@@ -34,7 +29,7 @@ module.exports = (crowi) => {
    *
    *  /search/indices:
    *    get:
-   *      tags: [Search]
+   *      tags: [FullTextSearch Management]
    *      summary: /search/indices
    *      description: Get current status of indices
    *      responses:
@@ -68,7 +63,7 @@ module.exports = (crowi) => {
    *
    *  /search/connection:
    *    get:
-   *      tags: [Search]
+   *      tags: [FullTextSearch Management]
    *      summary: /search/connection
    *      description: Reconnect to Elasticsearch
    *      responses:
@@ -103,7 +98,7 @@ module.exports = (crowi) => {
    *
    *  /search/indices:
    *    put:
-   *      tags: [Search]
+   *      tags: [FullTextSearch Management]
    *      summary: /search/indices
    *      description: Operate indices
    *      requestBody:

+ 26 - 32
apps/app/src/server/routes/apiv3/security-settings/index.js

@@ -108,12 +108,6 @@ const validator = {
   ],
 };
 
-/**
- * @swagger
- *  tags:
- *    name: SecuritySetting
- */
-
 
 /**
  * @swagger
@@ -333,9 +327,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/:
+   *    /security-setting/:
    *      get:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Get security paramators
    *        responses:
    *          200:
@@ -461,9 +455,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/authentication/enabled:
+   *    /security-setting/authentication/enabled:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update authentication isEnabled
    *        requestBody:
    *          required: true
@@ -576,9 +570,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/authentication:
+   *    /security-setting/authentication:
    *      get:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Get setup strategies for passport
    *        responses:
    *          200:
@@ -604,9 +598,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/general-setting:
+   *    /security-setting/general-setting:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update GeneralSetting
    *        requestBody:
    *          required: true
@@ -690,9 +684,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/share-link-setting:
+   *    /security-setting/share-link-setting:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update ShareLink Setting
    *        requestBody:
    *          required: true
@@ -733,9 +727,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/all-share-links:
+   *    /security-setting/all-share-links:
    *      get:
-   *        tags: [ShareLinkSettings, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Get All ShareLinks at Share Link Setting
    *        responses:
    *          200:
@@ -776,9 +770,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/all-share-links:
+   *    /security-setting/all-share-links:
    *      delete:
-   *        tags: [ShareLinkSettings, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Delete All ShareLinks at Share Link Setting
    *        responses:
    *          200:
@@ -801,9 +795,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/local-setting:
+   *    /security-setting/local-setting:
    *      put:
-   *        tags: [LocalSetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update LocalSetting
    *        requestBody:
    *          required: true
@@ -853,9 +847,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/ldap:
+   *    /security-setting/ldap:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update LDAP setting
    *        requestBody:
    *          required: true
@@ -918,9 +912,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/saml:
+   *    /security-setting/saml:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update SAML setting
    *        requestBody:
    *          required: true
@@ -1011,9 +1005,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/oidc:
+   *    /security-setting/oidc:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update OpenID Connect setting
    *        requestBody:
    *          required: true
@@ -1088,9 +1082,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/google-oauth:
+   *    /security-setting/google-oauth:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update google OAuth
    *        requestBody:
    *          required: true
@@ -1136,9 +1130,9 @@ module.exports = (crowi) => {
   /**
    * @swagger
    *
-   *    /_api/v3/security-setting/github-oauth:
+   *    /security-setting/github-oauth:
    *      put:
-   *        tags: [SecuritySetting, apiv3]
+   *        tags: [SecuritySetting]
    *        description: Update github OAuth
    *        requestBody:
    *          required: true

+ 1 - 7
apps/app/src/server/routes/apiv3/share-links.js

@@ -20,12 +20,6 @@ const validator = {};
 
 const today = new Date();
 
-/**
- * @swagger
- *  tags:
- *    name: ShareLink
- */
-
 module.exports = (crowi) => {
   const loginRequired = require('../../middlewares/login-required')(crowi);
   const adminRequired = require('../../middlewares/admin-required')(crowi);
@@ -212,7 +206,7 @@ module.exports = (crowi) => {
   *
   *    /share-links/all:
   *      delete:
-  *        tags: [ShareLinks]
+  *        tags: [ShareLink Management]
   *        description: delete all share links
   *        responses:
   *          200:

+ 0 - 6
apps/app/src/server/routes/apiv3/slack-integration-legacy-settings.js

@@ -22,12 +22,6 @@ const validator = {
   ],
 };
 
-/**
- * @swagger
- *  tags:
- *    name: SlackIntegrationLegacySetting
- */
-
 /**
  * @swagger
  *

+ 13 - 19
apps/app/src/server/routes/apiv3/slack-integration-settings.js

@@ -25,12 +25,6 @@ const logger = loggerFactory('growi:routes:apiv3:slack-integration-settings');
 const router = express.Router();
 
 
-/**
- * @swagger
- *  tags:
- *    name: SlackIntegrationSettings
- */
-
 /**
  * @swagger
  *
@@ -166,9 +160,9 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/:
    *      get:
-   *        tags: [SlackBotSettingParams]
+   *        tags: [SlackIntegrationSettings]
    *        operationId: getSlackBotSettingParams
-   *        summary: get /slack-integration
+   *        summary: /slack-integration
    *        description: Get current settings and connection statuses.
    *        responses:
    *          200:
@@ -295,7 +289,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/bot-type/:
    *      put:
-   *        tags: [botType]
+   *        tags: [SlackIntegrationSettings]
    *        operationId: putBotType
    *        summary: /slack-integration/bot-type
    *        description: Put botType setting.
@@ -334,7 +328,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration/bot-type/:
    *      delete:
-   *        tags: [botType]
+   *        tags: [SlackIntegrationSettings]
    *        operationId: deleteBotType
    *        summary: /slack-integration/bot-type
    *        description: Delete botType setting.
@@ -365,7 +359,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/without-proxy/update-settings/:
    *      put:
-   *        tags: [UpdateWithoutProxySettings]
+   *        tags: [SlackIntegrationSettings (without proxy)]
    *        operationId: putWithoutProxySettings
    *        summary: update customBotWithoutProxy settings
    *        description: Update customBotWithoutProxy setting.
@@ -405,7 +399,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/without-proxy/update-permissions/:
    *      put:
-   *        tags: [UpdateWithoutProxyPermissions]
+   *        tags: [SlackIntegrationSettings (without proxy)]
    *        operationId: putWithoutProxyPermissions
    *        summary: update customBotWithoutProxy permissions
    *        description: Update customBotWithoutProxy permissions.
@@ -448,7 +442,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/slack-app-integrations:
    *      post:
-   *        tags: [SlackIntegration]
+   *        tags: [SlackIntegrationSettings (with proxy)]
    *        operationId: putSlackAppIntegrations
    *        summary: /slack-integration
    *        description: Generate SlackAppIntegrations
@@ -498,7 +492,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/slack-app-integrations/:id:
    *      delete:
-   *        tags: [SlackIntegration]
+   *        tags: [SlackIntegrationSettings (with proxy)]
    *        operationId: deleteAccessTokens
    *        summary: delete accessTokens
    *        description: Delete accessTokens
@@ -556,7 +550,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/slack-app-integrations/:id/makeprimary:
    *      put:
-   *        tags: [SlackIntegration]
+   *        tags: [SlackIntegrationSettings (with proxy)]
    *        operationId: makePrimary
    *        summary: /slack-integration
    *        description: Make SlackAppTokens primary
@@ -603,7 +597,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/slack-app-integrations/:id/regenerate-tokens:
    *      put:
-   *        tags: [SlackIntegration]
+   *        tags: [SlackIntegrationSettings (with proxy)]
    *        operationId: putRegenerateTokens
    *        summary: /slack-integration
    *        description: Regenerate SlackAppTokens
@@ -636,7 +630,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/slack-app-integrations/:id/permissions:
    *      put:
-   *        tags: [SlackIntegration]
+   *        tags: [SlackIntegrationSettings (with proxy)]
    *        operationId: putSupportedCommands
    *        summary: /slack-integration-settings/:id/permissions
    *        description: update supported commands
@@ -695,7 +689,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/slack-app-integrations/:id/relation-test:
    *      post:
-   *        tags: [botType]
+   *        tags: [SlackIntegrationSettings (with proxy)]
    *        operationId: postRelationTest
    *        summary: Test relation
    *        description: Delete botType setting.
@@ -766,7 +760,7 @@ module.exports = (crowi) => {
    *
    *    /slack-integration-settings/without-proxy/test:
    *      post:
-   *        tags: [botType]
+   *        tags: [SlackIntegrationSettings (without proxy)]
    *        operationId: postTest
    *        summary: test the connection
    *        description: Test the connection with slack work space.

+ 0 - 5
apps/app/src/server/routes/apiv3/statistics.js

@@ -17,11 +17,6 @@ const USER_STATUS_MASTER = {
 };
 
 
-/**
- * @swagger
- *  tags:
- *    name: Statistics
- */
 module.exports = (crowi) => {
 
   const models = crowi.models;

+ 1 - 7
apps/app/src/server/routes/apiv3/user-group-relation.js

@@ -13,12 +13,6 @@ const router = express.Router();
 
 const validator = {};
 
-/**
- * @swagger
- *  tags:
- *    name: UserGroupRelation
- */
-
 module.exports = (crowi) => {
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
   const adminRequired = require('../../middlewares/admin-required')(crowi);
@@ -33,7 +27,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-group-relations:
    *      get:
-   *        tags: [UserGroupRelation]
+   *        tags: [UserGroupRelations]
    *        operationId: listUserGroupRelations
    *        summary: /user-group-relations
    *        description: Gets the user group relations

+ 14 - 20
apps/app/src/server/routes/apiv3/user-group.js

@@ -24,12 +24,6 @@ const logger = loggerFactory('growi:routes:apiv3:user-group'); // eslint-disable
 const router = express.Router();
 
 
-/**
- * @swagger
- *  tags:
- *    name: UserGroup
- */
-
 module.exports = (crowi) => {
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
   const adminRequired = require('../../middlewares/admin-required')(crowi);
@@ -94,7 +88,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getUserGroup
    *        summary: /user-groups
    *        description: Get usergroups
@@ -137,7 +131,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /ancestors:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getAncestorUserGroups
    *        summary: /ancestors
    *        description: Get ancestor user groups.
@@ -200,7 +194,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups:
    *      post:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: createUserGroup
    *        summary: /user-groups
    *        description: Adds userGroup
@@ -250,7 +244,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /selectable-parent-groups:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getSelectableParentGroups
    *        summary: /selectable-parent-groups
    *        description: Get selectable parent UserGroups
@@ -299,7 +293,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /selectable-child-groups:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getSelectableChildGroups
    *        summary: /selectable-child-groups
    *        description: Get selectable child UserGroups
@@ -351,7 +345,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getUserGroupFromGroupId
    *        summary: /user-groups/{id}
    *        description: Get UserGroup from Group ID
@@ -393,7 +387,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}:
    *      delete:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: deleteUserGroup
    *        summary: /user-groups/{id}
    *        description: Deletes userGroup
@@ -457,7 +451,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}:
    *      put:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: updateUserGroups
    *        summary: /user-groups/{id}
    *        description: Update userGroup
@@ -507,7 +501,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}/users:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getUsersUserGroups
    *        summary: /user-groups/{id}/users
    *        description: Get users related to the userGroup
@@ -558,7 +552,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}/unrelated-users:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getUnrelatedUsersUserGroups
    *        summary: /user-groups/{id}/unrelated-users
    *        description: Get users unrelated to the userGroup
@@ -619,7 +613,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}/users:
    *      post:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: addUserUserGroups
    *        summary: /user-groups/{id}/users
    *        description: Add a user to the userGroup
@@ -686,7 +680,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}/users:
    *      delete:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: deleteUsersUserGroups
    *        summary: /user-groups/{id}/users
    *        description: remove a user from the userGroup
@@ -738,7 +732,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}/user-group-relations:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getUserGroupRelationsUserGroups
    *        summary: /user-groups/{id}/user-group-relations
    *        description: Get the user group relations for the userGroup
@@ -785,7 +779,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /user-groups/{id}/pages:
    *      get:
-   *        tags: [UserGroup]
+   *        tags: [UserGroups]
    *        operationId: getPagesUserGroups
    *        summary: /user-groups/{id}/pages
    *        description: Get closed pages for the userGroup

+ 14 - 20
apps/app/src/server/routes/apiv3/users.js

@@ -30,12 +30,6 @@ const PAGE_ITEMS = 50;
 
 const validator = {};
 
-/**
- * @swagger
- *  tags:
- *    name: Users
- */
-
 /**
  * @swagger
  *
@@ -408,7 +402,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/invite:
    *      post:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: inviteUser
    *        summary: /users/invite
    *        description: Create new users and send Emails
@@ -476,7 +470,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/{id}/grant-admin:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: grantAdminUser
    *        summary: /users/{id}/grant-admin
    *        description: Grant user admin
@@ -523,7 +517,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/{id}/revoke-admin:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: revokeAdminUser
    *        summary: /users/{id}/revoke-admin
    *        description: Revoke user admin
@@ -570,7 +564,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/{id}/grant-read-only:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: ReadOnly
    *        summary: /users/{id}/grant-read-only
    *        description: Grant user read only access
@@ -622,7 +616,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/{id}/revoke-read-only:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: revokeReadOnly
    *        summary: /users/{id}/revoke-read-only
    *        description: Revoke user read only access
@@ -674,7 +668,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/{id}/activate:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: activateUser
    *        summary: /users/{id}/activate
    *        description: Activate user
@@ -728,7 +722,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/{id}/deactivate:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: deactivateUser
    *        summary: /users/{id}/deactivate
    *        description: Deactivate user
@@ -775,7 +769,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/{id}/remove:
    *      delete:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: removeUser
    *        summary: /users/{id}/remove
    *        description: Delete user
@@ -835,7 +829,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/external-accounts:
    *      get:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: listExternalAccountsUsers
    *        summary: /users/external-accounts
    *        description: Get external-account
@@ -868,7 +862,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/external-accounts/{id}/remove:
    *      delete:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: removeExternalAccountUser
    *        summary: /users/external-accounts/{id}/remove
    *        description: Delete ExternalAccount
@@ -911,7 +905,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/update.imageUrlCache:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: update.imageUrlCache
    *        summary: /users/update.imageUrlCache
    *        description: update imageUrlCache
@@ -963,7 +957,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/reset-password:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: resetPassword
    *        summary: /users/reset-password
    *        description: update imageUrlCache
@@ -1004,7 +998,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/reset-password-email:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: resetPasswordEmail
    *        summary: /users/reset-password-email
    *        description: send new password email
@@ -1051,7 +1045,7 @@ module.exports = (crowi) => {
    *  paths:
    *    /users/send-invitation-email:
    *      put:
-   *        tags: [Users]
+   *        tags: [Users Management]
    *        operationId: sendInvitationEmail
    *        summary: /users/send-invitation-email
    *        description: send invitation email

+ 4 - 3
apps/app/src/server/routes/attachment/api.js

@@ -253,9 +253,10 @@ export const routesFactory = (crowi) => {
 
     let attachment;
     try {
-      req.user.deleteImage();
+      const user = await User.findById(req.user._id);
+      await user.deleteImage();
       attachment = await attachmentService.createAttachment(file, req.user, null, AttachmentType.PROFILE_IMAGE);
-      await req.user.updateImage(attachment);
+      await user.updateImage(attachment);
     }
     catch (err) {
       logger.error(err);
@@ -274,7 +275,7 @@ export const routesFactory = (crowi) => {
    *
    *    /attachments.remove:
    *      post:
-   *        tags: [Attachments, CrowiCompatibles]
+   *        tags: [Attachments]
    *        operationId: removeAttachment
    *        summary: /attachments.remove
    *        description: Remove attachment

+ 4 - 4
apps/app/src/server/routes/comment.js

@@ -79,7 +79,7 @@ module.exports = function(crowi, app) {
    *
    *    /comments.get:
    *      get:
-   *        tags: [Comments, CrowiCompatibles]
+   *        tags: [Comments]
    *        operationId: getComments
    *        summary: /comments.get
    *        description: Get comments of the page of the revision
@@ -176,7 +176,7 @@ module.exports = function(crowi, app) {
    *
    *    /comments.add:
    *      post:
-   *        tags: [Comments, CrowiCompatibles]
+   *        tags: [Comments]
    *        operationId: addComment
    *        summary: /comments.add
    *        description: Post comment for the page
@@ -319,7 +319,7 @@ module.exports = function(crowi, app) {
    *
    *    /comments.update:
    *      post:
-   *        tags: [Comments, CrowiCompatibles]
+   *        tags: [Comments]
    *        operationId: updateComment
    *        summary: /comments.update
    *        description: Update comment dody
@@ -422,7 +422,7 @@ module.exports = function(crowi, app) {
    *
    *    /comments.remove:
    *      post:
-   *        tags: [Comments, CrowiCompatibles]
+   *        tags: [Comments]
    *        operationId: removeComment
    *        summary: /comments.remove
    *        description: Remove specified comment

+ 0 - 2
apps/app/src/server/routes/index.js

@@ -57,8 +57,6 @@ module.exports = function(crowi, app) {
 
   const [apiV3Router, apiV3AdminRouter, apiV3AuthRouter] = require('./apiv3')(crowi, app);
 
-  app.use('/api-docs', require('./apiv3/docs')(crowi, app));
-
   // Rate limiter
   app.use(rateLimiterFactory());
 

+ 19 - 21
apps/app/src/server/routes/login-passport.js

@@ -1,5 +1,4 @@
 import { ErrorV3 } from '@growi/core/dist/models';
-import next from 'next';
 
 import { SupportedAction } from '~/interfaces/activity';
 import { ExternalAccountLoginError } from '~/models/vo/external-account-login-error';
@@ -11,7 +10,6 @@ import { externalAccountService } from '../service/external-account';
 /* eslint-disable no-use-before-define */
 
 module.exports = function(crowi, app) {
-  const debug = require('debug')('growi:routes:login-passport');
   const logger = loggerFactory('growi:routes:login-passport');
   const passport = require('passport');
   const passportService = crowi.passportService;
@@ -58,7 +56,7 @@ module.exports = function(crowi, app) {
     user.updateLastLoginAt(new Date(), (err, userData) => {
       if (err) {
         logger.error(`updateLastLoginAt dumps error: ${err}`);
-        debug(`updateLastLoginAt dumps error: ${err}`);
+        logger.debug(`updateLastLoginAt dumps error: ${err}`);
       }
     });
 
@@ -164,7 +162,7 @@ module.exports = function(crowi, app) {
    */
   const loginWithLdap = async(req, res, next) => {
     if (!passportService.isLdapStrategySetup) {
-      debug('LdapStrategy has not been set up');
+      logger.debug('LdapStrategy has not been set up');
       return next();
     }
 
@@ -180,7 +178,7 @@ module.exports = function(crowi, app) {
       ldapAccountInfo = await promisifiedPassportAuthentication(strategyName, req, res);
     }
     catch (err) {
-      debug(err.message);
+      logger.debug(err.message);
       return next(err);
     }
 
@@ -227,7 +225,7 @@ module.exports = function(crowi, app) {
     // login
     await req.logIn(user, (err) => {
       if (err) {
-        debug(err.message);
+        logger.debug(err.message);
         return next(err);
       }
 
@@ -243,7 +241,7 @@ module.exports = function(crowi, app) {
    */
   const testLdapCredentials = (req, res) => {
     if (!passportService.isLdapStrategySetup) {
-      debug('LdapStrategy has not been set up');
+      logger.debug('LdapStrategy has not been set up');
       return res.json(ApiResponse.success({
         status: 'warning',
         message: req.t('message.strategy_has_not_been_set_up', { strategy: 'LdapStrategy' }),
@@ -299,7 +297,7 @@ module.exports = function(crowi, app) {
    */
   const loginWithLocal = (req, res, next) => {
     if (!passportService.isLocalStrategySetup) {
-      debug('LocalStrategy has not been set up');
+      logger.debug('LocalStrategy has not been set up');
       return next();
     }
 
@@ -308,9 +306,9 @@ module.exports = function(crowi, app) {
     }
 
     passport.authenticate('local', (err, user, info) => {
-      debug('--- authenticate with LocalStrategy ---');
-      debug('user', user);
-      debug('info', info);
+      logger.debug('--- authenticate with LocalStrategy ---');
+      logger.debug('user', user);
+      logger.debug('info', info);
 
       if (err) { // DB Error
         logger.error('Database Server Error: ', err);
@@ -321,7 +319,7 @@ module.exports = function(crowi, app) {
       }
       req.logIn(user, (err) => {
         if (err) {
-          debug(err.message);
+          logger.debug(err.message);
           return next(err);
         }
 
@@ -332,7 +330,7 @@ module.exports = function(crowi, app) {
 
   const loginWithGoogle = function(req, res, next) {
     if (!passportService.isGoogleStrategySetup) {
-      debug('GoogleStrategy has not been set up');
+      logger.debug('GoogleStrategy has not been set up');
       const error = new ExternalAccountLoginError('message.strategy_has_not_been_set_up', { strategy: 'GoogleStrategy' });
       return next(error);
     }
@@ -394,7 +392,7 @@ module.exports = function(crowi, app) {
 
     // login
     req.logIn(user, async(err) => {
-      if (err) { debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
+      if (err) { logger.debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
 
       return loginSuccessHandler(req, res, user, SupportedAction.ACTION_USER_LOGIN_WITH_GOOGLE, true);
     });
@@ -402,7 +400,7 @@ module.exports = function(crowi, app) {
 
   const loginWithGitHub = function(req, res, next) {
     if (!passportService.isGitHubStrategySetup) {
-      debug('GitHubStrategy has not been set up');
+      logger.debug('GitHubStrategy has not been set up');
       const error = new ExternalAccountLoginError('message.strategy_has_not_been_set_up', { strategy: 'GitHubStrategy' });
       return next(error);
     }
@@ -437,7 +435,7 @@ module.exports = function(crowi, app) {
 
     // login
     req.logIn(user, async(err) => {
-      if (err) { debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
+      if (err) { logger.debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
 
       return loginSuccessHandler(req, res, user, SupportedAction.ACTION_USER_LOGIN_WITH_GITHUB, true);
     });
@@ -445,7 +443,7 @@ module.exports = function(crowi, app) {
 
   const loginWithOidc = function(req, res, next) {
     if (!passportService.isOidcStrategySetup) {
-      debug('OidcStrategy has not been set up');
+      logger.debug('OidcStrategy has not been set up');
       const error = new ExternalAccountLoginError('message.strategy_has_not_been_set_up', { strategy: 'OidcStrategy' });
       return next(error);
     }
@@ -466,7 +464,7 @@ module.exports = function(crowi, app) {
       response = await promisifiedPassportAuthentication(strategyName, req, res);
     }
     catch (err) {
-      debug(err);
+      logger.debug(err);
       return next(new ExternalAccountLoginError(err.message));
     }
 
@@ -476,7 +474,7 @@ module.exports = function(crowi, app) {
       name: response[attrMapName],
       email: response[attrMapMail],
     };
-    debug('mapping response to userInfo', userInfo, response, attrMapId, attrMapUserName, attrMapMail);
+    logger.debug('mapping response to userInfo', userInfo, response, attrMapId, attrMapUserName, attrMapMail);
 
     const externalAccount = await externalAccountService.getOrCreateUser(userInfo, providerId);
     if (!externalAccount) {
@@ -486,7 +484,7 @@ module.exports = function(crowi, app) {
     // login
     const user = (await externalAccount.populate('user')).user;
     req.logIn(user, async(err) => {
-      if (err) { debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
+      if (err) { logger.debug(err.message); return next(new ExternalAccountLoginError(err.message)) }
 
       return loginSuccessHandler(req, res, user, SupportedAction.ACTION_USER_LOGIN_WITH_OIDC, true);
     });
@@ -494,7 +492,7 @@ module.exports = function(crowi, app) {
 
   const loginWithSaml = function(req, res, next) {
     if (!passportService.isSamlStrategySetup) {
-      debug('SamlStrategy has not been set up');
+      logger.debug('SamlStrategy has not been set up');
       const error = new ExternalAccountLoginError('message.strategy_has_not_been_set_up', { strategy: 'SamlStrategy' });
       return next(error);
     }

+ 1 - 2
apps/app/src/server/routes/login.js

@@ -6,7 +6,6 @@ import loggerFactory from '~/utils/logger';
 // because this file is a deprecated legacy of Crowi
 
 module.exports = function(crowi, app) {
-  const debug = require('debug')('growi:routes:login');
   const logger = loggerFactory('growi:routes:login');
   const path = require('path');
   const User = crowi.model('User');
@@ -161,7 +160,7 @@ module.exports = function(crowi, app) {
         }
       }
       if (errors.length > 0) {
-        debug('isError user register error', errOn);
+        logger.debug('isError user register error', errOn);
         return res.apiv3Err(errors, 400);
       }
 

+ 5 - 71
apps/app/src/server/routes/page.js

@@ -19,71 +19,6 @@ import UpdatePost from '../models/update-post';
  *
  *  components:
  *    schemas:
- *      Page:
- *        description: Page
- *        type: object
- *        properties:
- *          _id:
- *            type: string
- *            description: page ID
- *            example: 5e07345972560e001761fa63
- *          __v:
- *            type: number
- *            description: DB record version
- *            example: 0
- *          commentCount:
- *            type: number
- *            description: count of comments
- *            example: 3
- *          createdAt:
- *            type: string
- *            description: date created at
- *            example: 2010-01-01T00:00:00.000Z
- *          creator:
- *            $ref: '#/components/schemas/User'
- *          extended:
- *            type: object
- *            description: extend data
- *            example: {}
- *          grant:
- *            type: number
- *            description: grant
- *            example: 1
- *          grantedUsers:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: ["5ae5fccfc5577b0004dbd8ab"]
- *          lastUpdateUser:
- *            $ref: '#/components/schemas/User'
- *          liker:
- *            type: array
- *            description: granted users
- *            items:
- *              type: string
- *              description: user ID
- *            example: []
- *          path:
- *            type: string
- *            description: page path
- *            example: /
- *          revision:
- *            $ref: '#/components/schemas/Revision'
- *          status:
- *            type: string
- *            description: status
- *            enum:
- *              - 'wip'
- *              - 'published'
- *              - 'deleted'
- *              - 'deprecated'
- *            example: published
- *          updatedAt:
- *            type: string
- *            description: date updated at
- *            example: 2010-01-01T00:00:00.000Z
  *
  *      UpdatePost:
  *        description: UpdatePost
@@ -132,7 +67,6 @@ import UpdatePost from '../models/update-post';
  * @type { (crowi: import('../crowi').default, app) => any }
  */
 module.exports = function(crowi, app) {
-  const debug = require('debug')('growi:routes:page');
   const logger = loggerFactory('growi:routes:page');
 
   const { pagePathUtils } = require('@growi/core/dist/utils');
@@ -257,7 +191,7 @@ module.exports = function(crowi, app) {
    *
    *    /pages.updatePost:
    *      get:
-   *        tags: [Pages, CrowiCompatibles]
+   *        tags: [Pages]
    *        operationId: getUpdatePostPage
    *        summary: /pages.updatePost
    *        description: Get UpdatePost setting list
@@ -302,12 +236,12 @@ module.exports = function(crowi, app) {
         data = data.map((e) => {
           return e.channel;
         });
-        debug('Found updatePost data', data);
+        logger.debug('Found updatePost data', data);
         const result = { updatePost: data };
         return res.json(ApiResponse.success(result));
       })
       .catch((err) => {
-        debug('Error occured while get setting', err);
+        logger.debug('Error occured while get setting', err);
         return res.json(ApiResponse.error({}));
       });
   };
@@ -355,7 +289,7 @@ module.exports = function(crowi, app) {
 
     const creatorId = await crowi.pageService.getCreatorIdForCanDelete(page);
 
-    debug('Delete page', page._id, page.path);
+    logger.debug('Delete page', page._id, page.path);
 
     try {
       if (isCompletely) {
@@ -408,7 +342,7 @@ module.exports = function(crowi, app) {
       return res.json(ApiResponse.error('Failed to delete page.', err.message));
     }
 
-    debug('Page deleted', page.path);
+    logger.debug('Page deleted', page.path);
     const result = {};
     result.path = page.path;
     result.isRecursively = isRecursively;

+ 1 - 2
apps/app/src/server/routes/search.ts

@@ -35,7 +35,6 @@ const logger = loggerFactory('growi:routes:search');
  *
  */
 module.exports = function(crowi, app) {
-  // var debug = require('debug')('growi:routes:search')
   const ApiResponse = require('../util/apiResponse');
   const ApiPaginate = require('../util/apiPaginate');
 
@@ -47,7 +46,7 @@ module.exports = function(crowi, app) {
    *
    *   /search:
    *     get:
-   *       tags: [Search, CrowiCompatibles]
+   *       tags: [Search]
    *       operationId: searchPages
    *       summary: /search
    *       description: Search pages

+ 7 - 8
apps/app/src/server/service/page/index.ts

@@ -2,15 +2,16 @@ import type EventEmitter from 'events';
 import pathlib from 'path';
 import { Readable, Writable } from 'stream';
 
+import {
+  PageStatus, YDocStatus, getIdForRef,
+  getIdStringForRef,
+} from '@growi/core';
+import { PageGrant } from '@growi/core/dist/interfaces';
 import type {
   Ref, HasObjectId, IUserHasId, IUser,
   IPage, IPageInfo, IPageInfoAll, IPageInfoForEntity, IGrantedGroup, IRevisionHasId,
   IDataWithMeta,
-} from '@growi/core';
-import {
-  PageGrant, PageStatus, YDocStatus, getIdForRef,
-  getIdStringForRef,
-} from '@growi/core';
+} from '@growi/core/dist/interfaces';
 import {
   pagePathUtils, pathUtils,
 } from '@growi/core/dist/utils';
@@ -76,8 +77,6 @@ import { shouldUseV4Process } from './should-use-v4-process';
 export * from './page-service';
 
 
-const debug = require('debug')('growi:services:page');
-
 const logger = loggerFactory('growi:services:page');
 const {
   isTrashPage, isTopPage, omitDuplicateAreaPageFromPages, getUsernameByPath,
@@ -2378,7 +2377,7 @@ class PageService implements IPageService {
 
     page.status = Page.STATUS_PUBLISHED;
     page.lastUpdateUser = user;
-    debug('Revert deleted the page', page, newPath);
+    logger.debug('Revert deleted the page', page, newPath);
     const updatedPage = await Page.findByIdAndUpdate(page._id, {
       $set: {
         path: newPath, status: Page.STATUS_PUBLISHED, lastUpdateUser: user._id, deleteUser: null, deletedAt: null,

Неке датотеке нису приказане због велике количине промена