فهرست منبع

Merge remote-tracking branch 'origin/master' into fix/163491-multer-middreware-order

NaokiHigashi28 1 سال پیش
والد
کامیت
c1f6de75fc

+ 6 - 1
.devcontainer/app/initializeCommand.sh

@@ -1,4 +1,9 @@
 # prevent file not found error on docker compose up
 # prevent file not found error on docker compose up
 if [ ! -f ".devcontainer/compose.extend.yml" ]; then
 if [ ! -f ".devcontainer/compose.extend.yml" ]; then
-  touch .devcontainer/compose.extend.yml
+
+cat > ".devcontainer/compose.extend.yml" <<EOF
+services:
+  {}
+EOF
+
 fi
 fi

+ 6 - 1
.devcontainer/pdf-converter/initializeCommand.sh

@@ -1,4 +1,9 @@
 # prevent file not found error on docker compose up
 # prevent file not found error on docker compose up
 if [ ! -f ".devcontainer/compose.extend.yml" ]; then
 if [ ! -f ".devcontainer/compose.extend.yml" ]; then
-  touch .devcontainer/compose.extend.yml
+
+cat > ".devcontainer/compose.extend.yml" <<EOF
+services:
+  {}
+EOF
+
 fi
 fi

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

@@ -28,6 +28,11 @@ module.exports = {
         in: 'cookie',
         in: 'cookie',
         name: 'connect.sid',
         name: 'connect.sid',
       },
       },
+      transferHeaderAuth: {
+        type: 'apiKey',
+        in: 'header',
+        name: 'x-growi-transfer-key',
+      },
     },
     },
   },
   },
   'x-tagGroups': [
   'x-tagGroups': [
@@ -71,6 +76,7 @@ module.exports = {
         'CustomizeSetting',
         'CustomizeSetting',
         'Import',
         'Import',
         'Export',
         'Export',
+        'GROWI to GROWI Transfer',
         'MongoDB',
         'MongoDB',
         'NotificationSetting',
         'NotificationSetting',
         'QuestionnaireSetting',
         'QuestionnaireSetting',

+ 1 - 1
apps/app/package.json

@@ -167,7 +167,7 @@
     "multer": "~1.4.0",
     "multer": "~1.4.0",
     "multer-autoreap": "^1.0.3",
     "multer-autoreap": "^1.0.3",
     "mustache": "^4.2.0",
     "mustache": "^4.2.0",
-    "next": "^14.2.21",
+    "next": "^14.2.25",
     "next-dynamic-loading-props": "^0.1.1",
     "next-dynamic-loading-props": "^0.1.1",
     "next-i18next": "^15.3.1",
     "next-i18next": "^15.3.1",
     "next-superjson": "^0.0.4",
     "next-superjson": "^0.0.4",

+ 22 - 19
apps/app/src/server/routes/apiv3/bookmarks.js

@@ -40,10 +40,17 @@ const router = express.Router();
  *            description: date created at
  *            description: date created at
  *            example: 2010-01-01T00:00:00.000Z
  *            example: 2010-01-01T00:00:00.000Z
  *          page:
  *          page:
- *            $ref: '#/components/schemas/Page/properties/_id'
+ *            $ref: '#/components/schemas/Page'
  *          user:
  *          user:
  *            $ref: '#/components/schemas/User/properties/_id'
  *            $ref: '#/components/schemas/User/properties/_id'
- *
+ *      Bookmarks:
+ *        description: User Root Bookmarks
+ *        type: object
+ *        properties:
+ *          userRootBookmarks:
+ *            type: array
+ *            items:
+ *              $ref: '#/components/schemas/Bookmark'
  *      BookmarkParams:
  *      BookmarkParams:
  *        description: BookmarkParams
  *        description: BookmarkParams
  *        type: object
  *        type: object
@@ -66,6 +73,14 @@ const router = express.Router();
  *          isBookmarked:
  *          isBookmarked:
  *            type: boolean
  *            type: boolean
  *            description: Whether the request user bookmarked (will be returned if the user is included in the request)
  *            description: Whether the request user bookmarked (will be returned if the user is included in the request)
+ *          pageId:
+ *            type: string
+ *            description: page ID
+ *            example: 5e07345972560e001761fa63
+ *          bookmarkedUsers:
+ *            type: array
+ *            items:
+ *              $ref: '#/components/schemas/User'
  */
  */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 module.exports = (crowi) => {
 module.exports = (crowi) => {
@@ -165,28 +180,13 @@ module.exports = (crowi) => {
    *            description: user id
    *            description: user id
    *            schema:
    *            schema:
    *              type: string
    *              type: string
-   *          - name: page
-   *            in: query
-   *            description: selected page number
-   *            schema:
-   *              type: number
-   *          - name: limit
-   *            in: query
-   *            description: page item limit
-   *            schema:
-   *              type: number
-   *          - name: offset
-   *            in: query
-   *            description: page item offset
-   *            schema:
-   *              type: number
    *        responses:
    *        responses:
    *          200:
    *          200:
    *            description: Succeeded to get my bookmarked status.
    *            description: Succeeded to get my bookmarked status.
    *            content:
    *            content:
    *              application/json:
    *              application/json:
    *                schema:
    *                schema:
-   *                  $ref: '#/components/schemas/Bookmark'
+   *                  $ref: '#/components/schemas/Bookmarks'
    */
    */
   validator.userBookmarkList = [
   validator.userBookmarkList = [
     param('userId').isMongoId().withMessage('userId is required'),
     param('userId').isMongoId().withMessage('userId is required'),
@@ -244,7 +244,10 @@ module.exports = (crowi) => {
    *            content:
    *            content:
    *              application/json:
    *              application/json:
    *                schema:
    *                schema:
-   *                  $ref: '#/components/schemas/Bookmark'
+   *                  type: object
+   *                  properties:
+   *                    bookmark:
+   *                      $ref: '#/components/schemas/Bookmark'
    */
    */
   router.put('/', accessTokenParser, loginRequiredStrictly, addActivity, validator.bookmarks, apiV3FormValidator, async(req, res) => {
   router.put('/', accessTokenParser, loginRequiredStrictly, addActivity, validator.bookmarks, apiV3FormValidator, async(req, res) => {
     const { pageId, bool } = req.body;
     const { pageId, bool } = req.body;

+ 235 - 1
apps/app/src/server/routes/apiv3/g2g-transfer.ts

@@ -37,6 +37,43 @@ const validator = {
   ],
   ],
 };
 };
 
 
+/**
+ * @swagger
+ *
+ *  components:
+ *    schemas:
+ *      GrowiInfo:
+ *        type: object
+ *        properties:
+ *           version:
+ *             type: string
+ *             description: The version of the GROWI
+ *           userUpperLimit:
+ *             type: number
+ *             description: The upper limit of the number of users
+ *           fileUploadDisabled:
+ *             type: boolean
+ *           fileUploadTotalLimit:
+ *             type: number
+ *             description: The total limit of the file upload size
+ *           attachmentInfo:
+ *             type: object
+ *             properties:
+ *               type:
+ *                 type: string
+ *               writable:
+ *                 type: boolean
+ *               bucket:
+ *                 type: string
+ *               customEndpoint:
+ *                 type: string
+ *               uploadNamespace:
+ *                 type: string
+ *               accountName:
+ *                 type: string
+ *               containerName:
+ *                 type: string
+*/
 /*
 /*
  * Routes
  * Routes
  */
  */
@@ -131,13 +168,86 @@ module.exports = (crowi: Crowi): Router => {
   const receiveRouter = express.Router();
   const receiveRouter = express.Router();
   const pushRouter = express.Router();
   const pushRouter = express.Router();
 
 
+  /**
+   * @swagger
+   *
+   *  /g2g-transfer/files:
+   *    get:
+   *      summary: /g2g-transfer/files
+   *      tags: [GROWI to GROWI Transfer]
+   *      security:
+   *        - transferHeaderAuth: []
+   *      responses:
+   *        '200':
+   *          description: Successfully got the list of files
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   *                properties:
+   *                  files:
+   *                    type: array
+   *                    items:
+   *                      type: object
+   *                      properties:
+   *                        name:
+   *                          type: string
+   *                          description: The name of the file
+   *                        size:
+   *                          type: number
+   *                          description: The size of the file
+   */
   // eslint-disable-next-line max-len
   // eslint-disable-next-line max-len
   receiveRouter.get('/files', validateTransferKey, async(req: Request, res: ApiV3Response) => {
   receiveRouter.get('/files', validateTransferKey, async(req: Request, res: ApiV3Response) => {
     const files = await crowi.fileUploadService.listFiles();
     const files = await crowi.fileUploadService.listFiles();
     return res.apiv3({ files });
     return res.apiv3({ files });
   });
   });
 
 
-  // Auto import
+  /**
+   * @swagger
+   *
+   *  /g2g-transfer:
+   *    post:
+   *      summary: /g2g-transfer
+   *      tags: [GROWI to GROWI Transfer]
+   *      security:
+   *        - transferHeaderAuth: []
+   *      requestBody:
+   *        required: true
+   *        content:
+   *          multipart/form-data:
+   *            schema:
+   *              type: object
+   *              properties:
+   *                file:
+   *                  format: binary
+   *                  description: The zip file of the data to be transferred
+   *                collections:
+   *                  type: array
+   *                  description: The list of MongoDB collections to be transferred
+   *                  items:
+   *                    type: string
+   *                optionsMap:
+   *                  type: object
+   *                  description: The map of options for each collection
+   *                operatorUserId:
+   *                  type: string
+   *                  description: The ID of the operator user
+   *                uploadConfigs:
+   *                  type: object
+   *                  description: The map of upload configurations
+   *      responses:
+   *        '200':
+   *          description: Successfully started to receive transfer data
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   *                properties:
+   *                  message:
+   *                    type: string
+   *                    description: The message of the result
+   */
   // eslint-disable-next-line max-len
   // eslint-disable-next-line max-len
   receiveRouter.post('/', validateTransferKey, uploads.single('transferDataZipFile'), async(req: Request & { file: any; }, res: ApiV3Response) => {
   receiveRouter.post('/', validateTransferKey, uploads.single('transferDataZipFile'), async(req: Request & { file: any; }, res: ApiV3Response) => {
     const { file } = req;
     const { file } = req;
@@ -222,6 +332,40 @@ module.exports = (crowi: Crowi): Router => {
     return res.apiv3({ message: 'Successfully started to receive transfer data.' });
     return res.apiv3({ message: 'Successfully started to receive transfer data.' });
   });
   });
 
 
+  /**
+   * @swagger
+   *
+   *  /g2g-transfer/attachment:
+   *    post:
+   *      summary: /g2g-transfer/attachment
+   *      tags: [GROWI to GROWI Transfer]
+   *      security:
+   *        - transferHeaderAuth: []
+   *      requestBody:
+   *        required: true
+   *        content:
+   *          multipart/form-data:
+   *            schema:
+   *              type: object
+   *              properties:
+   *                file:
+   *                  format: binary
+   *                  description: The zip file of the data to be transferred
+   *                attachmentMetadata:
+   *                  type: object
+   *                  description: Metadata of the attachment
+   *      responses:
+   *        '200':
+   *          description:
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   *                properties:
+   *                  message:
+   *                    type: string
+   *                    description: The message of the result
+   */
   // This endpoint uses multer's MemoryStorage since the received data should be persisted directly on attachment storage.
   // This endpoint uses multer's MemoryStorage since the received data should be persisted directly on attachment storage.
   receiveRouter.post('/attachment', validateTransferKey, uploadsForAttachment.single('content'),
   receiveRouter.post('/attachment', validateTransferKey, uploadsForAttachment.single('content'),
     async(req: Request & { file: any; }, res: ApiV3Response) => {
     async(req: Request & { file: any; }, res: ApiV3Response) => {
@@ -251,6 +395,26 @@ module.exports = (crowi: Crowi): Router => {
       return res.apiv3({ message: 'Successfully imported attached file.' });
       return res.apiv3({ message: 'Successfully imported attached file.' });
     });
     });
 
 
+  /**
+   * @swagger
+   *
+   *  /g2g-transfer/growi-info:
+   *    get:
+   *      summary: /g2g-transfer/growi-info
+   *      tags: [GROWI to GROWI Transfer]
+   *      security:
+   *        - transferHeaderAuth: []
+   *      responses:
+   *        '200':
+   *          description:
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   *                properties:
+   *                  growiInfo:
+   *                    $ref: '#/components/schemas/GrowiInfo'
+   */
   receiveRouter.get('/growi-info', validateTransferKey, async(req: Request, res: ApiV3Response) => {
   receiveRouter.get('/growi-info', validateTransferKey, async(req: Request, res: ApiV3Response) => {
     let growiInfo: IDataGROWIInfo;
     let growiInfo: IDataGROWIInfo;
     try {
     try {
@@ -269,6 +433,37 @@ module.exports = (crowi: Crowi): Router => {
     return res.apiv3({ growiInfo });
     return res.apiv3({ growiInfo });
   });
   });
 
 
+  /**
+   * @swagger
+   *
+   *  /g2g-transfer/generate-key:
+   *    post:
+   *      summary: /g2g-transfer/generate-key
+   *      tags: [GROWI to GROWI Transfer]
+   *      security:
+   *        - api_key: []
+   *      requestBody:
+   *        required: true
+   *        content:
+   *          application/json:
+   *            schema:
+   *              type: object
+   *              properties:
+   *                appSiteUrl:
+   *                  type: string
+   *                  description: The URL of the GROWI
+   *      responses:
+   *        '200':
+   *          description: Successfully generated transfer key
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   *                properties:
+   *                  transferKey:
+   *                    type: string
+   *                    description: The transfer key
+   */
   // eslint-disable-next-line max-len
   // eslint-disable-next-line max-len
   receiveRouter.post('/generate-key', accessTokenParser, adminRequiredIfInstalled, appSiteUrlRequiredIfNotInstalled, async(req: Request, res: ApiV3Response) => {
   receiveRouter.post('/generate-key', accessTokenParser, adminRequiredIfInstalled, appSiteUrlRequiredIfNotInstalled, async(req: Request, res: ApiV3Response) => {
     const appSiteUrl = req.body.appSiteUrl ?? configManager.getConfig('app:siteUrl');
     const appSiteUrl = req.body.appSiteUrl ?? configManager.getConfig('app:siteUrl');
@@ -295,6 +490,45 @@ module.exports = (crowi: Crowi): Router => {
     return res.apiv3({ transferKey: transferKeyString });
     return res.apiv3({ transferKey: transferKeyString });
   });
   });
 
 
+  /**
+   * @swagger
+   *
+   *  /g2g-transfer/transfer:
+   *    post:
+   *      summary: /g2g-transfer/transfer
+   *      tags: [GROWI to GROWI Transfer]
+   *      security:
+   *        - api_key: []
+   *      requestBody:
+   *        required: true
+   *        content:
+   *          application/json:
+   *            schema:
+   *              type: object
+   *              properties:
+   *                transferKey:
+   *                  type: string
+   *                  description: The transfer key
+   *                collections:
+   *                  type: array
+   *                  description: The list of MongoDB collections to be transferred
+   *                  items:
+   *                    type: string
+   *                optionsMap:
+   *                  type: object
+   *                  description: The map of options for each collection
+   *      responses:
+   *        '200':
+   *          description: Successfully requested auto transfer
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   *                properties:
+   *                  message:
+   *                    type: string
+   *                    description: The message of the result
+   */
   // eslint-disable-next-line max-len
   // eslint-disable-next-line max-len
   pushRouter.post('/transfer', accessTokenParser, loginRequiredStrictly, adminRequired, validator.transfer, apiV3FormValidator, async(req: AuthorizedRequest, res: ApiV3Response) => {
   pushRouter.post('/transfer', accessTokenParser, loginRequiredStrictly, adminRequired, validator.transfer, apiV3FormValidator, async(req: AuthorizedRequest, res: ApiV3Response) => {
     const { transferKey, collections, optionsMap } = req.body;
     const { transferKey, collections, optionsMap } = req.body;

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

@@ -77,6 +77,7 @@ module.exports = (crowi) => {
    *  /healthcheck:
    *  /healthcheck:
    *    get:
    *    get:
    *      tags: [Healthcheck]
    *      tags: [Healthcheck]
+   *      security: []
    *      operationId: getHealthcheck
    *      operationId: getHealthcheck
    *      summary: /healthcheck
    *      summary: /healthcheck
    *      description: Check whether the server is healthy or not
    *      description: Check whether the server is healthy or not
@@ -116,8 +117,6 @@ module.exports = (crowi) => {
    *                    description: Errors
    *                    description: Errors
    *                    items:
    *                    items:
    *                      $ref: '#/components/schemas/ErrorV3'
    *                      $ref: '#/components/schemas/ErrorV3'
-   *                  info:
-   *                    $ref: '#/components/schemas/HealthcheckInfo'
    */
    */
   router.get('/', nocache(), async(req, res: ApiV3Response) => {
   router.get('/', nocache(), async(req, res: ApiV3Response) => {
     let checkServices = (() => {
     let checkServices = (() => {

+ 106 - 19
apps/app/src/server/routes/apiv3/import.js

@@ -31,17 +31,13 @@ const router = express.Router();
  *            description: Import mode
  *            description: Import mode
  *            type: string
  *            type: string
  *            enum: [insert, upsert, flushAndInsert]
  *            enum: [insert, upsert, flushAndInsert]
- */
-
-/**
- * @swagger
- *
- *  components:
- *    schemas:
  *      ImportStatus:
  *      ImportStatus:
  *        description: ImportStatus
  *        description: ImportStatus
  *        type: object
  *        type: object
  *        properties:
  *        properties:
+ *          isTheSameVersion:
+ *            type: boolean
+ *            description: whether the version of the uploaded data is the same as the current GROWI version
  *          zipFileStat:
  *          zipFileStat:
  *            type: object
  *            type: object
  *            description: the property object
  *            description: the property object
@@ -53,6 +49,77 @@ const router = express.Router();
  *          isImporting:
  *          isImporting:
  *            type: boolean
  *            type: boolean
  *            description: whether the current importing job exists or not
  *            description: whether the current importing job exists or not
+ *      FileImportResponse:
+ *        type: object
+ *        properties:
+ *          meta:
+ *            type: object
+ *            properties:
+ *              version:
+ *                type: string
+ *              url:
+ *                type: string
+ *              passwordSeed:
+ *                type: string
+ *              exportedAt:
+ *                type: string
+ *                format: date-time
+ *              envVars:
+ *                type: object
+ *                properties:
+ *                  ELASTICSEARCH_URI:
+ *                    type: string
+ *          fileName:
+ *            type: string
+ *          zipFilePath:
+ *            type: string
+ *          fileStat:
+ *            type: object
+ *            properties:
+ *              dev:
+ *                type: integer
+ *              mode:
+ *                type: integer
+ *              nlink:
+ *                type: integer
+ *              uid:
+ *                type: integer
+ *              gid:
+ *                type: integer
+ *              rdev:
+ *                type: integer
+ *              blksize:
+ *                type: integer
+ *              ino:
+ *                type: integer
+ *              size:
+ *                type: integer
+ *              blocks:
+ *                type: integer
+ *              atime:
+ *                type: string
+ *                format: date-time
+ *              mtime:
+ *                type: string
+ *                format: date-time
+ *              ctime:
+ *                type: string
+ *                format: date-time
+ *              birthtime:
+ *                type: string
+ *                format: date-time
+ *          innerFileStats:
+ *            type: array
+ *            items:
+ *              type: object
+ *              properties:
+ *                fileName:
+ *                  type: string
+ *                collectionName:
+ *                  type: string
+ *                size:
+ *                  type: integer
+ *                  nullable: true
  */
  */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 export default function route(crowi) {
 export default function route(crowi) {
@@ -101,6 +168,8 @@ export default function route(crowi) {
    *  /import:
    *  /import:
    *    get:
    *    get:
    *      tags: [Import]
    *      tags: [Import]
+   *      security:
+   *        - api_key: []
    *      operationId: getImportSettingsParams
    *      operationId: getImportSettingsParams
    *      summary: /import
    *      summary: /import
    *      description: Get import settings params
    *      description: Get import settings params
@@ -114,6 +183,19 @@ export default function route(crowi) {
    *                  importSettingsParams:
    *                  importSettingsParams:
    *                    type: object
    *                    type: object
    *                    description: import settings params
    *                    description: import settings params
+   *                    properties:
+   *                      esaTeamName:
+   *                        type: string
+   *                        description: the team name of esa.io
+   *                      esaAccessToken:
+   *                        type: string
+   *                        description: the access token of esa.io
+   *                      qiitaTeamName:
+   *                        type: string
+   *                        description: the team name of qiita.com
+   *                      qiitaAccessToken:
+   *                        type: string
+   *                        description: the access token of qiita.com
    */
    */
   router.get('/', accessTokenParser, loginRequired, adminRequired, async(req, res) => {
   router.get('/', accessTokenParser, loginRequired, adminRequired, async(req, res) => {
     try {
     try {
@@ -138,6 +220,8 @@ export default function route(crowi) {
    *  /import/status:
    *  /import/status:
    *    get:
    *    get:
    *      tags: [Import]
    *      tags: [Import]
+   *      security:
+   *        - api_key: []
    *      operationId: getImportStatus
    *      operationId: getImportStatus
    *      summary: /import/status
    *      summary: /import/status
    *      description: Get properties of stored zip files for import
    *      description: Get properties of stored zip files for import
@@ -167,6 +251,8 @@ export default function route(crowi) {
    *  /import:
    *  /import:
    *    post:
    *    post:
    *      tags: [Import]
    *      tags: [Import]
+   *      security:
+   *        - api_key: []
    *      operationId: executeImport
    *      operationId: executeImport
    *      summary: /import
    *      summary: /import
    *      description: import a collection from a zipped json
    *      description: import a collection from a zipped json
@@ -297,27 +383,26 @@ export default function route(crowi) {
    *  /import/upload:
    *  /import/upload:
    *    post:
    *    post:
    *      tags: [Import]
    *      tags: [Import]
+   *      security:
+   *        - api_key: []
    *      operationId: uploadImport
    *      operationId: uploadImport
    *      summary: /import/upload
    *      summary: /import/upload
    *      description: upload a zip file
    *      description: upload a zip file
+   *      requestBody:
+   *        content:
+   *          multipart/form-data:
+   *            schema:
+   *              type: object
+   *              properties:
+   *                file:
+   *                  format: binary
    *      responses:
    *      responses:
    *        200:
    *        200:
    *          description: the file is uploaded
    *          description: the file is uploaded
    *          content:
    *          content:
    *            application/json:
    *            application/json:
    *              schema:
    *              schema:
-   *                properties:
-   *                  meta:
-   *                    type: object
-   *                    description: the meta data of the uploaded file
-   *                  fileName:
-   *                    type: string
-   *                    description: the base name of the uploaded file
-   *                  fileStats:
-   *                    type: array
-   *                    items:
-   *                      type: object
-   *                      description: the property of each extracted file
+   *                $ref: '#/components/schemas/FileImportResponse'
    */
    */
   router.post('/upload', accessTokenParser, loginRequired, adminRequired, uploads.single('file'), addActivity, async(req, res) => {
   router.post('/upload', accessTokenParser, loginRequired, adminRequired, uploads.single('file'), addActivity, async(req, res) => {
     const { file } = req;
     const { file } = req;
@@ -354,6 +439,8 @@ export default function route(crowi) {
    *  /import/all:
    *  /import/all:
    *    delete:
    *    delete:
    *      tags: [Import]
    *      tags: [Import]
+   *      security:
+   *        - api_key: []
    *      operationId: deleteImportAll
    *      operationId: deleteImportAll
    *      summary: /import/all
    *      summary: /import/all
    *      description: Delete all zip files
    *      description: Delete all zip files

+ 173 - 0
apps/app/src/server/routes/apiv3/in-app-notification.ts

@@ -13,6 +13,78 @@ import type { ApiV3Response } from './interfaces/apiv3-response';
 
 
 const router = express.Router();
 const router = express.Router();
 
 
+/**
+ * @swagger
+ * components:
+ *   schemas:
+ *     InAppNotificationListResponse:
+ *       type: object
+ *       properties:
+ *         docs:
+ *           type: array
+ *           items:
+ *             $ref: '#/components/schemas/InAppNotificationDocument'
+ *         totalDocs:
+ *           type: integer
+ *           description: Total number of in app notification documents
+ *         offset:
+ *           type: integer
+ *           description: Offset value
+ *         limit:
+ *           type: integer
+ *           description: Limit per page
+ *         totalPages:
+ *           type: integer
+ *           description: Total pages available
+ *         page:
+ *           type: integer
+ *           description: Current page number
+ *         hasPrevPage:
+ *           type: boolean
+ *           description: Indicator for previous page
+ *         hasNextPage:
+ *           type: boolean
+ *           description: Indicator for next page
+ *         prevPage:
+ *           type: string
+ *           description: Previous page number or null
+ *         nextPage:
+ *           type: string
+ *           description: Next page number or null
+ *     InAppNotificationDocument:
+ *       type: object
+ *       properties:
+ *         _id:
+ *           type: string
+ *           description: In app notification document ID
+ *         action:
+ *           type: string
+ *           description: Action performed on the in app notification document
+ *         snapshot:
+ *           type: string
+ *           description: Snapshot details in JSON format
+ *         target:
+ *           $ref: '#/components/schemas/Page'
+ *         user:
+ *           $ref: '#/components/schemas/User'
+ *         createdAt:
+ *           type: string
+ *           format: date-time
+ *           description: Creation timestamp
+ *         status:
+ *           type: string
+ *           description: Status of the in app notification document
+ *         targetModel:
+ *           type: string
+ *           description: Model of the target
+ *         id:
+ *           type: string
+ *           description: In app notification document ID
+ *         actionUsers:
+ *           type: array
+ *           items:
+ *             $ref: '#/components/schemas/User'
+ */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 module.exports = (crowi) => {
 module.exports = (crowi) => {
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
   const loginRequiredStrictly = require('../../middlewares/login-required')(crowi);
@@ -24,6 +96,41 @@ module.exports = (crowi) => {
 
 
   const activityEvent = crowi.event('activity');
   const activityEvent = crowi.event('activity');
 
 
+  /**
+   * @swagger
+   *
+   *  /in-app-notification/list:
+   *    get:
+   *      tags: [NotificationSetting]
+   *      security:
+   *        - api_key: []
+   *      operationId: getInAppNotificationList
+   *      summary: /in-app-notification/list
+   *      description: Get the list of in-app notifications
+   *      parameters:
+   *        - name: limit
+   *          in: query
+   *          description: The number of notifications to get
+   *          schema:
+   *            type: integer
+   *        - name: offset
+   *          in: query
+   *          description: The number of notifications to skip
+   *          schema:
+   *            type: integer
+   *        - name: status
+   *          in: query
+   *          description: The status to categorize. 'UNOPENED' or 'OPENED'.
+   *          schema:
+   *            type: string
+   *      responses:
+   *        200:
+   *          description: The list of in-app notifications
+   *          content:
+   *            application/json:
+   *              schema:
+   *                $ref: '#/components/schemas/InAppNotificationListResponse'
+   */
   router.get('/list', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
   router.get('/list', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
     // user must be set by loginRequiredStrictly
     // user must be set by loginRequiredStrictly
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -79,6 +186,28 @@ module.exports = (crowi) => {
     return res.apiv3(serializedPaginationResult);
     return res.apiv3(serializedPaginationResult);
   });
   });
 
 
+  /**
+   * @swagger
+   *
+   *  /in-app-notification/status:
+   *    get:
+   *      tags: [NotificationSetting]
+   *      security:
+   *        - api_key: []
+   *      operationId: getInAppNotificationStatus
+   *      summary: /in-app-notification/status
+   *      description: Get the status of in-app notifications
+   *      responses:
+   *        200:
+   *          description: Get count of unread notifications
+   *          content:
+   *            application/json:
+   *              schema:
+   *                properties:
+   *                  count:
+   *                    type: integer
+   *                    description: Count of unread notifications
+   */
   router.get('/status', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
   router.get('/status', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
     // user must be set by loginRequiredStrictly
     // user must be set by loginRequiredStrictly
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -93,6 +222,35 @@ module.exports = (crowi) => {
     }
     }
   });
   });
 
 
+  /**
+   * @swagger
+   *
+   *  /in-app-notification/open:
+   *    post:
+   *      tags: [NotificationSetting]
+   *      security:
+   *        - api_key: []
+   *      operationId: openInAppNotification
+   *      summary: /in-app-notification/open
+   *      description: Open the in-app notification
+   *      requestBody:
+   *        content:
+   *          application/json:
+   *            schema:
+   *              properties:
+   *                id:
+   *                  type: string
+   *                  description: Notification ID
+   *              required:
+   *                - id
+   *      responses:
+   *        200:
+   *          description: Notification opened successfully
+   *          content:
+   *            application/json:
+   *              schema:
+   *                type: object
+   */
   router.post('/open', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
   router.post('/open', accessTokenParser, loginRequiredStrictly, async(req: CrowiRequest, res: ApiV3Response) => {
     // user must be set by loginRequiredStrictly
     // user must be set by loginRequiredStrictly
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
@@ -110,6 +268,21 @@ module.exports = (crowi) => {
     }
     }
   });
   });
 
 
+  /**
+   * @swagger
+   *
+   *  /in-app-notification/all-statuses-open:
+   *    put:
+   *      tags: [NotificationSetting]
+   *      security:
+   *        - api_key: []
+   *      operationId: openAllInAppNotification
+   *      summary: /in-app-notification/all-statuses-open
+   *      description: Open all in-app notifications
+   *      responses:
+   *        200:
+   *          description: All notifications opened successfully
+   */
   router.put('/all-statuses-open', accessTokenParser, loginRequiredStrictly, addActivity, async(req: CrowiRequest, res: ApiV3Response) => {
   router.put('/all-statuses-open', accessTokenParser, loginRequiredStrictly, addActivity, async(req: CrowiRequest, res: ApiV3Response) => {
     // user must be set by loginRequiredStrictly
     // user must be set by loginRequiredStrictly
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
     // eslint-disable-next-line @typescript-eslint/no-non-null-assertion

+ 36 - 0
apps/app/src/server/routes/apiv3/users.js

@@ -74,6 +74,42 @@ const validator = {};
  *            type: string
  *            type: string
  *            description: date created at
  *            description: date created at
  *            example: 2010-01-01T00:00:00.000Z
  *            example: 2010-01-01T00:00:00.000Z
+ *          imageUrlCached:
+ *            type: string
+ *            description: cached image URL
+ *            example: /images/user/5ae5fccfc5577b0004dbd8ab/profile.jpg
+ *          isEmailPublished:
+ *            type: boolean
+ *            description: whether the email is published
+ *            example: false
+ *          isGravatarEnabled:
+ *            type: boolean
+ *            description: whether the gravatar is enabled
+ *            example: false
+ *          isInvitationEmailSended:
+ *            type: boolean
+ *            description: whether the invitation email is sent
+ *            example: false
+ *          isQuestionnaireEnabled:
+ *            type: boolean
+ *            description: whether the questionnaire is enabled
+ *            example: false
+ *          lastLoginAt:
+ *            type: string
+ *            description: datetime last login at
+ *            example: 2010-01-01T00:00:00.000Z
+ *          readOnly:
+ *            type: boolean
+ *            description: whether the user is read only
+ *            example: false
+ *          updatedAt:
+ *            type: string
+ *            description: datetime updated at
+ *            example: 2010-01-01T00:00:00.000Z
+ *          __v:
+ *            type: integer
+ *            description: DB record version
+ *            example: 0
  */
  */
 
 
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */

+ 1 - 1
apps/pdf-converter/package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "@growi/pdf-converter",
   "name": "@growi/pdf-converter",
-  "version": "1.0.0-RC.0",
+  "version": "1.0.1-RC.0",
   "main": "dist/index.js",
   "main": "dist/index.js",
   "types": "dist/index.d.ts",
   "types": "dist/index.d.ts",
   "license": "MIT",
   "license": "MIT",

+ 1 - 1
package.json

@@ -96,7 +96,7 @@
     "turbo": "^2.1.3",
     "turbo": "^2.1.3",
     "typescript": "~5.0.0",
     "typescript": "~5.0.0",
     "typescript-transform-paths": "^3.4.7",
     "typescript-transform-paths": "^3.4.7",
-    "vite": "^5.4.12",
+    "vite": "^5.4.15",
     "vite-plugin-dts": "^3.9.1",
     "vite-plugin-dts": "^3.9.1",
     "vite-tsconfig-paths": "^5.0.1",
     "vite-tsconfig-paths": "^5.0.1",
     "vitest": "^2.1.1",
     "vitest": "^2.1.1",

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 283 - 108
pnpm-lock.yaml


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است