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

Merge branch 'master' into feat/enhanced-access-token

Shun Miyazawa 10 месяцев назад
Родитель
Сommit
45bd7442e2
66 измененных файлов с 1011 добавлено и 504 удалено
  1. 0 1
      .eslintignore
  2. 7 1
      .eslintrc.js
  3. 0 6
      apps/app/.eslintignore
  4. 14 2
      apps/app/.eslintrc.js
  5. 0 0
      apps/app/bin/openapi/definition-apiv1.js
  6. 0 0
      apps/app/bin/openapi/definition-apiv3.js
  7. 96 0
      apps/app/bin/openapi/generate-operation-ids/cli.spec.ts
  8. 29 0
      apps/app/bin/openapi/generate-operation-ids/cli.ts
  9. 219 0
      apps/app/bin/openapi/generate-operation-ids/generate-operation-ids.spec.ts
  10. 62 0
      apps/app/bin/openapi/generate-operation-ids/generate-operation-ids.ts
  11. 15 0
      apps/app/bin/openapi/generate-spec-apiv1.sh
  12. 9 4
      apps/app/bin/openapi/generate-spec-apiv3.sh
  13. 0 15
      apps/app/bin/swagger-jsdoc/generate-spec-apiv1.sh
  14. 11 7
      apps/app/package.json
  15. 2 2
      apps/app/playwright/40-admin/access-to-admin-page.spec.ts
  16. 0 1
      apps/app/public/static/locales/en_US/admin.json
  17. 0 1
      apps/app/public/static/locales/fr_FR/admin.json
  18. 20 21
      apps/app/public/static/locales/ja_JP/admin.json
  19. 0 1
      apps/app/public/static/locales/zh_CN/admin.json
  20. 94 52
      apps/app/src/client/components/Admin/Security/SecuritySetting.jsx
  21. 26 3
      apps/app/src/client/components/PageControls/PageControls.tsx
  22. 23 19
      apps/app/src/client/services/AdminGeneralSecurityContainer.js
  23. 0 15
      apps/app/src/features/external-user-group/server/routes/apiv3/external-user-group.ts
  24. 6 2
      apps/app/src/pages/[[...path]].page.tsx
  25. 10 0
      apps/app/src/server/models/openapi/object-id.ts
  26. 11 9
      apps/app/src/server/models/openapi/page.ts
  27. 12 8
      apps/app/src/server/models/openapi/paginate-result.ts
  28. 12 10
      apps/app/src/server/models/openapi/revision.ts
  29. 32 0
      apps/app/src/server/models/openapi/tag.ts
  30. 4 3
      apps/app/src/server/models/openapi/v1-response.js
  31. 0 1
      apps/app/src/server/routes/apiv3/admin-home.ts
  32. 0 10
      apps/app/src/server/routes/apiv3/app-settings.js
  33. 0 2
      apps/app/src/server/routes/apiv3/attachment.js
  34. 0 6
      apps/app/src/server/routes/apiv3/bookmark-folder.ts
  35. 1 4
      apps/app/src/server/routes/apiv3/bookmarks.js
  36. 1 17
      apps/app/src/server/routes/apiv3/customize-setting.js
  37. 0 3
      apps/app/src/server/routes/apiv3/export.js
  38. 2 0
      apps/app/src/server/routes/apiv3/g2g-transfer.ts
  39. 0 1
      apps/app/src/server/routes/apiv3/healthcheck.ts
  40. 1 5
      apps/app/src/server/routes/apiv3/import.js
  41. 0 4
      apps/app/src/server/routes/apiv3/in-app-notification.ts
  42. 0 1
      apps/app/src/server/routes/apiv3/installer.ts
  43. 0 1
      apps/app/src/server/routes/apiv3/invited.ts
  44. 0 4
      apps/app/src/server/routes/apiv3/markdown-setting.js
  45. 0 1
      apps/app/src/server/routes/apiv3/mongo.js
  46. 10 18
      apps/app/src/server/routes/apiv3/page/index.ts
  47. 5 42
      apps/app/src/server/routes/apiv3/pages/index.js
  48. 0 14
      apps/app/src/server/routes/apiv3/personal-setting/index.js
  49. 0 13
      apps/app/src/server/routes/apiv3/slack-integration-settings.js
  50. 0 1
      apps/app/src/server/routes/apiv3/statistics.js
  51. 0 1
      apps/app/src/server/routes/apiv3/user-activation.ts
  52. 0 1
      apps/app/src/server/routes/apiv3/user-group-relation.js
  53. 0 15
      apps/app/src/server/routes/apiv3/user-group.js
  54. 0 18
      apps/app/src/server/routes/apiv3/users.js
  55. 5 5
      apps/app/src/server/routes/attachment/api.js
  56. 29 27
      apps/app/src/server/routes/comment.js
  57. 4 4
      apps/app/src/server/routes/page.js
  58. 22 20
      apps/app/src/server/routes/search.ts
  59. 7 36
      apps/app/src/server/routes/tag.js
  60. 6 0
      apps/app/src/stores-universal/context.tsx
  61. 1 1
      apps/app/vitest.workspace.mts
  62. 0 1
      apps/pdf-converter/.eslintignore
  63. 6 0
      apps/pdf-converter/.eslintrc.cjs
  64. 0 3
      packages/pdf-converter-client/.eslintignore
  65. 11 0
      packages/pdf-converter-client/.eslintrc.cjs
  66. 186 41
      pnpm-lock.yaml

+ 0 - 1
.eslintignore

@@ -1 +0,0 @@
-node_modules/**

+ 7 - 1
.eslintrc.js

@@ -1,3 +1,6 @@
+/**
+ * @type {import('eslint').Linter.Config}
+ */
 module.exports = {
   root: true, // https://eslint.org/docs/user-guide/configuring/configuration-files#cascading-and-hierarchy
   extends: [
@@ -7,6 +10,9 @@ module.exports = {
   plugins: [
     'regex',
   ],
+  ignorePatterns: [
+    'node_modules/**',
+  ],
   rules: {
     'import/prefer-default-export': 'off',
     'import/order': [
@@ -73,7 +79,7 @@ module.exports = {
   overrides: [
     {
       // enable the rule specifically for TypeScript files
-      files: ['*.ts', '*.tsx'],
+      files: ['*.ts', '*.mts', '*.tsx'],
       rules: {
         '@typescript-eslint/explicit-module-boundary-types': ['error'],
       },

+ 0 - 6
apps/app/.eslintignore

@@ -1,6 +0,0 @@
-/dist/**
-/transpiled/**
-/public/**
-/src/linter-checker/**
-/tmp/**
-/next-env.d.ts

+ 14 - 2
apps/app/.eslintrc.js

@@ -1,3 +1,6 @@
+/**
+ * @type {import('eslint').Linter.Config}
+ */
 module.exports = {
   extends: [
     'next/core-web-vitals',
@@ -5,6 +8,15 @@ module.exports = {
   ],
   plugins: [
   ],
+  ignorePatterns: [
+    'dist/**',
+    '**/dist/**',
+    'transpiled/**',
+    'public/**',
+    'src/linter-checker/**',
+    'tmp/**',
+    'next-env.d.ts',
+  ],
   settings: {
     // resolve path aliases by eslint-import-resolver-typescript
     'import/resolver': {
@@ -25,7 +37,7 @@ module.exports = {
   overrides: [
     {
       // enable the rule specifically for JavaScript files
-      files: ['*.js', '*.jsx'],
+      files: ['*.js', '*.mjs', '*.jsx'],
       rules: {
         // set 'warn' temporarily -- 2023.08.14 Yuki Takei
         'react/prop-types': 'warn',
@@ -35,7 +47,7 @@ module.exports = {
     },
     {
       // enable the rule specifically for TypeScript files
-      files: ['*.ts', '*.tsx'],
+      files: ['*.ts', '*.mts', '*.tsx'],
       rules: {
         'no-unused-vars': 'off',
         // set 'warn' temporarily -- 2023.08.14 Yuki Takei

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


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


+ 96 - 0
apps/app/bin/openapi/generate-operation-ids/cli.spec.ts

@@ -0,0 +1,96 @@
+import { writeFileSync } from 'fs';
+
+import {
+  beforeEach, describe, expect, it, vi,
+} from 'vitest';
+
+import { generateOperationIds } from './generate-operation-ids';
+
+// Mock the modules
+vi.mock('fs');
+vi.mock('./generate-operation-ids');
+
+const originalArgv = process.argv;
+
+describe('cli', () => {
+  const mockJsonStrings = '{"test": "data"}';
+
+  beforeEach(() => {
+    vi.resetModules();
+    vi.resetAllMocks();
+    process.argv = [...originalArgv]; // Reset process.argv
+    // Mock console.error to avoid actual console output during tests
+    vi.spyOn(console, 'error').mockImplementation(() => {});
+  });
+
+  it('processes input file and writes output to specified file', async() => {
+    // Mock generateOperationIds to return success
+    vi.mocked(generateOperationIds).mockResolvedValue(mockJsonStrings);
+
+    // Mock process.argv
+    process.argv = ['node', 'cli.js', 'input.json', '-o', 'output.json'];
+
+    // Import the module that contains the main function
+    const cliModule = await import('./cli');
+    await cliModule.main();
+
+    // Verify generateOperationIds was called with correct arguments
+    expect(generateOperationIds).toHaveBeenCalledWith('input.json', { overwriteExisting: undefined });
+
+    // Verify writeFileSync was called with correct arguments
+    expect(writeFileSync).toHaveBeenCalledWith('output.json', mockJsonStrings);
+  });
+
+  it('uses input file as output when no output file is specified', async() => {
+    // Mock generateOperationIds to return success
+    vi.mocked(generateOperationIds).mockResolvedValue(mockJsonStrings);
+
+    // Mock process.argv
+    process.argv = ['node', 'cli.js', 'input.json'];
+
+    // Import the module that contains the main function
+    const cliModule = await import('./cli');
+    await cliModule.main();
+
+    // Verify generateOperationIds was called with correct arguments
+    expect(generateOperationIds).toHaveBeenCalledWith('input.json', { overwriteExisting: undefined });
+
+    // Verify writeFileSync was called with input file as output
+    expect(writeFileSync).toHaveBeenCalledWith('input.json', mockJsonStrings);
+  });
+
+  it('handles overwrite-existing option correctly', async() => {
+    // Mock generateOperationIds to return success
+    vi.mocked(generateOperationIds).mockResolvedValue(mockJsonStrings);
+
+    // Mock process.argv
+    process.argv = ['node', 'cli.js', 'input.json', '--overwrite-existing'];
+
+    // Import the module that contains the main function
+    const cliModule = await import('./cli');
+    await cliModule.main();
+
+    // Verify generateOperationIds was called with overwriteExisting option
+    expect(generateOperationIds).toHaveBeenCalledWith('input.json', { overwriteExisting: true });
+  });
+
+  it('handles generateOperationIds error correctly', async() => {
+    // Mock generateOperationIds to throw error
+    const error = new Error('Test error');
+    vi.mocked(generateOperationIds).mockRejectedValue(error);
+
+    // Mock process.argv
+    process.argv = ['node', 'cli.js', 'input.json'];
+
+    // Import the module that contains the main function
+    const cliModule = await import('./cli');
+    await cliModule.main();
+
+    // Verify error was logged
+    // eslint-disable-next-line no-console
+    expect(console.error).toHaveBeenCalledWith(error);
+
+    // Verify writeFileSync was not called
+    expect(writeFileSync).not.toHaveBeenCalled();
+  });
+});

+ 29 - 0
apps/app/bin/openapi/generate-operation-ids/cli.ts

@@ -0,0 +1,29 @@
+import { writeFileSync } from 'fs';
+
+import { Command } from 'commander';
+
+import { generateOperationIds } from './generate-operation-ids';
+
+export const main = async(): Promise<void> => {
+  // parse command line arguments
+  const program = new Command();
+  program
+    .name('generate-operation-ids')
+    .description('Generate operationId for OpenAPI specification')
+    .argument('<input-file>', 'OpenAPI specification file')
+    .option('-o, --out <output-file>', 'Output file (defaults to input file)')
+    .option('--overwrite-existing', 'Overwrite existing operationId values')
+    .parse();
+  const { out: outputFile, overwriteExisting } = program.opts();
+  const [inputFile] = program.args;
+
+  // eslint-disable-next-line no-console
+  const jsonStrings = await generateOperationIds(inputFile, { overwriteExisting }).catch(console.error);
+  if (jsonStrings != null) {
+    writeFileSync(outputFile ?? inputFile, jsonStrings);
+  }
+};
+
+if (import.meta.url === `file://${process.argv[1]}`) {
+  main();
+}

+ 219 - 0
apps/app/bin/openapi/generate-operation-ids/generate-operation-ids.spec.ts

@@ -0,0 +1,219 @@
+import fs from 'fs/promises';
+import { tmpdir } from 'os';
+import path from 'path';
+
+import type { OpenAPI3 } from 'openapi-typescript';
+import { describe, expect, it } from 'vitest';
+
+import { generateOperationIds } from './generate-operation-ids';
+
+
+async function createTempOpenAPIFile(spec: OpenAPI3): Promise<string> {
+  const tempDir = await fs.mkdtemp(path.join(tmpdir(), 'openapi-test-'));
+  const filePath = path.join(tempDir, 'openapi.json');
+  await fs.writeFile(filePath, JSON.stringify(spec));
+  return filePath;
+}
+
+async function cleanup(filePath: string): Promise<void> {
+  try {
+    await fs.unlink(filePath);
+    await fs.rmdir(path.dirname(filePath));
+  }
+  catch (err) {
+    // eslint-disable-next-line no-console
+    console.error('Cleanup failed:', err);
+  }
+}
+
+describe('generateOperationIds', () => {
+  it('should generate correct operationId for simple paths', async() => {
+    const spec: OpenAPI3 = {
+      openapi: '3.0.0',
+      info: { title: 'Test API', version: '1.0.0' },
+      paths: {
+        '/foo': {
+          get: {},
+          post: {},
+        },
+      },
+    };
+
+    const filePath = await createTempOpenAPIFile(spec);
+    try {
+      const result = await generateOperationIds(filePath);
+      const parsed = JSON.parse(result);
+
+      expect(parsed.paths['/foo'].get.operationId).toBe('getFoo');
+      expect(parsed.paths['/foo'].post.operationId).toBe('postFoo');
+    }
+    finally {
+      await cleanup(filePath);
+    }
+  });
+
+  it('should generate correct operationId for paths with parameters', async() => {
+    const spec: OpenAPI3 = {
+      openapi: '3.0.0',
+      info: { title: 'Test API', version: '1.0.0' },
+      paths: {
+        '/foo/{id}': {
+          get: {},
+        },
+        '/foo/{id}/bar/{page}': {
+          get: {},
+        },
+      },
+    };
+
+    const filePath = await createTempOpenAPIFile(spec);
+    try {
+      const result = await generateOperationIds(filePath);
+      const parsed = JSON.parse(result);
+
+      expect(parsed.paths['/foo/{id}'].get.operationId).toBe('getFooById');
+      expect(parsed.paths['/foo/{id}/bar/{page}'].get.operationId).toBe('getBarByPageByIdForFoo');
+    }
+    finally {
+      await cleanup(filePath);
+    }
+  });
+
+  it('should generate correct operationId for nested resources', async() => {
+    const spec: OpenAPI3 = {
+      openapi: '3.0.0',
+      info: { title: 'Test API', version: '1.0.0' },
+      paths: {
+        '/foo/bar': {
+          get: {},
+        },
+      },
+    };
+
+    const filePath = await createTempOpenAPIFile(spec);
+    try {
+      const result = await generateOperationIds(filePath);
+      const parsed = JSON.parse(result);
+
+      expect(parsed.paths['/foo/bar'].get.operationId).toBe('getBarForFoo');
+    }
+    finally {
+      await cleanup(filePath);
+    }
+  });
+
+  it('should preserve existing operationId when overwriteExisting is false', async() => {
+    const existingOperationId = 'existingOperation';
+    const spec: OpenAPI3 = {
+      openapi: '3.0.0',
+      info: { title: 'Test API', version: '1.0.0' },
+      paths: {
+        '/foo': {
+          get: {
+            operationId: existingOperationId,
+          },
+        },
+      },
+    };
+
+    const filePath = await createTempOpenAPIFile(spec);
+    try {
+      const result = await generateOperationIds(filePath, { overwriteExisting: false });
+      const parsed = JSON.parse(result);
+
+      expect(parsed.paths['/foo'].get.operationId).toBe(existingOperationId);
+    }
+    finally {
+      await cleanup(filePath);
+    }
+  });
+
+  it('should overwrite existing operationId when overwriteExisting is true', async() => {
+    const spec: OpenAPI3 = {
+      openapi: '3.0.0',
+      info: { title: 'Test API', version: '1.0.0' },
+      paths: {
+        '/foo': {
+          get: {
+            operationId: 'existingOperation',
+          },
+        },
+      },
+    };
+
+    const filePath = await createTempOpenAPIFile(spec);
+    try {
+      const result = await generateOperationIds(filePath, { overwriteExisting: true });
+      const parsed = JSON.parse(result);
+
+      expect(parsed.paths['/foo'].get.operationId).toBe('getFoo');
+    }
+    finally {
+      await cleanup(filePath);
+    }
+  });
+
+  it('should generate correct operationId for root path', async() => {
+    const spec: OpenAPI3 = {
+      openapi: '3.0.0',
+      info: { title: 'Test API', version: '1.0.0' },
+      paths: {
+        '/': {
+          get: {},
+        },
+      },
+    };
+
+    const filePath = await createTempOpenAPIFile(spec);
+    try {
+      const result = await generateOperationIds(filePath);
+      const parsed = JSON.parse(result);
+
+      expect(parsed.paths['/'].get.operationId).toBe('getRoot');
+    }
+    finally {
+      await cleanup(filePath);
+    }
+  });
+
+  it('should generate operationId for all HTTP methods', async() => {
+    const spec: OpenAPI3 = {
+      openapi: '3.0.0',
+      info: { title: 'Test API', version: '1.0.0' },
+      paths: {
+        '/foo': {
+          get: {},
+          post: {},
+          put: {},
+          delete: {},
+          patch: {},
+          options: {},
+          head: {},
+          trace: {},
+        },
+      },
+    };
+
+    const filePath = await createTempOpenAPIFile(spec);
+    try {
+      const result = await generateOperationIds(filePath);
+      const parsed = JSON.parse(result);
+
+      expect(parsed.paths['/foo'].get.operationId).toBe('getFoo');
+      expect(parsed.paths['/foo'].post.operationId).toBe('postFoo');
+      expect(parsed.paths['/foo'].put.operationId).toBe('putFoo');
+      expect(parsed.paths['/foo'].delete.operationId).toBe('deleteFoo');
+      expect(parsed.paths['/foo'].patch.operationId).toBe('patchFoo');
+      expect(parsed.paths['/foo'].options.operationId).toBe('optionsFoo');
+      expect(parsed.paths['/foo'].head.operationId).toBe('headFoo');
+      expect(parsed.paths['/foo'].trace.operationId).toBe('traceFoo');
+    }
+    finally {
+      await cleanup(filePath);
+    }
+  });
+
+  it('should throw error for non-existent file', async() => {
+    await expect(generateOperationIds('non-existent-file.json')).rejects.toThrow();
+  });
+});

+ 62 - 0
apps/app/bin/openapi/generate-operation-ids/generate-operation-ids.ts

@@ -0,0 +1,62 @@
+import SwaggerParser from '@apidevtools/swagger-parser';
+import type { OpenAPI3, OperationObject, PathItemObject } from 'openapi-typescript';
+
+const toPascal = (s: string): string => s.split('-').map(w => w[0]?.toUpperCase() + w.slice(1)).join('');
+
+const createParamSuffix = (params: string[]): string => {
+  return params.length > 0
+    ? params.reverse().map(param => `By${toPascal(param.slice(1, -1))}`).join('')
+    : '';
+};
+
+
+/**
+ * Generates a PascalCase operation name based on the HTTP method and path.
+ *
+ * e.g.
+ * - `GET /foo` -> `getFoo`
+ * - `POST /bar` -> `postBar`
+ * - `Get /foo/bar` -> `getBarForFoo`
+ * - `GET /foo/{id}` -> `getFooById`
+ * - `GET /foo/{id}/bar` -> `getBarByIdForFoo`
+ * - `GET /foo/{id}/{page}/bar` -> `getBarByPageByIdForFoo`
+ *
+ */
+function createOperationId(method: string, path: string): string {
+  const segments = path.split('/').filter(Boolean);
+  const params = segments.filter(s => s.startsWith('{'));
+  const paths = segments.filter(s => !s.startsWith('{'));
+
+  const paramSuffix = createParamSuffix(params);
+
+  if (paths.length <= 1) {
+    return `${method.toLowerCase()}${toPascal(paths[0] || 'root')}${paramSuffix}`;
+  }
+
+  const [resource, ...context] = paths.reverse();
+  return `${method.toLowerCase()}${toPascal(resource)}${paramSuffix}For${context.reverse().map(toPascal).join('')}`;
+}
+
+export async function generateOperationIds(inputFile: string, opts?: { overwriteExisting: boolean }): Promise<string> {
+  const api = await SwaggerParser.parse(inputFile) as OpenAPI3;
+
+  Object.entries(api.paths || {}).forEach(([path, pathItem]) => {
+    const item = pathItem as PathItemObject;
+    (['get', 'post', 'put', 'delete', 'patch', 'options', 'head', 'trace'] as const)
+      .forEach((method) => {
+        const operation = item[method] as OperationObject | undefined;
+        if (operation == null || (operation.operationId != null && !opts?.overwriteExisting)) {
+          return;
+        }
+        operation.operationId = createOperationId(method, path);
+      });
+  });
+
+  const output = JSON.stringify(api, null, 2);
+
+  if (output == null) {
+    throw new Error(`Failed to generate operation IDs for ${inputFile}`);
+  }
+
+  return output;
+}

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

@@ -0,0 +1,15 @@
+# USAGE:
+#   cd apps/app && sh bin/openapi/generate-spec-apiv1.sh
+#   APP_PATH=/path/to/apps/app sh bin/openapi/generate-spec-apiv1.sh
+#   APP_PATH=/path/to/apps/app OUT=/path/to/output sh bin/openapi/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/openapi/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}"

+ 9 - 4
apps/app/bin/swagger-jsdoc/generate-spec-apiv3.sh → apps/app/bin/openapi/generate-spec-apiv3.sh

@@ -1,7 +1,7 @@
 # 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
+#   cd apps/app && sh bin/openapi/generate-spec-apiv3.sh
+#   APP_PATH=/path/to/apps/app sh bin/openapi/generate-spec-apiv3.sh
+#   APP_PATH=/path/to/apps/app OUT=/path/to/output sh bin/openapi/generate-spec-apiv3.sh
 
 APP_PATH=${APP_PATH:-"."}
 
@@ -9,7 +9,7 @@ OUT=${OUT:-"${APP_PATH}/tmp/openapi-spec-apiv3.json"}
 
 swagger-jsdoc \
   -o "${OUT}" \
-  -d "${APP_PATH}/bin/swagger-jsdoc/definition-apiv3.js" \
+  -d "${APP_PATH}/bin/openapi/definition-apiv3.js" \
   "${APP_PATH}/src/features/external-user-group/server/routes/apiv3/*.ts" \
   "${APP_PATH}/src/features/questionnaire/server/routes/apiv3/*.ts" \
   "${APP_PATH}/src/features/templates/server/routes/apiv3/*.ts" \
@@ -17,3 +17,8 @@ swagger-jsdoc \
   "${APP_PATH}/src/server/routes/apiv3/**/*.{js,ts}" \
   "${APP_PATH}/src/server/routes/login.js" \
   "${APP_PATH}/src/server/models/openapi/**/*.{js,ts}"
+
+if [ $? -eq 0 ]; then
+  pnpm dlx tsx "${APP_PATH}/bin/openapi/generate-operation-ids/cli.ts" "${OUT}" --out "${OUT}" --overwrite-existing
+  echo "OpenAPI spec generated and transformed: ${OUT}"
+fi

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

@@ -1,15 +0,0 @@
-# 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}"

+ 11 - 7
apps/app/package.json

@@ -27,13 +27,13 @@
     "//// for CI": "",
     "launch-dev:ci": "cross-env NODE_ENV=development pnpm run dev:migrate && pnpm run ts-node src/server/app.ts --ci",
     "lint:typecheck": "vue-tsc --noEmit",
-    "lint:eslint": "eslint --quiet \"**/*.{js,jsx,ts,tsx}\"",
+    "lint:eslint": "eslint --quiet \"**/*.{js,mjs,jsx,ts,mts,tsx}\"",
     "lint:styles": "stylelint \"src/**/*.scss\"",
-    "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:openapi:apiv3": "node node_modules/swagger2openapi/oas-validate tmp/openapi-spec-apiv3.json",
+    "lint:openapi:apiv1": "node node_modules/swagger2openapi/oas-validate tmp/openapi-spec-apiv1.json",
     "lint": "run-p lint:**",
-    "prelint:swagger2openapi:apiv3": "pnpm run swagger2openapi:apiv3",
-    "prelint:swagger2openapi:apiv1": "pnpm run swagger2openapi:apiv1",
+    "prelint:openapi:apiv3": "pnpm run openapi:generate-spec:apiv3",
+    "prelint:openapi:apiv1": "pnpm run openapi:generate-spec: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",
@@ -43,8 +43,9 @@
     "//// misc": "",
     "console": "npm run repl",
     "repl": "cross-env NODE_ENV=development npm run ts-node src/server/repl.ts",
-    "swagger2openapi:apiv3": "sh bin/swagger-jsdoc/generate-spec-apiv3.sh",
-    "swagger2openapi:apiv1": "sh bin/swagger-jsdoc/generate-spec-apiv1.sh",
+    "openapi:build:generate-operation-ids": "vite build -c bin/openapi/generate-operation-ids/vite.config.ts",
+    "openapi:generate-spec:apiv3": "sh bin/openapi/generate-spec-apiv3.sh",
+    "openapi:generate-spec:apiv1": "sh bin/openapi/generate-spec-apiv1.sh",
     "ts-node": "node -r ts-node/register/transpile-only -r tsconfig-paths/register -r dotenv-flow/config",
     "version:patch": "pnpm version patch",
     "version:prerelease": "pnpm version prerelease --preid=RC",
@@ -258,6 +259,7 @@
     "mongodb": "mongoose which is used requires mongo@4.16.0."
   },
   "devDependencies": {
+    "@apidevtools/swagger-parser": "^10.1.1",
     "@emoji-mart/data": "^1.2.1",
     "@growi/core-styles": "workspace:^",
     "@growi/custom-icons": "workspace:^",
@@ -293,6 +295,7 @@
     "@types/uuid": "^10.0.0",
     "babel-loader": "^8.2.5",
     "bootstrap": "=5.3.2",
+    "commander": "^14.0.0",
     "connect-browser-sync": "^2.1.0",
     "diff2html": "^3.4.47",
     "downshift": "^8.2.3",
@@ -315,6 +318,7 @@
     "mongodb-memory-server-core": "^9.1.1",
     "morgan": "^1.10.0",
     "null-loader": "^4.0.1",
+    "openapi-typescript": "^7.8.0",
     "pretty-bytes": "^6.1.1",
     "react-copy-to-clipboard": "^5.0.1",
     "react-dnd": "^14.0.5",

+ 2 - 2
apps/app/playwright/40-admin/access-to-admin-page.spec.ts

@@ -21,8 +21,8 @@ test('admin/security is successfully loaded', async({ page }) => {
   await page.goto('/admin/security');
 
   await expect(page.getByTestId('admin-security')).toBeVisible();
-  await expect(page.locator('#isShowRestrictedByOwner')).not.toBeChecked();
-  await expect(page.locator('#isShowRestrictedByGroup')).not.toBeChecked();
+  await expect(page.locator('#isShowRestrictedByOwner')).toHaveText('Always displayed');
+  await expect(page.locator('#isShowRestrictedByGroup')).toHaveText('Always displayed');
 });
 
 test('admin/markdown is successfully loaded', async({ page }) => {

+ 0 - 1
apps/app/public/static/locales/en_US/admin.json

@@ -19,7 +19,6 @@
     "readonly_users_access": "Read only users' access",
     "always_hidden": "Always hidden",
     "always_displayed": "Always displayed",
-    "displayed_or_hidden": "Hidden / Displayed",
     "Fixed by env var": "This is fixed by the env var <code>{{key}}={{value}}</code>.",
     "register_limitation": "Register limitation",
     "register_limitation_desc": "Restriction of new users' registration",

+ 0 - 1
apps/app/public/static/locales/fr_FR/admin.json

@@ -19,7 +19,6 @@
     "readonly_users_access": "Accès des utilisateurs lecture seule",
     "always_hidden": "Toujours caché",
     "always_displayed": "Toujours affiché",
-    "displayed_or_hidden": "Caché / Affiché",
     "Fixed by env var": "Configuré par la variable d'environnement <code>{{key}}={{value}}</code>.",
     "register_limitation": "Paramètres d'inscription",
     "register_limitation_desc": "Restreindre l'inscription de nouveaux utilisateurs",

+ 20 - 21
apps/app/public/static/locales/ja_JP/admin.json

@@ -13,22 +13,21 @@
   "Execute": "実行",
   "last_login": "最終ログイン",
   "wiki_management_homepage": "Wiki管理トップ",
-  "public": "公開",
-  "anyone_with_the_link": "リンクを知っている人のみ",
+  "public": "公開」のページ",
+  "anyone_with_the_link": "リンクを知っている人のみ」のページ",
   "specified_users": "特定ユーザーのみ",
-  "only_me": "自分のみ",
-  "only_inside_the_group": "特定グループのみ",
+  "only_me": "自分のみ」のページ",
+  "only_inside_the_group": "特定グループのみ」のページ",
   "optional": "オプション",
   "days": "日",
   "security_settings": {
     "security_settings": "セキュリティ設定",
     "scope_of_page_disclosure": "ページの公開範囲",
     "set_point": "設定値",
-    "Guest Users Access":"ゲストユーザーのアクセス",
+    "Guest Users Access": "ゲストユーザーのアクセス",
     "readonly_users_access": "閲覧のみユーザーのアクセス",
-    "always_hidden": "非表示 (固定)",
-    "always_displayed": "表示 (固定)",
-    "displayed_or_hidden": "非表示 / 表示",
+    "always_hidden": "表示しない",
+    "always_displayed": "表示する",
     "Fixed by env var": "環境変数 <code>{{forcewikimode}}={{wikimode}}</code> により固定されています。",
     "register_limitation": "登録の制限",
     "register_limitation_desc": "新しいユーザーを登録する方法を制限します。",
@@ -73,7 +72,7 @@
     "forced_update_desc": "設定が強制変更されました。前回の設定: ",
     "page_delete_rights_caution": "「(子孫ページを含む)ゴミ箱に入れる操作 / 完全に削除する」の権限は、「ゴミ箱に入れる操作 / 完全に削除する」よりも強い権限になるように強制されます。 <br><br> 管理者のみ可能 > 管理者とページ作者が可能 > 誰でも可能",
     "Authentication mechanism settings": "認証機構設定",
-    "setup_is_not_yet_complete":"セットアップはまだ完了してません",
+    "setup_is_not_yet_complete": "セットアップはまだ完了してません",
     "xss_prevent_setting": "XSS(Cross Site Scripting)対策設定",
     "xss_prevent_setting_link": "マークダウン設定ページに移動",
     "callback_URL": "コールバックURL",
@@ -107,9 +106,9 @@
       "closed": "非公開 (登録には管理者による招待が必要)"
     },
     "share_link_management": "共有リンク管理",
-    "No_share_links":"共有リンクが存在しません",
-    "share_link_notice":"共有リンクを全て削除します",
-    "delete_all_share_links":"全ての共有リンクを削除します",
+    "No_share_links": "共有リンクが存在しません",
+    "share_link_notice": "共有リンクを全て削除します",
+    "delete_all_share_links": "全ての共有リンクを削除します",
     "share_link_rights": "シェアリンクの権限",
     "enable_link_sharing": "リンクのシェアを許可",
     "all_share_links": "全てのシェアリンク",
@@ -512,13 +511,13 @@
       "show_page_side_authors": "作成者・更新者を目次上部に常時表示する",
       "show_page_side_authors_desc": "ページサイドバーの目次上部に作成者と最終更新者の情報を表示します。"
     },
-    "presentation":"プレゼンテーション",
-    "presentation_options":{
+    "presentation": "プレゼンテーション",
+    "presentation_options": {
       "enable_marp": "Marp を有効化する",
       "enable_marp_desc": "プレゼンテーション表示に Marp を利用できるようになります。ただし、XSS に対して脆弱になる恐れがあります。",
       "marp_official_site": "参考:Marp 公式サイト",
       "marp_official_site_link": "https://marp.app",
-      "marp_in_growi" : "参考:GROWI Docs - Marp でスライドを作成する",
+      "marp_in_growi": "参考:GROWI Docs - Marp でスライドを作成する",
       "marp_in_growi_link": "https://docs.growi.org/ja/guide/features/marp.html"
     },
     "custom_title": "カスタム Title",
@@ -532,7 +531,7 @@
     "write_css": " システム全体に適用されるCSSを記述できます。",
     "ctrl_space": "Ctrl+Space でコード補完",
     "custom_script": "カスタムスクリプト",
-    "custom_presentation":"プレゼンテーション",
+    "custom_presentation": "プレゼンテーション",
     "write_java": "システム全体に適用されるJavaScriptを記述できます。",
     "reflect_change": "変更の反映はページの更新が必要です。",
     "custom_logo": "カスタムロゴ",
@@ -541,7 +540,7 @@
     "current_logo": "現在のロゴ",
     "upload_new_logo": "新しいロゴをアップロードする",
     "delete_logo": "ロゴを削除"
-   },
+  },
   "importer_management": {
     "import_data": "データインポート",
     "article": "記事",
@@ -681,7 +680,7 @@
     "delete": "削除",
     "integration_procedure": "連携手順",
     "custom_bot_without_proxy_settings": "Custom Bot without proxy 設定",
-    "integration_failed":"連携に失敗しました",
+    "integration_failed": "連携に失敗しました",
     "reset": "リセット",
     "reset_all_settings": "全ての設定をリセット",
     "delete_slackbot_settings": "Slack Bot 設定を削除する",
@@ -728,7 +727,7 @@
       "allow_specified_long": "特定のチャンネルを許可 (テキストボックスに入力されたチャンネルのみ許可されます)",
       "test_connection": "連携状況のテストをする",
       "test_connection_by_pressing_button": "以下のテストボタンを押して、Slack連携が完了しているかの確認をしましょう",
-      "test_connection_only_public_channel":"連携テストは public チャンネルで確認してください",
+      "test_connection_only_public_channel": "連携テストは public チャンネルで確認してください",
       "error_check_logs_below": "エラーが発生しました。下記のログを確認してください。",
       "send_message_to_slack_work_space": "Slack ワークスペースに送信しました",
       "add_slack_workspace": "Slackワークスペースを追加"
@@ -752,7 +751,7 @@
     }
   },
   "slack_integration_legacy": {
-    "slack_integration_legacy":  "Slack連携 (レガシー)",
+    "slack_integration_legacy": "Slack連携 (レガシー)",
     "alert_disabled": "<a href='/admin/slack-integration'>新しい設定</a>が有効になっているため、この 'Slack連携 (レガシー)' は現在無効になっています。",
     "alert_deplicated": "この 'Slack連携 (レガシー)' は将来廃止されます。代わりに<a href='/admin/slack-integration'>新しいSlack連携機能</a>を利用してください。"
   },
@@ -989,7 +988,7 @@
     "ADMIN_SITE_URL_UPDATE": "サイトURL設定の更新",
     "ADMIN_MAIL_SMTP_UPDATE": "メール設定(SMTP)の更新",
     "ADMIN_MAIL_SES_UPDATE": "メール設定(SES)の更新",
-    "ADMIN_MAIL_TEST_SUBMIT" : "テストメールの送信",
+    "ADMIN_MAIL_TEST_SUBMIT": "テストメールの送信",
     "ADMIN_FILE_UPLOAD_CONFIG_UPDATE": "ファイルアップロード設定の更新",
     "ADMIN_PLUGIN_UPDATE": "プラグイン設定の更新",
     "ADMIN_MAINTENANCEMODE_ENABLED": "メンテナンスモードの開始",

+ 0 - 1
apps/app/public/static/locales/zh_CN/admin.json

@@ -26,7 +26,6 @@
     "set_point": "设定值",
     "always_displayed": "始终显示",
     "always_hidden": "总是隐藏",
-    "displayed_or_hidden": "隐藏 / 显示",
     "Guest Users Access": "来宾用户访问",
     "readonly_users_access": "只浏览用户的访问",
 		"Fixed by env var": "这是由env var<code>%s=%s</code>修复的。",

+ 94 - 52
apps/app/src/client/components/Admin/Security/SecuritySetting.jsx

@@ -297,7 +297,7 @@ class SecuritySetting extends React.Component {
                     onClick={() => this.setExpantOtherDeleteOptionsState(deletionType, !expantDeleteOptionsState)}
                   >
                     <span className={`material-symbols-outlined me-1 ${expantDeleteOptionsState ? 'rotate-90' : ''}`}>navigate_next</span>
-                    { t('security_settings.other_options') }
+                    {t('security_settings.other_options')}
                   </button>
                   <Collapse isOpen={expantDeleteOptionsState}>
                     <div className="pb-4">
@@ -308,7 +308,7 @@ class SecuritySetting extends React.Component {
                           <span dangerouslySetInnerHTML={{ __html: t('security_settings.page_delete_rights_caution') }} />
                         </span>
                       </p>
-                      { this.previousPageRecursiveAuthorityState(deletionType) !== null && (
+                      {this.previousPageRecursiveAuthorityState(deletionType) !== null && (
                         <div className="mb-3">
                           <strong>
                             {t('security_settings.forced_update_desc')}
@@ -356,60 +356,102 @@ class SecuritySetting extends React.Component {
           </div>
         )}
 
-        <h4 className="mt-4">{ t('security_settings.page_list_and_search_results') }</h4>
-        <div className="row justify-content-md-center">
-          <table className="table table-bordered col-lg-9 mb-5">
-            <thead>
-              <tr>
-                <th scope="col">{ t('security_settings.scope_of_page_disclosure') }</th>
-                <th scope="col">{ t('security_settings.set_point') }</th>
-              </tr>
-            </thead>
-            <tbody>
-              <tr>
-                <th scope="row">{ t('public') }</th>
-                <td><span className="material-symbols-outlined text-success me-1">check_circle</span>{ t('security_settings.always_displayed') }</td>
-              </tr>
-              <tr>
-                <th scope="row">{ t('anyone_with_the_link') }</th>
-                <td><span className="material-symbols-outlined text-danger me-1">cancel</span>{ t('security_settings.always_hidden') }</td>
-              </tr>
-              <tr>
-                <th scope="row">{ t('only_me') }</th>
-                <td>
-                  <div className="form-check form-switch form-check-success">
-                    <input
-                      type="checkbox"
-                      className="form-check-input"
+        <h4 className="alert-anchor border-bottom mt-4">{t('security_settings.page_list_and_search_results')}</h4>
+        <div className="row mb-4">
+          <div className="col-md-10">
+            <div className="row">
+
+              {/* Left Column: Labels */}
+              <div className="col-5 d-flex flex-column align-items-end p-4">
+                <div className="fw-bold mb-4">{t('public')}</div>
+                <div className="fw-bold mb-4">{t('anyone_with_the_link')}</div>
+                <div className="fw-bold mb-4">{t('only_me')}</div>
+                <div className="fw-bold">{t('only_inside_the_group')}</div>
+              </div>
+
+              {/* Right Column: Content */}
+              <div className="col-7 d-flex flex-column align-items-start pt-4 pb-4">
+                <div className="mb-4 d-flex align-items-center">
+                  <span className="material-symbols-outlined text-success me-1"></span>
+                  {t('security_settings.always_displayed')}
+                </div>
+                <div className="mb-3 d-flex align-items-center">
+                  <span className="material-symbols-outlined text-danger me-1"></span>
+                  {t('security_settings.always_hidden')}
+                </div>
+
+                {/* Owner Restriction Dropdown */}
+                <div className="mb-3">
+                  <div className="dropdown">
+                    <button
+                      className="btn btn-outline-secondary dropdown-toggle text-end col-12 col-md-auto"
+                      type="button"
                       id="isShowRestrictedByOwner"
-                      checked={!adminGeneralSecurityContainer.state.isShowRestrictedByOwner}
-                      onChange={() => { adminGeneralSecurityContainer.switchIsShowRestrictedByOwner() }}
-                    />
-                    <label className="form-label form-check-label" htmlFor="isShowRestrictedByOwner">
-                      {t('security_settings.displayed_or_hidden')}
-                    </label>
+                      data-bs-toggle="dropdown"
+                      aria-haspopup="true"
+                      aria-expanded="true"
+                    >
+                      <span className="float-start">
+                        {adminGeneralSecurityContainer.state.currentOwnerRestrictionDisplayMode === 'Displayed' && t('security_settings.always_displayed')}
+                        {adminGeneralSecurityContainer.state.currentOwnerRestrictionDisplayMode === 'Hidden' && t('security_settings.always_hidden')}
+                      </span>
+                    </button>
+                    <div className="dropdown-menu" aria-labelledby="isShowRestrictedByOwner">
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeOwnerRestrictionDisplayMode('Displayed') }}
+                      >
+                        {t('security_settings.always_displayed')}
+                      </button>
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeOwnerRestrictionDisplayMode('Hidden') }}
+                      >
+                        {t('security_settings.always_hidden')}
+                      </button>
+                    </div>
                   </div>
-                </td>
-              </tr>
-              <tr>
-                <th scope="row">{ t('only_inside_the_group') }</th>
-                <td>
-                  <div className="form-check form-switch form-check-success">
-                    <input
-                      type="checkbox"
-                      className="form-check-input"
+                </div>
+
+                {/* Group Restriction Dropdown */}
+                <div className="">
+                  <div className="dropdown">
+                    <button
+                      className="btn btn-outline-secondary dropdown-toggle text-end col-12 col-md-auto"
+                      type="button"
                       id="isShowRestrictedByGroup"
-                      checked={!adminGeneralSecurityContainer.state.isShowRestrictedByGroup}
-                      onChange={() => { adminGeneralSecurityContainer.switchIsShowRestrictedByGroup() }}
-                    />
-                    <label className="form-label form-check-label" htmlFor="isShowRestrictedByGroup">
-                      {t('security_settings.displayed_or_hidden')}
-                    </label>
+                      data-bs-toggle="dropdown"
+                      aria-haspopup="true"
+                      aria-expanded="true"
+                    >
+                      <span className="float-start">
+                        {adminGeneralSecurityContainer.state.currentGroupRestrictionDisplayMode === 'Displayed' && t('security_settings.always_displayed')}
+                        {adminGeneralSecurityContainer.state.currentGroupRestrictionDisplayMode === 'Hidden' && t('security_settings.always_hidden')}
+                      </span>
+                    </button>
+                    <div className="dropdown-menu" aria-labelledby="isShowRestrictedByGroup">
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeGroupRestrictionDisplayMode('Displayed') }}
+                      >
+                        {t('security_settings.always_displayed')}
+                      </button>
+                      <button
+                        className="dropdown-item"
+                        type="button"
+                        onClick={() => { adminGeneralSecurityContainer.changeGroupRestrictionDisplayMode('Hidden') }}
+                      >
+                        {t('security_settings.always_hidden')}
+                      </button>
+                    </div>
                   </div>
-                </td>
-              </tr>
-            </tbody>
-          </table>
+                </div>
+              </div>
+            </div>
+          </div>
         </div>
 
         <h4 className="mb-3">{t('security_settings.page_access_rights')}</h4>

+ 26 - 3
apps/app/src/client/components/PageControls/PageControls.tsx

@@ -8,6 +8,7 @@ import type {
 import {
   isIPageInfoForEntity, isIPageInfoForOperation,
 } from '@growi/core';
+import { pagePathUtils } from '@growi/core/dist/utils';
 import { useRect } from '@growi/ui/dist/utils';
 import { useTranslation } from 'next-i18next';
 import { DropdownItem } from 'reactstrap';
@@ -17,7 +18,9 @@ import {
 } from '~/client/services/page-operation';
 import { toastError } from '~/client/util/toastr';
 import OpenDefaultAiAssistantButton from '~/features/openai/client/components/AiAssistant/OpenDefaultAiAssistantButton';
-import { useIsGuestUser, useIsReadOnlyUser, useIsSearchPage } from '~/stores-universal/context';
+import {
+  useIsGuestUser, useIsReadOnlyUser, useIsSearchPage, useIsUsersHomepageDeletionEnabled,
+} from '~/stores-universal/context';
 import {
   EditorMode, useEditorMode,
 } from '~/stores-universal/ui';
@@ -27,7 +30,7 @@ import {
 } from '~/stores/ui';
 import loggerFactory from '~/utils/logger';
 
-import { useSWRxPageInfo, useSWRxTagsInfo } from '../../../stores/page';
+import { useSWRxPageInfo, useSWRxTagsInfo, useCurrentPagePath } from '../../../stores/page';
 import { useSWRxUsersList } from '../../../stores/user';
 import type { AdditionalMenuItemsRendererProps, ForceHideMenuItems } from '../Common/Dropdown/PageItemControl';
 import {
@@ -134,6 +137,10 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
   const { data: editorMode } = useEditorMode();
   const { data: isDeviceLargerThanMd } = useIsDeviceLargerThanMd();
   const { data: isSearchPage } = useIsSearchPage();
+  const { data: isUsersHomepageDeletionEnabled } = useIsUsersHomepageDeletionEnabled();
+  const { data: currentPagePath } = useCurrentPagePath();
+
+  const isUsersHomepage = currentPagePath == null ? false : pagePathUtils.isUsersHomepage(currentPagePath);
 
   const { mutate: mutatePageInfo } = useSWRxPageInfo(pageId, shareLinkId);
 
@@ -249,6 +256,22 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
     }
   }, [expandContentWidth, isGuestUser, isReadOnlyUser, onClickSwitchContentWidth, pageId, pageInfo]);
 
+  const isEnableActions = useMemo(() => {
+    if (isGuestUser) {
+      return false;
+    }
+
+    if (currentPagePath == null) {
+      return false;
+    }
+
+    if (isUsersHomepage && !isUsersHomepageDeletionEnabled) {
+      return false;
+    }
+
+    return true;
+  }, [isGuestUser, isUsersHomepage, isUsersHomepageDeletionEnabled]);
+
   const additionalMenuItemOnTopRenderer = useMemo(() => {
     if (!isIPageInfoForEntity(pageInfo)) {
       return undefined;
@@ -332,7 +355,7 @@ const PageControlsSubstance = (props: PageControlsSubstanceProps): JSX.Element =
         <PageItemControl
           pageId={pageId}
           pageInfo={pageInfo}
-          isEnableActions={!isGuestUser}
+          isEnableActions={isEnableActions}
           isReadOnlyUser={!!isReadOnlyUser}
           forceHideMenuItems={forceHideMenuItemsWithAdditions}
           additionalMenuItemOnTopRenderer={!isReadOnlyUser ? additionalMenuItemOnTopRenderer : undefined}

+ 23 - 19
apps/app/src/client/services/AdminGeneralSecurityContainer.js

@@ -32,13 +32,14 @@ export default class AdminGeneralSecurityContainer extends Container {
       currentPageRecursiveDeletionAuthority: PageRecursiveDeleteConfigValue.Inherit,
       currentPageCompleteDeletionAuthority: PageSingleDeleteCompConfigValue.AdminOnly,
       currentPageRecursiveCompleteDeletionAuthority: PageRecursiveDeleteCompConfigValue.Inherit,
+      currentGroupRestrictionDisplayMode: 'Hidden',
+      currentOwnerRestrictionDisplayMode: 'Hidden',
       isAllGroupMembershipRequiredForPageCompleteDeletion: true,
       previousPageRecursiveDeletionAuthority: null,
       previousPageRecursiveCompleteDeletionAuthority: null,
       expandOtherOptionsForDeletion: false,
       expandOtherOptionsForCompleteDeletion: false,
       isShowRestrictedByOwner: false,
-      isShowRestrictedByGroup: false,
       isUsersHomepageDeletionEnabled: false,
       isForceDeleteUserHomepageOnUserDeletion: false,
       isRomUserAllowedToComment: false,
@@ -56,6 +57,8 @@ export default class AdminGeneralSecurityContainer extends Container {
       shareLinksActivePage: 1,
     };
 
+    this.changeOwnerRestrictionDisplayMode = this.changeOwnerRestrictionDisplayMode.bind(this);
+    this.changeGroupRestrictionDisplayMode = this.changeGroupRestrictionDisplayMode.bind(this);
     this.changePageDeletionAuthority = this.changePageDeletionAuthority.bind(this);
     this.changePageCompleteDeletionAuthority = this.changePageCompleteDeletionAuthority.bind(this);
     this.changePageRecursiveDeletionAuthority = this.changePageRecursiveDeletionAuthority.bind(this);
@@ -76,8 +79,9 @@ export default class AdminGeneralSecurityContainer extends Container {
       currentPageRecursiveDeletionAuthority: generalSetting.pageRecursiveDeletionAuthority,
       currentPageRecursiveCompleteDeletionAuthority: generalSetting.pageRecursiveCompleteDeletionAuthority,
       isAllGroupMembershipRequiredForPageCompleteDeletion: generalSetting.isAllGroupMembershipRequiredForPageCompleteDeletion,
-      isShowRestrictedByOwner: !generalSetting.hideRestrictedByOwner,
-      isShowRestrictedByGroup: !generalSetting.hideRestrictedByGroup,
+      // Set display to 'Hidden' if hideRestrictedByOwner is anything but false.
+      currentOwnerRestrictionDisplayMode: generalSetting.hideRestrictedByOwner === false ? 'Displayed' : 'Hidden',
+      currentGroupRestrictionDisplayMode: generalSetting.hideRestrictedByGroup === false ? 'Displayed' : 'Hidden',
       isUsersHomepageDeletionEnabled: generalSetting.isUsersHomepageDeletionEnabled,
       isForceDeleteUserHomepageOnUserDeletion: generalSetting.isForceDeleteUserHomepageOnUserDeletion,
       isRomUserAllowedToComment: generalSetting.isRomUserAllowedToComment,
@@ -123,6 +127,20 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.setState({ disableLinkSharing });
   }
 
+  /**
+   * Change ownerRestrictionDisplayMode
+   */
+  changeOwnerRestrictionDisplayMode(mode) {
+    this.setState({ currentOwnerRestrictionDisplayMode: mode });
+  }
+
+  /**
+   * Change groupRestrictionDisplayMode
+   */
+  changeGroupRestrictionDisplayMode(mode) {
+    this.setState({ currentGroupRestrictionDisplayMode: mode });
+  }
+
   /**
    * Change restrictGuestMode
    */
@@ -194,20 +212,6 @@ export default class AdminGeneralSecurityContainer extends Container {
     this.setState({ expandOtherOptionsForCompleteDeletion: bool });
   }
 
-  /**
-   * Switch showRestrictedByOwner
-   */
-  switchIsShowRestrictedByOwner() {
-    this.setState({ isShowRestrictedByOwner:  !this.state.isShowRestrictedByOwner });
-  }
-
-  /**
-   * Switch showRestrictedByGroup
-   */
-  switchIsShowRestrictedByGroup() {
-    this.setState({ isShowRestrictedByGroup:  !this.state.isShowRestrictedByGroup });
-  }
-
   /**
    * Switch isUsersHomepageDeletionEnabled
    */
@@ -245,8 +249,8 @@ export default class AdminGeneralSecurityContainer extends Container {
       pageRecursiveDeletionAuthority: this.state.currentPageRecursiveDeletionAuthority,
       pageRecursiveCompleteDeletionAuthority: this.state.currentPageRecursiveCompleteDeletionAuthority,
       isAllGroupMembershipRequiredForPageCompleteDeletion: this.state.isAllGroupMembershipRequiredForPageCompleteDeletion,
-      hideRestrictedByGroup: !this.state.isShowRestrictedByGroup,
-      hideRestrictedByOwner: !this.state.isShowRestrictedByOwner,
+      hideRestrictedByGroup: this.state.currentGroupRestrictionDisplayMode === 'Hidden',
+      hideRestrictedByOwner: this.state.currentOwnerRestrictionDisplayMode === 'Hidden',
       isUsersHomepageDeletionEnabled: this.state.isUsersHomepageDeletionEnabled,
       isForceDeleteUserHomepageOnUserDeletion: this.state.isForceDeleteUserHomepageOnUserDeletion,
       isRomUserAllowedToComment: this.state.isRomUserAllowedToComment,

+ 0 - 15
apps/app/src/features/external-user-group/server/routes/apiv3/external-user-group.ts

@@ -104,7 +104,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: listExternalUserGroups
    *         summary: /external-user-groups
    *         parameters:
    *           - name: page
@@ -175,7 +174,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: getAncestors
    *         summary: /external-user-groups/ancestors
    *         parameters:
    *           - name: groupId
@@ -222,7 +220,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: listChildren
    *         summary: /external-user-groups/children
    *         parameters:
    *           - name: parentIds
@@ -280,7 +277,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: getExternalUserGroup
    *         summary: /external-user-groups/{id}
    *         parameters:
    *           - name: id
@@ -323,7 +319,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: deleteExternalUserGroup
    *         summary: /external-user-groups/{id}
    *         parameters:
    *           - name: id
@@ -399,7 +394,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: updateExternalUserGroup
    *         summary: /external-user-groups/{id}
    *         parameters:
    *           - name: id
@@ -459,7 +453,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: getExternalUserGroupRelations
    *         summary: /external-user-groups/{id}/external-user-group-relations
    *         parameters:
    *           - name: id
@@ -508,7 +501,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: getLdapSyncSettings
    *         summary: Get LDAP sync settings
    *         responses:
    *           200:
@@ -559,7 +551,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: getKeycloakSyncSettings
    *         summary: Get Keycloak sync settings
    *         responses:
    *           200:
@@ -610,7 +601,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: updateLdapSyncSettings
    *         summary: Update LDAP sync settings
    *         requestBody:
    *           required: true
@@ -689,7 +679,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: updateKeycloakSyncSettings
    *         summary: /external-user-groups/keycloak/sync-settings
    *         requestBody:
    *           required: true
@@ -763,7 +752,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: syncExternalUserGroupsLdap
    *         summary: Start LDAP sync process
    *         responses:
    *           202:
@@ -811,7 +799,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: syncExternalUserGroupsKeycloak
    *         summary: /external-user-groups/keycloak/sync
    *         responses:
    *           202:
@@ -875,7 +862,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: getExternalUserGroupsLdapSyncStatus
    *         summary: Get LDAP sync status
    *         responses:
    *           200:
@@ -899,7 +885,6 @@ module.exports = (crowi: Crowi): Router => {
    *         tags: [ExternalUserGroups]
    *         security:
    *           - cookieAuth: []
-   *         operationId: getExternalUserGroupsLdapSyncStatus
    *         summary: /external-user-groups/ldap/sync-status
    *         responses:
    *           200:

+ 6 - 2
apps/app/src/pages/[[...path]].page.tsx

@@ -47,7 +47,7 @@ import {
   useIsLocalAccountRegistrationEnabled,
   useIsRomUserAllowedToComment,
   useIsPdfBulkExportEnabled,
-  useIsAiEnabled, useLimitLearnablePageCountPerAssistant,
+  useIsAiEnabled, useLimitLearnablePageCountPerAssistant, useIsUsersHomepageDeletionEnabled,
 } from '~/stores-universal/context';
 import { useEditingMarkdown } from '~/stores/editor';
 import {
@@ -200,6 +200,7 @@ type Props = CommonProps & {
 
   aiEnabled: boolean,
   limitLearnablePageCountPerAssistant: number,
+  isUsersHomepageDeletionEnabled: boolean,
 };
 
 const Page: NextPageWithLayout<Props> = (props: Props) => {
@@ -258,6 +259,9 @@ const Page: NextPageWithLayout<Props> = (props: Props) => {
   useIsAiEnabled(props.aiEnabled);
   useLimitLearnablePageCountPerAssistant(props.limitLearnablePageCountPerAssistant);
 
+  useIsUsersHomepageDeletionEnabled(props.isUsersHomepageDeletionEnabled);
+
+
   const { pageWithMeta } = props;
 
   const pageId = pageWithMeta?.data._id;
@@ -576,7 +580,7 @@ function injectServerConfigurations(context: GetServerSidePropsContext, props: P
 
   props.aiEnabled = configManager.getConfig('app:aiEnabled');
   props.limitLearnablePageCountPerAssistant = configManager.getConfig('openai:limitLearnablePageCountPerAssistant');
-
+  props.isUsersHomepageDeletionEnabled = configManager.getConfig('security:user-homepage-deletion:isEnabled');
   props.isSearchServiceConfigured = searchService.isConfigured;
   props.isSearchServiceReachable = searchService.isReachable;
   props.isSearchScopeChildrenAsDefault = configManager.getConfig('customize:isSearchScopeChildrenAsDefault');

+ 10 - 0
apps/app/src/server/models/openapi/object-id.ts

@@ -0,0 +1,10 @@
+/**
+ * @swagger
+ *
+ *  components:
+ *    schemas:
+ *      ObjectId:
+ *        type: string
+ *        description: Object ID
+ *        example: 5ae5fccfc5577b0004dbd8ab
+ */

+ 11 - 9
apps/app/src/server/models/openapi/page.ts

@@ -3,14 +3,20 @@
  *
  *  components:
  *    schemas:
+ *      PagePath:
+ *        description: Page path
+ *        type: string
+ *        example: /path/to/page
+ *      PageGrant:
+ *        description: Grant for page
+ *        type: number
+ *        example: 1
  *      Page:
  *        description: Page
  *        type: object
  *        properties:
  *          _id:
- *            type: string
- *            description: page ID
- *            example: 5e07345972560e001761fa63
+ *            $ref: '#/components/schemas/ObjectId'
  *          __v:
  *            type: number
  *            description: DB record version
@@ -30,9 +36,7 @@
  *            description: extend data
  *            example: {}
  *          grant:
- *            type: number
- *            description: grant
- *            example: 1
+ *            $ref: '#/components/schemas/PageGrant'
  *          grantedUsers:
  *            type: array
  *            description: granted users
@@ -50,9 +54,7 @@
  *              description: user ID
  *            example: []
  *          path:
- *            type: string
- *            description: page path
- *            example: /
+ *            $ref: '#/components/schemas/PagePath'
  *          revision:
  *            type: string
  *            description: page revision

+ 12 - 8
apps/app/src/server/models/openapi/paginate-result.js → apps/app/src/server/models/openapi/paginate-result.ts

@@ -4,6 +4,14 @@
  *
  *  components:
  *    schemas:
+ *      Offset:
+ *        description: Offset for pagination
+ *        type: integer
+ *        example: 0
+ *      Limit:
+ *        description: Limit for pagination
+ *        type: integer
+ *        example: 10
  *      PaginateResult:
  *        description: PaginateResult
  *        type: object
@@ -17,8 +25,7 @@
  *            type: number
  *            description: Total number of documents in collection that match a query
  *          limit:
- *            type: number
- *            description: Limit that was used
+ *            $ref: '#/components/schemas/Limit'
  *          hasPrevPage:
  *            type: number
  *            description: Availability of prev page.
@@ -32,8 +39,8 @@
  *            type: number
  *            description: Total number of pages.
  *          offset:
- *            type: number
  *            description: Only if specified or default page/offset values were used
+ *            $ref: '#/components/schemas/Offset'
  *          prefPage:
  *            type: number
  *            description: Previous page number if available or NULL
@@ -66,13 +73,10 @@
  *                description: Total number of documents in collection that match a query
  *                example: 35
  *              limit:
- *                type: integer
- *                description: Limit that was used
- *                example: 10
+ *                $ref: '#/components/schemas/Limit'
  *              offset:
- *                type: integer
  *                description: Only if specified or default page/offset values were used
- *                example: 20
+ *                $ref: '#/components/schemas/Offset'
  *          data:
  *            type: object
  *            description: Object of pagination meta data.

+ 12 - 10
apps/app/src/server/models/openapi/revision.ts

@@ -3,27 +3,29 @@
  *
  *  components:
  *    schemas:
+ *      RevisionBody:
+ *        description: Revision content body
+ *        type: string
+ *        example: |
+ *          # Header
+ *
+ *          - foo
+ *          - bar
+ *
  *      Revision:
  *        description: Revision
  *        type: object
  *        properties:
  *          _id:
- *            type: string
- *            description: revision ID
- *            example: 5e0734e472560e001761fa68
+ *            $ref: '#/components/schemas/ObjectId'
  *          __v:
  *            type: number
  *            description: DB record version
  *            example: 0
  *          author:
- *            $ref: '#/components/schemas/User/properties/_id'
+ *            $ref: '#/components/schemas/ObjectId'
  *          body:
- *            type: string
- *            description: content body
- *            example: |
- *              # test
- *
- *              test
+ *            $ref: '#/components/schemas/RevisionBody'
  *          format:
  *            type: string
  *            description: format

+ 32 - 0
apps/app/src/server/models/openapi/tag.ts

@@ -0,0 +1,32 @@
+/**
+ * @swagger
+ *
+ *  components:
+ *    schemas:
+ *      Tags:
+ *        description: Tags
+ *        type: array
+ *        items:
+ *          $ref: '#/components/schemas/TagName'
+ *        example: ['daily', 'report', 'tips']
+ *
+ *      TagName:
+ *        description: Tag name
+ *        type: string
+ *        example: daily
+ *
+ *      Tag:
+ *        description: Tag
+ *        type: object
+ *        properties:
+ *          _id:
+ *            type: string
+ *            description: tag ID
+ *            example: 5e2d6aede35da4004ef7e0b7
+ *          name:
+ *            $ref: '#/components/schemas/TagName'
+ *          count:
+ *            type: number
+ *            description: Count of tagged pages
+ *            example: 3
+ */

+ 4 - 3
apps/app/src/server/models/openapi/v1-response.js

@@ -3,14 +3,15 @@
  *
  *  components:
  *    schemas:
+ *      V1ResponseOK:
+ *        description: API is succeeded
+ *        type: boolean
  *      V1Response:
  *        description: Response v1
  *        type: object
  *        properties:
  *          ok:
- *            type: boolean
- *            description: API is succeeded
- *            example: true
+ *            $ref: '#/components/schemas/V1ResponseOK'
  *    responses:
  *      403:
  *        description: 'Forbidden'

+ 0 - 1
apps/app/src/server/routes/apiv3/admin-home.ts

@@ -70,7 +70,6 @@ module.exports = (crowi) => {
    *    /admin-home/:
    *      get:
    *        tags: [AdminHome]
-   *        operationId: getAdminHome
    *        summary: /admin-home
    *        security:
    *          - cookieAuth: []

+ 0 - 10
apps/app/src/server/routes/apiv3/app-settings.js

@@ -419,7 +419,6 @@ module.exports = (crowi) => {
    *    /app-settings:
    *      get:
    *        tags: [AppSettings]
-   *        operationId: getAppSettings
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []
@@ -516,7 +515,6 @@ module.exports = (crowi) => {
    *    /app-settings/app-setting:
    *      put:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettings
    *        security:
    *          - cookieAuth: []
    *        summary: /app-settings/app-setting
@@ -579,7 +577,6 @@ module.exports = (crowi) => {
    *    /app-settings/site-url-setting:
    *      put:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettingSiteUrlSetting
    *        security:
    *          - cookieAuth: []
    *        summary: /app-settings/site-url-setting
@@ -731,7 +728,6 @@ module.exports = (crowi) => {
    *    /app-settings/smtp-setting:
    *      put:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettingSmtpSetting
    *        security:
    *          - cookieAuth: []
    *        summary: /app-settings/smtp-setting
@@ -785,7 +781,6 @@ module.exports = (crowi) => {
    *    /app-settings/smtp-test:
    *      post:
    *        tags: [AppSettings]
-   *        operationId: postSmtpTest
    *        security:
    *          - cookieAuth: []
    *        summary: /app-settings/smtp-setting
@@ -822,7 +817,6 @@ module.exports = (crowi) => {
    *    /app-settings/ses-setting:
    *      put:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettingSesSetting
    *        security:
    *          - cookieAuth: []
    *        summary: /app-settings/ses-setting
@@ -876,7 +870,6 @@ module.exports = (crowi) => {
    *    /app-settings/file-upload-settings:
    *      put:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettingFileUploadSetting
    *        security:
    *          - cookieAuth: []
    *        summary: /app-settings/file-upload-setting
@@ -988,7 +981,6 @@ module.exports = (crowi) => {
    *    /app-settings/questionnaire-settings:
    *      put:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettingQuestionnaireSettings
    *        security:
    *          - cookieAuth: []
    *        summary: /app-settings/questionnaire-settings
@@ -1075,7 +1067,6 @@ module.exports = (crowi) => {
    *    /app-settings/v5-schema-migration:
    *      post:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettingV5SchemaMigration
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []
@@ -1122,7 +1113,6 @@ module.exports = (crowi) => {
    *    /app-settings/maintenance-mode:
    *      post:
    *        tags: [AppSettings]
-   *        operationId: updateAppSettingMaintenanceMode
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []

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

@@ -248,7 +248,6 @@ module.exports = (crowi) => {
    *    /attachment/limit:
    *      get:
    *        tags: [Attachment]
-   *        operationId: getAttachmentLimit
    *        summary: /attachment/limit
    *        description: Get available capacity of uploaded file with GridFS
    *        parameters:
@@ -295,7 +294,6 @@ module.exports = (crowi) => {
    *    /attachment:
    *      post:
    *        tags: [Attachment]
-   *        operationId: addAttachment
    *        summary: /attachment
    *        description: Add attachment to the page
    *        requestBody:

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

@@ -129,7 +129,6 @@ module.exports = (crowi) => {
    *    /bookmark-folder:
    *      post:
    *        tags: [BookmarkFolders]
-   *        operationId: createBookmarkFolder
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []
@@ -187,7 +186,6 @@ module.exports = (crowi) => {
    *    /bookmark-folder/list/{userId}:
    *      get:
    *        tags: [BookmarkFolders]
-   *        operationId: listBookmarkFolders
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []
@@ -277,7 +275,6 @@ module.exports = (crowi) => {
    *    /bookmark-folder/{id}:
    *      delete:
    *        tags: [BookmarkFolders]
-   *        operationId: deleteBookmarkFolder
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []
@@ -321,7 +318,6 @@ module.exports = (crowi) => {
    *    /bookmark-folder:
    *      put:
    *        tags: [BookmarkFolders]
-   *        operationId: updateBookmarkFolder
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []
@@ -380,7 +376,6 @@ module.exports = (crowi) => {
    *    /bookmark-folder/add-bookmark-to-folder:
    *      post:
    *        tags: [BookmarkFolders]
-   *        operationId: addBookmarkToFolder
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []
@@ -433,7 +428,6 @@ module.exports = (crowi) => {
    *    /bookmark-folder/update-bookmark:
    *      put:
    *        tags: [BookmarkFolders]
-   *        operationId: updateBookmarkInFolder
    *        security:
    *          - bearer: []
    *          - accessTokenInQuery: []

+ 1 - 4
apps/app/src/server/routes/apiv3/bookmarks.js

@@ -43,7 +43,7 @@ const router = express.Router();
  *          page:
  *            $ref: '#/components/schemas/Page'
  *          user:
- *            $ref: '#/components/schemas/User/properties/_id'
+ *            $ref: '#/components/schemas/ObjectId'
  *      Bookmarks:
  *        description: User Root Bookmarks
  *        type: object
@@ -111,7 +111,6 @@ module.exports = (crowi) => {
    *        tags: [Bookmarks]
    *        summary: /bookmarks/info
    *        description: Get bookmarked info
-   *        operationId: getBookmarkedInfo
    *        parameters:
    *          - name: pageId
    *            in: query
@@ -174,7 +173,6 @@ module.exports = (crowi) => {
    *        tags: [Bookmarks]
    *        summary: /bookmarks/{userId}
    *        description: Get my bookmarked status
-   *        operationId: getMyBookmarkedStatus
    *        parameters:
    *          - name: userId
    *            in: path
@@ -236,7 +234,6 @@ module.exports = (crowi) => {
    *        tags: [Bookmarks]
    *        summary: /bookmarks
    *        description: Update bookmarked status
-   *        operationId: updateBookmarkedStatus
    *        requestBody:
    *          content:
    *            application/json:

+ 1 - 17
apps/app/src/server/routes/apiv3/customize-setting.js

@@ -261,7 +261,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getCustomizeSetting
    *        summary: /customize-setting
    *        description: Get customize parameters
    *        responses:
@@ -308,7 +307,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getLayoutCustomizeSetting
    *        summary: /customize-setting/layout
    *        description: Get layout
    *        responses:
@@ -337,7 +335,6 @@ module.exports = (crowi) => {
    *    /customize-setting/layout:
    *      put:
    *        tags: [CustomizeSetting]
-   *        operationId: updateLayoutCustomizeSetting
    *        summary: /customize-setting/layout
    *        description: Update layout
    *        requestBody:
@@ -392,7 +389,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getThemeCustomizeSetting
    *        summary: /customize-setting/theme
    *        description: Get theme
    *        responses:
@@ -441,7 +437,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateThemeCustomizeSetting
    *        summary: /customize-setting/theme
    *        description: Update theme
    *        requestBody:
@@ -492,7 +487,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getCustomeSettingSidebar
    *        summary: /customize-setting/sidebar
    *        description: Get sidebar
    *        responses:
@@ -525,7 +519,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateCustomizeSettingSidebar
    *        summary: /customize-setting/sidebar
    *        description: Update sidebar
    *        requestBody:
@@ -579,7 +572,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *         - cookieAuth: []
-   *        operationId: updateFunctionCustomizeSetting
    *        summary: /customize-setting/function
    *        description: Update function
    *        requestBody:
@@ -649,7 +641,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *         - cookieAuth: []
-   *        operationId: updatePresentationCustomizeSetting
    *        summary: /customize-setting/presentation
    *        description: Update presentation
    *        requestBody:
@@ -700,7 +691,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateHighlightCustomizeSetting
    *        summary: /customize-setting/highlight
    *        description: Update highlight
    *        requestBody:
@@ -753,7 +743,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateCustomizeTitleCustomizeSetting
    *        summary: /customize-setting/customizeTitle
    *        description: Update title
    *        requestBody:
@@ -807,7 +796,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateCustomizeNoscriptCustomizeSetting
    *        summary: /customize-setting/customize-noscript
    *        description: Update noscript
    *        requestBody:
@@ -857,7 +845,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateCustomizeCssCustomizeSetting
    *        summary: /customize-setting/customize-css
    *        description: Update customize css
    *        requestBody:
@@ -910,7 +897,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateCustomizeScriptCustomizeSetting
    *        summary: /customize-setting/customize-script
    *        description: Update customize script
    *        requestBody:
@@ -960,7 +946,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateCustomizeLogoCustomizeSetting
    *        summary: /customize-setting/customize-logo
    *        description: Update customize logo
    *        requestBody:
@@ -1012,7 +997,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: uploadBrandLogoCustomizeSetting
    *        summary: /customize-setting/upload-brand-logo
    *        description: Upload brand logo
    *        requestBody:
@@ -1023,6 +1007,7 @@ module.exports = (crowi) => {
    *               type: object
    *               properties:
    *                 file:
+   *                   type: string
    *                   format: binary
    *        responses:
    *          200:
@@ -1089,7 +1074,6 @@ module.exports = (crowi) => {
    *        tags: [CustomizeSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: deleteBrandLogoCustomizeSetting
    *        summary: /customize-setting/delete-brand-logo
    *        description: Delete brand logo
    *        responses:

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

@@ -157,7 +157,6 @@ module.exports = (crowi) => {
    *  /export/status:
    *    get:
    *      tags: [Export]
-   *      operationId: getExportStatus
    *      summary: /export/status
    *      description: get properties of stored zip files for export
    *      responses:
@@ -189,7 +188,6 @@ module.exports = (crowi) => {
    *  /export:
    *    post:
    *      tags: [Export]
-   *      operationId: createExport
    *      summary: /export
    *      description: generate zipped jsons for collections
    *      requestBody:
@@ -242,7 +240,6 @@ module.exports = (crowi) => {
    *  /export/{fileName}:
    *    delete:
    *      tags: [Export]
-   *      operationId: deleteExport
    *      summary: /export/{fileName}
    *      description: delete the file
    *      parameters:

+ 2 - 0
apps/app/src/server/routes/apiv3/g2g-transfer.ts

@@ -220,6 +220,7 @@ module.exports = (crowi: Crowi): Router => {
    *              type: object
    *              properties:
    *                file:
+   *                  type: string
    *                  format: binary
    *                  description: The zip file of the data to be transferred
    *                collections:
@@ -348,6 +349,7 @@ module.exports = (crowi: Crowi): Router => {
    *              type: object
    *              properties:
    *                file:
+   *                  type: string
    *                  format: binary
    *                  description: The zip file of the data to be transferred
    *                attachmentMetadata:

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

@@ -111,7 +111,6 @@ module.exports = (crowi) => {
    *    get:
    *      tags: [Healthcheck]
    *      security: []
-   *      operationId: getHealthcheck
    *      summary: /healthcheck
    *      description: Check whether the server is healthy or not
    *      parameters:

+ 1 - 5
apps/app/src/server/routes/apiv3/import.js

@@ -172,7 +172,6 @@ export default function route(crowi) {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: getImportSettingsParams
    *      summary: /import
    *      description: Get import settings params
    *      responses:
@@ -225,7 +224,6 @@ export default function route(crowi) {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: getImportStatus
    *      summary: /import/status
    *      description: Get properties of stored zip files for import
    *      responses:
@@ -257,7 +255,6 @@ export default function route(crowi) {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: executeImport
    *      summary: /import
    *      description: import a collection from a zipped json
    *      requestBody:
@@ -390,7 +387,6 @@ export default function route(crowi) {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: uploadImport
    *      summary: /import/upload
    *      description: upload a zip file
    *      requestBody:
@@ -400,6 +396,7 @@ export default function route(crowi) {
    *              type: object
    *              properties:
    *                file:
+   *                  type: string
    *                  format: binary
    *      responses:
    *        200:
@@ -449,7 +446,6 @@ export default function route(crowi) {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: deleteImportAll
    *      summary: /import/all
    *      description: Delete all zip files
    *      responses:

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

@@ -107,7 +107,6 @@ module.exports = (crowi) => {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: getInAppNotificationList
    *      summary: /in-app-notification/list
    *      description: Get the list of in-app notifications
    *      parameters:
@@ -200,7 +199,6 @@ module.exports = (crowi) => {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: getInAppNotificationStatus
    *      summary: /in-app-notification/status
    *      description: Get the status of in-app notifications
    *      responses:
@@ -238,7 +236,6 @@ module.exports = (crowi) => {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: openInAppNotification
    *      summary: /in-app-notification/open
    *      description: Open the in-app notification
    *      requestBody:
@@ -286,7 +283,6 @@ module.exports = (crowi) => {
    *      security:
    *        - bearer: []
    *        - accessTokenInQuery: []
-   *      operationId: openAllInAppNotification
    *      summary: /in-app-notification/all-statuses-open
    *      description: Open all in-app notifications
    *      responses:

+ 0 - 1
apps/app/src/server/routes/apiv3/installer.ts

@@ -43,7 +43,6 @@ module.exports = (crowi: Crowi): Router => {
    *    post:
    *      tags: [Install]
    *      security: []
-   *      operationId: Install
    *      summary: /installer
    *      description: Install GROWI
    *      requestBody:

+ 0 - 1
apps/app/src/server/routes/apiv3/invited.ts

@@ -26,7 +26,6 @@ module.exports = (crowi: Crowi): Router => {
    *      tags: [Users]
    *      security:
    *        - cookieAuth: []
-   *      operationId: activateInvitedUser
    *      summary: /invited
    *      description: Activate invited user
    *      requestBody:

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

@@ -136,7 +136,6 @@ module.exports = (crowi) => {
    *        tags: [MarkDownSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getMarkdownSetting
    *        summary: Get markdown parameters
    *        responses:
    *          200:
@@ -173,7 +172,6 @@ module.exports = (crowi) => {
    *        tags: [MarkDownSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateLineBreakMarkdownSetting
    *        summary: Update lineBreak setting
    *        requestBody:
    *          required: true
@@ -228,7 +226,6 @@ module.exports = (crowi) => {
    *        tags: [MarkDownSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateIndentMarkdownSetting
    *        summary: Update indent setting
    *        requestBody:
    *          required: true
@@ -284,7 +281,6 @@ module.exports = (crowi) => {
    *        tags: [MarkDownSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateXssMarkdownSetting
    *        summary: Update XSS setting
    *        description: Update xss
    *        requestBody:

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

@@ -18,7 +18,6 @@ module.exports = (crowi) => {
    *  /mongo/collections:
    *    get:
    *      tags: [MongoDB]
-   *      operationId: getMongoCollections
    *      summary: /mongo/collections
    *      description: get mongodb collections names
    *      responses:

+ 10 - 18
apps/app/src/server/routes/apiv3/page/index.ts

@@ -191,7 +191,6 @@ module.exports = (crowi) => {
    *    /page:
    *      get:
    *        tags: [Page]
-   *        operationId: getPage
    *        summary: Get page
    *        description: get page by pagePath or pageId
    *        parameters:
@@ -199,12 +198,12 @@ module.exports = (crowi) => {
    *            in: query
    *            description: page id
    *            schema:
-   *              $ref: '#/components/schemas/Page/properties/_id'
+   *              $ref: '#/components/schemas/ObjectId'
    *          - name: path
    *            in: query
    *            description: page path
    *            schema:
-   *              $ref: '#/components/schemas/Page/properties/path'
+   *              $ref: '#/components/schemas/PagePath'
    *        responses:
    *          200:
    *            description: Page data
@@ -307,7 +306,6 @@ module.exports = (crowi) => {
    *      post:
    *        tags: [Page]
    *        summary: Create page
-   *        operationId: createPage
    *        description: Create page
    *        requestBody:
    *          content:
@@ -318,9 +316,9 @@ module.exports = (crowi) => {
    *                    type: string
    *                    description: Text of page
    *                  path:
-   *                    $ref: '#/components/schemas/Page/properties/path'
+   *                    $ref: '#/components/schemas/PagePath'
    *                  grant:
-   *                    $ref: '#/components/schemas/Page/properties/grant'
+   *                    $ref: '#/components/schemas/PageGrant'
    *                  grantUserGroupIds:
    *                    type: string
    *                    description: UserGroup ID
@@ -361,7 +359,6 @@ module.exports = (crowi) => {
    *    /page:
    *      put:
    *        tags: [Page]
-   *        operationId: updatePage
    *        description: Update page
    *        requestBody:
    *          content:
@@ -369,13 +366,13 @@ module.exports = (crowi) => {
    *              schema:
    *                properties:
    *                  body:
-   *                    $ref: '#/components/schemas/Revision/properties/body'
+   *                    $ref: '#/components/schemas/RevisionBody'
    *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                  revisionId:
-   *                    $ref: '#/components/schemas/Revision/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                  grant:
-   *                    $ref: '#/components/schemas/Page/properties/grant'
+   *                    $ref: '#/components/schemas/PageGrant'
    *                  userRelatedGrantUserGroupIds:
    *                    type: array
    *                    items:
@@ -429,7 +426,6 @@ module.exports = (crowi) => {
    *        tags: [Page]
    *        summary: Get page likes
    *        description: Update liked status
-   *        operationId: updateLikedStatus
    *        requestBody:
    *          content:
    *            application/json:
@@ -498,7 +494,6 @@ module.exports = (crowi) => {
    *        tags: [Page]
    *        summary: Get page info
    *        description: Retrieve current page info
-   *        operationId: getPageInfo
    *        requestBody:
    *          content:
    *            application/json:
@@ -542,13 +537,12 @@ module.exports = (crowi) => {
    *        tags: [Page]
    *        summary: Get page grant data
    *        description: Retrieve current page's grant data
-   *        operationId: getPageGrantData
    *        parameters:
    *          - name: pageId
    *            in: query
    *            description: page id
    *            schema:
-   *              $ref: '#/components/schemas/Page/properties/_id'
+   *              $ref: '#/components/schemas/ObjectId'
    *        responses:
    *          200:
    *            description: Successfully retrieved current grant data.
@@ -960,7 +954,6 @@ module.exports = (crowi) => {
    *          - cookieAuth: []
    *        summary: Get already exist paths
    *        description: Get already exist paths
-   *        operationId: getAlreadyExistPaths
    *        parameters:
    *          - name: fromPath
    *            in: query
@@ -1021,14 +1014,13 @@ module.exports = (crowi) => {
    *        tags: [Page]
    *        summary: Update subscription status
    *        description: Update subscription status
-   *        operationId: updateSubscriptionStatus
    *        requestBody:
    *          content:
    *            application/json:
    *              schema:
    *                properties:
    *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *        responses:
    *          200:
    *            description: Succeeded to update subscription status.

+ 5 - 42
apps/app/src/server/routes/apiv3/pages/index.js

@@ -30,35 +30,6 @@ const router = express.Router();
 const LIMIT_FOR_LIST = 10;
 const LIMIT_FOR_MULTIPLE_PAGE_OP = 20;
 
-/**
- * @swagger
- *
- *  components:
- *    schemas:
- *      Tags:
- *        description: Tags
- *        type: array
- *        items:
- *          $ref: '#/components/schemas/Tag/properties/name'
- *        example: ['daily', 'report', 'tips']
- *
- *      Tag:
- *        description: Tag
- *        type: object
- *        properties:
- *          _id:
- *            type: string
- *            description: tag ID
- *            example: 5e2d6aede35da4004ef7e0b7
- *          name:
- *            type: string
- *            description: tag name
- *            example: daily
- *          count:
- *            type: number
- *            description: Count of tagged pages
- *            example: 3
- */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 module.exports = (crowi) => {
   const loginRequired = require('../../../middlewares/login-required')(crowi, true);
@@ -229,7 +200,6 @@ module.exports = (crowi) => {
    *    /pages/rename:
    *      post:
    *        tags: [Pages]
-   *        operationId: renamePage
    *        description: Rename page
    *        requestBody:
    *          content:
@@ -237,9 +207,9 @@ module.exports = (crowi) => {
    *              schema:
    *                properties:
    *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                  path:
-   *                    $ref: '#/components/schemas/Page/properties/path'
+   *                    $ref: '#/components/schemas/PagePath'
    *                  revisionId:
    *                    type: string
    *                    description: revision ID
@@ -364,7 +334,6 @@ module.exports = (crowi) => {
     *    /pages/resume-rename:
     *      post:
     *        tags: [Pages]
-    *        operationId: resumeRenamePage
     *        description: Resume rename page operation
     *        requestBody:
     *          content:
@@ -372,7 +341,7 @@ module.exports = (crowi) => {
     *              schema:
     *                properties:
     *                  pageId:
-    *                    $ref: '#/components/schemas/Page/properties/_id'
+    *                    $ref: '#/components/schemas/ObjectId'
     *                required:
     *                  - pageId
     *        responses:
@@ -506,7 +475,6 @@ module.exports = (crowi) => {
     *    /pages/list:
     *      get:
     *        tags: [Pages]
-    *        operationId: getList
     *        description: Get list of pages
     *        parameters:
     *          - name: path
@@ -596,7 +564,6 @@ module.exports = (crowi) => {
    *    /pages/duplicate:
    *      post:
    *        tags: [Pages]
-   *        operationId: duplicatePage
    *        description: Duplicate page
    *        requestBody:
    *          content:
@@ -604,9 +571,9 @@ module.exports = (crowi) => {
    *              schema:
    *                properties:
    *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                  pageNameInput:
-   *                    $ref: '#/components/schemas/Page/properties/path'
+   *                    $ref: '#/components/schemas/PagePath'
    *                  isRecursively:
    *                    type: boolean
    *                    description: whether duplicate page with descendants
@@ -710,7 +677,6 @@ module.exports = (crowi) => {
    *    /pages/subordinated-list:
    *      get:
    *        tags: [Pages]
-   *        operationId: subordinatedList
    *        description: Get subordinated pages
    *        parameters:
    *          - name: path
@@ -760,7 +726,6 @@ module.exports = (crowi) => {
     *    /pages/delete:
     *      post:
     *        tags: [Pages]
-    *        operationId: deletePages
     *        description: Delete pages
     *        requestBody:
     *          content:
@@ -867,7 +832,6 @@ module.exports = (crowi) => {
    *    /pages/convert-pages-by-path:
    *      post:
    *        tags: [Pages]
-   *        operationId: convertPagesByPath
    *        description: Convert pages by path
    *        requestBody:
    *          content:
@@ -923,7 +887,6 @@ module.exports = (crowi) => {
    *    /pages/legacy-pages-migration:
    *      post:
    *        tags: [Pages]
-   *        operationId: legacyPagesMigration
    *        description: Migrate legacy pages
    *        requestBody:
    *          content:

+ 0 - 14
apps/app/src/server/routes/apiv3/personal-setting/index.js

@@ -140,7 +140,6 @@ module.exports = (crowi) => {
    *    /personal-setting:
    *      get:
    *        tags: [GeneralSetting]
-   *        operationId: getPersonalSetting
    *        summary: /personal-setting
    *        description: Get personal parameters
    *        responses:
@@ -179,7 +178,6 @@ module.exports = (crowi) => {
    *    /personal-setting/is-password-set:
    *      get:
    *        tags: [GeneralSetting]
-   *        operationId: getIsPasswordSet
    *        summary: /personal-setting
    *        description: Get whether a password has been set
    *        responses:
@@ -218,7 +216,6 @@ module.exports = (crowi) => {
    *    /personal-setting:
    *      put:
    *        tags: [GeneralSetting]
-   *        operationId: updatePersonalSetting
    *        summary: /personal-setting
    *        description: Update personal setting
    *        requestBody:
@@ -277,7 +274,6 @@ module.exports = (crowi) => {
    *    /personal-setting/image-type:
    *      put:
    *        tags: [GeneralSetting]
-   *        operationId: putUserImageType
    *        summary: /personal-setting/image-type
    *        description: Update user image type
    *        requestBody:
@@ -325,7 +321,6 @@ module.exports = (crowi) => {
    *    /personal-setting/external-accounts:
    *      get:
    *        tags: [GeneralSetting]
-   *        operationId: getExternalAccounts
    *        summary: /personal-setting/external-accounts
    *        description: Get external accounts that linked current user
    *        responses:
@@ -360,7 +355,6 @@ module.exports = (crowi) => {
    *    /personal-setting/password:
    *      put:
    *        tags: [GeneralSetting]
-   *        operationId: putUserPassword
    *        summary: /personal-setting/password
    *        description: Update user password
    *        requestBody:
@@ -417,7 +411,6 @@ module.exports = (crowi) => {
    *        tags: [GeneralSetting]
    *        security:
    *          - cookieAuth: []
-   *        operationId: putUserApiToken
    *        summary: /personal-setting/api-token
    *        description: Update user api token
    *        responses:
@@ -540,7 +533,6 @@ module.exports = (crowi) => {
    *    /personal-setting/associate-ldap:
    *      put:
    *        tags: [GeneralSetting]
-   *        operationId: associateLdapAccount
    *        summary: /personal-setting/associate-ldap
    *        description: associate Ldap account
    *        requestBody:
@@ -597,7 +589,6 @@ module.exports = (crowi) => {
    *    /personal-setting/disassociate-ldap:
    *      put:
    *        tags: [GeneralSetting]
-   *        operationId: disassociateLdapAccount
    *        summary: /personal-setting/disassociate-ldap
    *        description: disassociate Ldap account
    *        requestBody:
@@ -653,7 +644,6 @@ module.exports = (crowi) => {
    *    /personal-setting/editor-settings:
    *      put:
    *        tags: [EditorSetting]
-   *        operationId: putEditorSettings
    *        summary: /personal-setting/editor-settings
    *        description: Put editor preferences
    *        requestBody:
@@ -717,7 +707,6 @@ module.exports = (crowi) => {
    *    /personal-setting/editor-settings:
    *      get:
    *        tags: [EditorSetting]
-   *        operationId: getEditorSettings
    *        summary: /personal-setting/editor-settings
    *        description: Get editor preferences
    *        responses:
@@ -747,7 +736,6 @@ module.exports = (crowi) => {
    *    /personal-setting/in-app-notification-settings:
    *      put:
    *        tags: [InAppNotificationSettings]
-   *        operationId: putInAppNotificationSettings
    *        summary: /personal-setting/in-app-notification-settings
    *        description: Put InAppNotificationSettings
    *        requestBody:
@@ -804,7 +792,6 @@ module.exports = (crowi) => {
    *    /personal-setting/in-app-notification-settings:
    *      get:
    *        tags: [InAppNotificationSettings]
-   *        operationId: getInAppNotificationSettings
    *        summary: personal-setting/in-app-notification-settings
    *        description: Get InAppNotificationSettings
    *        responses:
@@ -835,7 +822,6 @@ module.exports = (crowi) => {
    *   /personal-setting/questionnaire-settings:
    *     put:
    *       tags: [QuestionnaireSetting]
-   *       operationId: putQuestionnaireSetting
    *       summary: /personal-setting/questionnaire-settings
    *       description: Update the questionnaire settings for the current user
    *       requestBody:

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

@@ -162,7 +162,6 @@ module.exports = (crowi) => {
    *    /slack-integration-settings/:
    *      get:
    *        tags: [SlackIntegrationSettings]
-   *        operationId: getSlackBotSettingParams
    *        summary: /slack-integration-settings
    *        description: Get current settings and connection statuses.
    *        responses:
@@ -321,7 +320,6 @@ module.exports = (crowi) => {
    *    /slack-integration-settings/bot-type/:
    *      put:
    *        tags: [SlackIntegrationSettings]
-   *        operationId: putBotType
    *        summary: /slack-integration/bot-type
    *        description: Put botType setting.
    *        requestBody:
@@ -361,7 +359,6 @@ module.exports = (crowi) => {
    *    /slack-integration/bot-type/:
    *      delete:
    *        tags: [SlackIntegrationSettings]
-   *        operationId: deleteBotType
    *        summary: /slack-integration/bot-type
    *        description: Delete botType setting.
    *        requestBody:
@@ -396,7 +393,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (without proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: putWithoutProxySettings
    *        summary: /slack-integration-settings/without-proxy/update-settings
    *        description: Update customBotWithoutProxy setting.
    *        requestBody:
@@ -450,7 +446,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (without proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: putWithoutProxyPermissions
    *        summary: /slack-integration-settings/without-proxy/update-permissions
    *        description: Update customBotWithoutProxy permissions.
    *        requestBody:
@@ -507,7 +502,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (with proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: putSlackAppIntegrations
    *        summary: /slack-integration-settings/slack-app-integrations
    *        description: Generate SlackAppIntegrations
    *        responses:
@@ -577,7 +571,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (with proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: deleteAccessTokens
    *        summary: /slack-integration-settings/slack-app-integrations/:id
    *        description: Delete accessTokens
    *        parameters:
@@ -629,7 +622,6 @@ module.exports = (crowi) => {
    *       tags: [SlackIntegrationSettings (with proxy)]
    *       security:
    *         - cookieAuth: []
-   *       operationId: putProxyUri
    *       summary: /slack-integration-settings/proxy-uri
    *       description: Update proxy uri
    *       requestBody:
@@ -679,7 +671,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (with proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: makePrimary
    *        summary: /slack-integration-settings/slack-app-integrations/:id/makeprimary
    *        description: Make SlackAppTokens primary
    *        parameters:
@@ -735,7 +726,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (with proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: putRegenerateTokens
    *        summary: /slack-integration-settings/slack-app-integrations/:id/regenerate-tokens
    *        description: Regenerate SlackAppTokens
    *        parameters:
@@ -781,7 +771,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (with proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: putSupportedCommands
    *        summary: /slack-integration-settings/slack-app-integrations/:id/permissions
    *        description: update supported commands
    *        parameters:
@@ -865,7 +854,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (with proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: postRelationTest
    *        summary: /slack-integration-settings/slack-app-integrations/:id/relation-test
    *        description: Delete botType setting.
    *        parameters:
@@ -952,7 +940,6 @@ module.exports = (crowi) => {
    *        tags: [SlackIntegrationSettings (without proxy)]
    *        security:
    *          - cookieAuth: []
-   *        operationId: postTest
    *        summary: /slack-integration-settings/without-proxy/test
    *        description: Test the connection with slack work space.
    *        requestBody:

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

@@ -121,7 +121,6 @@ module.exports = (crowi) => {
    *    get:
    *      tags: [Statistics]
    *      security: []
-   *      operationId: getStatisticsUser
    *      summary: /statistics/user
    *      description: Get statistics for user
    *      responses:

+ 0 - 1
apps/app/src/server/routes/apiv3/user-activation.ts

@@ -78,7 +78,6 @@ async function sendEmailToAllAdmins(userData, admins, appTitle, mailService, tem
  *     summary: /complete-registration
  *     tags: [Users]
  *     security: []
- *     operationId: completeRegistration
  *     requestBody:
  *       required: true
  *       content:

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

@@ -33,7 +33,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroupRelations]
    *        security:
    *          - cookieAuth: []
-   *        operationId: listUserGroupRelations
    *        summary: /user-group-relations
    *        description: Gets the user group relations
    *        responses:

+ 0 - 15
apps/app/src/server/routes/apiv3/user-group.js

@@ -93,7 +93,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getUserGroup
    *        summary: /user-groups
    *        description: Get usergroups
    *        parameters:
@@ -169,7 +168,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getAncestorUserGroups
    *        summary: /user-groups/ancestors
    *        description: Get ancestor user groups.
    *        parameters:
@@ -218,7 +216,6 @@ module.exports = (crowi) => {
    *          tags: [UserGroups]
    *          security:
    *            - cookieAuth: []
-   *          operationId: getUserGroupChildren
    *          summary: /user-groups/children
    *          description: Get child user groups
    *          parameters:
@@ -284,7 +281,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: createUserGroup
    *        summary: /user-groups
    *        description: Adds userGroup
    *        requestBody:
@@ -345,7 +341,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getSelectableParentGroups
    *        summary: /selectable-parent-groups
    *        description: Get selectable parent UserGroups
    *        parameters:
@@ -399,7 +394,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getSelectableChildGroups
    *        summary: /selectable-child-groups
    *        description: Get selectable child UserGroups
    *        parameters:
@@ -456,7 +450,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getUserGroupFromGroupId
    *        summary: /user-groups/{id}
    *        description: Get UserGroup from Group ID
    *        parameters:
@@ -503,7 +496,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: deleteUserGroup
    *        summary: /user-groups/{id}
    *        description: Deletes userGroup
    *        parameters:
@@ -577,7 +569,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: updateUserGroups
    *        summary: /user-groups/{id}
    *        description: Update userGroup
    *        parameters:
@@ -650,7 +641,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getUsersUserGroups
    *        summary: /user-groups/{id}/users
    *        description: Get users related to the userGroup
    *        parameters:
@@ -705,7 +695,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getUnrelatedUsersUserGroups
    *        summary: /user-groups/{id}/unrelated-users
    *        description: Get users unrelated to the userGroup
    *        parameters:
@@ -790,7 +779,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: addUserUserGroups
    *        summary: /user-groups/{id}/users/{username}
    *        description: Add a user to the userGroup
    *        parameters:
@@ -865,7 +853,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: deleteUsersUserGroups
    *        summary: /user-groups/{id}/users/{username}
    *        description: remove a user from the userGroup
    *        parameters:
@@ -926,7 +913,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getUserGroupRelationsUserGroups
    *        summary: /user-groups/{id}/user-group-relations
    *        description: Get the user group relations for the userGroup
    *        parameters:
@@ -977,7 +963,6 @@ module.exports = (crowi) => {
    *        tags: [UserGroups]
    *        security:
    *          - cookieAuth: []
-   *        operationId: getPagesUserGroups
    *        summary: /user-groups/{id}/pages
    *        description: Get closed pages for the userGroup
    *        parameters:

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

@@ -242,7 +242,6 @@ module.exports = (crowi) => {
    *    /users:
    *      get:
    *        tags: [Users]
-   *        operationId: listUsers
    *        summary: /users
    *        description: Select selected columns from users order by asc or desc
    *        parameters:
@@ -379,7 +378,6 @@ module.exports = (crowi) => {
    *    /{id}/recent:
    *      get:
    *        tags: [Users]
-   *        operationId: recent created page of user id
    *        summary: /usersIdReacent
    *        parameters:
    *          - name: id
@@ -456,7 +454,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: inviteUser
    *        summary: /users/invite
    *        description: Create new users and send Emails
    *        parameters:
@@ -537,7 +534,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: grantAdminUser
    *        summary: /users/{id}/grant-admin
    *        description: Grant user admin
    *        parameters:
@@ -586,7 +582,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: revokeAdminUser
    *        summary: /users/{id}/revoke-admin
    *        description: Revoke user admin
    *        parameters:
@@ -637,7 +632,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: ReadOnly
    *        summary: /users/{id}/grant-read-only
    *        description: Grant user read only access
    *        parameters:
@@ -692,7 +686,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: revokeReadOnly
    *        summary: /users/{id}/revoke-read-only
    *        description: Revoke user read only access
    *        parameters:
@@ -747,7 +740,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: activateUser
    *        summary: /users/{id}/activate
    *        description: Activate user
    *        parameters:
@@ -803,7 +795,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: deactivateUser
    *        summary: /users/{id}/deactivate
    *        description: Deactivate user
    *        parameters:
@@ -854,7 +845,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: removeUser
    *        summary: /users/{id}/remove
    *        description: Delete user
    *        parameters:
@@ -920,7 +910,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: listExternalAccountsUsers
    *        summary: /users/external-accounts
    *        description: Get external-account
    *        parameters:
@@ -961,7 +950,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: removeExternalAccountUser
    *        summary: /users/external-accounts/{id}/remove
    *        description: Delete ExternalAccount
    *        parameters:
@@ -1008,7 +996,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: update.imageUrlCache
    *        summary: /users/update.imageUrlCache
    *        description: update imageUrlCache
    *        requestBody:
@@ -1064,7 +1051,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: resetPassword
    *        summary: /users/reset-password
    *        description: update imageUrlCache
    *        requestBody:
@@ -1114,7 +1100,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: resetPasswordEmail
    *        summary: /users/reset-password-email
    *        description: send new password email
    *        requestBody:
@@ -1164,7 +1149,6 @@ module.exports = (crowi) => {
    *        tags: [Users Management]
    *        security:
    *          - cookieAuth: []
-   *        operationId: sendInvitationEmail
    *        summary: /users/send-invitation-email
    *        description: send invitation email
    *        requestBody:
@@ -1224,7 +1208,6 @@ module.exports = (crowi) => {
    *        get:
    *          tags: [Users]
    *          summary: /users/list
-   *          operationId: getUsersList
    *          description: Get list of users
    *          parameters:
    *            - in: query
@@ -1287,7 +1270,6 @@ module.exports = (crowi) => {
     *        get:
     *          tags: [Users]
     *          summary: /users/usernames
-    *          operationId: getUsernames
     *          description: Get list of usernames
     *          parameters:
     *            - in: query

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

@@ -110,7 +110,7 @@ const ApiResponse = require('../../util/apiResponse');
  *            description: original file name
  *            example: profile.png
  *          creator:
- *            $ref: '#/components/schemas/User/properties/_id'
+ *            $ref: '#/components/schemas/ObjectId'
  *          page:
  *            type: string
  *            description: page ID attached at
@@ -222,7 +222,7 @@ export const routesFactory = (crowi) => {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    attachment:
    *                      $ref: '#/components/schemas/AttachmentProfile'
    *          403:
@@ -289,7 +289,7 @@ export const routesFactory = (crowi) => {
    *              schema:
    *                properties:
    *                  attachment_id:
-   *                    $ref: '#/components/schemas/Attachment/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                required:
    *                  - attachment_id
    *        responses:
@@ -300,7 +300,7 @@ export const routesFactory = (crowi) => {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *          403:
    *            $ref: '#/components/responses/403'
    *          500:
@@ -365,7 +365,7 @@ export const routesFactory = (crowi) => {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *          403:
    *            $ref: '#/components/responses/403'
    *          500:

+ 29 - 27
apps/app/src/server/routes/comment.js

@@ -21,32 +21,34 @@ import { preNotifyService } from '../service/pre-notify';
  *
  *  components:
  *    schemas:
+ *      CommentBody:
+ *        description: The type for Comment.comment
+ *        type: string
+ *        example: good
+ *      CommentPosition:
+ *        description: comment position
+ *        type: number
+ *        example: 0
  *      Comment:
  *        description: Comment
  *        type: object
  *        properties:
  *          _id:
- *            type: string
- *            description: revision ID
- *            example: 5e079a0a0afa6700170a75fb
+ *            $ref: '#/components/schemas/ObjectId'
  *          __v:
  *            type: number
  *            description: DB record version
  *            example: 0
  *          page:
- *            $ref: '#/components/schemas/Page/properties/_id'
+ *            $ref: '#/components/schemas/ObjectId'
  *          creator:
- *            $ref: '#/components/schemas/User/properties/_id'
+ *            $ref: '#/components/schemas/ObjectId'
  *          revision:
- *            $ref: '#/components/schemas/Revision/properties/_id'
+ *            $ref: '#/components/schemas/ObjectId'
  *          comment:
- *            type: string
- *            description: comment
- *            example: good
+ *            $ref: '#/components/schemas/CommentBody'
  *          commentPosition:
- *            type: number
- *            description: comment position
- *            example: 0
+ *            $ref: '#/components/schemas/CommentPosition'
  *          createdAt:
  *            type: string
  *            description: date created at
@@ -88,11 +90,11 @@ module.exports = function(crowi, app) {
    *          - in: query
    *            name: page_id
    *            schema:
-   *              $ref: '#/components/schemas/Page/properties/_id'
+   *              $ref: '#/components/schemas/ObjectId'
    *          - in: query
    *            name: revision_id
    *            schema:
-   *              $ref: '#/components/schemas/Revision/properties/_id'
+   *              $ref: '#/components/schemas/ObjectId'
    *        responses:
    *          200:
    *            description: Succeeded to get comments of the page of the revision.
@@ -101,7 +103,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    comments:
    *                      type: array
    *                      items:
@@ -190,13 +192,13 @@ module.exports = function(crowi, app) {
    *                    type: object
    *                    properties:
    *                      page_id:
-   *                        $ref: '#/components/schemas/Page/properties/_id'
+   *                        $ref: '#/components/schemas/ObjectId'
    *                      revision_id:
-   *                        $ref: '#/components/schemas/Revision/properties/_id'
+   *                        $ref: '#/components/schemas/ObjectId'
    *                      comment:
-   *                        $ref: '#/components/schemas/Comment/properties/comment'
+   *                        $ref: '#/components/schemas/CommentBody'
    *                      comment_position:
-   *                        $ref: '#/components/schemas/Comment/properties/commentPosition'
+   *                        $ref: '#/components/schemas/CommentPosition'
    *                required:
    *                  - commentForm
    *        responses:
@@ -207,7 +209,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    comment:
    *                      $ref: '#/components/schemas/Comment'
    *          403:
@@ -336,13 +338,13 @@ module.exports = function(crowi, app) {
    *                        type: object
    *                        properties:
    *                          page_id:
-   *                            $ref: '#/components/schemas/Page/properties/_id'
+   *                            $ref: '#/components/schemas/ObjectId'
    *                          revision_id:
-   *                            $ref: '#/components/schemas/Revision/properties/_id'
+   *                            $ref: '#/components/schemas/ObjectId'
    *                          comment_id:
-   *                            $ref: '#/components/schemas/Comment/properties/_id'
+   *                            $ref: '#/components/schemas/ObjectId'
    *                          comment:
-   *                            $ref: '#/components/schemas/Comment/properties/comment'
+   *                            $ref: '#/components/schemas/CommentBody'
    *                required:
    *                  - form
    *        responses:
@@ -353,7 +355,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    comment:
    *                      $ref: '#/components/schemas/Comment'
    *          403:
@@ -433,7 +435,7 @@ module.exports = function(crowi, app) {
    *              schema:
    *                properties:
    *                  comment_id:
-   *                    $ref: '#/components/schemas/Comment/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                required:
    *                  - comment_id
    *        responses:
@@ -444,7 +446,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    comment:
    *                      $ref: '#/components/schemas/Comment'
    *          403:

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

@@ -149,7 +149,7 @@ module.exports = function(crowi, app) {
    *          - in: query
    *            name: pageId
    *            schema:
-   *              $ref: '#/components/schemas/Page/properties/_id'
+   *              $ref: '#/components/schemas/ObjectId'
    *        responses:
    *          200:
    *            description: Succeeded to get page tags.
@@ -158,7 +158,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    tags:
    *                      $ref: '#/components/schemas/Tags'
    *          403:
@@ -197,7 +197,7 @@ module.exports = function(crowi, app) {
    *          - in: query
    *            name: path
    *            schema:
-   *              $ref: '#/components/schemas/Page/properties/path'
+   *              $ref: '#/components/schemas/PagePath'
    *        responses:
    *          200:
    *            description: Succeeded to get UpdatePost setting list.
@@ -206,7 +206,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    updatePost:
    *                      $ref: '#/components/schemas/UpdatePost'
    *          403:

+ 22 - 20
apps/app/src/server/routes/search.ts

@@ -14,26 +14,28 @@ const logger = loggerFactory('growi:routes:search');
  *
  *   components:
  *     schemas:
+ *       ElasticsearchResultMeta:
+ *         type: object
+ *         properties:
+ *           took:
+ *             type: number
+ *             description: Time Elasticsearch took to execute a search(milliseconds)
+ *             example: 34
+ *           total:
+ *             type: number
+ *             description: Number of documents matching search criteria
+ *             example: 2
+ *           results:
+ *             type: number
+ *             description: Actual array length of search results
+ *             example: 2
+ *
  *       ElasticsearchResult:
  *         description: Elasticsearch result v1
  *         type: object
  *         properties:
  *           meta:
- *             type: object
- *             properties:
- *               took:
- *                 type: number
- *                 description: Time Elasticsearch took to execute a search(milliseconds)
- *                 example: 34
- *               total:
- *                 type: number
- *                 description: Number of documents matching search criteria
- *                 example: 2
- *               results:
- *                 type: number
- *                 description: Actual array length of search results
- *                 example: 2
- *
+ *             $ref: '#/components/schemas/ElasticsearchResultMeta'
  */
 module.exports = function(crowi: Crowi, app) {
   const ApiResponse = require('../util/apiResponse');
@@ -62,15 +64,15 @@ module.exports = function(crowi: Crowi, app) {
    *         - in: query
    *           name: path
    *           schema:
-   *             $ref: '#/components/schemas/Page/properties/path'
+   *             $ref: '#/components/schemas/PagePath'
    *         - in: query
    *           name: offset
    *           schema:
-   *             $ref: '#/components/schemas/V1PaginateResult/properties/meta/properties/offset'
+   *             $ref: '#/components/schemas/Offset'
    *         - in: query
    *           name: limit
    *           schema:
-   *             $ref: '#/components/schemas/V1PaginateResult/properties/meta/properties/limit'
+   *             $ref: '#/components/schemas/Limit'
    *       responses:
    *         200:
    *           description: Succeeded to get list of pages.
@@ -79,9 +81,9 @@ module.exports = function(crowi: Crowi, app) {
    *               schema:
    *                 properties:
    *                   ok:
-   *                     $ref: '#/components/schemas/V1Response/properties/ok'
+   *                     $ref: '#/components/schemas/V1ResponseOK'
    *                   meta:
-   *                     $ref: '#/components/schemas/ElasticsearchResult/properties/meta'
+   *                     $ref: '#/components/schemas/ElasticsearchResultMeta'
    *                   totalCount:
    *                     type: integer
    *                     description: total count of pages

+ 7 - 36
apps/app/src/server/routes/tag.js

@@ -5,35 +5,6 @@ import PageTagRelation from '../models/page-tag-relation';
 import { Revision } from '../models/revision';
 import ApiResponse from '../util/apiResponse';
 
-/**
- * @swagger
- *
- *  components:
- *    schemas:
- *      Tags:
- *        description: Tags
- *        type: array
- *        items:
- *          $ref: '#/components/schemas/Tag/properties/name'
- *        example: ['daily', 'report', 'tips']
- *
- *      Tag:
- *        description: Tag
- *        type: object
- *        properties:
- *          _id:
- *            type: string
- *            description: tag ID
- *            example: 5e2d6aede35da4004ef7e0b7
- *          name:
- *            type: string
- *            description: tag name
- *            example: daily
- *          count:
- *            type: number
- *            description: Count of tagged pages
- *            example: 3
- */
 /** @param {import('~/server/crowi').default} crowi Crowi instance */
 module.exports = function(crowi, app) {
 
@@ -68,7 +39,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    tags:
    *                      $ref: '#/components/schemas/Tags'
    *          403:
@@ -109,9 +80,9 @@ module.exports = function(crowi, app) {
    *              schema:
    *                properties:
    *                  pageId:
-   *                    $ref: '#/components/schemas/Page/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                  revisionId:
-   *                    $ref: '#/components/schemas/Revision/properties/_id'
+   *                    $ref: '#/components/schemas/ObjectId'
    *                  tags:
    *                    $ref: '#/components/schemas/Tags'
    *        responses:
@@ -122,7 +93,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    tags:
    *                      $ref: '#/components/schemas/Tags'
    *          403:
@@ -186,11 +157,11 @@ module.exports = function(crowi, app) {
    *          - in: query
    *            name: limit
    *            schema:
-   *              $ref: '#/components/schemas/V1PaginateResult/properties/meta/properties/limit'
+   *              $ref: '#/components/schemas/Limit'
    *          - in: query
    *            name: offset
    *            schema:
-   *              $ref: '#/components/schemas/V1PaginateResult/properties/meta/properties/offset'
+   *              $ref: '#/components/schemas/Offset'
    *        responses:
    *          200:
    *            description: Succeeded to tag list.
@@ -199,7 +170,7 @@ module.exports = function(crowi, app) {
    *                schema:
    *                  properties:
    *                    ok:
-   *                      $ref: '#/components/schemas/V1Response/properties/ok'
+   *                      $ref: '#/components/schemas/V1ResponseOK'
    *                    data:
    *                      type: array
    *                      items:

+ 6 - 0
apps/app/src/stores-universal/context.tsx

@@ -224,8 +224,14 @@ export const useLimitLearnablePageCountPerAssistant = (initialData?: number): SW
   return useContextSWR('limitLearnablePageCountPerAssistant', initialData);
 };
 
+
+export const useIsUsersHomepageDeletionEnabled = (initialData?: boolean): SWRResponse<boolean, false> => {
+  return useContextSWR('isUsersHomepageDeletionEnabled', initialData);
+};
+
 export const useIsEnableUnifiedMergeView = (initialData?: boolean): SWRResponse<boolean, Error> => {
   return useSWRStatic<boolean, Error>('isEnableUnifiedMergeView', initialData, { fallbackData: false });
+
 };
 
 /** **********************************************************

+ 1 - 1
apps/app/vitest.workspace.mts

@@ -15,7 +15,7 @@ const configShared = defineConfig({
       'test/**',
       'test-with-vite/**',
       'playwright/**',
-    ]
+    ],
   },
 });
 

+ 0 - 1
apps/pdf-converter/.eslintignore

@@ -1 +0,0 @@
-/dist/**

+ 6 - 0
apps/pdf-converter/.eslintrc.cjs

@@ -1,5 +1,11 @@
+/**
+ * @type {import('eslint').Linter.Config}
+ */
 module.exports = {
   extends: '../../.eslintrc.js',
+  ignorePatterns: [
+    'dist/**',
+  ],
   rules: {
     'no-useless-constructor': 'off',
     '@typescript-eslint/consistent-type-imports': 'off',

+ 0 - 3
packages/pdf-converter-client/.eslintignore

@@ -1,3 +0,0 @@
-src/index.ts
-dist/index.d.ts
-dist/index.js

+ 11 - 0
packages/pdf-converter-client/.eslintrc.cjs

@@ -0,0 +1,11 @@
+/**
+ * @type {import('eslint').Linter.Config}
+ */
+module.exports = {
+  extends: '../../.eslintrc.js',
+  ignorePatterns: [
+    'src/index.ts',
+    'dist/index.d.ts',
+    'dist/index.js',
+  ],
+};

+ 186 - 41
pnpm-lock.yaml

@@ -767,6 +767,9 @@ importers:
         specifier: ^3.24.2
         version: 3.24.2
     devDependencies:
+      '@apidevtools/swagger-parser':
+        specifier: ^10.1.1
+        version: 10.1.1(openapi-types@12.1.3)
       '@emoji-mart/data':
         specifier: ^1.2.1
         version: 1.2.1
@@ -872,6 +875,9 @@ importers:
       bootstrap:
         specifier: '=5.3.2'
         version: 5.3.2(@popperjs/core@2.11.8)
+      commander:
+        specifier: ^14.0.0
+        version: 14.0.0
       connect-browser-sync:
         specifier: ^2.1.0
         version: 2.1.0(browser-sync@3.0.2)
@@ -938,6 +944,9 @@ importers:
       null-loader:
         specifier: ^4.0.1
         version: 4.0.1(webpack@5.92.1(@swc/core@1.10.7(@swc/helpers@0.5.15)))
+      openapi-typescript:
+        specifier: ^7.8.0
+        version: 7.8.0(typescript@5.4.2)
       pretty-bytes:
         specifier: ^6.1.1
         version: 6.1.1
@@ -1860,8 +1869,9 @@ packages:
   '@antfu/utils@0.7.10':
     resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
 
-  '@apidevtools/json-schema-ref-parser@9.0.6':
-    resolution: {integrity: sha512-M3YgsLjI0lZxvrpeGVk9Ap032W6TPQkH6pRAZz81Ac3WUNF79VQooAFnp8umjvVzUmD93NkogxEwbSce7qMsUg==}
+  '@apidevtools/json-schema-ref-parser@11.7.2':
+    resolution: {integrity: sha512-4gY54eEGEstClvEkGnwVkTkrx0sqwemEFG5OSRRn3tD91XH0+Q8XIkYIfo7IwEWPpJZwILb9GUXeShtplRc/eA==}
+    engines: {node: '>= 16'}
 
   '@apidevtools/json-schema-ref-parser@9.1.2':
     resolution: {integrity: sha512-r1w81DpR+KyRWd3f+rk6TNqMgedmAxZP5v5KWlXQWlgMUUtyEJch0DKEci1SorPMiSeM8XPl7MZ3miJ60JIpQg==}
@@ -1878,8 +1888,8 @@ packages:
     peerDependencies:
       openapi-types: '>=7'
 
-  '@apidevtools/swagger-parser@10.1.0':
-    resolution: {integrity: sha512-9Kt7EuS/7WbMAUv2gSziqjvxwDbFSg3Xeyfuj5laUODX8o/k/CpsAKiQ8W7/R88eXFTMbJYg6+7uAmOWNKmwnw==}
+  '@apidevtools/swagger-parser@10.1.1':
+    resolution: {integrity: sha512-u/kozRnsPO/x8QtKYJOqoGtC4kH6yg1lfYkB9Au0WhYB0FNLpyFusttQtvhlwjtG3rOwiRz4D8DnnXa8iEpIKA==}
     peerDependencies:
       openapi-types: '>=7'
 
@@ -2256,6 +2266,10 @@ packages:
     resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/code-frame@7.27.1':
+    resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/compat-data@7.24.6':
     resolution: {integrity: sha512-aC2DGhBq5eEdyXWqrDInSqQjO0k8xtPRf5YylULqx8MCd6jBtzqfta/3ETMRpuKIc5hyswfO80ObyA1MvkCcUQ==}
     engines: {node: '>=6.9.0'}
@@ -2321,6 +2335,10 @@ packages:
     resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==}
     engines: {node: '>=6.9.0'}
 
+  '@babel/helper-validator-identifier@7.27.1':
+    resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+    engines: {node: '>=6.9.0'}
+
   '@babel/helper-validator-option@7.24.6':
     resolution: {integrity: sha512-Jktc8KkF3zIkePb48QO+IapbXlSapOW9S+ogZZkcO6bABgYAxtZcjZ/O005111YLf+j4M84uEgwYoidDkXbCkQ==}
     engines: {node: '>=6.9.0'}
@@ -4247,6 +4265,16 @@ packages:
   '@react-dnd/shallowequal@2.0.0':
     resolution: {integrity: sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg==}
 
+  '@redocly/ajv@8.11.2':
+    resolution: {integrity: sha512-io1JpnwtIcvojV7QKDUSIuMN/ikdOUd1ReEnUnMKGfDVridQZ31J0MmIuqwuRjWDZfmvr+Q0MqCcfHM2gTivOg==}
+
+  '@redocly/config@0.22.2':
+    resolution: {integrity: sha512-roRDai8/zr2S9YfmzUfNhKjOF0NdcOIqF7bhf4MVC5UxpjIysDjyudvlAiVbpPHp3eDRWbdzUgtkK1a7YiDNyQ==}
+
+  '@redocly/openapi-core@1.34.3':
+    resolution: {integrity: sha512-3arRdUp1fNx55itnjKiUhO6t4Mf91TsrTIYINDNLAZPS0TPd5YpiXRctwjel0qqWoOOhjA34cZ3m4dksLDFUYg==}
+    engines: {node: '>=18.17.0', npm: '>=9.5.0'}
+
   '@replit/codemirror-emacs@6.1.0':
     resolution: {integrity: sha512-74DITnht6Cs6sHg02PQ169IKb1XgtyhI9sLD0JeOFco6Ds18PT+dkD8+DgXBDokne9UIFKsBbKPnpFRAz60/Lw==}
     peerDependencies:
@@ -6831,6 +6859,9 @@ packages:
   colord@2.9.3:
     resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
 
+  colorette@1.4.0:
+    resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==}
+
   colorette@2.0.20:
     resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==}
 
@@ -6860,6 +6891,10 @@ packages:
     resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==}
     engines: {node: '>=18'}
 
+  commander@14.0.0:
+    resolution: {integrity: sha512-2uM9rYjPvyq39NwLRqaiLtWHyDC1FvryJDa2ATTVims5YAS4PupsEQsDvP14FqhFr0P49CYDugi59xaxJlTXRA==}
+    engines: {node: '>=20'}
+
   commander@2.20.3:
     resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==}
 
@@ -9420,6 +9455,10 @@ packages:
     resolution: {integrity: sha512-MWDKS3AS1bGCHLBA2VLImJz42f7bJh8wQsTGCzI3j519/CASStoDONUBVz2I/VID0MpiX3SGSnbOD2xUalbE5g==}
     engines: {node: '>=18'}
 
+  index-to-position@1.1.0:
+    resolution: {integrity: sha512-XPdx9Dq4t9Qk1mTMbWONJqU7boCoumEH7fRET37HX5+khDUl3J2W6PdALxhILYlIYx2amlwYcRPp28p0tSiojg==}
+    engines: {node: '>=18'}
+
   infer-owner@1.0.4:
     resolution: {integrity: sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==}
 
@@ -9983,6 +10022,10 @@ packages:
   jpeg-js@0.4.4:
     resolution: {integrity: sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==}
 
+  js-levenshtein@1.1.6:
+    resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==}
+    engines: {node: '>=0.10.0'}
+
   js-sha256@0.9.0:
     resolution: {integrity: sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==}
 
@@ -11503,6 +11546,12 @@ packages:
   openapi-types@12.1.3:
     resolution: {integrity: sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw==}
 
+  openapi-typescript@7.8.0:
+    resolution: {integrity: sha512-1EeVWmDzi16A+siQlo/SwSGIT7HwaFAVjvMA7/jG5HMLSnrUOzPL7uSTRZZa4v/LCRxHTApHKtNY6glApEoiUQ==}
+    hasBin: true
+    peerDependencies:
+      typescript: ^5.x
+
   openapi3-ts@4.2.2:
     resolution: {integrity: sha512-+9g4actZKeb3czfi9gVQ4Br2Ju3KwhCAQJBNaKgye5KggqcBLIhFHH+nIkcm0BUX00TrAJl6dH4JWgM4G4JWrw==}
 
@@ -11660,6 +11709,10 @@ packages:
     resolution: {integrity: sha512-rum1bPifK5SSar35Z6EKZuYPJx85pkNaFrxBK3mwdfSJ1/WKbYrjoW/zTPSjRRamfmVX1ACBIdFAO0VRErW/EA==}
     engines: {node: '>=18'}
 
+  parse-json@8.3.0:
+    resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==}
+    engines: {node: '>=18'}
+
   parse-ms@2.1.0:
     resolution: {integrity: sha512-kHt7kzLoS9VBZfUsiKjv43mr91ea+U05EyKkEtqp7vNbHxmaVuEqN7XxeEVnGrMtYOAxGrDElSi96K7EgO1zCA==}
     engines: {node: '>=6'}
@@ -13409,6 +13462,10 @@ packages:
     resolution: {integrity: sha512-aI59HBTlG9e2wTjxGJV+DygfNLgnWbGdZxiA/sgrnNNikIW8lbDvCtF6RnhZoJ82nU7qv7ZLjrvWqCEm52fAmw==}
     engines: {node: '>=14.18.0'}
 
+  supports-color@10.0.0:
+    resolution: {integrity: sha512-HRVVSbCCMbj7/kdWF9Q+bbckjBHLtHMEoJWlkmYzzdwhYMkjkOwubLM6t7NbWKjgKamGDrWL1++KrjUO1t9oAQ==}
+    engines: {node: '>=18'}
+
   supports-color@2.0.0:
     resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==}
     engines: {node: '>=0.8.0'}
@@ -13893,6 +13950,10 @@ packages:
     resolution: {integrity: sha512-UJShLPYi1aWqCdq9HycOL/gwsuqda1OISdBO3t8RlXQC4QvtuIz4b5FCfe2dQIWEpmlRExKmcTBfP1r9bhY7ig==}
     engines: {node: '>=16'}
 
+  type-fest@4.41.0:
+    resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==}
+    engines: {node: '>=16'}
+
   type-is@1.6.18:
     resolution: {integrity: sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==}
     engines: {node: '>= 0.6'}
@@ -14170,6 +14231,9 @@ packages:
   upper-case@2.0.2:
     resolution: {integrity: sha512-KgdgDGJt2TpuwBUIjgG6lzw2GWFRCW9Qkfkiv0DxqHHLYJHmtmdUIKcZd8rHgFSjopVTlw6ggzCm1b8MFQwikg==}
 
+  uri-js-replace@1.0.1:
+    resolution: {integrity: sha512-W+C9NWNLFOoBI2QWDp4UT9pv65r2w5Cx+3sTYFvtMdDBxkKt1syCqsUdSFAChbEe1uK5TfS04wt/nGwmaeIQ0g==}
+
   uri-js@4.2.2:
     resolution: {integrity: sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==}
 
@@ -14731,6 +14795,9 @@ packages:
   yallist@4.0.0:
     resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==}
 
+  yaml-ast-parser@0.0.43:
+    resolution: {integrity: sha512-2PTINUwsRqSd+s8XxKaJWQlUuEMHJQyEuh2edBbW8KNJz0SJPwUSD2zRWqezFEdN7IzAgeuYHFUCF7o8zRdZ0A==}
+
   yaml-eslint-parser@1.2.3:
     resolution: {integrity: sha512-4wZWvE398hCP7O8n3nXKu/vdq1HcH01ixYlCREaJL5NUMwQ0g3MaGFUBNSlmBtKmhbtVG/Cm6lyYmSVTEVil8A==}
     engines: {node: ^14.17.0 || >=16.0.0}
@@ -14851,11 +14918,11 @@ snapshots:
 
   '@antfu/utils@0.7.10': {}
 
-  '@apidevtools/json-schema-ref-parser@9.0.6':
+  '@apidevtools/json-schema-ref-parser@11.7.2':
     dependencies:
       '@jsdevtools/ono': 7.1.3
-      call-me-maybe: 1.0.2
-      js-yaml: 3.14.1
+      '@types/json-schema': 7.0.15
+      js-yaml: 4.1.0
 
   '@apidevtools/json-schema-ref-parser@9.1.2':
     dependencies:
@@ -14878,9 +14945,9 @@ snapshots:
       openapi-types: 12.1.3
       z-schema: 5.0.6
 
-  '@apidevtools/swagger-parser@10.1.0(openapi-types@12.1.3)':
+  '@apidevtools/swagger-parser@10.1.1(openapi-types@12.1.3)':
     dependencies:
-      '@apidevtools/json-schema-ref-parser': 9.0.6
+      '@apidevtools/json-schema-ref-parser': 11.7.2
       '@apidevtools/openapi-schemas': 2.1.0
       '@apidevtools/swagger-methods': 3.0.2
       '@jsdevtools/ono': 7.1.3
@@ -14983,7 +15050,7 @@ snapshots:
       '@aws-sdk/client-sso-oidc': 3.600.0
       '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)
       '@aws-sdk/core': 3.598.0
-      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))
+      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
       '@aws-sdk/middleware-host-header': 3.598.0
       '@aws-sdk/middleware-logger': 3.598.0
       '@aws-sdk/middleware-recursion-detection': 3.598.0
@@ -15091,7 +15158,7 @@ snapshots:
       '@aws-crypto/sha256-js': 5.2.0
       '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)
       '@aws-sdk/core': 3.598.0
-      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))
+      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
       '@aws-sdk/middleware-host-header': 3.598.0
       '@aws-sdk/middleware-logger': 3.598.0
       '@aws-sdk/middleware-recursion-detection': 3.598.0
@@ -15267,7 +15334,7 @@ snapshots:
       '@aws-crypto/sha256-js': 5.2.0
       '@aws-sdk/client-sso-oidc': 3.600.0
       '@aws-sdk/core': 3.598.0
-      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))
+      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
       '@aws-sdk/middleware-host-header': 3.598.0
       '@aws-sdk/middleware-logger': 3.598.0
       '@aws-sdk/middleware-recursion-detection': 3.598.0
@@ -15378,7 +15445,7 @@ snapshots:
     transitivePeerDependencies:
       - aws-crt
 
-  '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))':
+  '@aws-sdk/credential-provider-ini@3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)':
     dependencies:
       '@aws-sdk/client-sts': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)
       '@aws-sdk/credential-provider-env': 3.598.0
@@ -15413,11 +15480,11 @@ snapshots:
     transitivePeerDependencies:
       - aws-crt
 
-  '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))':
+  '@aws-sdk/credential-provider-node@3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)':
     dependencies:
       '@aws-sdk/credential-provider-env': 3.598.0
       '@aws-sdk/credential-provider-http': 3.598.0
-      '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))
+      '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
       '@aws-sdk/credential-provider-process': 3.598.0
       '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
       '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0)
@@ -15500,8 +15567,8 @@ snapshots:
       '@aws-sdk/credential-provider-cognito-identity': 3.600.0
       '@aws-sdk/credential-provider-env': 3.598.0
       '@aws-sdk/credential-provider-http': 3.598.0
-      '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))
-      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0(@aws-sdk/client-sso-oidc@3.600.0))
+      '@aws-sdk/credential-provider-ini': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
+      '@aws-sdk/credential-provider-node': 3.600.0(@aws-sdk/client-sso-oidc@3.600.0)(@aws-sdk/client-sts@3.600.0)
       '@aws-sdk/credential-provider-process': 3.598.0
       '@aws-sdk/credential-provider-sso': 3.598.0(@aws-sdk/client-sso-oidc@3.600.0)
       '@aws-sdk/credential-provider-web-identity': 3.598.0(@aws-sdk/client-sts@3.600.0)
@@ -15957,6 +16024,12 @@ snapshots:
       '@babel/highlight': 7.24.7
       picocolors: 1.1.1
 
+  '@babel/code-frame@7.27.1':
+    dependencies:
+      '@babel/helper-validator-identifier': 7.27.1
+      js-tokens: 4.0.0
+      picocolors: 1.1.1
+
   '@babel/compat-data@7.24.6': {}
 
   '@babel/core@7.24.6':
@@ -16040,6 +16113,8 @@ snapshots:
 
   '@babel/helper-validator-identifier@7.24.7': {}
 
+  '@babel/helper-validator-identifier@7.27.1': {}
+
   '@babel/helper-validator-option@7.24.6': {}
 
   '@babel/helpers@7.24.6':
@@ -17645,7 +17720,7 @@ snapshots:
 
   '@npmcli/agent@2.2.2':
     dependencies:
-      agent-base: 7.1.1
+      agent-base: 7.1.1(supports-color@10.0.0)
       http-proxy-agent: 7.0.2
       https-proxy-agent: 7.0.5
       lru-cache: 10.4.3
@@ -18403,7 +18478,7 @@ snapshots:
 
   '@orval/core@7.2.0(encoding@0.1.13)(openapi-types@12.1.3)':
     dependencies:
-      '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3)
+      '@apidevtools/swagger-parser': 10.1.1(openapi-types@12.1.3)
       '@ibm-cloud/openapi-ruleset': 1.23.2(encoding@0.1.13)
       acorn: 8.14.1
       ajv: 8.17.1
@@ -18598,6 +18673,29 @@ snapshots:
 
   '@react-dnd/shallowequal@2.0.0': {}
 
+  '@redocly/ajv@8.11.2':
+    dependencies:
+      fast-deep-equal: 3.1.3
+      json-schema-traverse: 1.0.0
+      require-from-string: 2.0.2
+      uri-js-replace: 1.0.1
+
+  '@redocly/config@0.22.2': {}
+
+  '@redocly/openapi-core@1.34.3(supports-color@10.0.0)':
+    dependencies:
+      '@redocly/ajv': 8.11.2
+      '@redocly/config': 0.22.2
+      colorette: 1.4.0
+      https-proxy-agent: 7.0.5(supports-color@10.0.0)
+      js-levenshtein: 1.1.6
+      js-yaml: 4.1.0
+      minimatch: 5.1.6
+      pluralize: 8.0.0
+      yaml-ast-parser: 0.0.43
+    transitivePeerDependencies:
+      - supports-color
+
   '@replit/codemirror-emacs@6.1.0(@codemirror/autocomplete@6.18.4)(@codemirror/commands@6.8.0)(@codemirror/search@6.5.6)(@codemirror/state@6.5.2)(@codemirror/view@6.36.4)':
     dependencies:
       '@codemirror/autocomplete': 6.18.4
@@ -21109,13 +21207,13 @@ snapshots:
 
   agent-base@6.0.2:
     dependencies:
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
     transitivePeerDependencies:
       - supports-color
 
-  agent-base@7.1.1:
+  agent-base@7.1.1(supports-color@10.0.0):
     dependencies:
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -22218,6 +22316,8 @@ snapshots:
 
   colord@2.9.3: {}
 
+  colorette@1.4.0: {}
+
   colorette@2.0.20: {}
 
   colors-cli@1.0.33: {}
@@ -22237,6 +22337,8 @@ snapshots:
 
   commander@12.1.0: {}
 
+  commander@14.0.0: {}
+
   commander@2.20.3: {}
 
   commander@4.1.1: {}
@@ -22856,15 +22958,23 @@ snapshots:
     dependencies:
       ms: 2.1.3
 
+  debug@4.4.0(supports-color@10.0.0):
+    dependencies:
+      ms: 2.1.3
+    optionalDependencies:
+      supports-color: 10.0.0
+
   debug@4.4.0(supports-color@5.5.0):
     dependencies:
       ms: 2.1.3
     optionalDependencies:
       supports-color: 5.5.0
 
-  debug@4.4.1:
+  debug@4.4.1(supports-color@10.0.0):
     dependencies:
       ms: 2.1.3
+    optionalDependencies:
+      supports-color: 10.0.0
 
   decamelize-keys@1.1.1:
     dependencies:
@@ -24310,7 +24420,7 @@ snapshots:
     dependencies:
       basic-ftp: 5.0.5
       data-uri-to-buffer: 6.0.2
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
       fs-extra: 11.2.0
     transitivePeerDependencies:
       - supports-color
@@ -24843,14 +24953,14 @@ snapshots:
     dependencies:
       '@tootallnate/once': 2.0.0
       agent-base: 6.0.2
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
     transitivePeerDependencies:
       - supports-color
 
   http-proxy-agent@7.0.2:
     dependencies:
-      agent-base: 7.1.1
-      debug: 4.4.1
+      agent-base: 7.1.1(supports-color@10.0.0)
+      debug: 4.4.1(supports-color@10.0.0)
     transitivePeerDependencies:
       - supports-color
 
@@ -24873,17 +24983,24 @@ snapshots:
   https-proxy-agent@5.0.1:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
     transitivePeerDependencies:
       - supports-color
 
   https-proxy-agent@7.0.5:
     dependencies:
-      agent-base: 7.1.1
+      agent-base: 7.1.1(supports-color@10.0.0)
       debug: 4.4.0(supports-color@5.5.0)
     transitivePeerDependencies:
       - supports-color
 
+  https-proxy-agent@7.0.5(supports-color@10.0.0):
+    dependencies:
+      agent-base: 7.1.1(supports-color@10.0.0)
+      debug: 4.4.0(supports-color@10.0.0)
+    transitivePeerDependencies:
+      - supports-color
+
   human-id@1.0.2: {}
 
   human-signals@2.1.0: {}
@@ -24991,6 +25108,8 @@ snapshots:
 
   index-to-position@0.1.2: {}
 
+  index-to-position@1.1.0: {}
+
   infer-owner@1.0.4: {}
 
   inflection@1.12.0: {}
@@ -25320,7 +25439,7 @@ snapshots:
 
   istanbul-lib-source-maps@4.0.1:
     dependencies:
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
       istanbul-lib-coverage: 3.2.2
       source-map: 0.6.1
     transitivePeerDependencies:
@@ -25681,6 +25800,8 @@ snapshots:
 
   jpeg-js@0.4.4: {}
 
+  js-levenshtein@1.1.6: {}
+
   js-sha256@0.9.0: {}
 
   js-tiktoken@1.0.15:
@@ -27596,6 +27717,16 @@ snapshots:
 
   openapi-types@12.1.3: {}
 
+  openapi-typescript@7.8.0(typescript@5.4.2):
+    dependencies:
+      '@redocly/openapi-core': 1.34.3(supports-color@10.0.0)
+      ansi-colors: 4.1.3
+      change-case: 5.4.4
+      parse-json: 8.3.0
+      supports-color: 10.0.0
+      typescript: 5.4.2
+      yargs-parser: 21.1.1
+
   openapi3-ts@4.2.2:
     dependencies:
       yaml: 2.4.5
@@ -27636,7 +27767,7 @@ snapshots:
 
   orval@7.2.0(encoding@0.1.13)(openapi-types@12.1.3)(typescript@5.4.2):
     dependencies:
-      '@apidevtools/swagger-parser': 10.1.0(openapi-types@12.1.3)
+      '@apidevtools/swagger-parser': 10.1.1(openapi-types@12.1.3)
       '@orval/angular': 7.2.0(encoding@0.1.13)(openapi-types@12.1.3)
       '@orval/axios': 7.2.0(encoding@0.1.13)(openapi-types@12.1.3)
       '@orval/core': 7.2.0(encoding@0.1.13)(openapi-types@12.1.3)
@@ -27745,8 +27876,8 @@ snapshots:
   pac-proxy-agent@7.0.2:
     dependencies:
       '@tootallnate/quickjs-emscripten': 0.23.0
-      agent-base: 7.1.1
-      debug: 4.4.1
+      agent-base: 7.1.1(supports-color@10.0.0)
+      debug: 4.4.1(supports-color@10.0.0)
       get-uri: 6.0.3
       http-proxy-agent: 7.0.2
       https-proxy-agent: 7.0.5
@@ -27826,6 +27957,12 @@ snapshots:
       index-to-position: 0.1.2
       type-fest: 4.30.2
 
+  parse-json@8.3.0:
+    dependencies:
+      '@babel/code-frame': 7.27.1
+      index-to-position: 1.1.0
+      type-fest: 4.41.0
+
   parse-ms@2.1.0: {}
 
   parse5-htmlparser2-tree-adapter@6.0.1:
@@ -28173,7 +28310,7 @@ snapshots:
 
   proxy-agent@6.4.0:
     dependencies:
-      agent-base: 7.1.1
+      agent-base: 7.1.1(supports-color@10.0.0)
       debug: 4.4.0(supports-color@5.5.0)
       http-proxy-agent: 7.0.2
       https-proxy-agent: 7.0.5
@@ -28977,7 +29114,7 @@ snapshots:
 
   require-in-the-middle@7.4.0:
     dependencies:
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
       module-details-from-path: 1.0.3
       resolve: 1.22.8
     transitivePeerDependencies:
@@ -29503,15 +29640,15 @@ snapshots:
   socks-proxy-agent@7.0.0:
     dependencies:
       agent-base: 6.0.2
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
       socks: 2.8.3
     transitivePeerDependencies:
       - supports-color
 
   socks-proxy-agent@8.0.4:
     dependencies:
-      agent-base: 7.1.1
-      debug: 4.4.1
+      agent-base: 7.1.1(supports-color@10.0.0)
+      debug: 4.4.1(supports-color@10.0.0)
       socks: 2.8.3
     transitivePeerDependencies:
       - supports-color
@@ -29669,7 +29806,7 @@ snapshots:
   streamroller@3.1.5:
     dependencies:
       date-format: 4.0.14
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
       fs-extra: 8.1.0
     transitivePeerDependencies:
       - supports-color
@@ -29929,7 +30066,7 @@ snapshots:
     dependencies:
       component-emitter: 1.3.1
       cookiejar: 2.1.4
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
       fast-safe-stringify: 2.1.1
       form-data: 4.0.0
       formidable: 3.5.4
@@ -29950,6 +30087,8 @@ snapshots:
     transitivePeerDependencies:
       - supports-color
 
+  supports-color@10.0.0: {}
+
   supports-color@2.0.0: {}
 
   supports-color@5.5.0:
@@ -30482,6 +30621,8 @@ snapshots:
 
   type-fest@4.30.2: {}
 
+  type-fest@4.41.0: {}
+
   type-is@1.6.18:
     dependencies:
       media-typer: 0.3.0
@@ -30774,6 +30915,8 @@ snapshots:
     dependencies:
       tslib: 2.8.1
 
+  uri-js-replace@1.0.1: {}
+
   uri-js@4.2.2:
     dependencies:
       punycode: 2.3.1
@@ -31029,7 +31172,7 @@ snapshots:
 
   vue-eslint-parser@7.11.0(eslint@8.41.0):
     dependencies:
-      debug: 4.4.1
+      debug: 4.4.1(supports-color@10.0.0)
       eslint: 8.41.0
       eslint-scope: 5.1.1
       eslint-visitor-keys: 1.3.0
@@ -31371,6 +31514,8 @@ snapshots:
 
   yallist@4.0.0: {}
 
+  yaml-ast-parser@0.0.43: {}
+
   yaml-eslint-parser@1.2.3:
     dependencies:
       eslint-visitor-keys: 3.4.3