Просмотр исходного кода

Merge branch 'master' into feat/154783-create-vector-store-file-for-each-divided-chunk

Shun Miyazawa 1 год назад
Родитель
Сommit
69b0873d7a
100 измененных файлов с 861 добавлено и 1219 удалено
  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. 7 7
      README.md
  18. 7 7
      README_JP.md
  19. 28 0
      apps/app/bin/swagger-jsdoc/definition-apiv1.js
  20. 93 0
      apps/app/bin/swagger-jsdoc/definition-apiv3.js
  21. 15 0
      apps/app/bin/swagger-jsdoc/generate-spec-apiv1.sh
  22. 14 0
      apps/app/bin/swagger-jsdoc/generate-spec-apiv3.sh
  23. 0 37
      apps/app/config/swagger-definition.js
  24. 27 70
      apps/app/docker/Dockerfile
  25. 1 0
      apps/app/docker/Dockerfile.dockerignore
  26. 1 3
      apps/app/docker/codebuild/buildspec.yml
  27. 4 0
      apps/app/nodemon.json
  28. 69 50
      apps/app/package.json
  29. 1 1
      apps/app/playwright.config.ts
  30. 4 4
      apps/app/src/client/components/Admin/AdminHome/SystemInfomationTable.tsx
  31. 0 1
      apps/app/src/client/components/Admin/UserGroup/UserGroupDeleteModal.tsx
  32. 12 11
      apps/app/src/client/components/Bookmarks/DragAndDropWrapper.tsx
  33. 1 1
      apps/app/src/client/components/InfiniteScroll.tsx
  34. 3 2
      apps/app/src/client/components/Me/InAppNotificationSettings.tsx
  35. 1 2
      apps/app/src/client/components/PageComment/CommentEditor.tsx
  36. 0 1
      apps/app/src/client/components/PageEditor/MarkdownTableDataImportForm.tsx
  37. 3 3
      apps/app/src/client/components/PageEditor/PageEditor.tsx
  38. 1 1
      apps/app/src/client/components/PageEditor/markdown-drawio-util-for-editor.ts
  39. 1 1
      apps/app/src/client/components/PageEditor/markdown-table-util-for-editor.ts
  40. 2 3
      apps/app/src/client/components/SearchPage/SearchResultList.tsx
  41. 3 3
      apps/app/src/client/services/AdminHomeContainer.js
  42. 1 1
      apps/app/src/features/callout/services/callout.ts
  43. 4 4
      apps/app/src/features/templates/server/routes/apiv3/index.ts
  44. 1 13
      apps/app/src/pages/admin/index.page.tsx
  45. 2 3
      apps/app/src/server/crowi/express-init.js
  46. 1 2
      apps/app/src/server/events/bookmark.js
  47. 10 7
      apps/app/src/server/events/page.js
  48. 3 2
      apps/app/src/server/middlewares/access-token-parser.js
  49. 10 7
      apps/app/src/server/models/bookmark.js
  50. 13 12
      apps/app/src/server/models/external-account.ts
  51. 5 6
      apps/app/src/server/models/obsolete-page.js
  52. 0 0
      apps/app/src/server/models/openapi/error-v3.ts
  53. 79 0
      apps/app/src/server/models/openapi/page.ts
  54. 39 0
      apps/app/src/server/models/openapi/revision.ts
  55. 2 2
      apps/app/src/server/models/revision.ts
  56. 10 7
      apps/app/src/server/models/user-group-relation.ts
  57. 4 10
      apps/app/src/server/routes/apiv3/admin-home.js
  58. 2 9
      apps/app/src/server/routes/apiv3/app-settings.js
  59. 1 8
      apps/app/src/server/routes/apiv3/attachment.js
  60. 0 6
      apps/app/src/server/routes/apiv3/bookmarks.js
  61. 0 6
      apps/app/src/server/routes/apiv3/customize-setting.js
  62. 0 6
      apps/app/src/server/routes/apiv3/export.js
  63. 0 6
      apps/app/src/server/routes/apiv3/healthcheck.ts
  64. 0 6
      apps/app/src/server/routes/apiv3/import.js
  65. 6 3
      apps/app/src/server/routes/apiv3/invited.ts
  66. 0 6
      apps/app/src/server/routes/apiv3/markdown-setting.js
  67. 1 7
      apps/app/src/server/routes/apiv3/mongo.js
  68. 0 6
      apps/app/src/server/routes/apiv3/notification-setting.js
  69. 4 83
      apps/app/src/server/routes/apiv3/page/index.ts
  70. 0 87
      apps/app/src/server/routes/apiv3/pages/index.js
  71. 11 17
      apps/app/src/server/routes/apiv3/personal-setting.js
  72. 0 45
      apps/app/src/server/routes/apiv3/revisions.js
  73. 3 8
      apps/app/src/server/routes/apiv3/search.js
  74. 26 32
      apps/app/src/server/routes/apiv3/security-settings/index.js
  75. 1 7
      apps/app/src/server/routes/apiv3/share-links.js
  76. 0 6
      apps/app/src/server/routes/apiv3/slack-integration-legacy-settings.js
  77. 13 19
      apps/app/src/server/routes/apiv3/slack-integration-settings.js
  78. 0 5
      apps/app/src/server/routes/apiv3/statistics.js
  79. 1 7
      apps/app/src/server/routes/apiv3/user-group-relation.js
  80. 14 20
      apps/app/src/server/routes/apiv3/user-group.js
  81. 14 20
      apps/app/src/server/routes/apiv3/users.js
  82. 4 3
      apps/app/src/server/routes/attachment/api.js
  83. 4 4
      apps/app/src/server/routes/comment.js
  84. 19 21
      apps/app/src/server/routes/login-passport.js
  85. 1 2
      apps/app/src/server/routes/login.js
  86. 5 71
      apps/app/src/server/routes/page.js
  87. 1 2
      apps/app/src/server/routes/search.ts
  88. 7 8
      apps/app/src/server/service/page/index.ts
  89. 7 4
      apps/app/src/server/util/slack.js
  90. 1 2
      apps/app/src/services/renderer/remark-plugins/pukiwiki-like-linker.ts
  91. 2 0
      apps/app/src/services/renderer/renderer.tsx
  92. 1 1
      apps/app/src/styles/vendor.scss
  93. 0 12
      apps/app/src/utils/logger/alias-for-debug.ts
  94. 1 1
      apps/app/src/utils/logger/index.ts
  95. 42 35
      apps/app/src/utils/next.config.utils.js
  96. 0 1
      apps/app/tsconfig.build.client.json
  97. 0 1
      apps/app/tsconfig.build.server.json
  98. 0 1
      apps/app/tsconfig.json
  99. 18 2
      apps/app/turbo.json
  100. 2 2
      apps/slackbot-proxy/docker-compose.dev.yml

+ 5 - 2
.changeset/config.json

@@ -17,8 +17,11 @@
     "@growi/custom-icons",
     "@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": [

+ 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 - 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",

+ 69 - 50
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,21 +71,19 @@
     "@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/jest": "^29.5.2",
-    "@types/ldapjs": "^2.2.5",
     "JSONStream": "^1.3.5",
     "archiver": "^5.3.0",
     "array.prototype.flatmap": "^1.2.2",
@@ -93,7 +94,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",
@@ -119,6 +120,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",
@@ -130,11 +132,17 @@
     "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 +168,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 +189,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,10 +234,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/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",
@@ -234,13 +249,19 @@
     "@testing-library/react": "^16.0.1",
     "@testing-library/user-event": "^14.5.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",
@@ -263,24 +284,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 - 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(),
         };
       }
     });

+ 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
  *
@@ -463,7 +456,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,
@@ -568,7 +561,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 - 6
apps/app/src/server/routes/apiv3/export.js

@@ -13,12 +13,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

@@ -46,91 +46,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
@@ -744,9 +665,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

+ 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,

+ 7 - 4
apps/app/src/server/util/slack.js

@@ -1,5 +1,8 @@
-const debug = require('debug')('growi:util:slack');
-const urljoin = require('url-join');
+import urljoin from 'url-join';
+
+import loggerFactory from '~/utils/logger';
+
+const logger = loggerFactory('growi:util:slack');
 
 /**
  * slack
@@ -39,7 +42,7 @@ const prepareAttachmentTextForUpdate = function(page, siteUrl, previousRevision)
   let diffText = '';
 
   diff.diffLines(previousRevision.body, page.revision.body).forEach((line) => {
-    debug('diff line', line);
+    logger.debug('diff line', line);
     const value = line.value.replace(/\r\n|\r/g, '\n'); // eslint-disable-line no-unused-vars
     if (line.added) {
       diffText += `${line.value} ... :lower_left_fountain_pen:`;
@@ -56,7 +59,7 @@ const prepareAttachmentTextForUpdate = function(page, siteUrl, previousRevision)
     }
   });
 
-  debug('diff is', diffText);
+  logger.debug('diff is', diffText);
 
   return diffText;
 };

+ 1 - 2
apps/app/src/services/renderer/remark-plugins/pukiwiki-like-linker.ts

@@ -1,7 +1,6 @@
-// 'mdast-util-wiki-link' and 'micromark-extension-wiki-link' are included in 'remark-wiki-link' package
 import { fromMarkdown, toMarkdown } from 'mdast-util-wiki-link';
 import { syntax } from 'micromark-extension-wiki-link';
-import { Plugin } from 'unified';
+import type { Plugin } from 'unified';
 
 
 type FromMarkdownExtension = {

+ 2 - 0
apps/app/src/services/renderer/renderer.tsx

@@ -5,6 +5,7 @@ import raw from 'rehype-raw';
 import sanitize from 'rehype-sanitize';
 import slug from 'rehype-slug';
 import breaks from 'remark-breaks';
+import remarkDirective from 'remark-directive';
 import emoji from 'remark-emoji';
 import remarkFrontmatter from 'remark-frontmatter';
 import gfm from 'remark-gfm';
@@ -99,6 +100,7 @@ export const generateCommonOptions = (pagePath: string|undefined): RendererOptio
       emoji,
       pukiwikiLikeLinker,
       growiDirective,
+      remarkDirective,
       remarkFrontmatter,
       codeBlock.remarkPlugin,
     ],

+ 1 - 1
apps/app/src/styles/vendor.scss

@@ -5,7 +5,7 @@
 @import 'react-bootstrap-typeahead/css/Typeahead';
 
 // SimpleBar
-@import 'simplebar/dist/simplebar.min.css';
+@import 'simplebar-react/dist/simplebar.min.css';
 @import './override-simplebar';
 
 // Handsontable

+ 0 - 12
apps/app/src/utils/logger/alias-for-debug.ts

@@ -1,12 +0,0 @@
-import generateBunyanLogger from './index';
-/**
- * return 'debug' method of bunyan logger
- *
- * This is supposed to be used as an replacement of "require('debug')"
- *
- * @param {string} name
- */
-module.exports = (name: string) => {
-  const bunyanLogger = generateBunyanLogger(name);
-  return bunyanLogger.debug.bind(bunyanLogger);
-};

+ 1 - 1
apps/app/src/utils/logger/index.ts

@@ -1,4 +1,4 @@
-import Logger from 'bunyan';
+import type Logger from 'bunyan';
 import { createLogger, type UniversalBunyanConfig } from 'universal-bunyan';
 
 import configForDev from '^/config/logger/config.dev';

+ 42 - 35
apps/app/src/utils/next.config.utils.js

@@ -3,7 +3,10 @@
 const fs = require('fs');
 const path = require('path');
 
-const nodeModulesPath = path.resolve(__dirname, '../../../../node_modules');
+const nodeModulesPaths = [
+  path.resolve(__dirname, '../../node_modules'),
+  path.resolve(__dirname, '../../../../node_modules'),
+];
 
 /**
  * @typedef { { ignorePackageNames: string[] } } Opts
@@ -19,26 +22,28 @@ exports.listScopedPackages = (scopes, opts = defaultOpts) => {
   /** @type {string[]} */
   const scopedPackages = [];
 
-  fs.readdirSync(nodeModulesPath)
-    .filter(name => scopes.includes(name))
-    .forEach((scope) => {
-      fs.readdirSync(path.resolve(nodeModulesPath, scope))
-        .filter(name => !name.startsWith('.'))
-        .forEach((folderName) => {
-          const packageJsonPath = path.resolve(
-            nodeModulesPath,
-            scope,
-            folderName,
-            'package.json',
-          );
-          if (fs.existsSync(packageJsonPath)) {
-            const { name } = require(packageJsonPath);
-            if (!opts.ignorePackageNames.includes(name)) {
-              scopedPackages.push(name);
+  nodeModulesPaths.forEach((nodeModulesPath) => {
+    fs.readdirSync(nodeModulesPath)
+      .filter(name => scopes.includes(name))
+      .forEach((scope) => {
+        fs.readdirSync(path.resolve(nodeModulesPath, scope))
+          .filter(name => !name.startsWith('.'))
+          .forEach((folderName) => {
+            const packageJsonPath = path.resolve(
+              nodeModulesPath,
+              scope,
+              folderName,
+              'package.json',
+            );
+            if (fs.existsSync(packageJsonPath)) {
+              const { name } = require(packageJsonPath);
+              if (!opts.ignorePackageNames.includes(name)) {
+                scopedPackages.push(name);
+              }
             }
-          }
-        });
-    });
+          });
+      });
+  });
 
   return scopedPackages;
 };
@@ -50,22 +55,24 @@ exports.listPrefixedPackages = (prefixes, opts = defaultOpts) => {
   /** @type {string[]} */
   const prefixedPackages = [];
 
-  fs.readdirSync(nodeModulesPath)
-    .filter(name => prefixes.some(prefix => name.startsWith(prefix)))
-    .filter(name => !name.startsWith('.'))
-    .forEach((folderName) => {
-      const packageJsonPath = path.resolve(
-        nodeModulesPath,
-        folderName,
-        'package.json',
-      );
-      if (fs.existsSync(packageJsonPath)) {
-        const { name } = require(packageJsonPath);
-        if (!opts.ignorePackageNames.includes(name)) {
-          prefixedPackages.push(name);
+  nodeModulesPaths.forEach((nodeModulesPath) => {
+    fs.readdirSync(nodeModulesPath)
+      .filter(name => prefixes.some(prefix => name.startsWith(prefix)))
+      .filter(name => !name.startsWith('.'))
+      .forEach((folderName) => {
+        const packageJsonPath = path.resolve(
+          nodeModulesPath,
+          folderName,
+          'package.json',
+        );
+        if (fs.existsSync(packageJsonPath)) {
+          const { name } = require(packageJsonPath);
+          if (!opts.ignorePackageNames.includes(name)) {
+            prefixedPackages.push(name);
+          }
         }
-      }
-    });
+      });
+  });
 
   return prefixedPackages;
 };

+ 0 - 1
apps/app/tsconfig.build.client.json

@@ -15,7 +15,6 @@
     "paths": {
       "~/*": ["./src/*"],
       "^/*": ["./*"],
-      "debug": ["./src/server/utils/logger/alias-for-debug"]
     },
     "plugins": [{"name": "next"}]
   }

+ 0 - 1
apps/app/tsconfig.build.server.json

@@ -14,7 +14,6 @@
     "paths": {
       "~/*": ["./src/*"],
       "^/*": ["./*"],
-      "debug": ["./src/utils/logger/alias-for-debug"]
     }
   },
   "exclude": [

+ 0 - 1
apps/app/tsconfig.json

@@ -12,7 +12,6 @@
     "paths": {
       "~/*": ["./src/*"],
       "^/*": ["./*"],
-      "debug": ["./src/server/utils/logger/alias-for-debug"]
     },
 
     /* TODO: remove below flags for strict checking */

+ 18 - 2
apps/app/turbo.json

@@ -64,9 +64,25 @@
       "outputLogs": "new-only"
     },
 
-    "version": {
+    "version:patch": {
       "cache": false,
-      "dependsOn": ["^version", "//#version"]
+      "dependsOn": ["^version:patch", "//#version:patch"]
+    },
+    "version:prerelease": {
+      "cache": false,
+      "dependsOn": ["^version:prerelease", "//#version:prerelease"]
+    },
+    "version:prepatch": {
+      "cache": false,
+      "dependsOn": ["^version:prepatch", "//#version:prepatch"]
+    },
+    "version:preminor": {
+      "cache": false,
+      "dependsOn": ["^version:preminor", "//#version:preminor"]
+    },
+    "version:premajor": {
+      "cache": false,
+      "dependsOn": ["^version:premajor", "//#version:premajor"]
     }
 
   }

+ 2 - 2
apps/slackbot-proxy/docker-compose.dev.yml

@@ -29,5 +29,5 @@ services:
 
 networks:
   default:
-    external:
-      name: growi_devcontainer_default
+    name: growi_devcontainer_default
+    external: true

Некоторые файлы не были показаны из-за большого количества измененных файлов