post.ts 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127
  1. import { createRoute, OpenAPIHono, z } from '@hono/zod-openapi';
  2. import { FileService } from '../../services/file.service';
  3. import { ErrorSchema } from '@d8d/shared-utils';
  4. import { AppDataSource } from '@d8d/shared-utils';
  5. import { AuthContext } from '@d8d/shared-types';
  6. import { authMiddleware } from '@d8d/auth-module/middleware';
  7. // 完成分片上传请求Schema
  8. const CompleteMultipartUploadDto = z.object({
  9. uploadId: z.string().openapi({
  10. description: '分片上传ID',
  11. example: '123e4567-e89b-12d3-a456-426614174000'
  12. }),
  13. bucket: z.string().openapi({
  14. description: '存储桶名称',
  15. example: 'my-bucket'
  16. }),
  17. key: z.string().openapi({
  18. description: '文件键名',
  19. example: 'documents/report.pdf'
  20. }),
  21. parts: z.array(
  22. z.object({
  23. partNumber: z.coerce.number().int().positive().openapi({
  24. description: '分片序号',
  25. example: 1
  26. }),
  27. etag: z.string().openapi({
  28. description: '分片ETag值',
  29. example: 'd41d8cd98f00b204e9800998ecf8427e'
  30. })
  31. })
  32. ).openapi({
  33. description: '分片信息列表',
  34. example: [
  35. { partNumber: 1, etag: 'd41d8cd98f00b204e9800998ecf8427e' },
  36. { partNumber: 2, etag: '5f4dcc3b5aa765d61d8327deb882cf99' }
  37. ]
  38. })
  39. });
  40. // 完成分片上传响应Schema
  41. const CompleteMultipartUploadResponse = z.object({
  42. fileId: z.number().openapi({
  43. description: '文件ID',
  44. example: 123456
  45. }),
  46. url: z.string().openapi({
  47. description: '文件访问URL',
  48. example: 'https://minio.example.com/my-bucket/documents/report.pdf'
  49. }),
  50. host: z.string().openapi({
  51. description: 'MinIO主机地址',
  52. example: 'minio.example.com'
  53. }),
  54. bucket: z.string().openapi({
  55. description: '存储桶名称',
  56. example: 'my-bucket'
  57. }),
  58. key: z.string().openapi({
  59. description: '文件键名',
  60. example: 'documents/report.pdf'
  61. }),
  62. size: z.number().openapi({
  63. description: '文件大小(字节)',
  64. example: 102400
  65. })
  66. });
  67. // 创建完成分片上传路由定义
  68. const completeMultipartUploadRoute = createRoute({
  69. method: 'post',
  70. path: '/',
  71. middleware: [authMiddleware],
  72. request: {
  73. body: {
  74. content: {
  75. 'application/json': { schema: CompleteMultipartUploadDto }
  76. }
  77. }
  78. },
  79. responses: {
  80. 200: {
  81. description: '完成分片上传成功',
  82. content: {
  83. 'application/json': { schema: CompleteMultipartUploadResponse }
  84. }
  85. },
  86. 401: {
  87. description: '未授权访问',
  88. content: { 'application/json': { schema: ErrorSchema } }
  89. },
  90. 400: {
  91. description: '请求参数错误',
  92. content: { 'application/json': { schema: ErrorSchema } }
  93. },
  94. 500: {
  95. description: '服务器错误',
  96. content: { 'application/json': { schema: ErrorSchema } }
  97. }
  98. }
  99. });
  100. // 创建路由实例并实现处理逻辑
  101. const app = new OpenAPIHono<AuthContext>().openapi(completeMultipartUploadRoute, async (c) => {
  102. try {
  103. const data = await c.req.json();
  104. // 初始化FileService
  105. const fileService = new FileService(AppDataSource);
  106. const result = await fileService.completeMultipartUpload(data);
  107. // 构建完整的响应包含host和bucket信息
  108. const response = {
  109. ...result,
  110. host: `${process.env.MINIO_USE_SSL ? 'https' : 'http'}://${process.env.MINIO_ENDPOINT}:${process.env.MINIO_PORT}`,
  111. bucket: data.bucket
  112. };
  113. return c.json(response, 200);
  114. } catch (error) {
  115. const message = error instanceof Error ? error.message : '完成分片上传失败';
  116. return c.json({ code: 500, message }, 500);
  117. }
  118. });
  119. export default app;