api.ts 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  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 err = {
  29. info: '',
  30. request,
  31. body,
  32. }
  33. if (Object.prototype.hasOwnProperty.call(data, 'code') && Object.prototype.hasOwnProperty.call(data, 'error')) {
  34. err.info = `${data.error} (${data.code})`
  35. }
  36. else if (Object.prototype.hasOwnProperty.call(data, 'detail')) {
  37. const details = data.detail[0]
  38. err.info = `${details.type} ${details.loc.join(', ')} (${details.msg})`
  39. }
  40. else {
  41. err.info = data
  42. }
  43. this.log.error(err)
  44. $snackbar.add({
  45. type: 'error',
  46. text: 'Возникла ошибка на сервере. Не переживайте, мы уже знаем о проблеме 👌'
  47. })
  48. }
  49. private instance = $fetch.create({
  50. baseURL: this.API_URL,
  51. onRequest: (ctx) => {
  52. if (!this.user.data.access_token && ctx.request !== '/login')
  53. navigateTo('/auth')
  54. },
  55. onResponse: (ctx) => {
  56. const statusCode = ctx.response.status
  57. if (statusCode > 300 && statusCode !== 400 && statusCode !== 401) {
  58. this.throwError(ctx.request.toString(), ctx.response._data, ctx.options.body)
  59. }
  60. },
  61. onResponseError: async (ctx) => {
  62. const statusCode = ctx.response.status
  63. if (statusCode === 401) {
  64. const newToken: TUserData = await this.get('/user/refresh', {
  65. access_token: ctx.request.toString().split('?')[1].split('=')[1],
  66. })
  67. this.user.data.access_token = newToken.access_token
  68. this.log.log('Access token обновлен:', this.user.data.access_token)
  69. return this.instance(ctx.request, {
  70. method: ctx.request.method,
  71. body: ctx.request.body,
  72. params: ctx.request.params,
  73. })
  74. }
  75. else if (statusCode === 400 && ctx.request !== `${this.API_URL}/user/login`) {
  76. this.user.logout()
  77. $snackbar.add({
  78. type: 'error',
  79. text: 'Ваша сессия устарела. Войдите заново.'
  80. })
  81. }
  82. else if (statusCode !== 400) {
  83. this.throwError(ctx.request.toString(), ctx.response._data, ctx.options.body)
  84. }
  85. },
  86. })
  87. get = <T>(url: string, params?: object): Promise<T> => {
  88. return this.instance(url, {
  89. method: 'GET',
  90. params: {
  91. access_token: this.user.data.access_token,
  92. ...params,
  93. },
  94. })
  95. }
  96. post = <T>(url: string, body?: object, params?: object): Promise<T> => {
  97. return this.instance(url, {
  98. method: 'POST',
  99. body,
  100. params: {
  101. access_token: this.user.data.access_token,
  102. ...params,
  103. },
  104. })
  105. }
  106. put = <T>(url: string, body?: object, params?: object): Promise<T> => {
  107. return this.instance(url, {
  108. method: 'PUT',
  109. body,
  110. params: {
  111. access_token: this.user.data.access_token,
  112. ...params,
  113. },
  114. })
  115. }
  116. delete = <T>(url: string, body?: object, params?: object): Promise<T> => {
  117. return this.instance(url, {
  118. method: 'DELETE',
  119. body,
  120. params: {
  121. access_token: this.user.data.access_token,
  122. ...params,
  123. },
  124. })
  125. }
  126. }