api.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { $fetch } from 'ofetch'
  2. import { useLogger } from '@/composables/useLogger'
  3. import { useUser } from '~/store/useUser'
  4. interface TUserData {
  5. user_id: number
  6. access_token: string
  7. }
  8. interface Detail {
  9. type: string
  10. loc: string[]
  11. msg: string
  12. }
  13. interface TException {
  14. error?: string
  15. code?: number
  16. detail?: Detail[]
  17. }
  18. export class API {
  19. private log
  20. private module: string = 'API'
  21. private API_URL: string = useRuntimeConfig().public.API_URL
  22. private user = useUser()
  23. constructor(module: string) {
  24. this.module = module
  25. this.log = useLogger(this.module)
  26. }
  27. private throwError(request: string, data: TException, body?: unknown) {
  28. const { $snackbar } = useNuxtApp()
  29. const err = {
  30. info: '',
  31. request,
  32. body,
  33. }
  34. if (Object.prototype.hasOwnProperty.call(data, 'code') && Object.prototype.hasOwnProperty.call(data, 'error')) {
  35. err.info = `${data.error} (${data.code})`
  36. }
  37. else if (Object.prototype.hasOwnProperty.call(data, 'detail')) {
  38. const details = data.detail[0]
  39. err.info = `${details.type} ${details.loc.join(', ')} (${details.msg})`
  40. }
  41. else {
  42. err.info = data
  43. }
  44. this.log.error(err)
  45. $snackbar.add({
  46. type: 'error',
  47. text: 'Возникла ошибка на сервере. Не переживайте, мы уже знаем о проблеме 👌'
  48. })
  49. }
  50. private instance = $fetch.create({
  51. baseURL: this.API_URL,
  52. onRequest: (ctx) => {
  53. if (!this.user.data.access_token && ctx.request !== '/login')
  54. navigateTo('/auth')
  55. },
  56. onResponse: (ctx) => {
  57. const statusCode = ctx.response.status
  58. if (statusCode > 300 && statusCode !== 400 && statusCode !== 401) {
  59. this.throwError(ctx.request.toString(), ctx.response._data, ctx.options.body)
  60. }
  61. },
  62. onResponseError: async (ctx) => {
  63. const statusCode = ctx.response.status
  64. if (statusCode === 401) {
  65. const newToken: TUserData = await this.get('/user/refresh', {
  66. access_token: ctx.request.toString().split('?')[1].split('=')[1],
  67. })
  68. this.user.data.access_token = newToken.access_token
  69. this.log.log('Access token обновлен:', this.user.data.access_token)
  70. return this.instance(ctx.request, {
  71. method: ctx.request.method,
  72. body: ctx.request.body,
  73. params: ctx.request.params,
  74. })
  75. }
  76. else if (statusCode === 400 && ctx.request !== `${this.API_URL}/user/login`) {
  77. this.user.logout()
  78. $snackbar.add({
  79. type: 'error',
  80. text: 'Ваша сессия устарела. Войдите заново.'
  81. })
  82. }
  83. else if (statusCode !== 400) {
  84. this.throwError(ctx.request.toString(), ctx.response._data, ctx.options.body)
  85. }
  86. },
  87. })
  88. get = <T>(url: string, params?: object): Promise<T> => {
  89. return this.instance(url, {
  90. method: 'GET',
  91. params: {
  92. access_token: this.user.data.access_token,
  93. ...params,
  94. },
  95. })
  96. }
  97. post = <T>(url: string, body?: object, params?: object): Promise<T> => {
  98. return this.instance(url, {
  99. method: 'POST',
  100. body,
  101. params: {
  102. access_token: this.user.data.access_token,
  103. ...params,
  104. },
  105. })
  106. }
  107. put = <T>(url: string, body?: object, params?: object): Promise<T> => {
  108. return this.instance(url, {
  109. method: 'PUT',
  110. body,
  111. params: {
  112. access_token: this.user.data.access_token,
  113. ...params,
  114. },
  115. })
  116. }
  117. delete = <T>(url: string, body?: object, params?: object): Promise<T> => {
  118. return this.instance(url, {
  119. method: 'DELETE',
  120. body,
  121. params: {
  122. access_token: this.user.data.access_token,
  123. ...params,
  124. },
  125. })
  126. }
  127. }