interceptors.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import type { CosyError } from './types'
  2. import { http, useAxios } from '@uozi-admin/request'
  3. import JSEncrypt from 'jsencrypt'
  4. import { storeToRefs } from 'pinia'
  5. import use2FAModal from '@/components/TwoFA/use2FAModal'
  6. import { useNProgress } from '@/lib/nprogress/nprogress'
  7. import { useSettingsStore, useUserStore } from '@/pinia'
  8. import router from '@/routes'
  9. import { handleApiError, useMessageDedupe } from './error'
  10. const { setRequestInterceptor, setResponseInterceptor } = useAxios()
  11. const nprogress = useNProgress()
  12. const dedupe = useMessageDedupe()
  13. // Helper function for encrypting JSON data
  14. // eslint-disable-next-line ts/no-explicit-any
  15. async function encryptJsonData(data: any): Promise<string> {
  16. const cryptoParams = await http.get('/crypto/public_key')
  17. const { public_key } = await cryptoParams
  18. // Encrypt data with RSA public key
  19. const encrypt = new JSEncrypt()
  20. encrypt.setPublicKey(public_key)
  21. return JSON.stringify({
  22. encrypted_params: encrypt.encrypt(JSON.stringify(data)),
  23. })
  24. }
  25. // Helper function for handling encrypted form data
  26. async function handleEncryptedFormData(formData: FormData): Promise<FormData> {
  27. const cryptoParams = await http.get('/crypto/public_key')
  28. const { public_key } = await cryptoParams
  29. // Extract form parameters that are not files
  30. // eslint-disable-next-line ts/no-explicit-any
  31. const formParams: Record<string, any> = {}
  32. const newFormData = new FormData()
  33. // Copy all files to new FormData
  34. for (const [key, value] of formData.entries()) {
  35. // Check if value is a File or Blob
  36. // eslint-disable-next-line ts/no-explicit-any
  37. if (typeof value !== 'string' && ((value as any) instanceof File || (value as any) instanceof Blob)) {
  38. newFormData.append(key, value)
  39. }
  40. else {
  41. // Collect non-file fields to encrypt
  42. formParams[key] = value
  43. }
  44. }
  45. // Encrypt the form parameters
  46. const encrypt = new JSEncrypt()
  47. encrypt.setPublicKey(public_key)
  48. // Add encrypted params to form data
  49. const encryptedData = encrypt.encrypt(JSON.stringify(formParams))
  50. if (encryptedData) {
  51. newFormData.append('encrypted_params', encryptedData)
  52. }
  53. return newFormData
  54. }
  55. // Setup request interceptor
  56. export function setupRequestInterceptor() {
  57. // Setup stores and refs
  58. const user = useUserStore()
  59. const settings = useSettingsStore()
  60. const { token, secureSessionId } = storeToRefs(user)
  61. setRequestInterceptor(
  62. async config => {
  63. nprogress.start()
  64. if (token.value) {
  65. config.headers.Authorization = token.value
  66. }
  67. if (settings.environment.id) {
  68. config.headers['X-Node-ID'] = settings.environment.id
  69. }
  70. if (secureSessionId.value) {
  71. config.headers['X-Secure-Session-ID'] = secureSessionId.value
  72. }
  73. // Handle JSON encryption
  74. if (config.headers?.['Content-Type'] !== 'multipart/form-data;charset=UTF-8') {
  75. config.headers['Content-Type'] = 'application/json'
  76. if (config.crypto) {
  77. config.data = await encryptJsonData(config.data)
  78. }
  79. }
  80. // Handle form data with encryption
  81. else if (config.crypto && config.data instanceof FormData) {
  82. config.data = await handleEncryptedFormData(config.data)
  83. }
  84. return config
  85. },
  86. err => {
  87. return Promise.reject(err)
  88. },
  89. )
  90. }
  91. // Setup response interceptor
  92. export function setupResponseInterceptor() {
  93. setResponseInterceptor(
  94. response => {
  95. nprogress.done()
  96. // Check if full response is requested in config
  97. if (response?.config?.returnFullResponse) {
  98. return Promise.resolve(response)
  99. }
  100. return Promise.resolve(response.data)
  101. },
  102. async error => {
  103. // Setup stores and refs
  104. const user = useUserStore()
  105. const { secureSessionId } = storeToRefs(user)
  106. nprogress.done()
  107. const otpModal = use2FAModal()
  108. // Handle authentication errors
  109. if (error?.response) {
  110. switch (error.response.status) {
  111. case 401:
  112. secureSessionId.value = ''
  113. await otpModal.open()
  114. break
  115. case 403:
  116. user.logout()
  117. await router.push('/login')
  118. return
  119. }
  120. }
  121. // Handle JSON error that comes back as Blob for blob request type
  122. if (error?.response?.data instanceof Blob && error?.response?.data?.type === 'application/json') {
  123. try {
  124. const text = await error.response.data.text()
  125. error.response.data = JSON.parse(text)
  126. }
  127. catch (e) {
  128. // If parsing fails, we'll continue with the original error.response.data
  129. console.error('Failed to parse blob error response as JSON', e)
  130. }
  131. }
  132. const err = error.response?.data as CosyError
  133. await handleApiError(err, dedupe)
  134. return Promise.reject(error.response?.data)
  135. },
  136. )
  137. }
  138. export function setupInterceptors() {
  139. setupRequestInterceptor()
  140. setupResponseInterceptor()
  141. }